##// END OF EJS Templates
bundle2: only use callable return as reply handler...
Pierre-Yves David -
r21941:dab31290 stable
parent child Browse files
Show More
@@ -1,811 +1,812
1 # exchange.py - utility to exchange data between repos.
1 # exchange.py - utility to exchange data between repos.
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, nullid
9 from node import hex, nullid
10 import errno, urllib
10 import errno, urllib
11 import util, scmutil, changegroup, base85, error
11 import util, scmutil, changegroup, base85, error
12 import discovery, phases, obsolete, bookmarks, bundle2, pushkey
12 import discovery, phases, obsolete, bookmarks, bundle2, pushkey
13
13
14 def readbundle(ui, fh, fname, vfs=None):
14 def readbundle(ui, fh, fname, vfs=None):
15 header = changegroup.readexactly(fh, 4)
15 header = changegroup.readexactly(fh, 4)
16
16
17 alg = None
17 alg = None
18 if not fname:
18 if not fname:
19 fname = "stream"
19 fname = "stream"
20 if not header.startswith('HG') and header.startswith('\0'):
20 if not header.startswith('HG') and header.startswith('\0'):
21 fh = changegroup.headerlessfixup(fh, header)
21 fh = changegroup.headerlessfixup(fh, header)
22 header = "HG10"
22 header = "HG10"
23 alg = 'UN'
23 alg = 'UN'
24 elif vfs:
24 elif vfs:
25 fname = vfs.join(fname)
25 fname = vfs.join(fname)
26
26
27 magic, version = header[0:2], header[2:4]
27 magic, version = header[0:2], header[2:4]
28
28
29 if magic != 'HG':
29 if magic != 'HG':
30 raise util.Abort(_('%s: not a Mercurial bundle') % fname)
30 raise util.Abort(_('%s: not a Mercurial bundle') % fname)
31 if version == '10':
31 if version == '10':
32 if alg is None:
32 if alg is None:
33 alg = changegroup.readexactly(fh, 2)
33 alg = changegroup.readexactly(fh, 2)
34 return changegroup.unbundle10(fh, alg)
34 return changegroup.unbundle10(fh, alg)
35 elif version == '2X':
35 elif version == '2X':
36 return bundle2.unbundle20(ui, fh, header=magic + version)
36 return bundle2.unbundle20(ui, fh, header=magic + version)
37 else:
37 else:
38 raise util.Abort(_('%s: unknown bundle version %s') % (fname, version))
38 raise util.Abort(_('%s: unknown bundle version %s') % (fname, version))
39
39
40
40
41 class pushoperation(object):
41 class pushoperation(object):
42 """A object that represent a single push operation
42 """A object that represent a single push operation
43
43
44 It purpose is to carry push related state and very common operation.
44 It purpose is to carry push related state and very common operation.
45
45
46 A new should be created at the beginning of each push and discarded
46 A new should be created at the beginning of each push and discarded
47 afterward.
47 afterward.
48 """
48 """
49
49
50 def __init__(self, repo, remote, force=False, revs=None, newbranch=False):
50 def __init__(self, repo, remote, force=False, revs=None, newbranch=False):
51 # repo we push from
51 # repo we push from
52 self.repo = repo
52 self.repo = repo
53 self.ui = repo.ui
53 self.ui = repo.ui
54 # repo we push to
54 # repo we push to
55 self.remote = remote
55 self.remote = remote
56 # force option provided
56 # force option provided
57 self.force = force
57 self.force = force
58 # revs to be pushed (None is "all")
58 # revs to be pushed (None is "all")
59 self.revs = revs
59 self.revs = revs
60 # allow push of new branch
60 # allow push of new branch
61 self.newbranch = newbranch
61 self.newbranch = newbranch
62 # did a local lock get acquired?
62 # did a local lock get acquired?
63 self.locallocked = None
63 self.locallocked = None
64 # step already performed
64 # step already performed
65 # (used to check what steps have been already performed through bundle2)
65 # (used to check what steps have been already performed through bundle2)
66 self.stepsdone = set()
66 self.stepsdone = set()
67 # Integer version of the push result
67 # Integer version of the push result
68 # - None means nothing to push
68 # - None means nothing to push
69 # - 0 means HTTP error
69 # - 0 means HTTP error
70 # - 1 means we pushed and remote head count is unchanged *or*
70 # - 1 means we pushed and remote head count is unchanged *or*
71 # we have outgoing changesets but refused to push
71 # we have outgoing changesets but refused to push
72 # - other values as described by addchangegroup()
72 # - other values as described by addchangegroup()
73 self.ret = None
73 self.ret = None
74 # discover.outgoing object (contains common and outgoing data)
74 # discover.outgoing object (contains common and outgoing data)
75 self.outgoing = None
75 self.outgoing = None
76 # all remote heads before the push
76 # all remote heads before the push
77 self.remoteheads = None
77 self.remoteheads = None
78 # testable as a boolean indicating if any nodes are missing locally.
78 # testable as a boolean indicating if any nodes are missing locally.
79 self.incoming = None
79 self.incoming = None
80 # set of all heads common after changeset bundle push
80 # set of all heads common after changeset bundle push
81 self.commonheads = None
81 self.commonheads = None
82
82
83 def push(repo, remote, force=False, revs=None, newbranch=False):
83 def push(repo, remote, force=False, revs=None, newbranch=False):
84 '''Push outgoing changesets (limited by revs) from a local
84 '''Push outgoing changesets (limited by revs) from a local
85 repository to remote. Return an integer:
85 repository to remote. Return an integer:
86 - None means nothing to push
86 - None means nothing to push
87 - 0 means HTTP error
87 - 0 means HTTP error
88 - 1 means we pushed and remote head count is unchanged *or*
88 - 1 means we pushed and remote head count is unchanged *or*
89 we have outgoing changesets but refused to push
89 we have outgoing changesets but refused to push
90 - other values as described by addchangegroup()
90 - other values as described by addchangegroup()
91 '''
91 '''
92 pushop = pushoperation(repo, remote, force, revs, newbranch)
92 pushop = pushoperation(repo, remote, force, revs, newbranch)
93 if pushop.remote.local():
93 if pushop.remote.local():
94 missing = (set(pushop.repo.requirements)
94 missing = (set(pushop.repo.requirements)
95 - pushop.remote.local().supported)
95 - pushop.remote.local().supported)
96 if missing:
96 if missing:
97 msg = _("required features are not"
97 msg = _("required features are not"
98 " supported in the destination:"
98 " supported in the destination:"
99 " %s") % (', '.join(sorted(missing)))
99 " %s") % (', '.join(sorted(missing)))
100 raise util.Abort(msg)
100 raise util.Abort(msg)
101
101
102 # there are two ways to push to remote repo:
102 # there are two ways to push to remote repo:
103 #
103 #
104 # addchangegroup assumes local user can lock remote
104 # addchangegroup assumes local user can lock remote
105 # repo (local filesystem, old ssh servers).
105 # repo (local filesystem, old ssh servers).
106 #
106 #
107 # unbundle assumes local user cannot lock remote repo (new ssh
107 # unbundle assumes local user cannot lock remote repo (new ssh
108 # servers, http servers).
108 # servers, http servers).
109
109
110 if not pushop.remote.canpush():
110 if not pushop.remote.canpush():
111 raise util.Abort(_("destination does not support push"))
111 raise util.Abort(_("destination does not support push"))
112 # get local lock as we might write phase data
112 # get local lock as we might write phase data
113 locallock = None
113 locallock = None
114 try:
114 try:
115 locallock = pushop.repo.lock()
115 locallock = pushop.repo.lock()
116 pushop.locallocked = True
116 pushop.locallocked = True
117 except IOError, err:
117 except IOError, err:
118 pushop.locallocked = False
118 pushop.locallocked = False
119 if err.errno != errno.EACCES:
119 if err.errno != errno.EACCES:
120 raise
120 raise
121 # source repo cannot be locked.
121 # source repo cannot be locked.
122 # We do not abort the push, but just disable the local phase
122 # We do not abort the push, but just disable the local phase
123 # synchronisation.
123 # synchronisation.
124 msg = 'cannot lock source repository: %s\n' % err
124 msg = 'cannot lock source repository: %s\n' % err
125 pushop.ui.debug(msg)
125 pushop.ui.debug(msg)
126 try:
126 try:
127 pushop.repo.checkpush(pushop)
127 pushop.repo.checkpush(pushop)
128 lock = None
128 lock = None
129 unbundle = pushop.remote.capable('unbundle')
129 unbundle = pushop.remote.capable('unbundle')
130 if not unbundle:
130 if not unbundle:
131 lock = pushop.remote.lock()
131 lock = pushop.remote.lock()
132 try:
132 try:
133 _pushdiscovery(pushop)
133 _pushdiscovery(pushop)
134 if (pushop.repo.ui.configbool('experimental', 'bundle2-exp',
134 if (pushop.repo.ui.configbool('experimental', 'bundle2-exp',
135 False)
135 False)
136 and pushop.remote.capable('bundle2-exp')):
136 and pushop.remote.capable('bundle2-exp')):
137 _pushbundle2(pushop)
137 _pushbundle2(pushop)
138 _pushchangeset(pushop)
138 _pushchangeset(pushop)
139 _pushcomputecommonheads(pushop)
139 _pushcomputecommonheads(pushop)
140 _pushsyncphase(pushop)
140 _pushsyncphase(pushop)
141 _pushobsolete(pushop)
141 _pushobsolete(pushop)
142 finally:
142 finally:
143 if lock is not None:
143 if lock is not None:
144 lock.release()
144 lock.release()
145 finally:
145 finally:
146 if locallock is not None:
146 if locallock is not None:
147 locallock.release()
147 locallock.release()
148
148
149 _pushbookmark(pushop)
149 _pushbookmark(pushop)
150 return pushop.ret
150 return pushop.ret
151
151
152 def _pushdiscovery(pushop):
152 def _pushdiscovery(pushop):
153 # discovery
153 # discovery
154 unfi = pushop.repo.unfiltered()
154 unfi = pushop.repo.unfiltered()
155 fci = discovery.findcommonincoming
155 fci = discovery.findcommonincoming
156 commoninc = fci(unfi, pushop.remote, force=pushop.force)
156 commoninc = fci(unfi, pushop.remote, force=pushop.force)
157 common, inc, remoteheads = commoninc
157 common, inc, remoteheads = commoninc
158 fco = discovery.findcommonoutgoing
158 fco = discovery.findcommonoutgoing
159 outgoing = fco(unfi, pushop.remote, onlyheads=pushop.revs,
159 outgoing = fco(unfi, pushop.remote, onlyheads=pushop.revs,
160 commoninc=commoninc, force=pushop.force)
160 commoninc=commoninc, force=pushop.force)
161 pushop.outgoing = outgoing
161 pushop.outgoing = outgoing
162 pushop.remoteheads = remoteheads
162 pushop.remoteheads = remoteheads
163 pushop.incoming = inc
163 pushop.incoming = inc
164
164
165 def _pushcheckoutgoing(pushop):
165 def _pushcheckoutgoing(pushop):
166 outgoing = pushop.outgoing
166 outgoing = pushop.outgoing
167 unfi = pushop.repo.unfiltered()
167 unfi = pushop.repo.unfiltered()
168 if not outgoing.missing:
168 if not outgoing.missing:
169 # nothing to push
169 # nothing to push
170 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
170 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
171 return False
171 return False
172 # something to push
172 # something to push
173 if not pushop.force:
173 if not pushop.force:
174 # if repo.obsstore == False --> no obsolete
174 # if repo.obsstore == False --> no obsolete
175 # then, save the iteration
175 # then, save the iteration
176 if unfi.obsstore:
176 if unfi.obsstore:
177 # this message are here for 80 char limit reason
177 # this message are here for 80 char limit reason
178 mso = _("push includes obsolete changeset: %s!")
178 mso = _("push includes obsolete changeset: %s!")
179 mst = "push includes %s changeset: %s!"
179 mst = "push includes %s changeset: %s!"
180 # plain versions for i18n tool to detect them
180 # plain versions for i18n tool to detect them
181 _("push includes unstable changeset: %s!")
181 _("push includes unstable changeset: %s!")
182 _("push includes bumped changeset: %s!")
182 _("push includes bumped changeset: %s!")
183 _("push includes divergent changeset: %s!")
183 _("push includes divergent changeset: %s!")
184 # If we are to push if there is at least one
184 # If we are to push if there is at least one
185 # obsolete or unstable changeset in missing, at
185 # obsolete or unstable changeset in missing, at
186 # least one of the missinghead will be obsolete or
186 # least one of the missinghead will be obsolete or
187 # unstable. So checking heads only is ok
187 # unstable. So checking heads only is ok
188 for node in outgoing.missingheads:
188 for node in outgoing.missingheads:
189 ctx = unfi[node]
189 ctx = unfi[node]
190 if ctx.obsolete():
190 if ctx.obsolete():
191 raise util.Abort(mso % ctx)
191 raise util.Abort(mso % ctx)
192 elif ctx.troubled():
192 elif ctx.troubled():
193 raise util.Abort(_(mst)
193 raise util.Abort(_(mst)
194 % (ctx.troubles()[0],
194 % (ctx.troubles()[0],
195 ctx))
195 ctx))
196 newbm = pushop.ui.configlist('bookmarks', 'pushing')
196 newbm = pushop.ui.configlist('bookmarks', 'pushing')
197 discovery.checkheads(unfi, pushop.remote, outgoing,
197 discovery.checkheads(unfi, pushop.remote, outgoing,
198 pushop.remoteheads,
198 pushop.remoteheads,
199 pushop.newbranch,
199 pushop.newbranch,
200 bool(pushop.incoming),
200 bool(pushop.incoming),
201 newbm)
201 newbm)
202 return True
202 return True
203
203
204 def _pushb2ctx(pushop, bundler):
204 def _pushb2ctx(pushop, bundler):
205 """handle changegroup push through bundle2
205 """handle changegroup push through bundle2
206
206
207 addchangegroup result is stored in the ``pushop.ret`` attribute.
207 addchangegroup result is stored in the ``pushop.ret`` attribute.
208 """
208 """
209 if 'changesets' in pushop.stepsdone:
209 if 'changesets' in pushop.stepsdone:
210 return
210 return
211 pushop.stepsdone.add('changesets')
211 pushop.stepsdone.add('changesets')
212 # Send known heads to the server for race detection.
212 # Send known heads to the server for race detection.
213 pushop.stepsdone.add('changesets')
213 pushop.stepsdone.add('changesets')
214 if not _pushcheckoutgoing(pushop):
214 if not _pushcheckoutgoing(pushop):
215 return
215 return
216 pushop.repo.prepushoutgoinghooks(pushop.repo,
216 pushop.repo.prepushoutgoinghooks(pushop.repo,
217 pushop.remote,
217 pushop.remote,
218 pushop.outgoing)
218 pushop.outgoing)
219 if not pushop.force:
219 if not pushop.force:
220 bundler.newpart('B2X:CHECK:HEADS', data=iter(pushop.remoteheads))
220 bundler.newpart('B2X:CHECK:HEADS', data=iter(pushop.remoteheads))
221 cg = changegroup.getlocalbundle(pushop.repo, 'push', pushop.outgoing)
221 cg = changegroup.getlocalbundle(pushop.repo, 'push', pushop.outgoing)
222 cgpart = bundler.newpart('B2X:CHANGEGROUP', data=cg.getchunks())
222 cgpart = bundler.newpart('B2X:CHANGEGROUP', data=cg.getchunks())
223 def handlereply(op):
223 def handlereply(op):
224 """extract addchangroup returns from server reply"""
224 """extract addchangroup returns from server reply"""
225 cgreplies = op.records.getreplies(cgpart.id)
225 cgreplies = op.records.getreplies(cgpart.id)
226 assert len(cgreplies['changegroup']) == 1
226 assert len(cgreplies['changegroup']) == 1
227 pushop.ret = cgreplies['changegroup'][0]['return']
227 pushop.ret = cgreplies['changegroup'][0]['return']
228 return handlereply
228 return handlereply
229
229
230 # list of function that may decide to add parts to an outgoing bundle2
230 # list of function that may decide to add parts to an outgoing bundle2
231 bundle2partsgenerators = [_pushb2ctx]
231 bundle2partsgenerators = [_pushb2ctx]
232
232
233 def _pushbundle2(pushop):
233 def _pushbundle2(pushop):
234 """push data to the remote using bundle2
234 """push data to the remote using bundle2
235
235
236 The only currently supported type of data is changegroup but this will
236 The only currently supported type of data is changegroup but this will
237 evolve in the future."""
237 evolve in the future."""
238 bundler = bundle2.bundle20(pushop.ui, bundle2.bundle2caps(pushop.remote))
238 bundler = bundle2.bundle20(pushop.ui, bundle2.bundle2caps(pushop.remote))
239 # create reply capability
239 # create reply capability
240 capsblob = bundle2.encodecaps(pushop.repo.bundle2caps)
240 capsblob = bundle2.encodecaps(pushop.repo.bundle2caps)
241 bundler.newpart('b2x:replycaps', data=capsblob)
241 bundler.newpart('b2x:replycaps', data=capsblob)
242 replyhandlers = []
242 replyhandlers = []
243 for partgen in bundle2partsgenerators:
243 for partgen in bundle2partsgenerators:
244 ret = partgen(pushop, bundler)
244 ret = partgen(pushop, bundler)
245 replyhandlers.append(ret)
245 if callable(ret):
246 replyhandlers.append(ret)
246 # do not push if nothing to push
247 # do not push if nothing to push
247 if bundler.nbparts <= 1:
248 if bundler.nbparts <= 1:
248 return
249 return
249 stream = util.chunkbuffer(bundler.getchunks())
250 stream = util.chunkbuffer(bundler.getchunks())
250 try:
251 try:
251 reply = pushop.remote.unbundle(stream, ['force'], 'push')
252 reply = pushop.remote.unbundle(stream, ['force'], 'push')
252 except error.BundleValueError, exc:
253 except error.BundleValueError, exc:
253 raise util.Abort('missing support for %s' % exc)
254 raise util.Abort('missing support for %s' % exc)
254 try:
255 try:
255 op = bundle2.processbundle(pushop.repo, reply)
256 op = bundle2.processbundle(pushop.repo, reply)
256 except error.BundleValueError, exc:
257 except error.BundleValueError, exc:
257 raise util.Abort('missing support for %s' % exc)
258 raise util.Abort('missing support for %s' % exc)
258 for rephand in replyhandlers:
259 for rephand in replyhandlers:
259 rephand(op)
260 rephand(op)
260
261
261 def _pushchangeset(pushop):
262 def _pushchangeset(pushop):
262 """Make the actual push of changeset bundle to remote repo"""
263 """Make the actual push of changeset bundle to remote repo"""
263 if 'changesets' in pushop.stepsdone:
264 if 'changesets' in pushop.stepsdone:
264 return
265 return
265 pushop.stepsdone.add('changesets')
266 pushop.stepsdone.add('changesets')
266 if not _pushcheckoutgoing(pushop):
267 if not _pushcheckoutgoing(pushop):
267 return
268 return
268 pushop.repo.prepushoutgoinghooks(pushop.repo,
269 pushop.repo.prepushoutgoinghooks(pushop.repo,
269 pushop.remote,
270 pushop.remote,
270 pushop.outgoing)
271 pushop.outgoing)
271 outgoing = pushop.outgoing
272 outgoing = pushop.outgoing
272 unbundle = pushop.remote.capable('unbundle')
273 unbundle = pushop.remote.capable('unbundle')
273 # TODO: get bundlecaps from remote
274 # TODO: get bundlecaps from remote
274 bundlecaps = None
275 bundlecaps = None
275 # create a changegroup from local
276 # create a changegroup from local
276 if pushop.revs is None and not (outgoing.excluded
277 if pushop.revs is None and not (outgoing.excluded
277 or pushop.repo.changelog.filteredrevs):
278 or pushop.repo.changelog.filteredrevs):
278 # push everything,
279 # push everything,
279 # use the fast path, no race possible on push
280 # use the fast path, no race possible on push
280 bundler = changegroup.bundle10(pushop.repo, bundlecaps)
281 bundler = changegroup.bundle10(pushop.repo, bundlecaps)
281 cg = changegroup.getsubset(pushop.repo,
282 cg = changegroup.getsubset(pushop.repo,
282 outgoing,
283 outgoing,
283 bundler,
284 bundler,
284 'push',
285 'push',
285 fastpath=True)
286 fastpath=True)
286 else:
287 else:
287 cg = changegroup.getlocalbundle(pushop.repo, 'push', outgoing,
288 cg = changegroup.getlocalbundle(pushop.repo, 'push', outgoing,
288 bundlecaps)
289 bundlecaps)
289
290
290 # apply changegroup to remote
291 # apply changegroup to remote
291 if unbundle:
292 if unbundle:
292 # local repo finds heads on server, finds out what
293 # local repo finds heads on server, finds out what
293 # revs it must push. once revs transferred, if server
294 # revs it must push. once revs transferred, if server
294 # finds it has different heads (someone else won
295 # finds it has different heads (someone else won
295 # commit/push race), server aborts.
296 # commit/push race), server aborts.
296 if pushop.force:
297 if pushop.force:
297 remoteheads = ['force']
298 remoteheads = ['force']
298 else:
299 else:
299 remoteheads = pushop.remoteheads
300 remoteheads = pushop.remoteheads
300 # ssh: return remote's addchangegroup()
301 # ssh: return remote's addchangegroup()
301 # http: return remote's addchangegroup() or 0 for error
302 # http: return remote's addchangegroup() or 0 for error
302 pushop.ret = pushop.remote.unbundle(cg, remoteheads,
303 pushop.ret = pushop.remote.unbundle(cg, remoteheads,
303 pushop.repo.url())
304 pushop.repo.url())
304 else:
305 else:
305 # we return an integer indicating remote head count
306 # we return an integer indicating remote head count
306 # change
307 # change
307 pushop.ret = pushop.remote.addchangegroup(cg, 'push', pushop.repo.url())
308 pushop.ret = pushop.remote.addchangegroup(cg, 'push', pushop.repo.url())
308
309
309 def _pushcomputecommonheads(pushop):
310 def _pushcomputecommonheads(pushop):
310 unfi = pushop.repo.unfiltered()
311 unfi = pushop.repo.unfiltered()
311 if pushop.ret:
312 if pushop.ret:
312 # push succeed, synchronize target of the push
313 # push succeed, synchronize target of the push
313 cheads = pushop.outgoing.missingheads
314 cheads = pushop.outgoing.missingheads
314 elif pushop.revs is None:
315 elif pushop.revs is None:
315 # All out push fails. synchronize all common
316 # All out push fails. synchronize all common
316 cheads = pushop.outgoing.commonheads
317 cheads = pushop.outgoing.commonheads
317 else:
318 else:
318 # I want cheads = heads(::missingheads and ::commonheads)
319 # I want cheads = heads(::missingheads and ::commonheads)
319 # (missingheads is revs with secret changeset filtered out)
320 # (missingheads is revs with secret changeset filtered out)
320 #
321 #
321 # This can be expressed as:
322 # This can be expressed as:
322 # cheads = ( (missingheads and ::commonheads)
323 # cheads = ( (missingheads and ::commonheads)
323 # + (commonheads and ::missingheads))"
324 # + (commonheads and ::missingheads))"
324 # )
325 # )
325 #
326 #
326 # while trying to push we already computed the following:
327 # while trying to push we already computed the following:
327 # common = (::commonheads)
328 # common = (::commonheads)
328 # missing = ((commonheads::missingheads) - commonheads)
329 # missing = ((commonheads::missingheads) - commonheads)
329 #
330 #
330 # We can pick:
331 # We can pick:
331 # * missingheads part of common (::commonheads)
332 # * missingheads part of common (::commonheads)
332 common = set(pushop.outgoing.common)
333 common = set(pushop.outgoing.common)
333 nm = pushop.repo.changelog.nodemap
334 nm = pushop.repo.changelog.nodemap
334 cheads = [node for node in pushop.revs if nm[node] in common]
335 cheads = [node for node in pushop.revs if nm[node] in common]
335 # and
336 # and
336 # * commonheads parents on missing
337 # * commonheads parents on missing
337 revset = unfi.set('%ln and parents(roots(%ln))',
338 revset = unfi.set('%ln and parents(roots(%ln))',
338 pushop.outgoing.commonheads,
339 pushop.outgoing.commonheads,
339 pushop.outgoing.missing)
340 pushop.outgoing.missing)
340 cheads.extend(c.node() for c in revset)
341 cheads.extend(c.node() for c in revset)
341 pushop.commonheads = cheads
342 pushop.commonheads = cheads
342
343
343 def _pushsyncphase(pushop):
344 def _pushsyncphase(pushop):
344 """synchronise phase information locally and remotely"""
345 """synchronise phase information locally and remotely"""
345 unfi = pushop.repo.unfiltered()
346 unfi = pushop.repo.unfiltered()
346 cheads = pushop.commonheads
347 cheads = pushop.commonheads
347 # even when we don't push, exchanging phase data is useful
348 # even when we don't push, exchanging phase data is useful
348 remotephases = pushop.remote.listkeys('phases')
349 remotephases = pushop.remote.listkeys('phases')
349 if (pushop.ui.configbool('ui', '_usedassubrepo', False)
350 if (pushop.ui.configbool('ui', '_usedassubrepo', False)
350 and remotephases # server supports phases
351 and remotephases # server supports phases
351 and pushop.ret is None # nothing was pushed
352 and pushop.ret is None # nothing was pushed
352 and remotephases.get('publishing', False)):
353 and remotephases.get('publishing', False)):
353 # When:
354 # When:
354 # - this is a subrepo push
355 # - this is a subrepo push
355 # - and remote support phase
356 # - and remote support phase
356 # - and no changeset was pushed
357 # - and no changeset was pushed
357 # - and remote is publishing
358 # - and remote is publishing
358 # We may be in issue 3871 case!
359 # We may be in issue 3871 case!
359 # We drop the possible phase synchronisation done by
360 # We drop the possible phase synchronisation done by
360 # courtesy to publish changesets possibly locally draft
361 # courtesy to publish changesets possibly locally draft
361 # on the remote.
362 # on the remote.
362 remotephases = {'publishing': 'True'}
363 remotephases = {'publishing': 'True'}
363 if not remotephases: # old server or public only reply from non-publishing
364 if not remotephases: # old server or public only reply from non-publishing
364 _localphasemove(pushop, cheads)
365 _localphasemove(pushop, cheads)
365 # don't push any phase data as there is nothing to push
366 # don't push any phase data as there is nothing to push
366 else:
367 else:
367 ana = phases.analyzeremotephases(pushop.repo, cheads,
368 ana = phases.analyzeremotephases(pushop.repo, cheads,
368 remotephases)
369 remotephases)
369 pheads, droots = ana
370 pheads, droots = ana
370 ### Apply remote phase on local
371 ### Apply remote phase on local
371 if remotephases.get('publishing', False):
372 if remotephases.get('publishing', False):
372 _localphasemove(pushop, cheads)
373 _localphasemove(pushop, cheads)
373 else: # publish = False
374 else: # publish = False
374 _localphasemove(pushop, pheads)
375 _localphasemove(pushop, pheads)
375 _localphasemove(pushop, cheads, phases.draft)
376 _localphasemove(pushop, cheads, phases.draft)
376 ### Apply local phase on remote
377 ### Apply local phase on remote
377
378
378 # Get the list of all revs draft on remote by public here.
379 # Get the list of all revs draft on remote by public here.
379 # XXX Beware that revset break if droots is not strictly
380 # XXX Beware that revset break if droots is not strictly
380 # XXX root we may want to ensure it is but it is costly
381 # XXX root we may want to ensure it is but it is costly
381 outdated = unfi.set('heads((%ln::%ln) and public())',
382 outdated = unfi.set('heads((%ln::%ln) and public())',
382 droots, cheads)
383 droots, cheads)
383
384
384 b2caps = bundle2.bundle2caps(pushop.remote)
385 b2caps = bundle2.bundle2caps(pushop.remote)
385 if 'b2x:pushkey' in b2caps:
386 if 'b2x:pushkey' in b2caps:
386 # server supports bundle2, let's do a batched push through it
387 # server supports bundle2, let's do a batched push through it
387 #
388 #
388 # This will eventually be unified with the changesets bundle2 push
389 # This will eventually be unified with the changesets bundle2 push
389 bundler = bundle2.bundle20(pushop.ui, b2caps)
390 bundler = bundle2.bundle20(pushop.ui, b2caps)
390 capsblob = bundle2.encodecaps(pushop.repo.bundle2caps)
391 capsblob = bundle2.encodecaps(pushop.repo.bundle2caps)
391 bundler.newpart('b2x:replycaps', data=capsblob)
392 bundler.newpart('b2x:replycaps', data=capsblob)
392 part2node = []
393 part2node = []
393 enc = pushkey.encode
394 enc = pushkey.encode
394 for newremotehead in outdated:
395 for newremotehead in outdated:
395 part = bundler.newpart('b2x:pushkey')
396 part = bundler.newpart('b2x:pushkey')
396 part.addparam('namespace', enc('phases'))
397 part.addparam('namespace', enc('phases'))
397 part.addparam('key', enc(newremotehead.hex()))
398 part.addparam('key', enc(newremotehead.hex()))
398 part.addparam('old', enc(str(phases.draft)))
399 part.addparam('old', enc(str(phases.draft)))
399 part.addparam('new', enc(str(phases.public)))
400 part.addparam('new', enc(str(phases.public)))
400 part2node.append((part.id, newremotehead))
401 part2node.append((part.id, newremotehead))
401 stream = util.chunkbuffer(bundler.getchunks())
402 stream = util.chunkbuffer(bundler.getchunks())
402 try:
403 try:
403 reply = pushop.remote.unbundle(stream, ['force'], 'push')
404 reply = pushop.remote.unbundle(stream, ['force'], 'push')
404 op = bundle2.processbundle(pushop.repo, reply)
405 op = bundle2.processbundle(pushop.repo, reply)
405 except error.BundleValueError, exc:
406 except error.BundleValueError, exc:
406 raise util.Abort('missing support for %s' % exc)
407 raise util.Abort('missing support for %s' % exc)
407 for partid, node in part2node:
408 for partid, node in part2node:
408 partrep = op.records.getreplies(partid)
409 partrep = op.records.getreplies(partid)
409 results = partrep['pushkey']
410 results = partrep['pushkey']
410 assert len(results) <= 1
411 assert len(results) <= 1
411 msg = None
412 msg = None
412 if not results:
413 if not results:
413 msg = _('server ignored update of %s to public!\n') % node
414 msg = _('server ignored update of %s to public!\n') % node
414 elif not int(results[0]['return']):
415 elif not int(results[0]['return']):
415 msg = _('updating %s to public failed!\n') % node
416 msg = _('updating %s to public failed!\n') % node
416 if msg is not None:
417 if msg is not None:
417 pushop.ui.warn(msg)
418 pushop.ui.warn(msg)
418
419
419 else:
420 else:
420 # fallback to independant pushkey command
421 # fallback to independant pushkey command
421 for newremotehead in outdated:
422 for newremotehead in outdated:
422 r = pushop.remote.pushkey('phases',
423 r = pushop.remote.pushkey('phases',
423 newremotehead.hex(),
424 newremotehead.hex(),
424 str(phases.draft),
425 str(phases.draft),
425 str(phases.public))
426 str(phases.public))
426 if not r:
427 if not r:
427 pushop.ui.warn(_('updating %s to public failed!\n')
428 pushop.ui.warn(_('updating %s to public failed!\n')
428 % newremotehead)
429 % newremotehead)
429
430
430 def _localphasemove(pushop, nodes, phase=phases.public):
431 def _localphasemove(pushop, nodes, phase=phases.public):
431 """move <nodes> to <phase> in the local source repo"""
432 """move <nodes> to <phase> in the local source repo"""
432 if pushop.locallocked:
433 if pushop.locallocked:
433 phases.advanceboundary(pushop.repo, phase, nodes)
434 phases.advanceboundary(pushop.repo, phase, nodes)
434 else:
435 else:
435 # repo is not locked, do not change any phases!
436 # repo is not locked, do not change any phases!
436 # Informs the user that phases should have been moved when
437 # Informs the user that phases should have been moved when
437 # applicable.
438 # applicable.
438 actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
439 actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
439 phasestr = phases.phasenames[phase]
440 phasestr = phases.phasenames[phase]
440 if actualmoves:
441 if actualmoves:
441 pushop.ui.status(_('cannot lock source repo, skipping '
442 pushop.ui.status(_('cannot lock source repo, skipping '
442 'local %s phase update\n') % phasestr)
443 'local %s phase update\n') % phasestr)
443
444
444 def _pushobsolete(pushop):
445 def _pushobsolete(pushop):
445 """utility function to push obsolete markers to a remote"""
446 """utility function to push obsolete markers to a remote"""
446 pushop.ui.debug('try to push obsolete markers to remote\n')
447 pushop.ui.debug('try to push obsolete markers to remote\n')
447 repo = pushop.repo
448 repo = pushop.repo
448 remote = pushop.remote
449 remote = pushop.remote
449 if (obsolete._enabled and repo.obsstore and
450 if (obsolete._enabled and repo.obsstore and
450 'obsolete' in remote.listkeys('namespaces')):
451 'obsolete' in remote.listkeys('namespaces')):
451 rslts = []
452 rslts = []
452 remotedata = repo.listkeys('obsolete')
453 remotedata = repo.listkeys('obsolete')
453 for key in sorted(remotedata, reverse=True):
454 for key in sorted(remotedata, reverse=True):
454 # reverse sort to ensure we end with dump0
455 # reverse sort to ensure we end with dump0
455 data = remotedata[key]
456 data = remotedata[key]
456 rslts.append(remote.pushkey('obsolete', key, '', data))
457 rslts.append(remote.pushkey('obsolete', key, '', data))
457 if [r for r in rslts if not r]:
458 if [r for r in rslts if not r]:
458 msg = _('failed to push some obsolete markers!\n')
459 msg = _('failed to push some obsolete markers!\n')
459 repo.ui.warn(msg)
460 repo.ui.warn(msg)
460
461
461 def _pushbookmark(pushop):
462 def _pushbookmark(pushop):
462 """Update bookmark position on remote"""
463 """Update bookmark position on remote"""
463 ui = pushop.ui
464 ui = pushop.ui
464 repo = pushop.repo.unfiltered()
465 repo = pushop.repo.unfiltered()
465 remote = pushop.remote
466 remote = pushop.remote
466 ui.debug("checking for updated bookmarks\n")
467 ui.debug("checking for updated bookmarks\n")
467 revnums = map(repo.changelog.rev, pushop.revs or [])
468 revnums = map(repo.changelog.rev, pushop.revs or [])
468 ancestors = [a for a in repo.changelog.ancestors(revnums, inclusive=True)]
469 ancestors = [a for a in repo.changelog.ancestors(revnums, inclusive=True)]
469 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid
470 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid
470 ) = bookmarks.compare(repo, repo._bookmarks, remote.listkeys('bookmarks'),
471 ) = bookmarks.compare(repo, repo._bookmarks, remote.listkeys('bookmarks'),
471 srchex=hex)
472 srchex=hex)
472
473
473 for b, scid, dcid in advsrc:
474 for b, scid, dcid in advsrc:
474 if ancestors and repo[scid].rev() not in ancestors:
475 if ancestors and repo[scid].rev() not in ancestors:
475 continue
476 continue
476 if remote.pushkey('bookmarks', b, dcid, scid):
477 if remote.pushkey('bookmarks', b, dcid, scid):
477 ui.status(_("updating bookmark %s\n") % b)
478 ui.status(_("updating bookmark %s\n") % b)
478 else:
479 else:
479 ui.warn(_('updating bookmark %s failed!\n') % b)
480 ui.warn(_('updating bookmark %s failed!\n') % b)
480
481
481 class pulloperation(object):
482 class pulloperation(object):
482 """A object that represent a single pull operation
483 """A object that represent a single pull operation
483
484
484 It purpose is to carry push related state and very common operation.
485 It purpose is to carry push related state and very common operation.
485
486
486 A new should be created at the beginning of each pull and discarded
487 A new should be created at the beginning of each pull and discarded
487 afterward.
488 afterward.
488 """
489 """
489
490
490 def __init__(self, repo, remote, heads=None, force=False):
491 def __init__(self, repo, remote, heads=None, force=False):
491 # repo we pull into
492 # repo we pull into
492 self.repo = repo
493 self.repo = repo
493 # repo we pull from
494 # repo we pull from
494 self.remote = remote
495 self.remote = remote
495 # revision we try to pull (None is "all")
496 # revision we try to pull (None is "all")
496 self.heads = heads
497 self.heads = heads
497 # do we force pull?
498 # do we force pull?
498 self.force = force
499 self.force = force
499 # the name the pull transaction
500 # the name the pull transaction
500 self._trname = 'pull\n' + util.hidepassword(remote.url())
501 self._trname = 'pull\n' + util.hidepassword(remote.url())
501 # hold the transaction once created
502 # hold the transaction once created
502 self._tr = None
503 self._tr = None
503 # set of common changeset between local and remote before pull
504 # set of common changeset between local and remote before pull
504 self.common = None
505 self.common = None
505 # set of pulled head
506 # set of pulled head
506 self.rheads = None
507 self.rheads = None
507 # list of missing changeset to fetch remotely
508 # list of missing changeset to fetch remotely
508 self.fetch = None
509 self.fetch = None
509 # result of changegroup pulling (used as return code by pull)
510 # result of changegroup pulling (used as return code by pull)
510 self.cgresult = None
511 self.cgresult = None
511 # list of step remaining todo (related to future bundle2 usage)
512 # list of step remaining todo (related to future bundle2 usage)
512 self.todosteps = set(['changegroup', 'phases', 'obsmarkers'])
513 self.todosteps = set(['changegroup', 'phases', 'obsmarkers'])
513
514
514 @util.propertycache
515 @util.propertycache
515 def pulledsubset(self):
516 def pulledsubset(self):
516 """heads of the set of changeset target by the pull"""
517 """heads of the set of changeset target by the pull"""
517 # compute target subset
518 # compute target subset
518 if self.heads is None:
519 if self.heads is None:
519 # We pulled every thing possible
520 # We pulled every thing possible
520 # sync on everything common
521 # sync on everything common
521 c = set(self.common)
522 c = set(self.common)
522 ret = list(self.common)
523 ret = list(self.common)
523 for n in self.rheads:
524 for n in self.rheads:
524 if n not in c:
525 if n not in c:
525 ret.append(n)
526 ret.append(n)
526 return ret
527 return ret
527 else:
528 else:
528 # We pulled a specific subset
529 # We pulled a specific subset
529 # sync on this subset
530 # sync on this subset
530 return self.heads
531 return self.heads
531
532
532 def gettransaction(self):
533 def gettransaction(self):
533 """get appropriate pull transaction, creating it if needed"""
534 """get appropriate pull transaction, creating it if needed"""
534 if self._tr is None:
535 if self._tr is None:
535 self._tr = self.repo.transaction(self._trname)
536 self._tr = self.repo.transaction(self._trname)
536 return self._tr
537 return self._tr
537
538
538 def closetransaction(self):
539 def closetransaction(self):
539 """close transaction if created"""
540 """close transaction if created"""
540 if self._tr is not None:
541 if self._tr is not None:
541 self._tr.close()
542 self._tr.close()
542
543
543 def releasetransaction(self):
544 def releasetransaction(self):
544 """release transaction if created"""
545 """release transaction if created"""
545 if self._tr is not None:
546 if self._tr is not None:
546 self._tr.release()
547 self._tr.release()
547
548
548 def pull(repo, remote, heads=None, force=False):
549 def pull(repo, remote, heads=None, force=False):
549 pullop = pulloperation(repo, remote, heads, force)
550 pullop = pulloperation(repo, remote, heads, force)
550 if pullop.remote.local():
551 if pullop.remote.local():
551 missing = set(pullop.remote.requirements) - pullop.repo.supported
552 missing = set(pullop.remote.requirements) - pullop.repo.supported
552 if missing:
553 if missing:
553 msg = _("required features are not"
554 msg = _("required features are not"
554 " supported in the destination:"
555 " supported in the destination:"
555 " %s") % (', '.join(sorted(missing)))
556 " %s") % (', '.join(sorted(missing)))
556 raise util.Abort(msg)
557 raise util.Abort(msg)
557
558
558 lock = pullop.repo.lock()
559 lock = pullop.repo.lock()
559 try:
560 try:
560 _pulldiscovery(pullop)
561 _pulldiscovery(pullop)
561 if (pullop.repo.ui.configbool('experimental', 'bundle2-exp', False)
562 if (pullop.repo.ui.configbool('experimental', 'bundle2-exp', False)
562 and pullop.remote.capable('bundle2-exp')):
563 and pullop.remote.capable('bundle2-exp')):
563 _pullbundle2(pullop)
564 _pullbundle2(pullop)
564 if 'changegroup' in pullop.todosteps:
565 if 'changegroup' in pullop.todosteps:
565 _pullchangeset(pullop)
566 _pullchangeset(pullop)
566 if 'phases' in pullop.todosteps:
567 if 'phases' in pullop.todosteps:
567 _pullphase(pullop)
568 _pullphase(pullop)
568 if 'obsmarkers' in pullop.todosteps:
569 if 'obsmarkers' in pullop.todosteps:
569 _pullobsolete(pullop)
570 _pullobsolete(pullop)
570 pullop.closetransaction()
571 pullop.closetransaction()
571 finally:
572 finally:
572 pullop.releasetransaction()
573 pullop.releasetransaction()
573 lock.release()
574 lock.release()
574
575
575 return pullop.cgresult
576 return pullop.cgresult
576
577
577 def _pulldiscovery(pullop):
578 def _pulldiscovery(pullop):
578 """discovery phase for the pull
579 """discovery phase for the pull
579
580
580 Current handle changeset discovery only, will change handle all discovery
581 Current handle changeset discovery only, will change handle all discovery
581 at some point."""
582 at some point."""
582 tmp = discovery.findcommonincoming(pullop.repo.unfiltered(),
583 tmp = discovery.findcommonincoming(pullop.repo.unfiltered(),
583 pullop.remote,
584 pullop.remote,
584 heads=pullop.heads,
585 heads=pullop.heads,
585 force=pullop.force)
586 force=pullop.force)
586 pullop.common, pullop.fetch, pullop.rheads = tmp
587 pullop.common, pullop.fetch, pullop.rheads = tmp
587
588
588 def _pullbundle2(pullop):
589 def _pullbundle2(pullop):
589 """pull data using bundle2
590 """pull data using bundle2
590
591
591 For now, the only supported data are changegroup."""
592 For now, the only supported data are changegroup."""
592 remotecaps = bundle2.bundle2caps(pullop.remote)
593 remotecaps = bundle2.bundle2caps(pullop.remote)
593 kwargs = {'bundlecaps': caps20to10(pullop.repo)}
594 kwargs = {'bundlecaps': caps20to10(pullop.repo)}
594 # pulling changegroup
595 # pulling changegroup
595 pullop.todosteps.remove('changegroup')
596 pullop.todosteps.remove('changegroup')
596
597
597 kwargs['common'] = pullop.common
598 kwargs['common'] = pullop.common
598 kwargs['heads'] = pullop.heads or pullop.rheads
599 kwargs['heads'] = pullop.heads or pullop.rheads
599 if 'b2x:listkeys' in remotecaps:
600 if 'b2x:listkeys' in remotecaps:
600 kwargs['listkeys'] = ['phase']
601 kwargs['listkeys'] = ['phase']
601 if not pullop.fetch:
602 if not pullop.fetch:
602 pullop.repo.ui.status(_("no changes found\n"))
603 pullop.repo.ui.status(_("no changes found\n"))
603 pullop.cgresult = 0
604 pullop.cgresult = 0
604 else:
605 else:
605 if pullop.heads is None and list(pullop.common) == [nullid]:
606 if pullop.heads is None and list(pullop.common) == [nullid]:
606 pullop.repo.ui.status(_("requesting all changes\n"))
607 pullop.repo.ui.status(_("requesting all changes\n"))
607 _pullbundle2extraprepare(pullop, kwargs)
608 _pullbundle2extraprepare(pullop, kwargs)
608 if kwargs.keys() == ['format']:
609 if kwargs.keys() == ['format']:
609 return # nothing to pull
610 return # nothing to pull
610 bundle = pullop.remote.getbundle('pull', **kwargs)
611 bundle = pullop.remote.getbundle('pull', **kwargs)
611 try:
612 try:
612 op = bundle2.processbundle(pullop.repo, bundle, pullop.gettransaction)
613 op = bundle2.processbundle(pullop.repo, bundle, pullop.gettransaction)
613 except error.BundleValueError, exc:
614 except error.BundleValueError, exc:
614 raise util.Abort('missing support for %s' % exc)
615 raise util.Abort('missing support for %s' % exc)
615
616
616 if pullop.fetch:
617 if pullop.fetch:
617 assert len(op.records['changegroup']) == 1
618 assert len(op.records['changegroup']) == 1
618 pullop.cgresult = op.records['changegroup'][0]['return']
619 pullop.cgresult = op.records['changegroup'][0]['return']
619
620
620 # processing phases change
621 # processing phases change
621 for namespace, value in op.records['listkeys']:
622 for namespace, value in op.records['listkeys']:
622 if namespace == 'phases':
623 if namespace == 'phases':
623 _pullapplyphases(pullop, value)
624 _pullapplyphases(pullop, value)
624
625
625 def _pullbundle2extraprepare(pullop, kwargs):
626 def _pullbundle2extraprepare(pullop, kwargs):
626 """hook function so that extensions can extend the getbundle call"""
627 """hook function so that extensions can extend the getbundle call"""
627 pass
628 pass
628
629
629 def _pullchangeset(pullop):
630 def _pullchangeset(pullop):
630 """pull changeset from unbundle into the local repo"""
631 """pull changeset from unbundle into the local repo"""
631 # We delay the open of the transaction as late as possible so we
632 # We delay the open of the transaction as late as possible so we
632 # don't open transaction for nothing or you break future useful
633 # don't open transaction for nothing or you break future useful
633 # rollback call
634 # rollback call
634 pullop.todosteps.remove('changegroup')
635 pullop.todosteps.remove('changegroup')
635 if not pullop.fetch:
636 if not pullop.fetch:
636 pullop.repo.ui.status(_("no changes found\n"))
637 pullop.repo.ui.status(_("no changes found\n"))
637 pullop.cgresult = 0
638 pullop.cgresult = 0
638 return
639 return
639 pullop.gettransaction()
640 pullop.gettransaction()
640 if pullop.heads is None and list(pullop.common) == [nullid]:
641 if pullop.heads is None and list(pullop.common) == [nullid]:
641 pullop.repo.ui.status(_("requesting all changes\n"))
642 pullop.repo.ui.status(_("requesting all changes\n"))
642 elif pullop.heads is None and pullop.remote.capable('changegroupsubset'):
643 elif pullop.heads is None and pullop.remote.capable('changegroupsubset'):
643 # issue1320, avoid a race if remote changed after discovery
644 # issue1320, avoid a race if remote changed after discovery
644 pullop.heads = pullop.rheads
645 pullop.heads = pullop.rheads
645
646
646 if pullop.remote.capable('getbundle'):
647 if pullop.remote.capable('getbundle'):
647 # TODO: get bundlecaps from remote
648 # TODO: get bundlecaps from remote
648 cg = pullop.remote.getbundle('pull', common=pullop.common,
649 cg = pullop.remote.getbundle('pull', common=pullop.common,
649 heads=pullop.heads or pullop.rheads)
650 heads=pullop.heads or pullop.rheads)
650 elif pullop.heads is None:
651 elif pullop.heads is None:
651 cg = pullop.remote.changegroup(pullop.fetch, 'pull')
652 cg = pullop.remote.changegroup(pullop.fetch, 'pull')
652 elif not pullop.remote.capable('changegroupsubset'):
653 elif not pullop.remote.capable('changegroupsubset'):
653 raise util.Abort(_("partial pull cannot be done because "
654 raise util.Abort(_("partial pull cannot be done because "
654 "other repository doesn't support "
655 "other repository doesn't support "
655 "changegroupsubset."))
656 "changegroupsubset."))
656 else:
657 else:
657 cg = pullop.remote.changegroupsubset(pullop.fetch, pullop.heads, 'pull')
658 cg = pullop.remote.changegroupsubset(pullop.fetch, pullop.heads, 'pull')
658 pullop.cgresult = changegroup.addchangegroup(pullop.repo, cg, 'pull',
659 pullop.cgresult = changegroup.addchangegroup(pullop.repo, cg, 'pull',
659 pullop.remote.url())
660 pullop.remote.url())
660
661
661 def _pullphase(pullop):
662 def _pullphase(pullop):
662 # Get remote phases data from remote
663 # Get remote phases data from remote
663 remotephases = pullop.remote.listkeys('phases')
664 remotephases = pullop.remote.listkeys('phases')
664 _pullapplyphases(pullop, remotephases)
665 _pullapplyphases(pullop, remotephases)
665
666
666 def _pullapplyphases(pullop, remotephases):
667 def _pullapplyphases(pullop, remotephases):
667 """apply phase movement from observed remote state"""
668 """apply phase movement from observed remote state"""
668 pullop.todosteps.remove('phases')
669 pullop.todosteps.remove('phases')
669 publishing = bool(remotephases.get('publishing', False))
670 publishing = bool(remotephases.get('publishing', False))
670 if remotephases and not publishing:
671 if remotephases and not publishing:
671 # remote is new and unpublishing
672 # remote is new and unpublishing
672 pheads, _dr = phases.analyzeremotephases(pullop.repo,
673 pheads, _dr = phases.analyzeremotephases(pullop.repo,
673 pullop.pulledsubset,
674 pullop.pulledsubset,
674 remotephases)
675 remotephases)
675 phases.advanceboundary(pullop.repo, phases.public, pheads)
676 phases.advanceboundary(pullop.repo, phases.public, pheads)
676 phases.advanceboundary(pullop.repo, phases.draft,
677 phases.advanceboundary(pullop.repo, phases.draft,
677 pullop.pulledsubset)
678 pullop.pulledsubset)
678 else:
679 else:
679 # Remote is old or publishing all common changesets
680 # Remote is old or publishing all common changesets
680 # should be seen as public
681 # should be seen as public
681 phases.advanceboundary(pullop.repo, phases.public,
682 phases.advanceboundary(pullop.repo, phases.public,
682 pullop.pulledsubset)
683 pullop.pulledsubset)
683
684
684 def _pullobsolete(pullop):
685 def _pullobsolete(pullop):
685 """utility function to pull obsolete markers from a remote
686 """utility function to pull obsolete markers from a remote
686
687
687 The `gettransaction` is function that return the pull transaction, creating
688 The `gettransaction` is function that return the pull transaction, creating
688 one if necessary. We return the transaction to inform the calling code that
689 one if necessary. We return the transaction to inform the calling code that
689 a new transaction have been created (when applicable).
690 a new transaction have been created (when applicable).
690
691
691 Exists mostly to allow overriding for experimentation purpose"""
692 Exists mostly to allow overriding for experimentation purpose"""
692 pullop.todosteps.remove('obsmarkers')
693 pullop.todosteps.remove('obsmarkers')
693 tr = None
694 tr = None
694 if obsolete._enabled:
695 if obsolete._enabled:
695 pullop.repo.ui.debug('fetching remote obsolete markers\n')
696 pullop.repo.ui.debug('fetching remote obsolete markers\n')
696 remoteobs = pullop.remote.listkeys('obsolete')
697 remoteobs = pullop.remote.listkeys('obsolete')
697 if 'dump0' in remoteobs:
698 if 'dump0' in remoteobs:
698 tr = pullop.gettransaction()
699 tr = pullop.gettransaction()
699 for key in sorted(remoteobs, reverse=True):
700 for key in sorted(remoteobs, reverse=True):
700 if key.startswith('dump'):
701 if key.startswith('dump'):
701 data = base85.b85decode(remoteobs[key])
702 data = base85.b85decode(remoteobs[key])
702 pullop.repo.obsstore.mergemarkers(tr, data)
703 pullop.repo.obsstore.mergemarkers(tr, data)
703 pullop.repo.invalidatevolatilesets()
704 pullop.repo.invalidatevolatilesets()
704 return tr
705 return tr
705
706
706 def caps20to10(repo):
707 def caps20to10(repo):
707 """return a set with appropriate options to use bundle20 during getbundle"""
708 """return a set with appropriate options to use bundle20 during getbundle"""
708 caps = set(['HG2X'])
709 caps = set(['HG2X'])
709 capsblob = bundle2.encodecaps(repo.bundle2caps)
710 capsblob = bundle2.encodecaps(repo.bundle2caps)
710 caps.add('bundle2=' + urllib.quote(capsblob))
711 caps.add('bundle2=' + urllib.quote(capsblob))
711 return caps
712 return caps
712
713
713 def getbundle(repo, source, heads=None, common=None, bundlecaps=None,
714 def getbundle(repo, source, heads=None, common=None, bundlecaps=None,
714 **kwargs):
715 **kwargs):
715 """return a full bundle (with potentially multiple kind of parts)
716 """return a full bundle (with potentially multiple kind of parts)
716
717
717 Could be a bundle HG10 or a bundle HG2X depending on bundlecaps
718 Could be a bundle HG10 or a bundle HG2X depending on bundlecaps
718 passed. For now, the bundle can contain only changegroup, but this will
719 passed. For now, the bundle can contain only changegroup, but this will
719 changes when more part type will be available for bundle2.
720 changes when more part type will be available for bundle2.
720
721
721 This is different from changegroup.getbundle that only returns an HG10
722 This is different from changegroup.getbundle that only returns an HG10
722 changegroup bundle. They may eventually get reunited in the future when we
723 changegroup bundle. They may eventually get reunited in the future when we
723 have a clearer idea of the API we what to query different data.
724 have a clearer idea of the API we what to query different data.
724
725
725 The implementation is at a very early stage and will get massive rework
726 The implementation is at a very early stage and will get massive rework
726 when the API of bundle is refined.
727 when the API of bundle is refined.
727 """
728 """
728 # build changegroup bundle here.
729 # build changegroup bundle here.
729 cg = changegroup.getbundle(repo, source, heads=heads,
730 cg = changegroup.getbundle(repo, source, heads=heads,
730 common=common, bundlecaps=bundlecaps)
731 common=common, bundlecaps=bundlecaps)
731 if bundlecaps is None or 'HG2X' not in bundlecaps:
732 if bundlecaps is None or 'HG2X' not in bundlecaps:
732 if kwargs:
733 if kwargs:
733 raise ValueError(_('unsupported getbundle arguments: %s')
734 raise ValueError(_('unsupported getbundle arguments: %s')
734 % ', '.join(sorted(kwargs.keys())))
735 % ', '.join(sorted(kwargs.keys())))
735 return cg
736 return cg
736 # very crude first implementation,
737 # very crude first implementation,
737 # the bundle API will change and the generation will be done lazily.
738 # the bundle API will change and the generation will be done lazily.
738 b2caps = {}
739 b2caps = {}
739 for bcaps in bundlecaps:
740 for bcaps in bundlecaps:
740 if bcaps.startswith('bundle2='):
741 if bcaps.startswith('bundle2='):
741 blob = urllib.unquote(bcaps[len('bundle2='):])
742 blob = urllib.unquote(bcaps[len('bundle2='):])
742 b2caps.update(bundle2.decodecaps(blob))
743 b2caps.update(bundle2.decodecaps(blob))
743 bundler = bundle2.bundle20(repo.ui, b2caps)
744 bundler = bundle2.bundle20(repo.ui, b2caps)
744 if cg:
745 if cg:
745 bundler.newpart('b2x:changegroup', data=cg.getchunks())
746 bundler.newpart('b2x:changegroup', data=cg.getchunks())
746 listkeys = kwargs.get('listkeys', ())
747 listkeys = kwargs.get('listkeys', ())
747 for namespace in listkeys:
748 for namespace in listkeys:
748 part = bundler.newpart('b2x:listkeys')
749 part = bundler.newpart('b2x:listkeys')
749 part.addparam('namespace', namespace)
750 part.addparam('namespace', namespace)
750 keys = repo.listkeys(namespace).items()
751 keys = repo.listkeys(namespace).items()
751 part.data = pushkey.encodekeys(keys)
752 part.data = pushkey.encodekeys(keys)
752 _getbundleextrapart(bundler, repo, source, heads=heads, common=common,
753 _getbundleextrapart(bundler, repo, source, heads=heads, common=common,
753 bundlecaps=bundlecaps, **kwargs)
754 bundlecaps=bundlecaps, **kwargs)
754 return util.chunkbuffer(bundler.getchunks())
755 return util.chunkbuffer(bundler.getchunks())
755
756
756 def _getbundleextrapart(bundler, repo, source, heads=None, common=None,
757 def _getbundleextrapart(bundler, repo, source, heads=None, common=None,
757 bundlecaps=None, **kwargs):
758 bundlecaps=None, **kwargs):
758 """hook function to let extensions add parts to the requested bundle"""
759 """hook function to let extensions add parts to the requested bundle"""
759 pass
760 pass
760
761
761 def check_heads(repo, their_heads, context):
762 def check_heads(repo, their_heads, context):
762 """check if the heads of a repo have been modified
763 """check if the heads of a repo have been modified
763
764
764 Used by peer for unbundling.
765 Used by peer for unbundling.
765 """
766 """
766 heads = repo.heads()
767 heads = repo.heads()
767 heads_hash = util.sha1(''.join(sorted(heads))).digest()
768 heads_hash = util.sha1(''.join(sorted(heads))).digest()
768 if not (their_heads == ['force'] or their_heads == heads or
769 if not (their_heads == ['force'] or their_heads == heads or
769 their_heads == ['hashed', heads_hash]):
770 their_heads == ['hashed', heads_hash]):
770 # someone else committed/pushed/unbundled while we
771 # someone else committed/pushed/unbundled while we
771 # were transferring data
772 # were transferring data
772 raise error.PushRaced('repository changed while %s - '
773 raise error.PushRaced('repository changed while %s - '
773 'please try again' % context)
774 'please try again' % context)
774
775
775 def unbundle(repo, cg, heads, source, url):
776 def unbundle(repo, cg, heads, source, url):
776 """Apply a bundle to a repo.
777 """Apply a bundle to a repo.
777
778
778 this function makes sure the repo is locked during the application and have
779 this function makes sure the repo is locked during the application and have
779 mechanism to check that no push race occurred between the creation of the
780 mechanism to check that no push race occurred between the creation of the
780 bundle and its application.
781 bundle and its application.
781
782
782 If the push was raced as PushRaced exception is raised."""
783 If the push was raced as PushRaced exception is raised."""
783 r = 0
784 r = 0
784 # need a transaction when processing a bundle2 stream
785 # need a transaction when processing a bundle2 stream
785 tr = None
786 tr = None
786 lock = repo.lock()
787 lock = repo.lock()
787 try:
788 try:
788 check_heads(repo, heads, 'uploading changes')
789 check_heads(repo, heads, 'uploading changes')
789 # push can proceed
790 # push can proceed
790 if util.safehasattr(cg, 'params'):
791 if util.safehasattr(cg, 'params'):
791 try:
792 try:
792 tr = repo.transaction('unbundle')
793 tr = repo.transaction('unbundle')
793 tr.hookargs['bundle2-exp'] = '1'
794 tr.hookargs['bundle2-exp'] = '1'
794 r = bundle2.processbundle(repo, cg, lambda: tr).reply
795 r = bundle2.processbundle(repo, cg, lambda: tr).reply
795 cl = repo.unfiltered().changelog
796 cl = repo.unfiltered().changelog
796 p = cl.writepending() and repo.root or ""
797 p = cl.writepending() and repo.root or ""
797 repo.hook('b2x-pretransactionclose', throw=True, source=source,
798 repo.hook('b2x-pretransactionclose', throw=True, source=source,
798 url=url, pending=p, **tr.hookargs)
799 url=url, pending=p, **tr.hookargs)
799 tr.close()
800 tr.close()
800 repo.hook('b2x-transactionclose', source=source, url=url,
801 repo.hook('b2x-transactionclose', source=source, url=url,
801 **tr.hookargs)
802 **tr.hookargs)
802 except Exception, exc:
803 except Exception, exc:
803 exc.duringunbundle2 = True
804 exc.duringunbundle2 = True
804 raise
805 raise
805 else:
806 else:
806 r = changegroup.addchangegroup(repo, cg, source, url)
807 r = changegroup.addchangegroup(repo, cg, source, url)
807 finally:
808 finally:
808 if tr is not None:
809 if tr is not None:
809 tr.release()
810 tr.release()
810 lock.release()
811 lock.release()
811 return r
812 return r
@@ -1,1107 +1,1106
1
1
2 Create an extension to test bundle2 API
2 Create an extension to test bundle2 API
3
3
4 $ cat > bundle2.py << EOF
4 $ cat > bundle2.py << EOF
5 > """A small extension to test bundle2 implementation
5 > """A small extension to test bundle2 implementation
6 >
6 >
7 > Current bundle2 implementation is far too limited to be used in any core
7 > Current bundle2 implementation is far too limited to be used in any core
8 > code. We still need to be able to test it while it grow up.
8 > code. We still need to be able to test it while it grow up.
9 > """
9 > """
10 >
10 >
11 > try:
11 > try:
12 > import msvcrt
12 > import msvcrt
13 > msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
13 > msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
14 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
14 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
15 > msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
15 > msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
16 > except ImportError:
16 > except ImportError:
17 > pass
17 > pass
18 >
18 >
19 > import sys
19 > import sys
20 > from mercurial import cmdutil
20 > from mercurial import cmdutil
21 > from mercurial import util
21 > from mercurial import util
22 > from mercurial import bundle2
22 > from mercurial import bundle2
23 > from mercurial import scmutil
23 > from mercurial import scmutil
24 > from mercurial import discovery
24 > from mercurial import discovery
25 > from mercurial import changegroup
25 > from mercurial import changegroup
26 > from mercurial import error
26 > from mercurial import error
27 > cmdtable = {}
27 > cmdtable = {}
28 > command = cmdutil.command(cmdtable)
28 > command = cmdutil.command(cmdtable)
29 >
29 >
30 > ELEPHANTSSONG = """Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
30 > ELEPHANTSSONG = """Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
31 > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
31 > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
32 > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
32 > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
33 > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
33 > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
34 >
34 >
35 > @bundle2.parthandler('test:song')
35 > @bundle2.parthandler('test:song')
36 > def songhandler(op, part):
36 > def songhandler(op, part):
37 > """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
37 > """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
38 > op.ui.write('The choir starts singing:\n')
38 > op.ui.write('The choir starts singing:\n')
39 > verses = 0
39 > verses = 0
40 > for line in part.read().split('\n'):
40 > for line in part.read().split('\n'):
41 > op.ui.write(' %s\n' % line)
41 > op.ui.write(' %s\n' % line)
42 > verses += 1
42 > verses += 1
43 > op.records.add('song', {'verses': verses})
43 > op.records.add('song', {'verses': verses})
44 >
44 >
45 > @bundle2.parthandler('test:ping')
45 > @bundle2.parthandler('test:ping')
46 > def pinghandler(op, part):
46 > def pinghandler(op, part):
47 > op.ui.write('received ping request (id %i)\n' % part.id)
47 > op.ui.write('received ping request (id %i)\n' % part.id)
48 > if op.reply is not None and 'ping-pong' in op.reply.capabilities:
48 > if op.reply is not None and 'ping-pong' in op.reply.capabilities:
49 > op.ui.write_err('replying to ping request (id %i)\n' % part.id)
49 > op.ui.write_err('replying to ping request (id %i)\n' % part.id)
50 > op.reply.newpart('test:pong', [('in-reply-to', str(part.id))])
50 > op.reply.newpart('test:pong', [('in-reply-to', str(part.id))])
51 >
51 >
52 > @bundle2.parthandler('test:debugreply')
52 > @bundle2.parthandler('test:debugreply')
53 > def debugreply(op, part):
53 > def debugreply(op, part):
54 > """print data about the capacity of the bundle reply"""
54 > """print data about the capacity of the bundle reply"""
55 > if op.reply is None:
55 > if op.reply is None:
56 > op.ui.write('debugreply: no reply\n')
56 > op.ui.write('debugreply: no reply\n')
57 > else:
57 > else:
58 > op.ui.write('debugreply: capabilities:\n')
58 > op.ui.write('debugreply: capabilities:\n')
59 > for cap in sorted(op.reply.capabilities):
59 > for cap in sorted(op.reply.capabilities):
60 > op.ui.write('debugreply: %r\n' % cap)
60 > op.ui.write('debugreply: %r\n' % cap)
61 > for val in op.reply.capabilities[cap]:
61 > for val in op.reply.capabilities[cap]:
62 > op.ui.write('debugreply: %r\n' % val)
62 > op.ui.write('debugreply: %r\n' % val)
63 >
63 >
64 > @command('bundle2',
64 > @command('bundle2',
65 > [('', 'param', [], 'stream level parameter'),
65 > [('', 'param', [], 'stream level parameter'),
66 > ('', 'unknown', False, 'include an unknown mandatory part in the bundle'),
66 > ('', 'unknown', False, 'include an unknown mandatory part in the bundle'),
67 > ('', 'unknownparams', False, 'include an unknown part parameters in the bundle'),
67 > ('', 'unknownparams', False, 'include an unknown part parameters in the bundle'),
68 > ('', 'parts', False, 'include some arbitrary parts to the bundle'),
68 > ('', 'parts', False, 'include some arbitrary parts to the bundle'),
69 > ('', 'reply', False, 'produce a reply bundle'),
69 > ('', 'reply', False, 'produce a reply bundle'),
70 > ('', 'pushrace', False, 'includes a check:head part with unknown nodes'),
70 > ('', 'pushrace', False, 'includes a check:head part with unknown nodes'),
71 > ('r', 'rev', [], 'includes those changeset in the bundle'),],
71 > ('r', 'rev', [], 'includes those changeset in the bundle'),],
72 > '[OUTPUTFILE]')
72 > '[OUTPUTFILE]')
73 > def cmdbundle2(ui, repo, path=None, **opts):
73 > def cmdbundle2(ui, repo, path=None, **opts):
74 > """write a bundle2 container on standard ouput"""
74 > """write a bundle2 container on standard ouput"""
75 > bundler = bundle2.bundle20(ui)
75 > bundler = bundle2.bundle20(ui)
76 > for p in opts['param']:
76 > for p in opts['param']:
77 > p = p.split('=', 1)
77 > p = p.split('=', 1)
78 > try:
78 > try:
79 > bundler.addparam(*p)
79 > bundler.addparam(*p)
80 > except ValueError, exc:
80 > except ValueError, exc:
81 > raise util.Abort('%s' % exc)
81 > raise util.Abort('%s' % exc)
82 >
82 >
83 > if opts['reply']:
83 > if opts['reply']:
84 > capsstring = 'ping-pong\nelephants=babar,celeste\ncity%3D%21=celeste%2Cville'
84 > capsstring = 'ping-pong\nelephants=babar,celeste\ncity%3D%21=celeste%2Cville'
85 > bundler.newpart('b2x:replycaps', data=capsstring)
85 > bundler.newpart('b2x:replycaps', data=capsstring)
86 >
86 >
87 > if opts['pushrace']:
87 > if opts['pushrace']:
88 > # also serve to test the assignement of data outside of init
88 > # also serve to test the assignement of data outside of init
89 > part = bundler.newpart('b2x:check:heads')
89 > part = bundler.newpart('b2x:check:heads')
90 > part.data = '01234567890123456789'
90 > part.data = '01234567890123456789'
91 >
91 >
92 > revs = opts['rev']
92 > revs = opts['rev']
93 > if 'rev' in opts:
93 > if 'rev' in opts:
94 > revs = scmutil.revrange(repo, opts['rev'])
94 > revs = scmutil.revrange(repo, opts['rev'])
95 > if revs:
95 > if revs:
96 > # very crude version of a changegroup part creation
96 > # very crude version of a changegroup part creation
97 > bundled = repo.revs('%ld::%ld', revs, revs)
97 > bundled = repo.revs('%ld::%ld', revs, revs)
98 > headmissing = [c.node() for c in repo.set('heads(%ld)', revs)]
98 > headmissing = [c.node() for c in repo.set('heads(%ld)', revs)]
99 > headcommon = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)]
99 > headcommon = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)]
100 > outgoing = discovery.outgoing(repo.changelog, headcommon, headmissing)
100 > outgoing = discovery.outgoing(repo.changelog, headcommon, headmissing)
101 > cg = changegroup.getlocalbundle(repo, 'test:bundle2', outgoing, None)
101 > cg = changegroup.getlocalbundle(repo, 'test:bundle2', outgoing, None)
102 > bundler.newpart('b2x:changegroup', data=cg.getchunks())
102 > bundler.newpart('b2x:changegroup', data=cg.getchunks())
103 >
103 >
104 > if opts['parts']:
104 > if opts['parts']:
105 > bundler.newpart('test:empty')
105 > bundler.newpart('test:empty')
106 > # add a second one to make sure we handle multiple parts
106 > # add a second one to make sure we handle multiple parts
107 > bundler.newpart('test:empty')
107 > bundler.newpart('test:empty')
108 > bundler.newpart('test:song', data=ELEPHANTSSONG)
108 > bundler.newpart('test:song', data=ELEPHANTSSONG)
109 > bundler.newpart('test:debugreply')
109 > bundler.newpart('test:debugreply')
110 > mathpart = bundler.newpart('test:math')
110 > mathpart = bundler.newpart('test:math')
111 > mathpart.addparam('pi', '3.14')
111 > mathpart.addparam('pi', '3.14')
112 > mathpart.addparam('e', '2.72')
112 > mathpart.addparam('e', '2.72')
113 > mathpart.addparam('cooking', 'raw', mandatory=False)
113 > mathpart.addparam('cooking', 'raw', mandatory=False)
114 > mathpart.data = '42'
114 > mathpart.data = '42'
115 > # advisory known part with unknown mandatory param
115 > # advisory known part with unknown mandatory param
116 > bundler.newpart('test:song', [('randomparam','')])
116 > bundler.newpart('test:song', [('randomparam','')])
117 > if opts['unknown']:
117 > if opts['unknown']:
118 > bundler.newpart('test:UNKNOWN', data='some random content')
118 > bundler.newpart('test:UNKNOWN', data='some random content')
119 > if opts['unknownparams']:
119 > if opts['unknownparams']:
120 > bundler.newpart('test:SONG', [('randomparams', '')])
120 > bundler.newpart('test:SONG', [('randomparams', '')])
121 > if opts['parts']:
121 > if opts['parts']:
122 > bundler.newpart('test:ping')
122 > bundler.newpart('test:ping')
123 >
123 >
124 > if path is None:
124 > if path is None:
125 > file = sys.stdout
125 > file = sys.stdout
126 > else:
126 > else:
127 > file = open(path, 'w')
127 > file = open(path, 'w')
128 >
128 >
129 > for chunk in bundler.getchunks():
129 > for chunk in bundler.getchunks():
130 > file.write(chunk)
130 > file.write(chunk)
131 >
131 >
132 > @command('unbundle2', [], '')
132 > @command('unbundle2', [], '')
133 > def cmdunbundle2(ui, repo, replypath=None):
133 > def cmdunbundle2(ui, repo, replypath=None):
134 > """process a bundle2 stream from stdin on the current repo"""
134 > """process a bundle2 stream from stdin on the current repo"""
135 > try:
135 > try:
136 > tr = None
136 > tr = None
137 > lock = repo.lock()
137 > lock = repo.lock()
138 > tr = repo.transaction('processbundle')
138 > tr = repo.transaction('processbundle')
139 > try:
139 > try:
140 > unbundler = bundle2.unbundle20(ui, sys.stdin)
140 > unbundler = bundle2.unbundle20(ui, sys.stdin)
141 > op = bundle2.processbundle(repo, unbundler, lambda: tr)
141 > op = bundle2.processbundle(repo, unbundler, lambda: tr)
142 > tr.close()
142 > tr.close()
143 > except error.BundleValueError, exc:
143 > except error.BundleValueError, exc:
144 > raise util.Abort('missing support for %s' % exc)
144 > raise util.Abort('missing support for %s' % exc)
145 > except error.PushRaced, exc:
145 > except error.PushRaced, exc:
146 > raise util.Abort('push race: %s' % exc)
146 > raise util.Abort('push race: %s' % exc)
147 > finally:
147 > finally:
148 > if tr is not None:
148 > if tr is not None:
149 > tr.release()
149 > tr.release()
150 > lock.release()
150 > lock.release()
151 > remains = sys.stdin.read()
151 > remains = sys.stdin.read()
152 > ui.write('%i unread bytes\n' % len(remains))
152 > ui.write('%i unread bytes\n' % len(remains))
153 > if op.records['song']:
153 > if op.records['song']:
154 > totalverses = sum(r['verses'] for r in op.records['song'])
154 > totalverses = sum(r['verses'] for r in op.records['song'])
155 > ui.write('%i total verses sung\n' % totalverses)
155 > ui.write('%i total verses sung\n' % totalverses)
156 > for rec in op.records['changegroup']:
156 > for rec in op.records['changegroup']:
157 > ui.write('addchangegroup return: %i\n' % rec['return'])
157 > ui.write('addchangegroup return: %i\n' % rec['return'])
158 > if op.reply is not None and replypath is not None:
158 > if op.reply is not None and replypath is not None:
159 > file = open(replypath, 'w')
159 > file = open(replypath, 'w')
160 > for chunk in op.reply.getchunks():
160 > for chunk in op.reply.getchunks():
161 > file.write(chunk)
161 > file.write(chunk)
162 >
162 >
163 > @command('statbundle2', [], '')
163 > @command('statbundle2', [], '')
164 > def cmdstatbundle2(ui, repo):
164 > def cmdstatbundle2(ui, repo):
165 > """print statistic on the bundle2 container read from stdin"""
165 > """print statistic on the bundle2 container read from stdin"""
166 > unbundler = bundle2.unbundle20(ui, sys.stdin)
166 > unbundler = bundle2.unbundle20(ui, sys.stdin)
167 > try:
167 > try:
168 > params = unbundler.params
168 > params = unbundler.params
169 > except error.BundleValueError, exc:
169 > except error.BundleValueError, exc:
170 > raise util.Abort('unknown parameters: %s' % exc)
170 > raise util.Abort('unknown parameters: %s' % exc)
171 > ui.write('options count: %i\n' % len(params))
171 > ui.write('options count: %i\n' % len(params))
172 > for key in sorted(params):
172 > for key in sorted(params):
173 > ui.write('- %s\n' % key)
173 > ui.write('- %s\n' % key)
174 > value = params[key]
174 > value = params[key]
175 > if value is not None:
175 > if value is not None:
176 > ui.write(' %s\n' % value)
176 > ui.write(' %s\n' % value)
177 > count = 0
177 > count = 0
178 > for p in unbundler.iterparts():
178 > for p in unbundler.iterparts():
179 > count += 1
179 > count += 1
180 > ui.write(' :%s:\n' % p.type)
180 > ui.write(' :%s:\n' % p.type)
181 > ui.write(' mandatory: %i\n' % len(p.mandatoryparams))
181 > ui.write(' mandatory: %i\n' % len(p.mandatoryparams))
182 > ui.write(' advisory: %i\n' % len(p.advisoryparams))
182 > ui.write(' advisory: %i\n' % len(p.advisoryparams))
183 > ui.write(' payload: %i bytes\n' % len(p.read()))
183 > ui.write(' payload: %i bytes\n' % len(p.read()))
184 > ui.write('parts count: %i\n' % count)
184 > ui.write('parts count: %i\n' % count)
185 > EOF
185 > EOF
186 $ cat >> $HGRCPATH << EOF
186 $ cat >> $HGRCPATH << EOF
187 > [extensions]
187 > [extensions]
188 > bundle2=$TESTTMP/bundle2.py
188 > bundle2=$TESTTMP/bundle2.py
189 > [experimental]
189 > [experimental]
190 > bundle2-exp=True
190 > bundle2-exp=True
191 > [ui]
191 > [ui]
192 > ssh=python "$TESTDIR/dummyssh"
192 > ssh=python "$TESTDIR/dummyssh"
193 > logtemplate={rev}:{node|short} {phase} {author} {desc|firstline}
193 > logtemplate={rev}:{node|short} {phase} {author} {desc|firstline}
194 > [web]
194 > [web]
195 > push_ssl = false
195 > push_ssl = false
196 > allow_push = *
196 > allow_push = *
197 > [phases]
197 > [phases]
198 > publish=False
198 > publish=False
199 > EOF
199 > EOF
200
200
201 The extension requires a repo (currently unused)
201 The extension requires a repo (currently unused)
202
202
203 $ hg init main
203 $ hg init main
204 $ cd main
204 $ cd main
205 $ touch a
205 $ touch a
206 $ hg add a
206 $ hg add a
207 $ hg commit -m 'a'
207 $ hg commit -m 'a'
208
208
209
209
210 Empty bundle
210 Empty bundle
211 =================
211 =================
212
212
213 - no option
213 - no option
214 - no parts
214 - no parts
215
215
216 Test bundling
216 Test bundling
217
217
218 $ hg bundle2
218 $ hg bundle2
219 HG2X\x00\x00\x00\x00 (no-eol) (esc)
219 HG2X\x00\x00\x00\x00 (no-eol) (esc)
220
220
221 Test unbundling
221 Test unbundling
222
222
223 $ hg bundle2 | hg statbundle2
223 $ hg bundle2 | hg statbundle2
224 options count: 0
224 options count: 0
225 parts count: 0
225 parts count: 0
226
226
227 Test old style bundle are detected and refused
227 Test old style bundle are detected and refused
228
228
229 $ hg bundle --all ../bundle.hg
229 $ hg bundle --all ../bundle.hg
230 1 changesets found
230 1 changesets found
231 $ hg statbundle2 < ../bundle.hg
231 $ hg statbundle2 < ../bundle.hg
232 abort: unknown bundle version 10
232 abort: unknown bundle version 10
233 [255]
233 [255]
234
234
235 Test parameters
235 Test parameters
236 =================
236 =================
237
237
238 - some options
238 - some options
239 - no parts
239 - no parts
240
240
241 advisory parameters, no value
241 advisory parameters, no value
242 -------------------------------
242 -------------------------------
243
243
244 Simplest possible parameters form
244 Simplest possible parameters form
245
245
246 Test generation simple option
246 Test generation simple option
247
247
248 $ hg bundle2 --param 'caution'
248 $ hg bundle2 --param 'caution'
249 HG2X\x00\x07caution\x00\x00 (no-eol) (esc)
249 HG2X\x00\x07caution\x00\x00 (no-eol) (esc)
250
250
251 Test unbundling
251 Test unbundling
252
252
253 $ hg bundle2 --param 'caution' | hg statbundle2
253 $ hg bundle2 --param 'caution' | hg statbundle2
254 options count: 1
254 options count: 1
255 - caution
255 - caution
256 parts count: 0
256 parts count: 0
257
257
258 Test generation multiple option
258 Test generation multiple option
259
259
260 $ hg bundle2 --param 'caution' --param 'meal'
260 $ hg bundle2 --param 'caution' --param 'meal'
261 HG2X\x00\x0ccaution meal\x00\x00 (no-eol) (esc)
261 HG2X\x00\x0ccaution meal\x00\x00 (no-eol) (esc)
262
262
263 Test unbundling
263 Test unbundling
264
264
265 $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
265 $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
266 options count: 2
266 options count: 2
267 - caution
267 - caution
268 - meal
268 - meal
269 parts count: 0
269 parts count: 0
270
270
271 advisory parameters, with value
271 advisory parameters, with value
272 -------------------------------
272 -------------------------------
273
273
274 Test generation
274 Test generation
275
275
276 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants'
276 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants'
277 HG2X\x00\x1ccaution meal=vegan elephants\x00\x00 (no-eol) (esc)
277 HG2X\x00\x1ccaution meal=vegan elephants\x00\x00 (no-eol) (esc)
278
278
279 Test unbundling
279 Test unbundling
280
280
281 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
281 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
282 options count: 3
282 options count: 3
283 - caution
283 - caution
284 - elephants
284 - elephants
285 - meal
285 - meal
286 vegan
286 vegan
287 parts count: 0
287 parts count: 0
288
288
289 parameter with special char in value
289 parameter with special char in value
290 ---------------------------------------------------
290 ---------------------------------------------------
291
291
292 Test generation
292 Test generation
293
293
294 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple
294 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple
295 HG2X\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
295 HG2X\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
296
296
297 Test unbundling
297 Test unbundling
298
298
299 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
299 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
300 options count: 2
300 options count: 2
301 - e|! 7/
301 - e|! 7/
302 babar%#==tutu
302 babar%#==tutu
303 - simple
303 - simple
304 parts count: 0
304 parts count: 0
305
305
306 Test unknown mandatory option
306 Test unknown mandatory option
307 ---------------------------------------------------
307 ---------------------------------------------------
308
308
309 $ hg bundle2 --param 'Gravity' | hg statbundle2
309 $ hg bundle2 --param 'Gravity' | hg statbundle2
310 abort: unknown parameters: Stream Parameter - Gravity
310 abort: unknown parameters: Stream Parameter - Gravity
311 [255]
311 [255]
312
312
313 Test debug output
313 Test debug output
314 ---------------------------------------------------
314 ---------------------------------------------------
315
315
316 bundling debug
316 bundling debug
317
317
318 $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2
318 $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2
319 start emission of HG2X stream
319 start emission of HG2X stream
320 bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
320 bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
321 start of parts
321 start of parts
322 end of bundle
322 end of bundle
323
323
324 file content is ok
324 file content is ok
325
325
326 $ cat ../out.hg2
326 $ cat ../out.hg2
327 HG2X\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
327 HG2X\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
328
328
329 unbundling debug
329 unbundling debug
330
330
331 $ hg statbundle2 --debug < ../out.hg2
331 $ hg statbundle2 --debug < ../out.hg2
332 start processing of HG2X stream
332 start processing of HG2X stream
333 reading bundle2 stream parameters
333 reading bundle2 stream parameters
334 ignoring unknown parameter 'e|! 7/'
334 ignoring unknown parameter 'e|! 7/'
335 ignoring unknown parameter 'simple'
335 ignoring unknown parameter 'simple'
336 options count: 2
336 options count: 2
337 - e|! 7/
337 - e|! 7/
338 babar%#==tutu
338 babar%#==tutu
339 - simple
339 - simple
340 start extraction of bundle2 parts
340 start extraction of bundle2 parts
341 part header size: 0
341 part header size: 0
342 end of bundle2 stream
342 end of bundle2 stream
343 parts count: 0
343 parts count: 0
344
344
345
345
346 Test buggy input
346 Test buggy input
347 ---------------------------------------------------
347 ---------------------------------------------------
348
348
349 empty parameter name
349 empty parameter name
350
350
351 $ hg bundle2 --param '' --quiet
351 $ hg bundle2 --param '' --quiet
352 abort: empty parameter name
352 abort: empty parameter name
353 [255]
353 [255]
354
354
355 bad parameter name
355 bad parameter name
356
356
357 $ hg bundle2 --param 42babar
357 $ hg bundle2 --param 42babar
358 abort: non letter first character: '42babar'
358 abort: non letter first character: '42babar'
359 [255]
359 [255]
360
360
361
361
362 Test part
362 Test part
363 =================
363 =================
364
364
365 $ hg bundle2 --parts ../parts.hg2 --debug
365 $ hg bundle2 --parts ../parts.hg2 --debug
366 start emission of HG2X stream
366 start emission of HG2X stream
367 bundle parameter:
367 bundle parameter:
368 start of parts
368 start of parts
369 bundle part: "test:empty"
369 bundle part: "test:empty"
370 bundle part: "test:empty"
370 bundle part: "test:empty"
371 bundle part: "test:song"
371 bundle part: "test:song"
372 bundle part: "test:debugreply"
372 bundle part: "test:debugreply"
373 bundle part: "test:math"
373 bundle part: "test:math"
374 bundle part: "test:song"
374 bundle part: "test:song"
375 bundle part: "test:ping"
375 bundle part: "test:ping"
376 end of bundle
376 end of bundle
377
377
378 $ cat ../parts.hg2
378 $ cat ../parts.hg2
379 HG2X\x00\x00\x00\x11 (esc)
379 HG2X\x00\x00\x00\x11 (esc)
380 test:empty\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11 (esc)
380 test:empty\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11 (esc)
381 test:empty\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x10 test:song\x00\x00\x00\x02\x00\x00\x00\x00\x00\xb2Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko (esc)
381 test:empty\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x10 test:song\x00\x00\x00\x02\x00\x00\x00\x00\x00\xb2Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko (esc)
382 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
382 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
383 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00\x16\x0ftest:debugreply\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00+ test:math\x00\x00\x00\x04\x02\x01\x02\x04\x01\x04\x07\x03pi3.14e2.72cookingraw\x00\x00\x00\x0242\x00\x00\x00\x00\x00\x1d test:song\x00\x00\x00\x05\x01\x00\x0b\x00randomparam\x00\x00\x00\x00\x00\x10 test:ping\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
383 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00\x16\x0ftest:debugreply\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00+ test:math\x00\x00\x00\x04\x02\x01\x02\x04\x01\x04\x07\x03pi3.14e2.72cookingraw\x00\x00\x00\x0242\x00\x00\x00\x00\x00\x1d test:song\x00\x00\x00\x05\x01\x00\x0b\x00randomparam\x00\x00\x00\x00\x00\x10 test:ping\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
384
384
385
385
386 $ hg statbundle2 < ../parts.hg2
386 $ hg statbundle2 < ../parts.hg2
387 options count: 0
387 options count: 0
388 :test:empty:
388 :test:empty:
389 mandatory: 0
389 mandatory: 0
390 advisory: 0
390 advisory: 0
391 payload: 0 bytes
391 payload: 0 bytes
392 :test:empty:
392 :test:empty:
393 mandatory: 0
393 mandatory: 0
394 advisory: 0
394 advisory: 0
395 payload: 0 bytes
395 payload: 0 bytes
396 :test:song:
396 :test:song:
397 mandatory: 0
397 mandatory: 0
398 advisory: 0
398 advisory: 0
399 payload: 178 bytes
399 payload: 178 bytes
400 :test:debugreply:
400 :test:debugreply:
401 mandatory: 0
401 mandatory: 0
402 advisory: 0
402 advisory: 0
403 payload: 0 bytes
403 payload: 0 bytes
404 :test:math:
404 :test:math:
405 mandatory: 2
405 mandatory: 2
406 advisory: 1
406 advisory: 1
407 payload: 2 bytes
407 payload: 2 bytes
408 :test:song:
408 :test:song:
409 mandatory: 1
409 mandatory: 1
410 advisory: 0
410 advisory: 0
411 payload: 0 bytes
411 payload: 0 bytes
412 :test:ping:
412 :test:ping:
413 mandatory: 0
413 mandatory: 0
414 advisory: 0
414 advisory: 0
415 payload: 0 bytes
415 payload: 0 bytes
416 parts count: 7
416 parts count: 7
417
417
418 $ hg statbundle2 --debug < ../parts.hg2
418 $ hg statbundle2 --debug < ../parts.hg2
419 start processing of HG2X stream
419 start processing of HG2X stream
420 reading bundle2 stream parameters
420 reading bundle2 stream parameters
421 options count: 0
421 options count: 0
422 start extraction of bundle2 parts
422 start extraction of bundle2 parts
423 part header size: 17
423 part header size: 17
424 part type: "test:empty"
424 part type: "test:empty"
425 part id: "0"
425 part id: "0"
426 part parameters: 0
426 part parameters: 0
427 :test:empty:
427 :test:empty:
428 mandatory: 0
428 mandatory: 0
429 advisory: 0
429 advisory: 0
430 payload chunk size: 0
430 payload chunk size: 0
431 payload: 0 bytes
431 payload: 0 bytes
432 part header size: 17
432 part header size: 17
433 part type: "test:empty"
433 part type: "test:empty"
434 part id: "1"
434 part id: "1"
435 part parameters: 0
435 part parameters: 0
436 :test:empty:
436 :test:empty:
437 mandatory: 0
437 mandatory: 0
438 advisory: 0
438 advisory: 0
439 payload chunk size: 0
439 payload chunk size: 0
440 payload: 0 bytes
440 payload: 0 bytes
441 part header size: 16
441 part header size: 16
442 part type: "test:song"
442 part type: "test:song"
443 part id: "2"
443 part id: "2"
444 part parameters: 0
444 part parameters: 0
445 :test:song:
445 :test:song:
446 mandatory: 0
446 mandatory: 0
447 advisory: 0
447 advisory: 0
448 payload chunk size: 178
448 payload chunk size: 178
449 payload chunk size: 0
449 payload chunk size: 0
450 payload: 178 bytes
450 payload: 178 bytes
451 part header size: 22
451 part header size: 22
452 part type: "test:debugreply"
452 part type: "test:debugreply"
453 part id: "3"
453 part id: "3"
454 part parameters: 0
454 part parameters: 0
455 :test:debugreply:
455 :test:debugreply:
456 mandatory: 0
456 mandatory: 0
457 advisory: 0
457 advisory: 0
458 payload chunk size: 0
458 payload chunk size: 0
459 payload: 0 bytes
459 payload: 0 bytes
460 part header size: 43
460 part header size: 43
461 part type: "test:math"
461 part type: "test:math"
462 part id: "4"
462 part id: "4"
463 part parameters: 3
463 part parameters: 3
464 :test:math:
464 :test:math:
465 mandatory: 2
465 mandatory: 2
466 advisory: 1
466 advisory: 1
467 payload chunk size: 2
467 payload chunk size: 2
468 payload chunk size: 0
468 payload chunk size: 0
469 payload: 2 bytes
469 payload: 2 bytes
470 part header size: 29
470 part header size: 29
471 part type: "test:song"
471 part type: "test:song"
472 part id: "5"
472 part id: "5"
473 part parameters: 1
473 part parameters: 1
474 :test:song:
474 :test:song:
475 mandatory: 1
475 mandatory: 1
476 advisory: 0
476 advisory: 0
477 payload chunk size: 0
477 payload chunk size: 0
478 payload: 0 bytes
478 payload: 0 bytes
479 part header size: 16
479 part header size: 16
480 part type: "test:ping"
480 part type: "test:ping"
481 part id: "6"
481 part id: "6"
482 part parameters: 0
482 part parameters: 0
483 :test:ping:
483 :test:ping:
484 mandatory: 0
484 mandatory: 0
485 advisory: 0
485 advisory: 0
486 payload chunk size: 0
486 payload chunk size: 0
487 payload: 0 bytes
487 payload: 0 bytes
488 part header size: 0
488 part header size: 0
489 end of bundle2 stream
489 end of bundle2 stream
490 parts count: 7
490 parts count: 7
491
491
492 Test actual unbundling of test part
492 Test actual unbundling of test part
493 =======================================
493 =======================================
494
494
495 Process the bundle
495 Process the bundle
496
496
497 $ hg unbundle2 --debug < ../parts.hg2
497 $ hg unbundle2 --debug < ../parts.hg2
498 start processing of HG2X stream
498 start processing of HG2X stream
499 reading bundle2 stream parameters
499 reading bundle2 stream parameters
500 start extraction of bundle2 parts
500 start extraction of bundle2 parts
501 part header size: 17
501 part header size: 17
502 part type: "test:empty"
502 part type: "test:empty"
503 part id: "0"
503 part id: "0"
504 part parameters: 0
504 part parameters: 0
505 ignoring unsupported advisory part test:empty
505 ignoring unsupported advisory part test:empty
506 payload chunk size: 0
506 payload chunk size: 0
507 part header size: 17
507 part header size: 17
508 part type: "test:empty"
508 part type: "test:empty"
509 part id: "1"
509 part id: "1"
510 part parameters: 0
510 part parameters: 0
511 ignoring unsupported advisory part test:empty
511 ignoring unsupported advisory part test:empty
512 payload chunk size: 0
512 payload chunk size: 0
513 part header size: 16
513 part header size: 16
514 part type: "test:song"
514 part type: "test:song"
515 part id: "2"
515 part id: "2"
516 part parameters: 0
516 part parameters: 0
517 found a handler for part 'test:song'
517 found a handler for part 'test:song'
518 The choir starts singing:
518 The choir starts singing:
519 payload chunk size: 178
519 payload chunk size: 178
520 payload chunk size: 0
520 payload chunk size: 0
521 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
521 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
522 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
522 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
523 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
523 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
524 part header size: 22
524 part header size: 22
525 part type: "test:debugreply"
525 part type: "test:debugreply"
526 part id: "3"
526 part id: "3"
527 part parameters: 0
527 part parameters: 0
528 found a handler for part 'test:debugreply'
528 found a handler for part 'test:debugreply'
529 debugreply: no reply
529 debugreply: no reply
530 payload chunk size: 0
530 payload chunk size: 0
531 part header size: 43
531 part header size: 43
532 part type: "test:math"
532 part type: "test:math"
533 part id: "4"
533 part id: "4"
534 part parameters: 3
534 part parameters: 3
535 ignoring unsupported advisory part test:math
535 ignoring unsupported advisory part test:math
536 payload chunk size: 2
536 payload chunk size: 2
537 payload chunk size: 0
537 payload chunk size: 0
538 part header size: 29
538 part header size: 29
539 part type: "test:song"
539 part type: "test:song"
540 part id: "5"
540 part id: "5"
541 part parameters: 1
541 part parameters: 1
542 found a handler for part 'test:song'
542 found a handler for part 'test:song'
543 ignoring unsupported advisory part test:song - randomparam
543 ignoring unsupported advisory part test:song - randomparam
544 payload chunk size: 0
544 payload chunk size: 0
545 part header size: 16
545 part header size: 16
546 part type: "test:ping"
546 part type: "test:ping"
547 part id: "6"
547 part id: "6"
548 part parameters: 0
548 part parameters: 0
549 found a handler for part 'test:ping'
549 found a handler for part 'test:ping'
550 received ping request (id 6)
550 received ping request (id 6)
551 payload chunk size: 0
551 payload chunk size: 0
552 part header size: 0
552 part header size: 0
553 end of bundle2 stream
553 end of bundle2 stream
554 0 unread bytes
554 0 unread bytes
555 3 total verses sung
555 3 total verses sung
556
556
557 Unbundle with an unknown mandatory part
557 Unbundle with an unknown mandatory part
558 (should abort)
558 (should abort)
559
559
560 $ hg bundle2 --parts --unknown ../unknown.hg2
560 $ hg bundle2 --parts --unknown ../unknown.hg2
561
561
562 $ hg unbundle2 < ../unknown.hg2
562 $ hg unbundle2 < ../unknown.hg2
563 The choir starts singing:
563 The choir starts singing:
564 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
564 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
565 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
565 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
566 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
566 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
567 debugreply: no reply
567 debugreply: no reply
568 0 unread bytes
568 0 unread bytes
569 abort: missing support for test:unknown
569 abort: missing support for test:unknown
570 [255]
570 [255]
571
571
572 Unbundle with an unknown mandatory part parameters
572 Unbundle with an unknown mandatory part parameters
573 (should abort)
573 (should abort)
574
574
575 $ hg bundle2 --unknownparams ../unknown.hg2
575 $ hg bundle2 --unknownparams ../unknown.hg2
576
576
577 $ hg unbundle2 < ../unknown.hg2
577 $ hg unbundle2 < ../unknown.hg2
578 0 unread bytes
578 0 unread bytes
579 abort: missing support for test:song - randomparams
579 abort: missing support for test:song - randomparams
580 [255]
580 [255]
581
581
582 unbundle with a reply
582 unbundle with a reply
583
583
584 $ hg bundle2 --parts --reply ../parts-reply.hg2
584 $ hg bundle2 --parts --reply ../parts-reply.hg2
585 $ hg unbundle2 ../reply.hg2 < ../parts-reply.hg2
585 $ hg unbundle2 ../reply.hg2 < ../parts-reply.hg2
586 0 unread bytes
586 0 unread bytes
587 3 total verses sung
587 3 total verses sung
588
588
589 The reply is a bundle
589 The reply is a bundle
590
590
591 $ cat ../reply.hg2
591 $ cat ../reply.hg2
592 HG2X\x00\x00\x00\x1f (esc)
592 HG2X\x00\x00\x00\x1f (esc)
593 b2x:output\x00\x00\x00\x00\x00\x01\x0b\x01in-reply-to3\x00\x00\x00\xd9The choir starts singing: (esc)
593 b2x:output\x00\x00\x00\x00\x00\x01\x0b\x01in-reply-to3\x00\x00\x00\xd9The choir starts singing: (esc)
594 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
594 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
595 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
595 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
596 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
596 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
597 \x00\x00\x00\x00\x00\x1f (esc)
597 \x00\x00\x00\x00\x00\x1f (esc)
598 b2x:output\x00\x00\x00\x01\x00\x01\x0b\x01in-reply-to4\x00\x00\x00\xc9debugreply: capabilities: (esc)
598 b2x:output\x00\x00\x00\x01\x00\x01\x0b\x01in-reply-to4\x00\x00\x00\xc9debugreply: capabilities: (esc)
599 debugreply: 'city=!'
599 debugreply: 'city=!'
600 debugreply: 'celeste,ville'
600 debugreply: 'celeste,ville'
601 debugreply: 'elephants'
601 debugreply: 'elephants'
602 debugreply: 'babar'
602 debugreply: 'babar'
603 debugreply: 'celeste'
603 debugreply: 'celeste'
604 debugreply: 'ping-pong'
604 debugreply: 'ping-pong'
605 \x00\x00\x00\x00\x00\x1e test:pong\x00\x00\x00\x02\x01\x00\x0b\x01in-reply-to7\x00\x00\x00\x00\x00\x1f (esc)
605 \x00\x00\x00\x00\x00\x1e test:pong\x00\x00\x00\x02\x01\x00\x0b\x01in-reply-to7\x00\x00\x00\x00\x00\x1f (esc)
606 b2x:output\x00\x00\x00\x03\x00\x01\x0b\x01in-reply-to7\x00\x00\x00=received ping request (id 7) (esc)
606 b2x:output\x00\x00\x00\x03\x00\x01\x0b\x01in-reply-to7\x00\x00\x00=received ping request (id 7) (esc)
607 replying to ping request (id 7)
607 replying to ping request (id 7)
608 \x00\x00\x00\x00\x00\x00 (no-eol) (esc)
608 \x00\x00\x00\x00\x00\x00 (no-eol) (esc)
609
609
610 The reply is valid
610 The reply is valid
611
611
612 $ hg statbundle2 < ../reply.hg2
612 $ hg statbundle2 < ../reply.hg2
613 options count: 0
613 options count: 0
614 :b2x:output:
614 :b2x:output:
615 mandatory: 0
615 mandatory: 0
616 advisory: 1
616 advisory: 1
617 payload: 217 bytes
617 payload: 217 bytes
618 :b2x:output:
618 :b2x:output:
619 mandatory: 0
619 mandatory: 0
620 advisory: 1
620 advisory: 1
621 payload: 201 bytes
621 payload: 201 bytes
622 :test:pong:
622 :test:pong:
623 mandatory: 1
623 mandatory: 1
624 advisory: 0
624 advisory: 0
625 payload: 0 bytes
625 payload: 0 bytes
626 :b2x:output:
626 :b2x:output:
627 mandatory: 0
627 mandatory: 0
628 advisory: 1
628 advisory: 1
629 payload: 61 bytes
629 payload: 61 bytes
630 parts count: 4
630 parts count: 4
631
631
632 Unbundle the reply to get the output:
632 Unbundle the reply to get the output:
633
633
634 $ hg unbundle2 < ../reply.hg2
634 $ hg unbundle2 < ../reply.hg2
635 remote: The choir starts singing:
635 remote: The choir starts singing:
636 remote: Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
636 remote: Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
637 remote: Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
637 remote: Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
638 remote: Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
638 remote: Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
639 remote: debugreply: capabilities:
639 remote: debugreply: capabilities:
640 remote: debugreply: 'city=!'
640 remote: debugreply: 'city=!'
641 remote: debugreply: 'celeste,ville'
641 remote: debugreply: 'celeste,ville'
642 remote: debugreply: 'elephants'
642 remote: debugreply: 'elephants'
643 remote: debugreply: 'babar'
643 remote: debugreply: 'babar'
644 remote: debugreply: 'celeste'
644 remote: debugreply: 'celeste'
645 remote: debugreply: 'ping-pong'
645 remote: debugreply: 'ping-pong'
646 remote: received ping request (id 7)
646 remote: received ping request (id 7)
647 remote: replying to ping request (id 7)
647 remote: replying to ping request (id 7)
648 0 unread bytes
648 0 unread bytes
649
649
650 Test push race detection
650 Test push race detection
651
651
652 $ hg bundle2 --pushrace ../part-race.hg2
652 $ hg bundle2 --pushrace ../part-race.hg2
653
653
654 $ hg unbundle2 < ../part-race.hg2
654 $ hg unbundle2 < ../part-race.hg2
655 0 unread bytes
655 0 unread bytes
656 abort: push race: repository changed while pushing - please try again
656 abort: push race: repository changed while pushing - please try again
657 [255]
657 [255]
658
658
659 Support for changegroup
659 Support for changegroup
660 ===================================
660 ===================================
661
661
662 $ hg unbundle $TESTDIR/bundles/rebase.hg
662 $ hg unbundle $TESTDIR/bundles/rebase.hg
663 adding changesets
663 adding changesets
664 adding manifests
664 adding manifests
665 adding file changes
665 adding file changes
666 added 8 changesets with 7 changes to 7 files (+3 heads)
666 added 8 changesets with 7 changes to 7 files (+3 heads)
667 (run 'hg heads' to see heads, 'hg merge' to merge)
667 (run 'hg heads' to see heads, 'hg merge' to merge)
668
668
669 $ hg log -G
669 $ hg log -G
670 o 8:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> H
670 o 8:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> H
671 |
671 |
672 | o 7:eea13746799a draft Nicolas Dumazet <nicdumz.commits@gmail.com> G
672 | o 7:eea13746799a draft Nicolas Dumazet <nicdumz.commits@gmail.com> G
673 |/|
673 |/|
674 o | 6:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
674 o | 6:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
675 | |
675 | |
676 | o 5:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
676 | o 5:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
677 |/
677 |/
678 | o 4:32af7686d403 draft Nicolas Dumazet <nicdumz.commits@gmail.com> D
678 | o 4:32af7686d403 draft Nicolas Dumazet <nicdumz.commits@gmail.com> D
679 | |
679 | |
680 | o 3:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> C
680 | o 3:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> C
681 | |
681 | |
682 | o 2:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> B
682 | o 2:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> B
683 |/
683 |/
684 o 1:cd010b8cd998 draft Nicolas Dumazet <nicdumz.commits@gmail.com> A
684 o 1:cd010b8cd998 draft Nicolas Dumazet <nicdumz.commits@gmail.com> A
685
685
686 @ 0:3903775176ed draft test a
686 @ 0:3903775176ed draft test a
687
687
688
688
689 $ hg bundle2 --debug --rev '8+7+5+4' ../rev.hg2
689 $ hg bundle2 --debug --rev '8+7+5+4' ../rev.hg2
690 4 changesets found
690 4 changesets found
691 list of changesets:
691 list of changesets:
692 32af7686d403cf45b5d95f2d70cebea587ac806a
692 32af7686d403cf45b5d95f2d70cebea587ac806a
693 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
693 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
694 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
694 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
695 02de42196ebee42ef284b6780a87cdc96e8eaab6
695 02de42196ebee42ef284b6780a87cdc96e8eaab6
696 start emission of HG2X stream
696 start emission of HG2X stream
697 bundle parameter:
697 bundle parameter:
698 start of parts
698 start of parts
699 bundle part: "b2x:changegroup"
699 bundle part: "b2x:changegroup"
700 bundling: 1/4 changesets (25.00%)
700 bundling: 1/4 changesets (25.00%)
701 bundling: 2/4 changesets (50.00%)
701 bundling: 2/4 changesets (50.00%)
702 bundling: 3/4 changesets (75.00%)
702 bundling: 3/4 changesets (75.00%)
703 bundling: 4/4 changesets (100.00%)
703 bundling: 4/4 changesets (100.00%)
704 bundling: 1/4 manifests (25.00%)
704 bundling: 1/4 manifests (25.00%)
705 bundling: 2/4 manifests (50.00%)
705 bundling: 2/4 manifests (50.00%)
706 bundling: 3/4 manifests (75.00%)
706 bundling: 3/4 manifests (75.00%)
707 bundling: 4/4 manifests (100.00%)
707 bundling: 4/4 manifests (100.00%)
708 bundling: D 1/3 files (33.33%)
708 bundling: D 1/3 files (33.33%)
709 bundling: E 2/3 files (66.67%)
709 bundling: E 2/3 files (66.67%)
710 bundling: H 3/3 files (100.00%)
710 bundling: H 3/3 files (100.00%)
711 end of bundle
711 end of bundle
712
712
713 $ cat ../rev.hg2
713 $ cat ../rev.hg2
714 HG2X\x00\x00\x00\x16\x0fb2x:changegroup\x00\x00\x00\x00\x00\x00\x00\x00\x06\x13\x00\x00\x00\xa42\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j_\xdd\xd9\x89W\xc8\xa5JMCm\xfe\x1d\xa9\xd8\x7f!\xa1\xb9{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c (esc)
714 HG2X\x00\x00\x00\x16\x0fb2x:changegroup\x00\x00\x00\x00\x00\x00\x00\x00\x06\x13\x00\x00\x00\xa42\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j_\xdd\xd9\x89W\xc8\xa5JMCm\xfe\x1d\xa9\xd8\x7f!\xa1\xb9{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c (esc)
715 \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02D (esc)
715 \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02D (esc)
716 \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01D\x00\x00\x00\xa4\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xcd\x01\x0b\x8c\xd9\x98\xf3\x98\x1aZ\x81\x15\xf9O\x8d\xa4\xabP`\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)4dece9c826f69490507b98c6383a3009b295837d (esc)
716 \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01D\x00\x00\x00\xa4\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xcd\x01\x0b\x8c\xd9\x98\xf3\x98\x1aZ\x81\x15\xf9O\x8d\xa4\xabP`\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)4dece9c826f69490507b98c6383a3009b295837d (esc)
717 \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02E (esc)
717 \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02E (esc)
718 \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01E\x00\x00\x00\xa2\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)365b93d57fdf4814e2b5911d6bacff2b12014441 (esc)
718 \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01E\x00\x00\x00\xa2\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)365b93d57fdf4814e2b5911d6bacff2b12014441 (esc)
719 \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x00\x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01G\x00\x00\x00\xa4\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
719 \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x00\x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01G\x00\x00\x00\xa4\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
720 \x87\xcd\xc9n\x8e\xaa\xb6$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
720 \x87\xcd\xc9n\x8e\xaa\xb6$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
721 \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)8bee48edc7318541fc0013ee41b089276a8c24bf (esc)
721 \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)8bee48edc7318541fc0013ee41b089276a8c24bf (esc)
722 \x00\x00\x00f\x00\x00\x00f\x00\x00\x00\x02H (esc)
722 \x00\x00\x00f\x00\x00\x00f\x00\x00\x00\x02H (esc)
723 \x00\x00\x00g\x00\x00\x00h\x00\x00\x00\x01H\x00\x00\x00\x00\x00\x00\x00\x8bn\x1fLG\xec\xb53\xff\xd0\xc8\xe5,\xdc\x88\xaf\xb6\xcd9\xe2\x0cf\xa5\xa0\x18\x17\xfd\xf5#\x9c'8\x02\xb5\xb7a\x8d\x05\x1c\x89\xe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+D\x00c3f1ca2924c16a19b0656a84900e504e5b0aec2d (esc)
723 \x00\x00\x00g\x00\x00\x00h\x00\x00\x00\x01H\x00\x00\x00\x00\x00\x00\x00\x8bn\x1fLG\xec\xb53\xff\xd0\xc8\xe5,\xdc\x88\xaf\xb6\xcd9\xe2\x0cf\xa5\xa0\x18\x17\xfd\xf5#\x9c'8\x02\xb5\xb7a\x8d\x05\x1c\x89\xe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+D\x00c3f1ca2924c16a19b0656a84900e504e5b0aec2d (esc)
724 \x00\x00\x00\x8bM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0 \xb2\x95\x83}\x00}\x8c\x9d\x88\x84\x13%\xf5\xc6\xb0cq\xb3[N\x8a+\x1a\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00+\x00\x00\x00\xac\x00\x00\x00+E\x009c6fd0350a6c0d0c49d4a9c5017cf07043f54e58 (esc)
724 \x00\x00\x00\x8bM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0 \xb2\x95\x83}\x00}\x8c\x9d\x88\x84\x13%\xf5\xc6\xb0cq\xb3[N\x8a+\x1a\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00+\x00\x00\x00\xac\x00\x00\x00+E\x009c6fd0350a6c0d0c49d4a9c5017cf07043f54e58 (esc)
725 \x00\x00\x00\x8b6[\x93\xd5\x7f\xdfH\x14\xe2\xb5\x91\x1dk\xac\xff+\x12\x01DA(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xceM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0 \xb2\x95\x83}\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00V\x00\x00\x00V\x00\x00\x00+F\x0022bfcfd62a21a3287edbd4d656218d0f525ed76a (esc)
725 \x00\x00\x00\x8b6[\x93\xd5\x7f\xdfH\x14\xe2\xb5\x91\x1dk\xac\xff+\x12\x01DA(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xceM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0 \xb2\x95\x83}\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00V\x00\x00\x00V\x00\x00\x00+F\x0022bfcfd62a21a3287edbd4d656218d0f525ed76a (esc)
726 \x00\x00\x00\x97\x8b\xeeH\xed\xc71\x85A\xfc\x00\x13\xeeA\xb0\x89'j\x8c$\xbf(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
726 \x00\x00\x00\x97\x8b\xeeH\xed\xc71\x85A\xfc\x00\x13\xeeA\xb0\x89'j\x8c$\xbf(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
727 \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00+\x00\x00\x00V\x00\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+H\x008500189e74a9e0475e822093bc7db0d631aeb0b4 (esc)
727 \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00+\x00\x00\x00V\x00\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+H\x008500189e74a9e0475e822093bc7db0d631aeb0b4 (esc)
728 \x00\x00\x00\x00\x00\x00\x00\x05D\x00\x00\x00b\xc3\xf1\xca)$\xc1j\x19\xb0ej\x84\x90\x0ePN[ (esc)
728 \x00\x00\x00\x00\x00\x00\x00\x05D\x00\x00\x00b\xc3\xf1\xca)$\xc1j\x19\xb0ej\x84\x90\x0ePN[ (esc)
729 \xec-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02D (esc)
729 \xec-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02D (esc)
730 \x00\x00\x00\x00\x00\x00\x00\x05E\x00\x00\x00b\x9co\xd05 (esc)
730 \x00\x00\x00\x00\x00\x00\x00\x05E\x00\x00\x00b\x9co\xd05 (esc)
731 l\r (no-eol) (esc)
731 l\r (no-eol) (esc)
732 \x0cI\xd4\xa9\xc5\x01|\xf0pC\xf5NX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02E (esc)
732 \x0cI\xd4\xa9\xc5\x01|\xf0pC\xf5NX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02E (esc)
733 \x00\x00\x00\x00\x00\x00\x00\x05H\x00\x00\x00b\x85\x00\x18\x9et\xa9\xe0G^\x82 \x93\xbc}\xb0\xd61\xae\xb0\xb4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
733 \x00\x00\x00\x00\x00\x00\x00\x05H\x00\x00\x00b\x85\x00\x18\x9et\xa9\xe0G^\x82 \x93\xbc}\xb0\xd61\xae\xb0\xb4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
734 \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02H (esc)
734 \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02H (esc)
735 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
735 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
736
736
737 $ hg unbundle2 < ../rev.hg2
737 $ hg unbundle2 < ../rev.hg2
738 adding changesets
738 adding changesets
739 adding manifests
739 adding manifests
740 adding file changes
740 adding file changes
741 added 0 changesets with 0 changes to 3 files
741 added 0 changesets with 0 changes to 3 files
742 0 unread bytes
742 0 unread bytes
743 addchangegroup return: 1
743 addchangegroup return: 1
744
744
745 with reply
745 with reply
746
746
747 $ hg bundle2 --rev '8+7+5+4' --reply ../rev-rr.hg2
747 $ hg bundle2 --rev '8+7+5+4' --reply ../rev-rr.hg2
748 $ hg unbundle2 ../rev-reply.hg2 < ../rev-rr.hg2
748 $ hg unbundle2 ../rev-reply.hg2 < ../rev-rr.hg2
749 0 unread bytes
749 0 unread bytes
750 addchangegroup return: 1
750 addchangegroup return: 1
751
751
752 $ cat ../rev-reply.hg2
752 $ cat ../rev-reply.hg2
753 HG2X\x00\x00\x003\x15b2x:reply:changegroup\x00\x00\x00\x00\x00\x02\x0b\x01\x06\x01in-reply-to1return1\x00\x00\x00\x00\x00\x1f (esc)
753 HG2X\x00\x00\x003\x15b2x:reply:changegroup\x00\x00\x00\x00\x00\x02\x0b\x01\x06\x01in-reply-to1return1\x00\x00\x00\x00\x00\x1f (esc)
754 b2x:output\x00\x00\x00\x01\x00\x01\x0b\x01in-reply-to1\x00\x00\x00dadding changesets (esc)
754 b2x:output\x00\x00\x00\x01\x00\x01\x0b\x01in-reply-to1\x00\x00\x00dadding changesets (esc)
755 adding manifests
755 adding manifests
756 adding file changes
756 adding file changes
757 added 0 changesets with 0 changes to 3 files
757 added 0 changesets with 0 changes to 3 files
758 \x00\x00\x00\x00\x00\x00 (no-eol) (esc)
758 \x00\x00\x00\x00\x00\x00 (no-eol) (esc)
759
759
760 Real world exchange
760 Real world exchange
761 =====================
761 =====================
762
762
763
763
764 clone --pull
764 clone --pull
765
765
766 $ cd ..
766 $ cd ..
767 $ hg -R main phase --public cd010b8cd998
767 $ hg -R main phase --public cd010b8cd998
768 $ hg clone main other --pull --rev 9520eea781bc
768 $ hg clone main other --pull --rev 9520eea781bc
769 adding changesets
769 adding changesets
770 adding manifests
770 adding manifests
771 adding file changes
771 adding file changes
772 added 2 changesets with 2 changes to 2 files
772 added 2 changesets with 2 changes to 2 files
773 updating to branch default
773 updating to branch default
774 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
774 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
775 $ hg -R other log -G
775 $ hg -R other log -G
776 @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
776 @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
777 |
777 |
778 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
778 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
779
779
780
780
781 pull
781 pull
782
782
783 $ hg -R main phase --public 9520eea781bc
783 $ hg -R main phase --public 9520eea781bc
784 $ hg -R other pull -r 24b6387c8c8c
784 $ hg -R other pull -r 24b6387c8c8c
785 pulling from $TESTTMP/main (glob)
785 pulling from $TESTTMP/main (glob)
786 searching for changes
786 searching for changes
787 adding changesets
787 adding changesets
788 adding manifests
788 adding manifests
789 adding file changes
789 adding file changes
790 added 1 changesets with 1 changes to 1 files (+1 heads)
790 added 1 changesets with 1 changes to 1 files (+1 heads)
791 (run 'hg heads' to see heads, 'hg merge' to merge)
791 (run 'hg heads' to see heads, 'hg merge' to merge)
792 $ hg -R other log -G
792 $ hg -R other log -G
793 o 2:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
793 o 2:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
794 |
794 |
795 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
795 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
796 |/
796 |/
797 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
797 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
798
798
799
799
800 pull empty (with phase movement)
800 pull empty (with phase movement)
801
801
802 $ hg -R main phase --public 24b6387c8c8c
802 $ hg -R main phase --public 24b6387c8c8c
803 $ hg -R other pull -r 24b6387c8c8c
803 $ hg -R other pull -r 24b6387c8c8c
804 pulling from $TESTTMP/main (glob)
804 pulling from $TESTTMP/main (glob)
805 no changes found
805 no changes found
806 $ hg -R other log -G
806 $ hg -R other log -G
807 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
807 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
808 |
808 |
809 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
809 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
810 |/
810 |/
811 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
811 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
812
812
813 pull empty
813 pull empty
814
814
815 $ hg -R other pull -r 24b6387c8c8c
815 $ hg -R other pull -r 24b6387c8c8c
816 pulling from $TESTTMP/main (glob)
816 pulling from $TESTTMP/main (glob)
817 no changes found
817 no changes found
818 $ hg -R other log -G
818 $ hg -R other log -G
819 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
819 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
820 |
820 |
821 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
821 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
822 |/
822 |/
823 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
823 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
824
824
825
825
826 push
826 push
827
827
828 $ hg -R main phase --public eea13746799a
828 $ hg -R main phase --public eea13746799a
829 $ hg -R main push other --rev eea13746799a
829 $ hg -R main push other --rev eea13746799a
830 pushing to other
830 pushing to other
831 searching for changes
831 searching for changes
832 remote: adding changesets
832 remote: adding changesets
833 remote: adding manifests
833 remote: adding manifests
834 remote: adding file changes
834 remote: adding file changes
835 remote: added 1 changesets with 0 changes to 0 files (-1 heads)
835 remote: added 1 changesets with 0 changes to 0 files (-1 heads)
836 $ hg -R other log -G
836 $ hg -R other log -G
837 o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> G
837 o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> G
838 |\
838 |\
839 | o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
839 | o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
840 | |
840 | |
841 @ | 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
841 @ | 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
842 |/
842 |/
843 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
843 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
844
844
845
845
846 pull over ssh
846 pull over ssh
847
847
848 $ hg -R other pull ssh://user@dummy/main -r 02de42196ebe --traceback
848 $ hg -R other pull ssh://user@dummy/main -r 02de42196ebe --traceback
849 pulling from ssh://user@dummy/main
849 pulling from ssh://user@dummy/main
850 searching for changes
850 searching for changes
851 adding changesets
851 adding changesets
852 adding manifests
852 adding manifests
853 adding file changes
853 adding file changes
854 added 1 changesets with 1 changes to 1 files (+1 heads)
854 added 1 changesets with 1 changes to 1 files (+1 heads)
855 (run 'hg heads' to see heads, 'hg merge' to merge)
855 (run 'hg heads' to see heads, 'hg merge' to merge)
856
856
857 pull over http
857 pull over http
858
858
859 $ hg -R main serve -p $HGPORT -d --pid-file=main.pid -E main-error.log
859 $ hg -R main serve -p $HGPORT -d --pid-file=main.pid -E main-error.log
860 $ cat main.pid >> $DAEMON_PIDS
860 $ cat main.pid >> $DAEMON_PIDS
861
861
862 $ hg -R other pull http://localhost:$HGPORT/ -r 42ccdea3bb16
862 $ hg -R other pull http://localhost:$HGPORT/ -r 42ccdea3bb16
863 pulling from http://localhost:$HGPORT/
863 pulling from http://localhost:$HGPORT/
864 searching for changes
864 searching for changes
865 adding changesets
865 adding changesets
866 adding manifests
866 adding manifests
867 adding file changes
867 adding file changes
868 added 1 changesets with 1 changes to 1 files (+1 heads)
868 added 1 changesets with 1 changes to 1 files (+1 heads)
869 (run 'hg heads .' to see heads, 'hg merge' to merge)
869 (run 'hg heads .' to see heads, 'hg merge' to merge)
870 $ cat main-error.log
870 $ cat main-error.log
871
871
872 push over ssh
872 push over ssh
873
873
874 $ hg -R main push ssh://user@dummy/other -r 5fddd98957c8
874 $ hg -R main push ssh://user@dummy/other -r 5fddd98957c8
875 pushing to ssh://user@dummy/other
875 pushing to ssh://user@dummy/other
876 searching for changes
876 searching for changes
877 remote: adding changesets
877 remote: adding changesets
878 remote: adding manifests
878 remote: adding manifests
879 remote: adding file changes
879 remote: adding file changes
880 remote: added 1 changesets with 1 changes to 1 files
880 remote: added 1 changesets with 1 changes to 1 files
881 $ hg -R other log -G
881 $ hg -R other log -G
882 o 6:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> C
882 o 6:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> C
883 |
883 |
884 o 5:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> B
884 o 5:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> B
885 |
885 |
886 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> H
886 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> H
887 | |
887 | |
888 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> G
888 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> G
889 | |/|
889 | |/|
890 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
890 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
891 |/ /
891 |/ /
892 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
892 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
893 |/
893 |/
894 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
894 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
895
895
896
896
897 push over http
897 push over http
898
898
899 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
899 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
900 $ cat other.pid >> $DAEMON_PIDS
900 $ cat other.pid >> $DAEMON_PIDS
901
901
902 $ hg -R main phase --public 32af7686d403
902 $ hg -R main phase --public 32af7686d403
903 $ hg -R main push http://localhost:$HGPORT2/ -r 32af7686d403
903 $ hg -R main push http://localhost:$HGPORT2/ -r 32af7686d403
904 pushing to http://localhost:$HGPORT2/
904 pushing to http://localhost:$HGPORT2/
905 searching for changes
905 searching for changes
906 remote: adding changesets
906 remote: adding changesets
907 remote: adding manifests
907 remote: adding manifests
908 remote: adding file changes
908 remote: adding file changes
909 remote: added 1 changesets with 1 changes to 1 files
909 remote: added 1 changesets with 1 changes to 1 files
910 $ cat other-error.log
910 $ cat other-error.log
911
911
912 Check final content.
912 Check final content.
913
913
914 $ hg -R other log -G
914 $ hg -R other log -G
915 o 7:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> D
915 o 7:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> D
916 |
916 |
917 o 6:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> C
917 o 6:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> C
918 |
918 |
919 o 5:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> B
919 o 5:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> B
920 |
920 |
921 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> H
921 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> H
922 | |
922 | |
923 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> G
923 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> G
924 | |/|
924 | |/|
925 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
925 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
926 |/ /
926 |/ /
927 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
927 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
928 |/
928 |/
929 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
929 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
930
930
931
931
932 Error Handling
932 Error Handling
933 ==============
933 ==============
934
934
935 Check that errors are properly returned to the client during push.
935 Check that errors are properly returned to the client during push.
936
936
937 Setting up
937 Setting up
938
938
939 $ cat > failpush.py << EOF
939 $ cat > failpush.py << EOF
940 > """A small extension that makes push fails when using bundle2
940 > """A small extension that makes push fails when using bundle2
941 >
941 >
942 > used to test error handling in bundle2
942 > used to test error handling in bundle2
943 > """
943 > """
944 >
944 >
945 > from mercurial import util
945 > from mercurial import util
946 > from mercurial import bundle2
946 > from mercurial import bundle2
947 > from mercurial import exchange
947 > from mercurial import exchange
948 > from mercurial import extensions
948 > from mercurial import extensions
949 >
949 >
950 > def _pushbundle2failpart(pushop, bundler):
950 > def _pushbundle2failpart(pushop, bundler):
951 > reason = pushop.ui.config('failpush', 'reason', None)
951 > reason = pushop.ui.config('failpush', 'reason', None)
952 > part = None
952 > part = None
953 > if reason == 'abort':
953 > if reason == 'abort':
954 > bundler.newpart('test:abort')
954 > bundler.newpart('test:abort')
955 > if reason == 'unknown':
955 > if reason == 'unknown':
956 > bundler.newpart('TEST:UNKNOWN')
956 > bundler.newpart('TEST:UNKNOWN')
957 > if reason == 'race':
957 > if reason == 'race':
958 > # 20 Bytes of crap
958 > # 20 Bytes of crap
959 > bundler.newpart('b2x:check:heads', data='01234567890123456789')
959 > bundler.newpart('b2x:check:heads', data='01234567890123456789')
960 > return lambda op: None
961 >
960 >
962 > @bundle2.parthandler("test:abort")
961 > @bundle2.parthandler("test:abort")
963 > def handleabort(op, part):
962 > def handleabort(op, part):
964 > raise util.Abort('Abandon ship!', hint="don't panic")
963 > raise util.Abort('Abandon ship!', hint="don't panic")
965 >
964 >
966 > def uisetup(ui):
965 > def uisetup(ui):
967 > exchange.bundle2partsgenerators.insert(0, _pushbundle2failpart)
966 > exchange.bundle2partsgenerators.insert(0, _pushbundle2failpart)
968 >
967 >
969 > EOF
968 > EOF
970
969
971 $ cd main
970 $ cd main
972 $ hg up tip
971 $ hg up tip
973 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
972 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
974 $ echo 'I' > I
973 $ echo 'I' > I
975 $ hg add I
974 $ hg add I
976 $ hg ci -m 'I'
975 $ hg ci -m 'I'
977 $ hg id
976 $ hg id
978 e7ec4e813ba6 tip
977 e7ec4e813ba6 tip
979 $ cd ..
978 $ cd ..
980
979
981 $ cat << EOF >> $HGRCPATH
980 $ cat << EOF >> $HGRCPATH
982 > [extensions]
981 > [extensions]
983 > failpush=$TESTTMP/failpush.py
982 > failpush=$TESTTMP/failpush.py
984 > EOF
983 > EOF
985
984
986 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
985 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
987 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
986 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
988 $ cat other.pid >> $DAEMON_PIDS
987 $ cat other.pid >> $DAEMON_PIDS
989
988
990 Doing the actual push: Abort error
989 Doing the actual push: Abort error
991
990
992 $ cat << EOF >> $HGRCPATH
991 $ cat << EOF >> $HGRCPATH
993 > [failpush]
992 > [failpush]
994 > reason = abort
993 > reason = abort
995 > EOF
994 > EOF
996
995
997 $ hg -R main push other -r e7ec4e813ba6
996 $ hg -R main push other -r e7ec4e813ba6
998 pushing to other
997 pushing to other
999 searching for changes
998 searching for changes
1000 abort: Abandon ship!
999 abort: Abandon ship!
1001 (don't panic)
1000 (don't panic)
1002 [255]
1001 [255]
1003
1002
1004 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
1003 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
1005 pushing to ssh://user@dummy/other
1004 pushing to ssh://user@dummy/other
1006 searching for changes
1005 searching for changes
1007 abort: Abandon ship!
1006 abort: Abandon ship!
1008 (don't panic)
1007 (don't panic)
1009 [255]
1008 [255]
1010
1009
1011 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
1010 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
1012 pushing to http://localhost:$HGPORT2/
1011 pushing to http://localhost:$HGPORT2/
1013 searching for changes
1012 searching for changes
1014 abort: Abandon ship!
1013 abort: Abandon ship!
1015 (don't panic)
1014 (don't panic)
1016 [255]
1015 [255]
1017
1016
1018
1017
1019 Doing the actual push: unknown mandatory parts
1018 Doing the actual push: unknown mandatory parts
1020
1019
1021 $ cat << EOF >> $HGRCPATH
1020 $ cat << EOF >> $HGRCPATH
1022 > [failpush]
1021 > [failpush]
1023 > reason = unknown
1022 > reason = unknown
1024 > EOF
1023 > EOF
1025
1024
1026 $ hg -R main push other -r e7ec4e813ba6
1025 $ hg -R main push other -r e7ec4e813ba6
1027 pushing to other
1026 pushing to other
1028 searching for changes
1027 searching for changes
1029 abort: missing support for test:unknown
1028 abort: missing support for test:unknown
1030 [255]
1029 [255]
1031
1030
1032 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
1031 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
1033 pushing to ssh://user@dummy/other
1032 pushing to ssh://user@dummy/other
1034 searching for changes
1033 searching for changes
1035 abort: missing support for test:unknown
1034 abort: missing support for test:unknown
1036 [255]
1035 [255]
1037
1036
1038 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
1037 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
1039 pushing to http://localhost:$HGPORT2/
1038 pushing to http://localhost:$HGPORT2/
1040 searching for changes
1039 searching for changes
1041 abort: missing support for test:unknown
1040 abort: missing support for test:unknown
1042 [255]
1041 [255]
1043
1042
1044 Doing the actual push: race
1043 Doing the actual push: race
1045
1044
1046 $ cat << EOF >> $HGRCPATH
1045 $ cat << EOF >> $HGRCPATH
1047 > [failpush]
1046 > [failpush]
1048 > reason = race
1047 > reason = race
1049 > EOF
1048 > EOF
1050
1049
1051 $ hg -R main push other -r e7ec4e813ba6
1050 $ hg -R main push other -r e7ec4e813ba6
1052 pushing to other
1051 pushing to other
1053 searching for changes
1052 searching for changes
1054 abort: push failed:
1053 abort: push failed:
1055 'repository changed while pushing - please try again'
1054 'repository changed while pushing - please try again'
1056 [255]
1055 [255]
1057
1056
1058 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
1057 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
1059 pushing to ssh://user@dummy/other
1058 pushing to ssh://user@dummy/other
1060 searching for changes
1059 searching for changes
1061 abort: push failed:
1060 abort: push failed:
1062 'repository changed while pushing - please try again'
1061 'repository changed while pushing - please try again'
1063 [255]
1062 [255]
1064
1063
1065 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
1064 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
1066 pushing to http://localhost:$HGPORT2/
1065 pushing to http://localhost:$HGPORT2/
1067 searching for changes
1066 searching for changes
1068 abort: push failed:
1067 abort: push failed:
1069 'repository changed while pushing - please try again'
1068 'repository changed while pushing - please try again'
1070 [255]
1069 [255]
1071
1070
1072 Doing the actual push: hook abort
1071 Doing the actual push: hook abort
1073
1072
1074 $ cat << EOF >> $HGRCPATH
1073 $ cat << EOF >> $HGRCPATH
1075 > [failpush]
1074 > [failpush]
1076 > reason =
1075 > reason =
1077 > [hooks]
1076 > [hooks]
1078 > b2x-pretransactionclose.failpush = false
1077 > b2x-pretransactionclose.failpush = false
1079 > EOF
1078 > EOF
1080
1079
1081 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
1080 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
1082 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
1081 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
1083 $ cat other.pid >> $DAEMON_PIDS
1082 $ cat other.pid >> $DAEMON_PIDS
1084
1083
1085 $ hg -R main push other -r e7ec4e813ba6
1084 $ hg -R main push other -r e7ec4e813ba6
1086 pushing to other
1085 pushing to other
1087 searching for changes
1086 searching for changes
1088 transaction abort!
1087 transaction abort!
1089 rollback completed
1088 rollback completed
1090 abort: b2x-pretransactionclose.failpush hook exited with status 1
1089 abort: b2x-pretransactionclose.failpush hook exited with status 1
1091 [255]
1090 [255]
1092
1091
1093 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
1092 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
1094 pushing to ssh://user@dummy/other
1093 pushing to ssh://user@dummy/other
1095 searching for changes
1094 searching for changes
1096 abort: b2x-pretransactionclose.failpush hook exited with status 1
1095 abort: b2x-pretransactionclose.failpush hook exited with status 1
1097 remote: transaction abort!
1096 remote: transaction abort!
1098 remote: rollback completed
1097 remote: rollback completed
1099 [255]
1098 [255]
1100
1099
1101 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
1100 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
1102 pushing to http://localhost:$HGPORT2/
1101 pushing to http://localhost:$HGPORT2/
1103 searching for changes
1102 searching for changes
1104 abort: b2x-pretransactionclose.failpush hook exited with status 1
1103 abort: b2x-pretransactionclose.failpush hook exited with status 1
1105 [255]
1104 [255]
1106
1105
1107
1106
General Comments 0
You need to be logged in to leave comments. Login now