##// END OF EJS Templates
push: move `remote` argument in the push object...
Pierre-Yves David -
r20348:d64c904d default
parent child Browse files
Show More
@@ -1,249 +1,253 b''
1 # exchange.py - utily to exchange data between repo.
1 # exchange.py - utily to exchange data between repo.
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
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
13
14
14
15 class pushoperation(object):
15 class pushoperation(object):
16 """A object that represent a single push operation
16 """A object that represent a single push operation
17
17
18 It purpose is to carry push related state and very common operation.
18 It purpose is to carry push related state and very common operation.
19
19
20 A new should be created at the begining of each push and discarded
20 A new should be created at the begining of each push and discarded
21 afterward.
21 afterward.
22 """
22 """
23
23
24 def __init__(self, repo):
24 def __init__(self, repo, remote):
25 # repo we push from
25 # repo we push from
26 self.repo = repo
26 self.repo = repo
27 self.ui = repo.ui
27 self.ui = repo.ui
28 # repo we push to
29 self.remote = remote
28
30
29 def push(repo, remote, force=False, revs=None, newbranch=False):
31 def push(repo, remote, force=False, revs=None, newbranch=False):
30 '''Push outgoing changesets (limited by revs) from a local
32 '''Push outgoing changesets (limited by revs) from a local
31 repository to remote. Return an integer:
33 repository to remote. Return an integer:
32 - None means nothing to push
34 - None means nothing to push
33 - 0 means HTTP error
35 - 0 means HTTP error
34 - 1 means we pushed and remote head count is unchanged *or*
36 - 1 means we pushed and remote head count is unchanged *or*
35 we have outgoing changesets but refused to push
37 we have outgoing changesets but refused to push
36 - other values as described by addchangegroup()
38 - other values as described by addchangegroup()
37 '''
39 '''
38 pushop = pushoperation(repo)
40 pushop = pushoperation(repo, remote)
39 if remote.local():
41 if pushop.remote.local():
40 missing = set(pushop.repo.requirements) - remote.local().supported
42 missing = (set(pushop.repo.requirements)
43 - pushop.remote.local().supported)
41 if missing:
44 if missing:
42 msg = _("required features are not"
45 msg = _("required features are not"
43 " supported in the destination:"
46 " supported in the destination:"
44 " %s") % (', '.join(sorted(missing)))
47 " %s") % (', '.join(sorted(missing)))
45 raise util.Abort(msg)
48 raise util.Abort(msg)
46
49
47 # there are two ways to push to remote repo:
50 # there are two ways to push to remote repo:
48 #
51 #
49 # addchangegroup assumes local user can lock remote
52 # addchangegroup assumes local user can lock remote
50 # repo (local filesystem, old ssh servers).
53 # repo (local filesystem, old ssh servers).
51 #
54 #
52 # unbundle assumes local user cannot lock remote repo (new ssh
55 # unbundle assumes local user cannot lock remote repo (new ssh
53 # servers, http servers).
56 # servers, http servers).
54
57
55 if not remote.canpush():
58 if not pushop.remote.canpush():
56 raise util.Abort(_("destination does not support push"))
59 raise util.Abort(_("destination does not support push"))
57 unfi = pushop.repo.unfiltered()
60 unfi = pushop.repo.unfiltered()
58 def localphasemove(nodes, phase=phases.public):
61 def localphasemove(nodes, phase=phases.public):
59 """move <nodes> to <phase> in the local source repo"""
62 """move <nodes> to <phase> in the local source repo"""
60 if locallock is not None:
63 if locallock is not None:
61 phases.advanceboundary(pushop.repo, phase, nodes)
64 phases.advanceboundary(pushop.repo, phase, nodes)
62 else:
65 else:
63 # repo is not locked, do not change any phases!
66 # repo is not locked, do not change any phases!
64 # Informs the user that phases should have been moved when
67 # Informs the user that phases should have been moved when
65 # applicable.
68 # applicable.
66 actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
69 actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
67 phasestr = phases.phasenames[phase]
70 phasestr = phases.phasenames[phase]
68 if actualmoves:
71 if actualmoves:
69 pushop.ui.status(_('cannot lock source repo, skipping '
72 pushop.ui.status(_('cannot lock source repo, skipping '
70 'local %s phase update\n') % phasestr)
73 'local %s phase update\n') % phasestr)
71 # get local lock as we might write phase data
74 # get local lock as we might write phase data
72 locallock = None
75 locallock = None
73 try:
76 try:
74 locallock = pushop.repo.lock()
77 locallock = pushop.repo.lock()
75 except IOError, err:
78 except IOError, err:
76 if err.errno != errno.EACCES:
79 if err.errno != errno.EACCES:
77 raise
80 raise
78 # source repo cannot be locked.
81 # source repo cannot be locked.
79 # We do not abort the push, but just disable the local phase
82 # We do not abort the push, but just disable the local phase
80 # synchronisation.
83 # synchronisation.
81 msg = 'cannot lock source repository: %s\n' % err
84 msg = 'cannot lock source repository: %s\n' % err
82 pushop.ui.debug(msg)
85 pushop.ui.debug(msg)
83 try:
86 try:
84 pushop.repo.checkpush(force, revs)
87 pushop.repo.checkpush(force, revs)
85 lock = None
88 lock = None
86 unbundle = remote.capable('unbundle')
89 unbundle = pushop.remote.capable('unbundle')
87 if not unbundle:
90 if not unbundle:
88 lock = remote.lock()
91 lock = pushop.remote.lock()
89 try:
92 try:
90 # discovery
93 # discovery
91 fci = discovery.findcommonincoming
94 fci = discovery.findcommonincoming
92 commoninc = fci(unfi, remote, force=force)
95 commoninc = fci(unfi, pushop.remote, force=force)
93 common, inc, remoteheads = commoninc
96 common, inc, remoteheads = commoninc
94 fco = discovery.findcommonoutgoing
97 fco = discovery.findcommonoutgoing
95 outgoing = fco(unfi, remote, onlyheads=revs,
98 outgoing = fco(unfi, pushop.remote, onlyheads=revs,
96 commoninc=commoninc, force=force)
99 commoninc=commoninc, force=force)
97
100
98
101
99 if not outgoing.missing:
102 if not outgoing.missing:
100 # nothing to push
103 # nothing to push
101 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
104 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
102 ret = None
105 ret = None
103 else:
106 else:
104 # something to push
107 # something to push
105 if not force:
108 if not force:
106 # if repo.obsstore == False --> no obsolete
109 # if repo.obsstore == False --> no obsolete
107 # then, save the iteration
110 # then, save the iteration
108 if unfi.obsstore:
111 if unfi.obsstore:
109 # this message are here for 80 char limit reason
112 # this message are here for 80 char limit reason
110 mso = _("push includes obsolete changeset: %s!")
113 mso = _("push includes obsolete changeset: %s!")
111 mst = "push includes %s changeset: %s!"
114 mst = "push includes %s changeset: %s!"
112 # plain versions for i18n tool to detect them
115 # plain versions for i18n tool to detect them
113 _("push includes unstable changeset: %s!")
116 _("push includes unstable changeset: %s!")
114 _("push includes bumped changeset: %s!")
117 _("push includes bumped changeset: %s!")
115 _("push includes divergent changeset: %s!")
118 _("push includes divergent changeset: %s!")
116 # If we are to push if there is at least one
119 # If we are to push if there is at least one
117 # obsolete or unstable changeset in missing, at
120 # obsolete or unstable changeset in missing, at
118 # least one of the missinghead will be obsolete or
121 # least one of the missinghead will be obsolete or
119 # unstable. So checking heads only is ok
122 # unstable. So checking heads only is ok
120 for node in outgoing.missingheads:
123 for node in outgoing.missingheads:
121 ctx = unfi[node]
124 ctx = unfi[node]
122 if ctx.obsolete():
125 if ctx.obsolete():
123 raise util.Abort(mso % ctx)
126 raise util.Abort(mso % ctx)
124 elif ctx.troubled():
127 elif ctx.troubled():
125 raise util.Abort(_(mst)
128 raise util.Abort(_(mst)
126 % (ctx.troubles()[0],
129 % (ctx.troubles()[0],
127 ctx))
130 ctx))
128 newbm = pushop.ui.configlist('bookmarks', 'pushing')
131 newbm = pushop.ui.configlist('bookmarks', 'pushing')
129 discovery.checkheads(unfi, remote, outgoing,
132 discovery.checkheads(unfi, pushop.remote, outgoing,
130 remoteheads, newbranch,
133 remoteheads, newbranch,
131 bool(inc), newbm)
134 bool(inc), newbm)
132
135
133 # TODO: get bundlecaps from remote
136 # TODO: get bundlecaps from remote
134 bundlecaps = None
137 bundlecaps = None
135 # create a changegroup from local
138 # create a changegroup from local
136 if revs is None and not (outgoing.excluded
139 if revs is None and not (outgoing.excluded
137 or pushop.repo.changelog.filteredrevs):
140 or pushop.repo.changelog.filteredrevs):
138 # push everything,
141 # push everything,
139 # use the fast path, no race possible on push
142 # use the fast path, no race possible on push
140 bundler = changegroup.bundle10(pushop.repo, bundlecaps)
143 bundler = changegroup.bundle10(pushop.repo, bundlecaps)
141 cg = pushop.repo._changegroupsubset(outgoing,
144 cg = pushop.repo._changegroupsubset(outgoing,
142 bundler,
145 bundler,
143 'push',
146 'push',
144 fastpath=True)
147 fastpath=True)
145 else:
148 else:
146 cg = pushop.repo.getlocalbundle('push', outgoing,
149 cg = pushop.repo.getlocalbundle('push', outgoing,
147 bundlecaps)
150 bundlecaps)
148
151
149 # apply changegroup to remote
152 # apply changegroup to remote
150 if unbundle:
153 if unbundle:
151 # local repo finds heads on server, finds out what
154 # local repo finds heads on server, finds out what
152 # revs it must push. once revs transferred, if server
155 # revs it must push. once revs transferred, if server
153 # finds it has different heads (someone else won
156 # finds it has different heads (someone else won
154 # commit/push race), server aborts.
157 # commit/push race), server aborts.
155 if force:
158 if force:
156 remoteheads = ['force']
159 remoteheads = ['force']
157 # ssh: return remote's addchangegroup()
160 # ssh: return remote's addchangegroup()
158 # http: return remote's addchangegroup() or 0 for error
161 # http: return remote's addchangegroup() or 0 for error
159 ret = remote.unbundle(cg, remoteheads, 'push')
162 ret = pushop.remote.unbundle(cg, remoteheads, 'push')
160 else:
163 else:
161 # we return an integer indicating remote head count
164 # we return an integer indicating remote head count
162 # change
165 # change
163 ret = remote.addchangegroup(cg, 'push', pushop.repo.url())
166 ret = pushop.remote.addchangegroup(cg, 'push',
167 pushop.repo.url())
164
168
165 if ret:
169 if ret:
166 # push succeed, synchronize target of the push
170 # push succeed, synchronize target of the push
167 cheads = outgoing.missingheads
171 cheads = outgoing.missingheads
168 elif revs is None:
172 elif revs is None:
169 # All out push fails. synchronize all common
173 # All out push fails. synchronize all common
170 cheads = outgoing.commonheads
174 cheads = outgoing.commonheads
171 else:
175 else:
172 # I want cheads = heads(::missingheads and ::commonheads)
176 # I want cheads = heads(::missingheads and ::commonheads)
173 # (missingheads is revs with secret changeset filtered out)
177 # (missingheads is revs with secret changeset filtered out)
174 #
178 #
175 # This can be expressed as:
179 # This can be expressed as:
176 # cheads = ( (missingheads and ::commonheads)
180 # cheads = ( (missingheads and ::commonheads)
177 # + (commonheads and ::missingheads))"
181 # + (commonheads and ::missingheads))"
178 # )
182 # )
179 #
183 #
180 # while trying to push we already computed the following:
184 # while trying to push we already computed the following:
181 # common = (::commonheads)
185 # common = (::commonheads)
182 # missing = ((commonheads::missingheads) - commonheads)
186 # missing = ((commonheads::missingheads) - commonheads)
183 #
187 #
184 # We can pick:
188 # We can pick:
185 # * missingheads part of common (::commonheads)
189 # * missingheads part of common (::commonheads)
186 common = set(outgoing.common)
190 common = set(outgoing.common)
187 nm = pushop.repo.changelog.nodemap
191 nm = pushop.repo.changelog.nodemap
188 cheads = [node for node in revs if nm[node] in common]
192 cheads = [node for node in revs if nm[node] in common]
189 # and
193 # and
190 # * commonheads parents on missing
194 # * commonheads parents on missing
191 revset = unfi.set('%ln and parents(roots(%ln))',
195 revset = unfi.set('%ln and parents(roots(%ln))',
192 outgoing.commonheads,
196 outgoing.commonheads,
193 outgoing.missing)
197 outgoing.missing)
194 cheads.extend(c.node() for c in revset)
198 cheads.extend(c.node() for c in revset)
195 # even when we don't push, exchanging phase data is useful
199 # even when we don't push, exchanging phase data is useful
196 remotephases = remote.listkeys('phases')
200 remotephases = pushop.remote.listkeys('phases')
197 if (pushop.ui.configbool('ui', '_usedassubrepo', False)
201 if (pushop.ui.configbool('ui', '_usedassubrepo', False)
198 and remotephases # server supports phases
202 and remotephases # server supports phases
199 and ret is None # nothing was pushed
203 and ret is None # nothing was pushed
200 and remotephases.get('publishing', False)):
204 and remotephases.get('publishing', False)):
201 # When:
205 # When:
202 # - this is a subrepo push
206 # - this is a subrepo push
203 # - and remote support phase
207 # - and remote support phase
204 # - and no changeset was pushed
208 # - and no changeset was pushed
205 # - and remote is publishing
209 # - and remote is publishing
206 # We may be in issue 3871 case!
210 # We may be in issue 3871 case!
207 # We drop the possible phase synchronisation done by
211 # We drop the possible phase synchronisation done by
208 # courtesy to publish changesets possibly locally draft
212 # courtesy to publish changesets possibly locally draft
209 # on the remote.
213 # on the remote.
210 remotephases = {'publishing': 'True'}
214 remotephases = {'publishing': 'True'}
211 if not remotephases: # old server or public only repo
215 if not remotephases: # old server or public only repo
212 localphasemove(cheads)
216 localphasemove(cheads)
213 # don't push any phase data as there is nothing to push
217 # don't push any phase data as there is nothing to push
214 else:
218 else:
215 ana = phases.analyzeremotephases(pushop.repo, cheads,
219 ana = phases.analyzeremotephases(pushop.repo, cheads,
216 remotephases)
220 remotephases)
217 pheads, droots = ana
221 pheads, droots = ana
218 ### Apply remote phase on local
222 ### Apply remote phase on local
219 if remotephases.get('publishing', False):
223 if remotephases.get('publishing', False):
220 localphasemove(cheads)
224 localphasemove(cheads)
221 else: # publish = False
225 else: # publish = False
222 localphasemove(pheads)
226 localphasemove(pheads)
223 localphasemove(cheads, phases.draft)
227 localphasemove(cheads, phases.draft)
224 ### Apply local phase on remote
228 ### Apply local phase on remote
225
229
226 # Get the list of all revs draft on remote by public here.
230 # Get the list of all revs draft on remote by public here.
227 # XXX Beware that revset break if droots is not strictly
231 # XXX Beware that revset break if droots is not strictly
228 # XXX root we may want to ensure it is but it is costly
232 # XXX root we may want to ensure it is but it is costly
229 outdated = unfi.set('heads((%ln::%ln) and public())',
233 outdated = unfi.set('heads((%ln::%ln) and public())',
230 droots, cheads)
234 droots, cheads)
231 for newremotehead in outdated:
235 for newremotehead in outdated:
232 r = remote.pushkey('phases',
236 r = pushop.remote.pushkey('phases',
233 newremotehead.hex(),
237 newremotehead.hex(),
234 str(phases.draft),
238 str(phases.draft),
235 str(phases.public))
239 str(phases.public))
236 if not r:
240 if not r:
237 pushop.ui.warn(_('updating %s to public failed!\n')
241 pushop.ui.warn(_('updating %s to public failed!\n')
238 % newremotehead)
242 % newremotehead)
239 pushop.ui.debug('try to push obsolete markers to remote\n')
243 pushop.ui.debug('try to push obsolete markers to remote\n')
240 obsolete.syncpush(pushop.repo, remote)
244 obsolete.syncpush(pushop.repo, pushop.remote)
241 finally:
245 finally:
242 if lock is not None:
246 if lock is not None:
243 lock.release()
247 lock.release()
244 finally:
248 finally:
245 if locallock is not None:
249 if locallock is not None:
246 locallock.release()
250 locallock.release()
247
251
248 bookmarks.updateremote(pushop.ui, unfi, remote, revs)
252 bookmarks.updateremote(pushop.ui, unfi, pushop.remote, revs)
249 return ret
253 return ret
General Comments 0
You need to be logged in to leave comments. Login now