comparison mercurial/localrepo.py @ 2439:e8c4f3d3df8c

extend network protocol to stop clients from locking servers now all repositories have capabilities slot, tuple with list of names. if 'unbundle' capability present, repo supports push where client does not need to lock server. repository classes that have unbundle capability also have unbundle method. implemented for ssh now, will be base for push over http. unbundle protocol acts this way. server tells client what heads it has during normal negotiate step. client starts unbundle by repeat server's heads back to it. if server has new heads, abort immediately. otherwise, transfer changes to server. once data transferred, server locks and checks heads again. if heads same, changes can be added. else someone else added heads, and server aborts. if client wants to force server to add heads, sends special heads list of 'force'.
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Thu, 15 Jun 2006 16:37:23 -0700
parents 092039246d73
children b77a2ef61b81
comparison
equal deleted inserted replaced
2436:f910b91dd912 2439:e8c4f3d3df8c
13 demandload(globals(), "appendfile changegroup") 13 demandload(globals(), "appendfile changegroup")
14 demandload(globals(), "re lock transaction tempfile stat mdiff errno ui") 14 demandload(globals(), "re lock transaction tempfile stat mdiff errno ui")
15 demandload(globals(), "revlog") 15 demandload(globals(), "revlog")
16 16
17 class localrepository(object): 17 class localrepository(object):
18 capabilities = ()
19
18 def __del__(self): 20 def __del__(self):
19 self.transhandle = None 21 self.transhandle = None
20 def __init__(self, parentui, path=None, create=0): 22 def __init__(self, parentui, path=None, create=0):
21 if not path: 23 if not path:
22 p = os.getcwd() 24 p = os.getcwd()
1103 else: 1105 else:
1104 cg = remote.changegroupsubset(fetch, heads, 'pull') 1106 cg = remote.changegroupsubset(fetch, heads, 'pull')
1105 return self.addchangegroup(cg, 'pull') 1107 return self.addchangegroup(cg, 'pull')
1106 1108
1107 def push(self, remote, force=False, revs=None): 1109 def push(self, remote, force=False, revs=None):
1108 lock = remote.lock() 1110 # there are two ways to push to remote repo:
1109 1111 #
1112 # addchangegroup assumes local user can lock remote
1113 # repo (local filesystem, old ssh servers).
1114 #
1115 # unbundle assumes local user cannot lock remote repo (new ssh
1116 # servers, http servers).
1117
1118 if 'unbundle' in remote.capabilities:
1119 self.push_unbundle(remote, force, revs)
1120 else:
1121 self.push_addchangegroup(remote, force, revs)
1122
1123 def prepush(self, remote, force, revs):
1110 base = {} 1124 base = {}
1111 remote_heads = remote.heads() 1125 remote_heads = remote.heads()
1112 inc = self.findincoming(remote, base, remote_heads, force=force) 1126 inc = self.findincoming(remote, base, remote_heads, force=force)
1113 if not force and inc: 1127 if not force and inc:
1114 self.ui.warn(_("abort: unsynced remote changes!\n")) 1128 self.ui.warn(_("abort: unsynced remote changes!\n"))
1115 self.ui.status(_("(did you forget to sync?" 1129 self.ui.status(_("(did you forget to sync?"
1116 " use push -f to force)\n")) 1130 " use push -f to force)\n"))
1117 return 1 1131 return None, 1
1118 1132
1119 update, updated_heads = self.findoutgoing(remote, base, remote_heads) 1133 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1120 if revs is not None: 1134 if revs is not None:
1121 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs) 1135 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1122 else: 1136 else:
1123 bases, heads = update, self.changelog.heads() 1137 bases, heads = update, self.changelog.heads()
1124 1138
1125 if not bases: 1139 if not bases:
1126 self.ui.status(_("no changes found\n")) 1140 self.ui.status(_("no changes found\n"))
1127 return 1 1141 return None, 1
1128 elif not force: 1142 elif not force:
1129 # FIXME we don't properly detect creation of new heads 1143 # FIXME we don't properly detect creation of new heads
1130 # in the push -r case, assume the user knows what he's doing 1144 # in the push -r case, assume the user knows what he's doing
1131 if not revs and len(remote_heads) < len(heads) \ 1145 if not revs and len(remote_heads) < len(heads) \
1132 and remote_heads != [nullid]: 1146 and remote_heads != [nullid]:
1133 self.ui.warn(_("abort: push creates new remote branches!\n")) 1147 self.ui.warn(_("abort: push creates new remote branches!\n"))
1134 self.ui.status(_("(did you forget to merge?" 1148 self.ui.status(_("(did you forget to merge?"
1135 " use push -f to force)\n")) 1149 " use push -f to force)\n"))
1136 return 1 1150 return None, 1
1137 1151
1138 if revs is None: 1152 if revs is None:
1139 cg = self.changegroup(update, 'push') 1153 cg = self.changegroup(update, 'push')
1140 else: 1154 else:
1141 cg = self.changegroupsubset(update, revs, 'push') 1155 cg = self.changegroupsubset(update, revs, 'push')
1142 return remote.addchangegroup(cg, 'push') 1156 return cg, remote_heads
1157
1158 def push_addchangegroup(self, remote, force, revs):
1159 lock = remote.lock()
1160
1161 ret = self.prepush(remote, force, revs)
1162 if ret[0] is not None:
1163 cg, remote_heads = ret
1164 return remote.addchangegroup(cg, 'push')
1165 return ret[1]
1166
1167 def push_unbundle(self, remote, force, revs):
1168 # local repo finds heads on server, finds out what revs it
1169 # must push. once revs transferred, if server finds it has
1170 # different heads (someone else won commit/push race), server
1171 # aborts.
1172
1173 ret = self.prepush(remote, force, revs)
1174 if ret[0] is not None:
1175 cg, remote_heads = ret
1176 if force: remote_heads = ['force']
1177 return remote.unbundle(cg, remote_heads, 'push')
1178 return ret[1]
1143 1179
1144 def changegroupsubset(self, bases, heads, source): 1180 def changegroupsubset(self, bases, heads, source):
1145 """This function generates a changegroup consisting of all the nodes 1181 """This function generates a changegroup consisting of all the nodes
1146 that are descendents of any of the bases, and ancestors of any of 1182 that are descendents of any of the bases, and ancestors of any of
1147 the heads. 1183 the heads.