Mercurial > public > mercurial-scm > hg
comparison mercurial/exchange.py @ 20346:42df1fe32552
push: introduce a pushoperation object
This object will hold all data and state gathered through the push. This will
allow us to split the long function into multiple small one. Smaller function
will be easier to maintains and wrap. The idea is to blindly store all
information related to the push in this object so that each step and extension
can use them if necessary.
We start by putting the `repo` variable in the object. More migration in other
changeset.
author | Pierre-Yves David <pierre-yves.david@logilab.fr> |
---|---|
date | Thu, 30 Jan 2014 16:43:11 -0800 |
parents | 8567b4ea76ac |
children | 3ec5f833348e |
comparison
equal
deleted
inserted
replaced
20345:8567b4ea76ac | 20346:42df1fe32552 |
---|---|
8 from i18n import _ | 8 from i18n import _ |
9 from node import hex | 9 from node import hex |
10 import errno | 10 import errno |
11 import util, scmutil, changegroup | 11 import util, scmutil, changegroup |
12 import discovery, phases, obsolete, bookmarks | 12 import discovery, phases, obsolete, bookmarks |
13 | |
14 | |
15 class pushoperation(object): | |
16 """A object that represent a single push operation | |
17 | |
18 It purpose is to carry push related state and very common operation. | |
19 | |
20 A new should be created at the begining of each push and discarded | |
21 afterward. | |
22 """ | |
23 | |
24 def __init__(self, repo): | |
25 # repo we push from | |
26 self.repo = repo | |
13 | 27 |
14 def push(repo, remote, force=False, revs=None, newbranch=False): | 28 def push(repo, remote, force=False, revs=None, newbranch=False): |
15 '''Push outgoing changesets (limited by revs) from a local | 29 '''Push outgoing changesets (limited by revs) from a local |
16 repository to remote. Return an integer: | 30 repository to remote. Return an integer: |
17 - None means nothing to push | 31 - None means nothing to push |
18 - 0 means HTTP error | 32 - 0 means HTTP error |
19 - 1 means we pushed and remote head count is unchanged *or* | 33 - 1 means we pushed and remote head count is unchanged *or* |
20 we have outgoing changesets but refused to push | 34 we have outgoing changesets but refused to push |
21 - other values as described by addchangegroup() | 35 - other values as described by addchangegroup() |
22 ''' | 36 ''' |
37 pushop = pushoperation(repo) | |
23 if remote.local(): | 38 if remote.local(): |
24 missing = set(repo.requirements) - remote.local().supported | 39 missing = set(pushop.repo.requirements) - remote.local().supported |
25 if missing: | 40 if missing: |
26 msg = _("required features are not" | 41 msg = _("required features are not" |
27 " supported in the destination:" | 42 " supported in the destination:" |
28 " %s") % (', '.join(sorted(missing))) | 43 " %s") % (', '.join(sorted(missing))) |
29 raise util.Abort(msg) | 44 raise util.Abort(msg) |
36 # unbundle assumes local user cannot lock remote repo (new ssh | 51 # unbundle assumes local user cannot lock remote repo (new ssh |
37 # servers, http servers). | 52 # servers, http servers). |
38 | 53 |
39 if not remote.canpush(): | 54 if not remote.canpush(): |
40 raise util.Abort(_("destination does not support push")) | 55 raise util.Abort(_("destination does not support push")) |
41 unfi = repo.unfiltered() | 56 unfi = pushop.repo.unfiltered() |
42 def localphasemove(nodes, phase=phases.public): | 57 def localphasemove(nodes, phase=phases.public): |
43 """move <nodes> to <phase> in the local source repo""" | 58 """move <nodes> to <phase> in the local source repo""" |
44 if locallock is not None: | 59 if locallock is not None: |
45 phases.advanceboundary(repo, phase, nodes) | 60 phases.advanceboundary(pushop.repo, phase, nodes) |
46 else: | 61 else: |
47 # repo is not locked, do not change any phases! | 62 # repo is not locked, do not change any phases! |
48 # Informs the user that phases should have been moved when | 63 # Informs the user that phases should have been moved when |
49 # applicable. | 64 # applicable. |
50 actualmoves = [n for n in nodes if phase < repo[n].phase()] | 65 actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()] |
51 phasestr = phases.phasenames[phase] | 66 phasestr = phases.phasenames[phase] |
52 if actualmoves: | 67 if actualmoves: |
53 repo.ui.status(_('cannot lock source repo, skipping local' | 68 pushop.repo.ui.status(_('cannot lock source repo, skipping ' |
54 ' %s phase update\n') % phasestr) | 69 'local %s phase update\n') % phasestr) |
55 # get local lock as we might write phase data | 70 # get local lock as we might write phase data |
56 locallock = None | 71 locallock = None |
57 try: | 72 try: |
58 locallock = repo.lock() | 73 locallock = pushop.repo.lock() |
59 except IOError, err: | 74 except IOError, err: |
60 if err.errno != errno.EACCES: | 75 if err.errno != errno.EACCES: |
61 raise | 76 raise |
62 # source repo cannot be locked. | 77 # source repo cannot be locked. |
63 # We do not abort the push, but just disable the local phase | 78 # We do not abort the push, but just disable the local phase |
64 # synchronisation. | 79 # synchronisation. |
65 msg = 'cannot lock source repository: %s\n' % err | 80 msg = 'cannot lock source repository: %s\n' % err |
66 repo.ui.debug(msg) | 81 pushop.repo.ui.debug(msg) |
67 try: | 82 try: |
68 repo.checkpush(force, revs) | 83 pushop.repo.checkpush(force, revs) |
69 lock = None | 84 lock = None |
70 unbundle = remote.capable('unbundle') | 85 unbundle = remote.capable('unbundle') |
71 if not unbundle: | 86 if not unbundle: |
72 lock = remote.lock() | 87 lock = remote.lock() |
73 try: | 88 try: |
107 raise util.Abort(mso % ctx) | 122 raise util.Abort(mso % ctx) |
108 elif ctx.troubled(): | 123 elif ctx.troubled(): |
109 raise util.Abort(_(mst) | 124 raise util.Abort(_(mst) |
110 % (ctx.troubles()[0], | 125 % (ctx.troubles()[0], |
111 ctx)) | 126 ctx)) |
112 newbm = repo.ui.configlist('bookmarks', 'pushing') | 127 newbm = pushop.repo.ui.configlist('bookmarks', 'pushing') |
113 discovery.checkheads(unfi, remote, outgoing, | 128 discovery.checkheads(unfi, remote, outgoing, |
114 remoteheads, newbranch, | 129 remoteheads, newbranch, |
115 bool(inc), newbm) | 130 bool(inc), newbm) |
116 | 131 |
117 # TODO: get bundlecaps from remote | 132 # TODO: get bundlecaps from remote |
118 bundlecaps = None | 133 bundlecaps = None |
119 # create a changegroup from local | 134 # create a changegroup from local |
120 if revs is None and not (outgoing.excluded | 135 if revs is None and not (outgoing.excluded |
121 or repo.changelog.filteredrevs): | 136 or pushop.repo.changelog.filteredrevs): |
122 # push everything, | 137 # push everything, |
123 # use the fast path, no race possible on push | 138 # use the fast path, no race possible on push |
124 bundler = changegroup.bundle10(repo, bundlecaps) | 139 bundler = changegroup.bundle10(pushop.repo, bundlecaps) |
125 cg = repo._changegroupsubset(outgoing, | 140 cg = pushop.repo._changegroupsubset(outgoing, |
126 bundler, | 141 bundler, |
127 'push', | 142 'push', |
128 fastpath=True) | 143 fastpath=True) |
129 else: | 144 else: |
130 cg = repo.getlocalbundle('push', outgoing, bundlecaps) | 145 cg = pushop.repo.getlocalbundle('push', outgoing, |
146 bundlecaps) | |
131 | 147 |
132 # apply changegroup to remote | 148 # apply changegroup to remote |
133 if unbundle: | 149 if unbundle: |
134 # local repo finds heads on server, finds out what | 150 # local repo finds heads on server, finds out what |
135 # revs it must push. once revs transferred, if server | 151 # revs it must push. once revs transferred, if server |
141 # http: return remote's addchangegroup() or 0 for error | 157 # http: return remote's addchangegroup() or 0 for error |
142 ret = remote.unbundle(cg, remoteheads, 'push') | 158 ret = remote.unbundle(cg, remoteheads, 'push') |
143 else: | 159 else: |
144 # we return an integer indicating remote head count | 160 # we return an integer indicating remote head count |
145 # change | 161 # change |
146 ret = remote.addchangegroup(cg, 'push', repo.url()) | 162 ret = remote.addchangegroup(cg, 'push', pushop.repo.url()) |
147 | 163 |
148 if ret: | 164 if ret: |
149 # push succeed, synchronize target of the push | 165 # push succeed, synchronize target of the push |
150 cheads = outgoing.missingheads | 166 cheads = outgoing.missingheads |
151 elif revs is None: | 167 elif revs is None: |
165 # missing = ((commonheads::missingheads) - commonheads) | 181 # missing = ((commonheads::missingheads) - commonheads) |
166 # | 182 # |
167 # We can pick: | 183 # We can pick: |
168 # * missingheads part of common (::commonheads) | 184 # * missingheads part of common (::commonheads) |
169 common = set(outgoing.common) | 185 common = set(outgoing.common) |
170 nm = repo.changelog.nodemap | 186 nm = pushop.repo.changelog.nodemap |
171 cheads = [node for node in revs if nm[node] in common] | 187 cheads = [node for node in revs if nm[node] in common] |
172 # and | 188 # and |
173 # * commonheads parents on missing | 189 # * commonheads parents on missing |
174 revset = unfi.set('%ln and parents(roots(%ln))', | 190 revset = unfi.set('%ln and parents(roots(%ln))', |
175 outgoing.commonheads, | 191 outgoing.commonheads, |
176 outgoing.missing) | 192 outgoing.missing) |
177 cheads.extend(c.node() for c in revset) | 193 cheads.extend(c.node() for c in revset) |
178 # even when we don't push, exchanging phase data is useful | 194 # even when we don't push, exchanging phase data is useful |
179 remotephases = remote.listkeys('phases') | 195 remotephases = remote.listkeys('phases') |
180 if (repo.ui.configbool('ui', '_usedassubrepo', False) | 196 if (pushop.repo.ui.configbool('ui', '_usedassubrepo', False) |
181 and remotephases # server supports phases | 197 and remotephases # server supports phases |
182 and ret is None # nothing was pushed | 198 and ret is None # nothing was pushed |
183 and remotephases.get('publishing', False)): | 199 and remotephases.get('publishing', False)): |
184 # When: | 200 # When: |
185 # - this is a subrepo push | 201 # - this is a subrepo push |
193 remotephases = {'publishing': 'True'} | 209 remotephases = {'publishing': 'True'} |
194 if not remotephases: # old server or public only repo | 210 if not remotephases: # old server or public only repo |
195 localphasemove(cheads) | 211 localphasemove(cheads) |
196 # don't push any phase data as there is nothing to push | 212 # don't push any phase data as there is nothing to push |
197 else: | 213 else: |
198 ana = phases.analyzeremotephases(repo, cheads, remotephases) | 214 ana = phases.analyzeremotephases(pushop.repo, cheads, |
215 remotephases) | |
199 pheads, droots = ana | 216 pheads, droots = ana |
200 ### Apply remote phase on local | 217 ### Apply remote phase on local |
201 if remotephases.get('publishing', False): | 218 if remotephases.get('publishing', False): |
202 localphasemove(cheads) | 219 localphasemove(cheads) |
203 else: # publish = False | 220 else: # publish = False |
214 r = remote.pushkey('phases', | 231 r = remote.pushkey('phases', |
215 newremotehead.hex(), | 232 newremotehead.hex(), |
216 str(phases.draft), | 233 str(phases.draft), |
217 str(phases.public)) | 234 str(phases.public)) |
218 if not r: | 235 if not r: |
219 repo.ui.warn(_('updating %s to public failed!\n') | 236 pushop.repo.ui.warn(_('updating %s to public failed!\n') |
220 % newremotehead) | 237 % newremotehead) |
221 repo.ui.debug('try to push obsolete markers to remote\n') | 238 pushop.repo.ui.debug('try to push obsolete markers to remote\n') |
222 obsolete.syncpush(repo, remote) | 239 obsolete.syncpush(pushop.repo, remote) |
223 finally: | 240 finally: |
224 if lock is not None: | 241 if lock is not None: |
225 lock.release() | 242 lock.release() |
226 finally: | 243 finally: |
227 if locallock is not None: | 244 if locallock is not None: |
228 locallock.release() | 245 locallock.release() |
229 | 246 |
230 bookmarks.updateremote(repo.ui, unfi, remote, revs) | 247 bookmarks.updateremote(pushop.repo.ui, unfi, remote, revs) |
231 return ret | 248 return ret |