Mercurial > public > mercurial-scm > hg
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. |