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