##// END OF EJS Templates
changegroup: move message about added changes to transaction summary...
marmoute -
r43167:d7304434 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,1426 +1,1441 b''
1 1 # changegroup.py - Mercurial changegroup manipulation functions
2 2 #
3 3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import os
11 11 import struct
12 12 import weakref
13 13
14 14 from .i18n import _
15 15 from .node import (
16 16 hex,
17 17 nullid,
18 18 nullrev,
19 19 short,
20 20 )
21 21
22 22 from . import (
23 23 error,
24 24 match as matchmod,
25 25 mdiff,
26 26 phases,
27 27 pycompat,
28 28 util,
29 29 )
30 30
31 31 from .interfaces import (
32 32 repository,
33 33 )
34 34
35 35 _CHANGEGROUPV1_DELTA_HEADER = struct.Struct("20s20s20s20s")
36 36 _CHANGEGROUPV2_DELTA_HEADER = struct.Struct("20s20s20s20s20s")
37 37 _CHANGEGROUPV3_DELTA_HEADER = struct.Struct(">20s20s20s20s20sH")
38 38
39 39 LFS_REQUIREMENT = 'lfs'
40 40
41 41 readexactly = util.readexactly
42 42
43 43 def getchunk(stream):
44 44 """return the next chunk from stream as a string"""
45 45 d = readexactly(stream, 4)
46 46 l = struct.unpack(">l", d)[0]
47 47 if l <= 4:
48 48 if l:
49 49 raise error.Abort(_("invalid chunk length %d") % l)
50 50 return ""
51 51 return readexactly(stream, l - 4)
52 52
53 53 def chunkheader(length):
54 54 """return a changegroup chunk header (string)"""
55 55 return struct.pack(">l", length + 4)
56 56
57 57 def closechunk():
58 58 """return a changegroup chunk header (string) for a zero-length chunk"""
59 59 return struct.pack(">l", 0)
60 60
61 61 def _fileheader(path):
62 62 """Obtain a changegroup chunk header for a named path."""
63 63 return chunkheader(len(path)) + path
64 64
65 65 def writechunks(ui, chunks, filename, vfs=None):
66 66 """Write chunks to a file and return its filename.
67 67
68 68 The stream is assumed to be a bundle file.
69 69 Existing files will not be overwritten.
70 70 If no filename is specified, a temporary file is created.
71 71 """
72 72 fh = None
73 73 cleanup = None
74 74 try:
75 75 if filename:
76 76 if vfs:
77 77 fh = vfs.open(filename, "wb")
78 78 else:
79 79 # Increase default buffer size because default is usually
80 80 # small (4k is common on Linux).
81 81 fh = open(filename, "wb", 131072)
82 82 else:
83 83 fd, filename = pycompat.mkstemp(prefix="hg-bundle-", suffix=".hg")
84 84 fh = os.fdopen(fd, r"wb")
85 85 cleanup = filename
86 86 for c in chunks:
87 87 fh.write(c)
88 88 cleanup = None
89 89 return filename
90 90 finally:
91 91 if fh is not None:
92 92 fh.close()
93 93 if cleanup is not None:
94 94 if filename and vfs:
95 95 vfs.unlink(cleanup)
96 96 else:
97 97 os.unlink(cleanup)
98 98
99 99 class cg1unpacker(object):
100 100 """Unpacker for cg1 changegroup streams.
101 101
102 102 A changegroup unpacker handles the framing of the revision data in
103 103 the wire format. Most consumers will want to use the apply()
104 104 method to add the changes from the changegroup to a repository.
105 105
106 106 If you're forwarding a changegroup unmodified to another consumer,
107 107 use getchunks(), which returns an iterator of changegroup
108 108 chunks. This is mostly useful for cases where you need to know the
109 109 data stream has ended by observing the end of the changegroup.
110 110
111 111 deltachunk() is useful only if you're applying delta data. Most
112 112 consumers should prefer apply() instead.
113 113
114 114 A few other public methods exist. Those are used only for
115 115 bundlerepo and some debug commands - their use is discouraged.
116 116 """
117 117 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
118 118 deltaheadersize = deltaheader.size
119 119 version = '01'
120 120 _grouplistcount = 1 # One list of files after the manifests
121 121
122 122 def __init__(self, fh, alg, extras=None):
123 123 if alg is None:
124 124 alg = 'UN'
125 125 if alg not in util.compengines.supportedbundletypes:
126 126 raise error.Abort(_('unknown stream compression type: %s')
127 127 % alg)
128 128 if alg == 'BZ':
129 129 alg = '_truncatedBZ'
130 130
131 131 compengine = util.compengines.forbundletype(alg)
132 132 self._stream = compengine.decompressorreader(fh)
133 133 self._type = alg
134 134 self.extras = extras or {}
135 135 self.callback = None
136 136
137 137 # These methods (compressed, read, seek, tell) all appear to only
138 138 # be used by bundlerepo, but it's a little hard to tell.
139 139 def compressed(self):
140 140 return self._type is not None and self._type != 'UN'
141 141 def read(self, l):
142 142 return self._stream.read(l)
143 143 def seek(self, pos):
144 144 return self._stream.seek(pos)
145 145 def tell(self):
146 146 return self._stream.tell()
147 147 def close(self):
148 148 return self._stream.close()
149 149
150 150 def _chunklength(self):
151 151 d = readexactly(self._stream, 4)
152 152 l = struct.unpack(">l", d)[0]
153 153 if l <= 4:
154 154 if l:
155 155 raise error.Abort(_("invalid chunk length %d") % l)
156 156 return 0
157 157 if self.callback:
158 158 self.callback()
159 159 return l - 4
160 160
161 161 def changelogheader(self):
162 162 """v10 does not have a changelog header chunk"""
163 163 return {}
164 164
165 165 def manifestheader(self):
166 166 """v10 does not have a manifest header chunk"""
167 167 return {}
168 168
169 169 def filelogheader(self):
170 170 """return the header of the filelogs chunk, v10 only has the filename"""
171 171 l = self._chunklength()
172 172 if not l:
173 173 return {}
174 174 fname = readexactly(self._stream, l)
175 175 return {'filename': fname}
176 176
177 177 def _deltaheader(self, headertuple, prevnode):
178 178 node, p1, p2, cs = headertuple
179 179 if prevnode is None:
180 180 deltabase = p1
181 181 else:
182 182 deltabase = prevnode
183 183 flags = 0
184 184 return node, p1, p2, deltabase, cs, flags
185 185
186 186 def deltachunk(self, prevnode):
187 187 l = self._chunklength()
188 188 if not l:
189 189 return {}
190 190 headerdata = readexactly(self._stream, self.deltaheadersize)
191 191 header = self.deltaheader.unpack(headerdata)
192 192 delta = readexactly(self._stream, l - self.deltaheadersize)
193 193 node, p1, p2, deltabase, cs, flags = self._deltaheader(header, prevnode)
194 194 return (node, p1, p2, cs, deltabase, delta, flags)
195 195
196 196 def getchunks(self):
197 197 """returns all the chunks contains in the bundle
198 198
199 199 Used when you need to forward the binary stream to a file or another
200 200 network API. To do so, it parse the changegroup data, otherwise it will
201 201 block in case of sshrepo because it don't know the end of the stream.
202 202 """
203 203 # For changegroup 1 and 2, we expect 3 parts: changelog, manifestlog,
204 204 # and a list of filelogs. For changegroup 3, we expect 4 parts:
205 205 # changelog, manifestlog, a list of tree manifestlogs, and a list of
206 206 # filelogs.
207 207 #
208 208 # Changelog and manifestlog parts are terminated with empty chunks. The
209 209 # tree and file parts are a list of entry sections. Each entry section
210 210 # is a series of chunks terminating in an empty chunk. The list of these
211 211 # entry sections is terminated in yet another empty chunk, so we know
212 212 # we've reached the end of the tree/file list when we reach an empty
213 213 # chunk that was proceeded by no non-empty chunks.
214 214
215 215 parts = 0
216 216 while parts < 2 + self._grouplistcount:
217 217 noentries = True
218 218 while True:
219 219 chunk = getchunk(self)
220 220 if not chunk:
221 221 # The first two empty chunks represent the end of the
222 222 # changelog and the manifestlog portions. The remaining
223 223 # empty chunks represent either A) the end of individual
224 224 # tree or file entries in the file list, or B) the end of
225 225 # the entire list. It's the end of the entire list if there
226 226 # were no entries (i.e. noentries is True).
227 227 if parts < 2:
228 228 parts += 1
229 229 elif noentries:
230 230 parts += 1
231 231 break
232 232 noentries = False
233 233 yield chunkheader(len(chunk))
234 234 pos = 0
235 235 while pos < len(chunk):
236 236 next = pos + 2**20
237 237 yield chunk[pos:next]
238 238 pos = next
239 239 yield closechunk()
240 240
241 241 def _unpackmanifests(self, repo, revmap, trp, prog):
242 242 self.callback = prog.increment
243 243 # no need to check for empty manifest group here:
244 244 # if the result of the merge of 1 and 2 is the same in 3 and 4,
245 245 # no new manifest will be created and the manifest group will
246 246 # be empty during the pull
247 247 self.manifestheader()
248 248 deltas = self.deltaiter()
249 249 repo.manifestlog.getstorage(b'').addgroup(deltas, revmap, trp)
250 250 prog.complete()
251 251 self.callback = None
252 252
253 253 def apply(self, repo, tr, srctype, url, targetphase=phases.draft,
254 254 expectedtotal=None):
255 255 """Add the changegroup returned by source.read() to this repo.
256 256 srctype is a string like 'push', 'pull', or 'unbundle'. url is
257 257 the URL of the repo where this changegroup is coming from.
258 258
259 259 Return an integer summarizing the change to this repo:
260 260 - nothing changed or no source: 0
261 261 - more heads than before: 1+added heads (2..n)
262 262 - fewer heads than before: -1-removed heads (-2..-n)
263 263 - number of heads stays the same: 1
264 264 """
265 265 repo = repo.unfiltered()
266 266 def csmap(x):
267 267 repo.ui.debug("add changeset %s\n" % short(x))
268 268 return len(cl)
269 269
270 270 def revmap(x):
271 271 return cl.rev(x)
272 272
273 changesets = files = revisions = 0
273 changesets = 0
274 274
275 275 try:
276 276 # The transaction may already carry source information. In this
277 277 # case we use the top level data. We overwrite the argument
278 278 # because we need to use the top level value (if they exist)
279 279 # in this function.
280 280 srctype = tr.hookargs.setdefault('source', srctype)
281 281 tr.hookargs.setdefault('url', url)
282 282 repo.hook('prechangegroup',
283 283 throw=True, **pycompat.strkwargs(tr.hookargs))
284 284
285 285 # write changelog data to temp files so concurrent readers
286 286 # will not see an inconsistent view
287 287 cl = repo.changelog
288 288 cl.delayupdate(tr)
289 289 oldheads = set(cl.heads())
290 290
291 291 trp = weakref.proxy(tr)
292 292 # pull off the changeset group
293 293 repo.ui.status(_("adding changesets\n"))
294 294 clstart = len(cl)
295 295 progress = repo.ui.makeprogress(_('changesets'), unit=_('chunks'),
296 296 total=expectedtotal)
297 297 self.callback = progress.increment
298 298
299 299 efiles = set()
300 300 def onchangelog(cl, node):
301 301 efiles.update(cl.readfiles(node))
302 302
303 303 self.changelogheader()
304 304 deltas = self.deltaiter()
305 305 cgnodes = cl.addgroup(deltas, csmap, trp, addrevisioncb=onchangelog)
306 306 efiles = len(efiles)
307 307
308 308 if not cgnodes:
309 309 repo.ui.develwarn('applied empty changelog from changegroup',
310 310 config='warn-empty-changegroup')
311 311 clend = len(cl)
312 312 changesets = clend - clstart
313 313 progress.complete()
314 314 self.callback = None
315 315
316 316 # pull off the manifest group
317 317 repo.ui.status(_("adding manifests\n"))
318 318 # We know that we'll never have more manifests than we had
319 319 # changesets.
320 320 progress = repo.ui.makeprogress(_('manifests'), unit=_('chunks'),
321 321 total=changesets)
322 322 self._unpackmanifests(repo, revmap, trp, progress)
323 323
324 324 needfiles = {}
325 325 if repo.ui.configbool('server', 'validate'):
326 326 cl = repo.changelog
327 327 ml = repo.manifestlog
328 328 # validate incoming csets have their manifests
329 329 for cset in pycompat.xrange(clstart, clend):
330 330 mfnode = cl.changelogrevision(cset).manifest
331 331 mfest = ml[mfnode].readdelta()
332 332 # store file cgnodes we must see
333 333 for f, n in mfest.iteritems():
334 334 needfiles.setdefault(f, set()).add(n)
335 335
336 336 # process the files
337 337 repo.ui.status(_("adding file changes\n"))
338 338 newrevs, newfiles = _addchangegroupfiles(
339 339 repo, self, revmap, trp, efiles, needfiles)
340 revisions += newrevs
341 files += newfiles
340
341 # making sure the value exists
342 tr.changes.setdefault('changegroup-count-changesets', 0)
343 tr.changes.setdefault('changegroup-count-revisions', 0)
344 tr.changes.setdefault('changegroup-count-files', 0)
345 tr.changes.setdefault('changegroup-count-heads', 0)
346
347 # some code use bundle operation for internal purpose. They usually
348 # set `ui.quiet` to do this outside of user sight. Size the report
349 # of such operation now happens at the end of the transaction, that
350 # ui.quiet has not direct effect on the output.
351 #
352 # To preserve this intend use an inelegant hack, we fail to report
353 # the change if `quiet` is set. We should probably move to
354 # something better, but this is a good first step to allow the "end
355 # of transaction report" to pass tests.
356 if not repo.ui.quiet:
357 tr.changes['changegroup-count-changesets'] += changesets
358 tr.changes['changegroup-count-revisions'] += newrevs
359 tr.changes['changegroup-count-files'] += newfiles
342 360
343 361 deltaheads = 0
344 362 if oldheads:
345 363 heads = cl.heads()
346 deltaheads = len(heads) - len(oldheads)
364 deltaheads += len(heads) - len(oldheads)
347 365 for h in heads:
348 366 if h not in oldheads and repo[h].closesbranch():
349 367 deltaheads -= 1
350 htext = ""
351 if deltaheads:
352 htext = _(" (%+d heads)") % deltaheads
353 368
354 repo.ui.status(_("added %d changesets"
355 " with %d changes to %d files%s\n")
356 % (changesets, revisions, files, htext))
369 # see previous comment about checking ui.quiet
370 if not repo.ui.quiet:
371 tr.changes['changegroup-count-heads'] += deltaheads
357 372 repo.invalidatevolatilesets()
358 373
359 374 if changesets > 0:
360 375 if 'node' not in tr.hookargs:
361 376 tr.hookargs['node'] = hex(cl.node(clstart))
362 377 tr.hookargs['node_last'] = hex(cl.node(clend - 1))
363 378 hookargs = dict(tr.hookargs)
364 379 else:
365 380 hookargs = dict(tr.hookargs)
366 381 hookargs['node'] = hex(cl.node(clstart))
367 382 hookargs['node_last'] = hex(cl.node(clend - 1))
368 383 repo.hook('pretxnchangegroup',
369 384 throw=True, **pycompat.strkwargs(hookargs))
370 385
371 386 added = [cl.node(r) for r in pycompat.xrange(clstart, clend)]
372 387 phaseall = None
373 388 if srctype in ('push', 'serve'):
374 389 # Old servers can not push the boundary themselves.
375 390 # New servers won't push the boundary if changeset already
376 391 # exists locally as secret
377 392 #
378 393 # We should not use added here but the list of all change in
379 394 # the bundle
380 395 if repo.publishing():
381 396 targetphase = phaseall = phases.public
382 397 else:
383 398 # closer target phase computation
384 399
385 400 # Those changesets have been pushed from the
386 401 # outside, their phases are going to be pushed
387 402 # alongside. Therefor `targetphase` is
388 403 # ignored.
389 404 targetphase = phaseall = phases.draft
390 405 if added:
391 406 phases.registernew(repo, tr, targetphase, added)
392 407 if phaseall is not None:
393 408 phases.advanceboundary(repo, tr, phaseall, cgnodes)
394 409
395 410 if changesets > 0:
396 411
397 412 def runhooks():
398 413 # These hooks run when the lock releases, not when the
399 414 # transaction closes. So it's possible for the changelog
400 415 # to have changed since we last saw it.
401 416 if clstart >= len(repo):
402 417 return
403 418
404 419 repo.hook("changegroup", **pycompat.strkwargs(hookargs))
405 420
406 421 for n in added:
407 422 args = hookargs.copy()
408 423 args['node'] = hex(n)
409 424 del args['node_last']
410 425 repo.hook("incoming", **pycompat.strkwargs(args))
411 426
412 427 newheads = [h for h in repo.heads()
413 428 if h not in oldheads]
414 429 repo.ui.log("incoming",
415 430 "%d incoming changes - new heads: %s\n",
416 431 len(added),
417 432 ', '.join([hex(c[:6]) for c in newheads]))
418 433
419 434 tr.addpostclose('changegroup-runhooks-%020i' % clstart,
420 435 lambda tr: repo._afterlock(runhooks))
421 436 finally:
422 437 repo.ui.flush()
423 438 # never return 0 here:
424 439 if deltaheads < 0:
425 440 ret = deltaheads - 1
426 441 else:
427 442 ret = deltaheads + 1
428 443 return ret
429 444
430 445 def deltaiter(self):
431 446 """
432 447 returns an iterator of the deltas in this changegroup
433 448
434 449 Useful for passing to the underlying storage system to be stored.
435 450 """
436 451 chain = None
437 452 for chunkdata in iter(lambda: self.deltachunk(chain), {}):
438 453 # Chunkdata: (node, p1, p2, cs, deltabase, delta, flags)
439 454 yield chunkdata
440 455 chain = chunkdata[0]
441 456
442 457 class cg2unpacker(cg1unpacker):
443 458 """Unpacker for cg2 streams.
444 459
445 460 cg2 streams add support for generaldelta, so the delta header
446 461 format is slightly different. All other features about the data
447 462 remain the same.
448 463 """
449 464 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
450 465 deltaheadersize = deltaheader.size
451 466 version = '02'
452 467
453 468 def _deltaheader(self, headertuple, prevnode):
454 469 node, p1, p2, deltabase, cs = headertuple
455 470 flags = 0
456 471 return node, p1, p2, deltabase, cs, flags
457 472
458 473 class cg3unpacker(cg2unpacker):
459 474 """Unpacker for cg3 streams.
460 475
461 476 cg3 streams add support for exchanging treemanifests and revlog
462 477 flags. It adds the revlog flags to the delta header and an empty chunk
463 478 separating manifests and files.
464 479 """
465 480 deltaheader = _CHANGEGROUPV3_DELTA_HEADER
466 481 deltaheadersize = deltaheader.size
467 482 version = '03'
468 483 _grouplistcount = 2 # One list of manifests and one list of files
469 484
470 485 def _deltaheader(self, headertuple, prevnode):
471 486 node, p1, p2, deltabase, cs, flags = headertuple
472 487 return node, p1, p2, deltabase, cs, flags
473 488
474 489 def _unpackmanifests(self, repo, revmap, trp, prog):
475 490 super(cg3unpacker, self)._unpackmanifests(repo, revmap, trp, prog)
476 491 for chunkdata in iter(self.filelogheader, {}):
477 492 # If we get here, there are directory manifests in the changegroup
478 493 d = chunkdata["filename"]
479 494 repo.ui.debug("adding %s revisions\n" % d)
480 495 deltas = self.deltaiter()
481 496 if not repo.manifestlog.getstorage(d).addgroup(deltas, revmap, trp):
482 497 raise error.Abort(_("received dir revlog group is empty"))
483 498
484 499 class headerlessfixup(object):
485 500 def __init__(self, fh, h):
486 501 self._h = h
487 502 self._fh = fh
488 503 def read(self, n):
489 504 if self._h:
490 505 d, self._h = self._h[:n], self._h[n:]
491 506 if len(d) < n:
492 507 d += readexactly(self._fh, n - len(d))
493 508 return d
494 509 return readexactly(self._fh, n)
495 510
496 511 def _revisiondeltatochunks(delta, headerfn):
497 512 """Serialize a revisiondelta to changegroup chunks."""
498 513
499 514 # The captured revision delta may be encoded as a delta against
500 515 # a base revision or as a full revision. The changegroup format
501 516 # requires that everything on the wire be deltas. So for full
502 517 # revisions, we need to invent a header that says to rewrite
503 518 # data.
504 519
505 520 if delta.delta is not None:
506 521 prefix, data = b'', delta.delta
507 522 elif delta.basenode == nullid:
508 523 data = delta.revision
509 524 prefix = mdiff.trivialdiffheader(len(data))
510 525 else:
511 526 data = delta.revision
512 527 prefix = mdiff.replacediffheader(delta.baserevisionsize,
513 528 len(data))
514 529
515 530 meta = headerfn(delta)
516 531
517 532 yield chunkheader(len(meta) + len(prefix) + len(data))
518 533 yield meta
519 534 if prefix:
520 535 yield prefix
521 536 yield data
522 537
523 538 def _sortnodesellipsis(store, nodes, cl, lookup):
524 539 """Sort nodes for changegroup generation."""
525 540 # Ellipses serving mode.
526 541 #
527 542 # In a perfect world, we'd generate better ellipsis-ified graphs
528 543 # for non-changelog revlogs. In practice, we haven't started doing
529 544 # that yet, so the resulting DAGs for the manifestlog and filelogs
530 545 # are actually full of bogus parentage on all the ellipsis
531 546 # nodes. This has the side effect that, while the contents are
532 547 # correct, the individual DAGs might be completely out of whack in
533 548 # a case like 882681bc3166 and its ancestors (back about 10
534 549 # revisions or so) in the main hg repo.
535 550 #
536 551 # The one invariant we *know* holds is that the new (potentially
537 552 # bogus) DAG shape will be valid if we order the nodes in the
538 553 # order that they're introduced in dramatis personae by the
539 554 # changelog, so what we do is we sort the non-changelog histories
540 555 # by the order in which they are used by the changelog.
541 556 key = lambda n: cl.rev(lookup(n))
542 557 return sorted(nodes, key=key)
543 558
544 559 def _resolvenarrowrevisioninfo(cl, store, ischangelog, rev, linkrev,
545 560 linknode, clrevtolocalrev, fullclnodes,
546 561 precomputedellipsis):
547 562 linkparents = precomputedellipsis[linkrev]
548 563 def local(clrev):
549 564 """Turn a changelog revnum into a local revnum.
550 565
551 566 The ellipsis dag is stored as revnums on the changelog,
552 567 but when we're producing ellipsis entries for
553 568 non-changelog revlogs, we need to turn those numbers into
554 569 something local. This does that for us, and during the
555 570 changelog sending phase will also expand the stored
556 571 mappings as needed.
557 572 """
558 573 if clrev == nullrev:
559 574 return nullrev
560 575
561 576 if ischangelog:
562 577 return clrev
563 578
564 579 # Walk the ellipsis-ized changelog breadth-first looking for a
565 580 # change that has been linked from the current revlog.
566 581 #
567 582 # For a flat manifest revlog only a single step should be necessary
568 583 # as all relevant changelog entries are relevant to the flat
569 584 # manifest.
570 585 #
571 586 # For a filelog or tree manifest dirlog however not every changelog
572 587 # entry will have been relevant, so we need to skip some changelog
573 588 # nodes even after ellipsis-izing.
574 589 walk = [clrev]
575 590 while walk:
576 591 p = walk[0]
577 592 walk = walk[1:]
578 593 if p in clrevtolocalrev:
579 594 return clrevtolocalrev[p]
580 595 elif p in fullclnodes:
581 596 walk.extend([pp for pp in cl.parentrevs(p)
582 597 if pp != nullrev])
583 598 elif p in precomputedellipsis:
584 599 walk.extend([pp for pp in precomputedellipsis[p]
585 600 if pp != nullrev])
586 601 else:
587 602 # In this case, we've got an ellipsis with parents
588 603 # outside the current bundle (likely an
589 604 # incremental pull). We "know" that we can use the
590 605 # value of this same revlog at whatever revision
591 606 # is pointed to by linknode. "Know" is in scare
592 607 # quotes because I haven't done enough examination
593 608 # of edge cases to convince myself this is really
594 609 # a fact - it works for all the (admittedly
595 610 # thorough) cases in our testsuite, but I would be
596 611 # somewhat unsurprised to find a case in the wild
597 612 # where this breaks down a bit. That said, I don't
598 613 # know if it would hurt anything.
599 614 for i in pycompat.xrange(rev, 0, -1):
600 615 if store.linkrev(i) == clrev:
601 616 return i
602 617 # We failed to resolve a parent for this node, so
603 618 # we crash the changegroup construction.
604 619 raise error.Abort(
605 620 'unable to resolve parent while packing %r %r'
606 621 ' for changeset %r' % (store.indexfile, rev, clrev))
607 622
608 623 return nullrev
609 624
610 625 if not linkparents or (
611 626 store.parentrevs(rev) == (nullrev, nullrev)):
612 627 p1, p2 = nullrev, nullrev
613 628 elif len(linkparents) == 1:
614 629 p1, = sorted(local(p) for p in linkparents)
615 630 p2 = nullrev
616 631 else:
617 632 p1, p2 = sorted(local(p) for p in linkparents)
618 633
619 634 p1node, p2node = store.node(p1), store.node(p2)
620 635
621 636 return p1node, p2node, linknode
622 637
623 638 def deltagroup(repo, store, nodes, ischangelog, lookup, forcedeltaparentprev,
624 639 topic=None,
625 640 ellipses=False, clrevtolocalrev=None, fullclnodes=None,
626 641 precomputedellipsis=None):
627 642 """Calculate deltas for a set of revisions.
628 643
629 644 Is a generator of ``revisiondelta`` instances.
630 645
631 646 If topic is not None, progress detail will be generated using this
632 647 topic name (e.g. changesets, manifests, etc).
633 648 """
634 649 if not nodes:
635 650 return
636 651
637 652 cl = repo.changelog
638 653
639 654 if ischangelog:
640 655 # `hg log` shows changesets in storage order. To preserve order
641 656 # across clones, send out changesets in storage order.
642 657 nodesorder = 'storage'
643 658 elif ellipses:
644 659 nodes = _sortnodesellipsis(store, nodes, cl, lookup)
645 660 nodesorder = 'nodes'
646 661 else:
647 662 nodesorder = None
648 663
649 664 # Perform ellipses filtering and revision massaging. We do this before
650 665 # emitrevisions() because a) filtering out revisions creates less work
651 666 # for emitrevisions() b) dropping revisions would break emitrevisions()'s
652 667 # assumptions about delta choices and we would possibly send a delta
653 668 # referencing a missing base revision.
654 669 #
655 670 # Also, calling lookup() has side-effects with regards to populating
656 671 # data structures. If we don't call lookup() for each node or if we call
657 672 # lookup() after the first pass through each node, things can break -
658 673 # possibly intermittently depending on the python hash seed! For that
659 674 # reason, we store a mapping of all linknodes during the initial node
660 675 # pass rather than use lookup() on the output side.
661 676 if ellipses:
662 677 filtered = []
663 678 adjustedparents = {}
664 679 linknodes = {}
665 680
666 681 for node in nodes:
667 682 rev = store.rev(node)
668 683 linknode = lookup(node)
669 684 linkrev = cl.rev(linknode)
670 685 clrevtolocalrev[linkrev] = rev
671 686
672 687 # If linknode is in fullclnodes, it means the corresponding
673 688 # changeset was a full changeset and is being sent unaltered.
674 689 if linknode in fullclnodes:
675 690 linknodes[node] = linknode
676 691
677 692 # If the corresponding changeset wasn't in the set computed
678 693 # as relevant to us, it should be dropped outright.
679 694 elif linkrev not in precomputedellipsis:
680 695 continue
681 696
682 697 else:
683 698 # We could probably do this later and avoid the dict
684 699 # holding state. But it likely doesn't matter.
685 700 p1node, p2node, linknode = _resolvenarrowrevisioninfo(
686 701 cl, store, ischangelog, rev, linkrev, linknode,
687 702 clrevtolocalrev, fullclnodes, precomputedellipsis)
688 703
689 704 adjustedparents[node] = (p1node, p2node)
690 705 linknodes[node] = linknode
691 706
692 707 filtered.append(node)
693 708
694 709 nodes = filtered
695 710
696 711 # We expect the first pass to be fast, so we only engage the progress
697 712 # meter for constructing the revision deltas.
698 713 progress = None
699 714 if topic is not None:
700 715 progress = repo.ui.makeprogress(topic, unit=_('chunks'),
701 716 total=len(nodes))
702 717
703 718 configtarget = repo.ui.config('devel', 'bundle.delta')
704 719 if configtarget not in ('', 'p1', 'full'):
705 720 msg = _("""config "devel.bundle.delta" as unknown value: %s""")
706 721 repo.ui.warn(msg % configtarget)
707 722
708 723 deltamode = repository.CG_DELTAMODE_STD
709 724 if forcedeltaparentprev:
710 725 deltamode = repository.CG_DELTAMODE_PREV
711 726 elif configtarget == 'p1':
712 727 deltamode = repository.CG_DELTAMODE_P1
713 728 elif configtarget == 'full':
714 729 deltamode = repository.CG_DELTAMODE_FULL
715 730
716 731 revisions = store.emitrevisions(
717 732 nodes,
718 733 nodesorder=nodesorder,
719 734 revisiondata=True,
720 735 assumehaveparentrevisions=not ellipses,
721 736 deltamode=deltamode)
722 737
723 738 for i, revision in enumerate(revisions):
724 739 if progress:
725 740 progress.update(i + 1)
726 741
727 742 if ellipses:
728 743 linknode = linknodes[revision.node]
729 744
730 745 if revision.node in adjustedparents:
731 746 p1node, p2node = adjustedparents[revision.node]
732 747 revision.p1node = p1node
733 748 revision.p2node = p2node
734 749 revision.flags |= repository.REVISION_FLAG_ELLIPSIS
735 750
736 751 else:
737 752 linknode = lookup(revision.node)
738 753
739 754 revision.linknode = linknode
740 755 yield revision
741 756
742 757 if progress:
743 758 progress.complete()
744 759
745 760 class cgpacker(object):
746 761 def __init__(self, repo, oldmatcher, matcher, version,
747 762 builddeltaheader, manifestsend,
748 763 forcedeltaparentprev=False,
749 764 bundlecaps=None, ellipses=False,
750 765 shallow=False, ellipsisroots=None, fullnodes=None):
751 766 """Given a source repo, construct a bundler.
752 767
753 768 oldmatcher is a matcher that matches on files the client already has.
754 769 These will not be included in the changegroup.
755 770
756 771 matcher is a matcher that matches on files to include in the
757 772 changegroup. Used to facilitate sparse changegroups.
758 773
759 774 forcedeltaparentprev indicates whether delta parents must be against
760 775 the previous revision in a delta group. This should only be used for
761 776 compatibility with changegroup version 1.
762 777
763 778 builddeltaheader is a callable that constructs the header for a group
764 779 delta.
765 780
766 781 manifestsend is a chunk to send after manifests have been fully emitted.
767 782
768 783 ellipses indicates whether ellipsis serving mode is enabled.
769 784
770 785 bundlecaps is optional and can be used to specify the set of
771 786 capabilities which can be used to build the bundle. While bundlecaps is
772 787 unused in core Mercurial, extensions rely on this feature to communicate
773 788 capabilities to customize the changegroup packer.
774 789
775 790 shallow indicates whether shallow data might be sent. The packer may
776 791 need to pack file contents not introduced by the changes being packed.
777 792
778 793 fullnodes is the set of changelog nodes which should not be ellipsis
779 794 nodes. We store this rather than the set of nodes that should be
780 795 ellipsis because for very large histories we expect this to be
781 796 significantly smaller.
782 797 """
783 798 assert oldmatcher
784 799 assert matcher
785 800 self._oldmatcher = oldmatcher
786 801 self._matcher = matcher
787 802
788 803 self.version = version
789 804 self._forcedeltaparentprev = forcedeltaparentprev
790 805 self._builddeltaheader = builddeltaheader
791 806 self._manifestsend = manifestsend
792 807 self._ellipses = ellipses
793 808
794 809 # Set of capabilities we can use to build the bundle.
795 810 if bundlecaps is None:
796 811 bundlecaps = set()
797 812 self._bundlecaps = bundlecaps
798 813 self._isshallow = shallow
799 814 self._fullclnodes = fullnodes
800 815
801 816 # Maps ellipsis revs to their roots at the changelog level.
802 817 self._precomputedellipsis = ellipsisroots
803 818
804 819 self._repo = repo
805 820
806 821 if self._repo.ui.verbose and not self._repo.ui.debugflag:
807 822 self._verbosenote = self._repo.ui.note
808 823 else:
809 824 self._verbosenote = lambda s: None
810 825
811 826 def generate(self, commonrevs, clnodes, fastpathlinkrev, source,
812 827 changelog=True):
813 828 """Yield a sequence of changegroup byte chunks.
814 829 If changelog is False, changelog data won't be added to changegroup
815 830 """
816 831
817 832 repo = self._repo
818 833 cl = repo.changelog
819 834
820 835 self._verbosenote(_('uncompressed size of bundle content:\n'))
821 836 size = 0
822 837
823 838 clstate, deltas = self._generatechangelog(cl, clnodes,
824 839 generate=changelog)
825 840 for delta in deltas:
826 841 for chunk in _revisiondeltatochunks(delta,
827 842 self._builddeltaheader):
828 843 size += len(chunk)
829 844 yield chunk
830 845
831 846 close = closechunk()
832 847 size += len(close)
833 848 yield closechunk()
834 849
835 850 self._verbosenote(_('%8.i (changelog)\n') % size)
836 851
837 852 clrevorder = clstate['clrevorder']
838 853 manifests = clstate['manifests']
839 854 changedfiles = clstate['changedfiles']
840 855
841 856 # We need to make sure that the linkrev in the changegroup refers to
842 857 # the first changeset that introduced the manifest or file revision.
843 858 # The fastpath is usually safer than the slowpath, because the filelogs
844 859 # are walked in revlog order.
845 860 #
846 861 # When taking the slowpath when the manifest revlog uses generaldelta,
847 862 # the manifest may be walked in the "wrong" order. Without 'clrevorder',
848 863 # we would get an incorrect linkrev (see fix in cc0ff93d0c0c).
849 864 #
850 865 # When taking the fastpath, we are only vulnerable to reordering
851 866 # of the changelog itself. The changelog never uses generaldelta and is
852 867 # never reordered. To handle this case, we simply take the slowpath,
853 868 # which already has the 'clrevorder' logic. This was also fixed in
854 869 # cc0ff93d0c0c.
855 870
856 871 # Treemanifests don't work correctly with fastpathlinkrev
857 872 # either, because we don't discover which directory nodes to
858 873 # send along with files. This could probably be fixed.
859 874 fastpathlinkrev = fastpathlinkrev and (
860 875 'treemanifest' not in repo.requirements)
861 876
862 877 fnodes = {} # needed file nodes
863 878
864 879 size = 0
865 880 it = self.generatemanifests(
866 881 commonrevs, clrevorder, fastpathlinkrev, manifests, fnodes, source,
867 882 clstate['clrevtomanifestrev'])
868 883
869 884 for tree, deltas in it:
870 885 if tree:
871 886 assert self.version == b'03'
872 887 chunk = _fileheader(tree)
873 888 size += len(chunk)
874 889 yield chunk
875 890
876 891 for delta in deltas:
877 892 chunks = _revisiondeltatochunks(delta, self._builddeltaheader)
878 893 for chunk in chunks:
879 894 size += len(chunk)
880 895 yield chunk
881 896
882 897 close = closechunk()
883 898 size += len(close)
884 899 yield close
885 900
886 901 self._verbosenote(_('%8.i (manifests)\n') % size)
887 902 yield self._manifestsend
888 903
889 904 mfdicts = None
890 905 if self._ellipses and self._isshallow:
891 906 mfdicts = [(self._repo.manifestlog[n].read(), lr)
892 907 for (n, lr) in manifests.iteritems()]
893 908
894 909 manifests.clear()
895 910 clrevs = set(cl.rev(x) for x in clnodes)
896 911
897 912 it = self.generatefiles(changedfiles, commonrevs,
898 913 source, mfdicts, fastpathlinkrev,
899 914 fnodes, clrevs)
900 915
901 916 for path, deltas in it:
902 917 h = _fileheader(path)
903 918 size = len(h)
904 919 yield h
905 920
906 921 for delta in deltas:
907 922 chunks = _revisiondeltatochunks(delta, self._builddeltaheader)
908 923 for chunk in chunks:
909 924 size += len(chunk)
910 925 yield chunk
911 926
912 927 close = closechunk()
913 928 size += len(close)
914 929 yield close
915 930
916 931 self._verbosenote(_('%8.i %s\n') % (size, path))
917 932
918 933 yield closechunk()
919 934
920 935 if clnodes:
921 936 repo.hook('outgoing', node=hex(clnodes[0]), source=source)
922 937
923 938 def _generatechangelog(self, cl, nodes, generate=True):
924 939 """Generate data for changelog chunks.
925 940
926 941 Returns a 2-tuple of a dict containing state and an iterable of
927 942 byte chunks. The state will not be fully populated until the
928 943 chunk stream has been fully consumed.
929 944
930 945 if generate is False, the state will be fully populated and no chunk
931 946 stream will be yielded
932 947 """
933 948 clrevorder = {}
934 949 manifests = {}
935 950 mfl = self._repo.manifestlog
936 951 changedfiles = set()
937 952 clrevtomanifestrev = {}
938 953
939 954 state = {
940 955 'clrevorder': clrevorder,
941 956 'manifests': manifests,
942 957 'changedfiles': changedfiles,
943 958 'clrevtomanifestrev': clrevtomanifestrev,
944 959 }
945 960
946 961 if not (generate or self._ellipses):
947 962 # sort the nodes in storage order
948 963 nodes = sorted(nodes, key=cl.rev)
949 964 for node in nodes:
950 965 c = cl.changelogrevision(node)
951 966 clrevorder[node] = len(clrevorder)
952 967 # record the first changeset introducing this manifest version
953 968 manifests.setdefault(c.manifest, node)
954 969 # Record a complete list of potentially-changed files in
955 970 # this manifest.
956 971 changedfiles.update(c.files)
957 972
958 973 return state, ()
959 974
960 975 # Callback for the changelog, used to collect changed files and
961 976 # manifest nodes.
962 977 # Returns the linkrev node (identity in the changelog case).
963 978 def lookupcl(x):
964 979 c = cl.changelogrevision(x)
965 980 clrevorder[x] = len(clrevorder)
966 981
967 982 if self._ellipses:
968 983 # Only update manifests if x is going to be sent. Otherwise we
969 984 # end up with bogus linkrevs specified for manifests and
970 985 # we skip some manifest nodes that we should otherwise
971 986 # have sent.
972 987 if (x in self._fullclnodes
973 988 or cl.rev(x) in self._precomputedellipsis):
974 989
975 990 manifestnode = c.manifest
976 991 # Record the first changeset introducing this manifest
977 992 # version.
978 993 manifests.setdefault(manifestnode, x)
979 994 # Set this narrow-specific dict so we have the lowest
980 995 # manifest revnum to look up for this cl revnum. (Part of
981 996 # mapping changelog ellipsis parents to manifest ellipsis
982 997 # parents)
983 998 clrevtomanifestrev.setdefault(
984 999 cl.rev(x), mfl.rev(manifestnode))
985 1000 # We can't trust the changed files list in the changeset if the
986 1001 # client requested a shallow clone.
987 1002 if self._isshallow:
988 1003 changedfiles.update(mfl[c.manifest].read().keys())
989 1004 else:
990 1005 changedfiles.update(c.files)
991 1006 else:
992 1007 # record the first changeset introducing this manifest version
993 1008 manifests.setdefault(c.manifest, x)
994 1009 # Record a complete list of potentially-changed files in
995 1010 # this manifest.
996 1011 changedfiles.update(c.files)
997 1012
998 1013 return x
999 1014
1000 1015 gen = deltagroup(
1001 1016 self._repo, cl, nodes, True, lookupcl,
1002 1017 self._forcedeltaparentprev,
1003 1018 ellipses=self._ellipses,
1004 1019 topic=_('changesets'),
1005 1020 clrevtolocalrev={},
1006 1021 fullclnodes=self._fullclnodes,
1007 1022 precomputedellipsis=self._precomputedellipsis)
1008 1023
1009 1024 return state, gen
1010 1025
1011 1026 def generatemanifests(self, commonrevs, clrevorder, fastpathlinkrev,
1012 1027 manifests, fnodes, source, clrevtolocalrev):
1013 1028 """Returns an iterator of changegroup chunks containing manifests.
1014 1029
1015 1030 `source` is unused here, but is used by extensions like remotefilelog to
1016 1031 change what is sent based in pulls vs pushes, etc.
1017 1032 """
1018 1033 repo = self._repo
1019 1034 mfl = repo.manifestlog
1020 1035 tmfnodes = {'': manifests}
1021 1036
1022 1037 # Callback for the manifest, used to collect linkrevs for filelog
1023 1038 # revisions.
1024 1039 # Returns the linkrev node (collected in lookupcl).
1025 1040 def makelookupmflinknode(tree, nodes):
1026 1041 if fastpathlinkrev:
1027 1042 assert not tree
1028 1043 return manifests.__getitem__
1029 1044
1030 1045 def lookupmflinknode(x):
1031 1046 """Callback for looking up the linknode for manifests.
1032 1047
1033 1048 Returns the linkrev node for the specified manifest.
1034 1049
1035 1050 SIDE EFFECT:
1036 1051
1037 1052 1) fclnodes gets populated with the list of relevant
1038 1053 file nodes if we're not using fastpathlinkrev
1039 1054 2) When treemanifests are in use, collects treemanifest nodes
1040 1055 to send
1041 1056
1042 1057 Note that this means manifests must be completely sent to
1043 1058 the client before you can trust the list of files and
1044 1059 treemanifests to send.
1045 1060 """
1046 1061 clnode = nodes[x]
1047 1062 mdata = mfl.get(tree, x).readfast(shallow=True)
1048 1063 for p, n, fl in mdata.iterentries():
1049 1064 if fl == 't': # subdirectory manifest
1050 1065 subtree = tree + p + '/'
1051 1066 tmfclnodes = tmfnodes.setdefault(subtree, {})
1052 1067 tmfclnode = tmfclnodes.setdefault(n, clnode)
1053 1068 if clrevorder[clnode] < clrevorder[tmfclnode]:
1054 1069 tmfclnodes[n] = clnode
1055 1070 else:
1056 1071 f = tree + p
1057 1072 fclnodes = fnodes.setdefault(f, {})
1058 1073 fclnode = fclnodes.setdefault(n, clnode)
1059 1074 if clrevorder[clnode] < clrevorder[fclnode]:
1060 1075 fclnodes[n] = clnode
1061 1076 return clnode
1062 1077 return lookupmflinknode
1063 1078
1064 1079 while tmfnodes:
1065 1080 tree, nodes = tmfnodes.popitem()
1066 1081
1067 1082 should_visit = self._matcher.visitdir(tree[:-1])
1068 1083 if tree and not should_visit:
1069 1084 continue
1070 1085
1071 1086 store = mfl.getstorage(tree)
1072 1087
1073 1088 if not should_visit:
1074 1089 # No nodes to send because this directory is out of
1075 1090 # the client's view of the repository (probably
1076 1091 # because of narrow clones). Do this even for the root
1077 1092 # directory (tree=='')
1078 1093 prunednodes = []
1079 1094 else:
1080 1095 # Avoid sending any manifest nodes we can prove the
1081 1096 # client already has by checking linkrevs. See the
1082 1097 # related comment in generatefiles().
1083 1098 prunednodes = self._prunemanifests(store, nodes, commonrevs)
1084 1099
1085 1100 if tree and not prunednodes:
1086 1101 continue
1087 1102
1088 1103 lookupfn = makelookupmflinknode(tree, nodes)
1089 1104
1090 1105 deltas = deltagroup(
1091 1106 self._repo, store, prunednodes, False, lookupfn,
1092 1107 self._forcedeltaparentprev,
1093 1108 ellipses=self._ellipses,
1094 1109 topic=_('manifests'),
1095 1110 clrevtolocalrev=clrevtolocalrev,
1096 1111 fullclnodes=self._fullclnodes,
1097 1112 precomputedellipsis=self._precomputedellipsis)
1098 1113
1099 1114 if not self._oldmatcher.visitdir(store.tree[:-1]):
1100 1115 yield tree, deltas
1101 1116 else:
1102 1117 # 'deltas' is a generator and we need to consume it even if
1103 1118 # we are not going to send it because a side-effect is that
1104 1119 # it updates tmdnodes (via lookupfn)
1105 1120 for d in deltas:
1106 1121 pass
1107 1122 if not tree:
1108 1123 yield tree, []
1109 1124
1110 1125 def _prunemanifests(self, store, nodes, commonrevs):
1111 1126 if not self._ellipses:
1112 1127 # In non-ellipses case and large repositories, it is better to
1113 1128 # prevent calling of store.rev and store.linkrev on a lot of
1114 1129 # nodes as compared to sending some extra data
1115 1130 return nodes.copy()
1116 1131 # This is split out as a separate method to allow filtering
1117 1132 # commonrevs in extension code.
1118 1133 #
1119 1134 # TODO(augie): this shouldn't be required, instead we should
1120 1135 # make filtering of revisions to send delegated to the store
1121 1136 # layer.
1122 1137 frev, flr = store.rev, store.linkrev
1123 1138 return [n for n in nodes if flr(frev(n)) not in commonrevs]
1124 1139
1125 1140 # The 'source' parameter is useful for extensions
1126 1141 def generatefiles(self, changedfiles, commonrevs, source,
1127 1142 mfdicts, fastpathlinkrev, fnodes, clrevs):
1128 1143 changedfiles = [f for f in changedfiles
1129 1144 if self._matcher(f) and not self._oldmatcher(f)]
1130 1145
1131 1146 if not fastpathlinkrev:
1132 1147 def normallinknodes(unused, fname):
1133 1148 return fnodes.get(fname, {})
1134 1149 else:
1135 1150 cln = self._repo.changelog.node
1136 1151
1137 1152 def normallinknodes(store, fname):
1138 1153 flinkrev = store.linkrev
1139 1154 fnode = store.node
1140 1155 revs = ((r, flinkrev(r)) for r in store)
1141 1156 return dict((fnode(r), cln(lr))
1142 1157 for r, lr in revs if lr in clrevs)
1143 1158
1144 1159 clrevtolocalrev = {}
1145 1160
1146 1161 if self._isshallow:
1147 1162 # In a shallow clone, the linknodes callback needs to also include
1148 1163 # those file nodes that are in the manifests we sent but weren't
1149 1164 # introduced by those manifests.
1150 1165 commonctxs = [self._repo[c] for c in commonrevs]
1151 1166 clrev = self._repo.changelog.rev
1152 1167
1153 1168 def linknodes(flog, fname):
1154 1169 for c in commonctxs:
1155 1170 try:
1156 1171 fnode = c.filenode(fname)
1157 1172 clrevtolocalrev[c.rev()] = flog.rev(fnode)
1158 1173 except error.ManifestLookupError:
1159 1174 pass
1160 1175 links = normallinknodes(flog, fname)
1161 1176 if len(links) != len(mfdicts):
1162 1177 for mf, lr in mfdicts:
1163 1178 fnode = mf.get(fname, None)
1164 1179 if fnode in links:
1165 1180 links[fnode] = min(links[fnode], lr, key=clrev)
1166 1181 elif fnode:
1167 1182 links[fnode] = lr
1168 1183 return links
1169 1184 else:
1170 1185 linknodes = normallinknodes
1171 1186
1172 1187 repo = self._repo
1173 1188 progress = repo.ui.makeprogress(_('files'), unit=_('files'),
1174 1189 total=len(changedfiles))
1175 1190 for i, fname in enumerate(sorted(changedfiles)):
1176 1191 filerevlog = repo.file(fname)
1177 1192 if not filerevlog:
1178 1193 raise error.Abort(_("empty or missing file data for %s") %
1179 1194 fname)
1180 1195
1181 1196 clrevtolocalrev.clear()
1182 1197
1183 1198 linkrevnodes = linknodes(filerevlog, fname)
1184 1199 # Lookup for filenodes, we collected the linkrev nodes above in the
1185 1200 # fastpath case and with lookupmf in the slowpath case.
1186 1201 def lookupfilelog(x):
1187 1202 return linkrevnodes[x]
1188 1203
1189 1204 frev, flr = filerevlog.rev, filerevlog.linkrev
1190 1205 # Skip sending any filenode we know the client already
1191 1206 # has. This avoids over-sending files relatively
1192 1207 # inexpensively, so it's not a problem if we under-filter
1193 1208 # here.
1194 1209 filenodes = [n for n in linkrevnodes
1195 1210 if flr(frev(n)) not in commonrevs]
1196 1211
1197 1212 if not filenodes:
1198 1213 continue
1199 1214
1200 1215 progress.update(i + 1, item=fname)
1201 1216
1202 1217 deltas = deltagroup(
1203 1218 self._repo, filerevlog, filenodes, False, lookupfilelog,
1204 1219 self._forcedeltaparentprev,
1205 1220 ellipses=self._ellipses,
1206 1221 clrevtolocalrev=clrevtolocalrev,
1207 1222 fullclnodes=self._fullclnodes,
1208 1223 precomputedellipsis=self._precomputedellipsis)
1209 1224
1210 1225 yield fname, deltas
1211 1226
1212 1227 progress.complete()
1213 1228
1214 1229 def _makecg1packer(repo, oldmatcher, matcher, bundlecaps,
1215 1230 ellipses=False, shallow=False, ellipsisroots=None,
1216 1231 fullnodes=None):
1217 1232 builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack(
1218 1233 d.node, d.p1node, d.p2node, d.linknode)
1219 1234
1220 1235 return cgpacker(repo, oldmatcher, matcher, b'01',
1221 1236 builddeltaheader=builddeltaheader,
1222 1237 manifestsend=b'',
1223 1238 forcedeltaparentprev=True,
1224 1239 bundlecaps=bundlecaps,
1225 1240 ellipses=ellipses,
1226 1241 shallow=shallow,
1227 1242 ellipsisroots=ellipsisroots,
1228 1243 fullnodes=fullnodes)
1229 1244
1230 1245 def _makecg2packer(repo, oldmatcher, matcher, bundlecaps,
1231 1246 ellipses=False, shallow=False, ellipsisroots=None,
1232 1247 fullnodes=None):
1233 1248 builddeltaheader = lambda d: _CHANGEGROUPV2_DELTA_HEADER.pack(
1234 1249 d.node, d.p1node, d.p2node, d.basenode, d.linknode)
1235 1250
1236 1251 return cgpacker(repo, oldmatcher, matcher, b'02',
1237 1252 builddeltaheader=builddeltaheader,
1238 1253 manifestsend=b'',
1239 1254 bundlecaps=bundlecaps,
1240 1255 ellipses=ellipses,
1241 1256 shallow=shallow,
1242 1257 ellipsisroots=ellipsisroots,
1243 1258 fullnodes=fullnodes)
1244 1259
1245 1260 def _makecg3packer(repo, oldmatcher, matcher, bundlecaps,
1246 1261 ellipses=False, shallow=False, ellipsisroots=None,
1247 1262 fullnodes=None):
1248 1263 builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack(
1249 1264 d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags)
1250 1265
1251 1266 return cgpacker(repo, oldmatcher, matcher, b'03',
1252 1267 builddeltaheader=builddeltaheader,
1253 1268 manifestsend=closechunk(),
1254 1269 bundlecaps=bundlecaps,
1255 1270 ellipses=ellipses,
1256 1271 shallow=shallow,
1257 1272 ellipsisroots=ellipsisroots,
1258 1273 fullnodes=fullnodes)
1259 1274
1260 1275 _packermap = {'01': (_makecg1packer, cg1unpacker),
1261 1276 # cg2 adds support for exchanging generaldelta
1262 1277 '02': (_makecg2packer, cg2unpacker),
1263 1278 # cg3 adds support for exchanging revlog flags and treemanifests
1264 1279 '03': (_makecg3packer, cg3unpacker),
1265 1280 }
1266 1281
1267 1282 def allsupportedversions(repo):
1268 1283 versions = set(_packermap.keys())
1269 1284 if not (repo.ui.configbool('experimental', 'changegroup3') or
1270 1285 repo.ui.configbool('experimental', 'treemanifest') or
1271 1286 'treemanifest' in repo.requirements):
1272 1287 versions.discard('03')
1273 1288 return versions
1274 1289
1275 1290 # Changegroup versions that can be applied to the repo
1276 1291 def supportedincomingversions(repo):
1277 1292 return allsupportedversions(repo)
1278 1293
1279 1294 # Changegroup versions that can be created from the repo
1280 1295 def supportedoutgoingversions(repo):
1281 1296 versions = allsupportedversions(repo)
1282 1297 if 'treemanifest' in repo.requirements:
1283 1298 # Versions 01 and 02 support only flat manifests and it's just too
1284 1299 # expensive to convert between the flat manifest and tree manifest on
1285 1300 # the fly. Since tree manifests are hashed differently, all of history
1286 1301 # would have to be converted. Instead, we simply don't even pretend to
1287 1302 # support versions 01 and 02.
1288 1303 versions.discard('01')
1289 1304 versions.discard('02')
1290 1305 if repository.NARROW_REQUIREMENT in repo.requirements:
1291 1306 # Versions 01 and 02 don't support revlog flags, and we need to
1292 1307 # support that for stripping and unbundling to work.
1293 1308 versions.discard('01')
1294 1309 versions.discard('02')
1295 1310 if LFS_REQUIREMENT in repo.requirements:
1296 1311 # Versions 01 and 02 don't support revlog flags, and we need to
1297 1312 # mark LFS entries with REVIDX_EXTSTORED.
1298 1313 versions.discard('01')
1299 1314 versions.discard('02')
1300 1315
1301 1316 return versions
1302 1317
1303 1318 def localversion(repo):
1304 1319 # Finds the best version to use for bundles that are meant to be used
1305 1320 # locally, such as those from strip and shelve, and temporary bundles.
1306 1321 return max(supportedoutgoingversions(repo))
1307 1322
1308 1323 def safeversion(repo):
1309 1324 # Finds the smallest version that it's safe to assume clients of the repo
1310 1325 # will support. For example, all hg versions that support generaldelta also
1311 1326 # support changegroup 02.
1312 1327 versions = supportedoutgoingversions(repo)
1313 1328 if 'generaldelta' in repo.requirements:
1314 1329 versions.discard('01')
1315 1330 assert versions
1316 1331 return min(versions)
1317 1332
1318 1333 def getbundler(version, repo, bundlecaps=None, oldmatcher=None,
1319 1334 matcher=None, ellipses=False, shallow=False,
1320 1335 ellipsisroots=None, fullnodes=None):
1321 1336 assert version in supportedoutgoingversions(repo)
1322 1337
1323 1338 if matcher is None:
1324 1339 matcher = matchmod.always()
1325 1340 if oldmatcher is None:
1326 1341 oldmatcher = matchmod.never()
1327 1342
1328 1343 if version == '01' and not matcher.always():
1329 1344 raise error.ProgrammingError('version 01 changegroups do not support '
1330 1345 'sparse file matchers')
1331 1346
1332 1347 if ellipses and version in (b'01', b'02'):
1333 1348 raise error.Abort(
1334 1349 _('ellipsis nodes require at least cg3 on client and server, '
1335 1350 'but negotiated version %s') % version)
1336 1351
1337 1352 # Requested files could include files not in the local store. So
1338 1353 # filter those out.
1339 1354 matcher = repo.narrowmatch(matcher)
1340 1355
1341 1356 fn = _packermap[version][0]
1342 1357 return fn(repo, oldmatcher, matcher, bundlecaps, ellipses=ellipses,
1343 1358 shallow=shallow, ellipsisroots=ellipsisroots,
1344 1359 fullnodes=fullnodes)
1345 1360
1346 1361 def getunbundler(version, fh, alg, extras=None):
1347 1362 return _packermap[version][1](fh, alg, extras=extras)
1348 1363
1349 1364 def _changegroupinfo(repo, nodes, source):
1350 1365 if repo.ui.verbose or source == 'bundle':
1351 1366 repo.ui.status(_("%d changesets found\n") % len(nodes))
1352 1367 if repo.ui.debugflag:
1353 1368 repo.ui.debug("list of changesets:\n")
1354 1369 for node in nodes:
1355 1370 repo.ui.debug("%s\n" % hex(node))
1356 1371
1357 1372 def makechangegroup(repo, outgoing, version, source, fastpath=False,
1358 1373 bundlecaps=None):
1359 1374 cgstream = makestream(repo, outgoing, version, source,
1360 1375 fastpath=fastpath, bundlecaps=bundlecaps)
1361 1376 return getunbundler(version, util.chunkbuffer(cgstream), None,
1362 1377 {'clcount': len(outgoing.missing) })
1363 1378
1364 1379 def makestream(repo, outgoing, version, source, fastpath=False,
1365 1380 bundlecaps=None, matcher=None):
1366 1381 bundler = getbundler(version, repo, bundlecaps=bundlecaps,
1367 1382 matcher=matcher)
1368 1383
1369 1384 repo = repo.unfiltered()
1370 1385 commonrevs = outgoing.common
1371 1386 csets = outgoing.missing
1372 1387 heads = outgoing.missingheads
1373 1388 # We go through the fast path if we get told to, or if all (unfiltered
1374 1389 # heads have been requested (since we then know there all linkrevs will
1375 1390 # be pulled by the client).
1376 1391 heads.sort()
1377 1392 fastpathlinkrev = fastpath or (
1378 1393 repo.filtername is None and heads == sorted(repo.heads()))
1379 1394
1380 1395 repo.hook('preoutgoing', throw=True, source=source)
1381 1396 _changegroupinfo(repo, csets, source)
1382 1397 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
1383 1398
1384 1399 def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles):
1385 1400 revisions = 0
1386 1401 files = 0
1387 1402 progress = repo.ui.makeprogress(_('files'), unit=_('files'),
1388 1403 total=expectedfiles)
1389 1404 for chunkdata in iter(source.filelogheader, {}):
1390 1405 files += 1
1391 1406 f = chunkdata["filename"]
1392 1407 repo.ui.debug("adding %s revisions\n" % f)
1393 1408 progress.increment()
1394 1409 fl = repo.file(f)
1395 1410 o = len(fl)
1396 1411 try:
1397 1412 deltas = source.deltaiter()
1398 1413 if not fl.addgroup(deltas, revmap, trp):
1399 1414 raise error.Abort(_("received file revlog group is empty"))
1400 1415 except error.CensoredBaseError as e:
1401 1416 raise error.Abort(_("received delta base is censored: %s") % e)
1402 1417 revisions += len(fl) - o
1403 1418 if f in needfiles:
1404 1419 needs = needfiles[f]
1405 1420 for new in pycompat.xrange(o, len(fl)):
1406 1421 n = fl.node(new)
1407 1422 if n in needs:
1408 1423 needs.remove(n)
1409 1424 else:
1410 1425 raise error.Abort(
1411 1426 _("received spurious file revlog entry"))
1412 1427 if not needs:
1413 1428 del needfiles[f]
1414 1429 progress.complete()
1415 1430
1416 1431 for f, needs in needfiles.iteritems():
1417 1432 fl = repo.file(f)
1418 1433 for n in needs:
1419 1434 try:
1420 1435 fl.rev(n)
1421 1436 except error.LookupError:
1422 1437 raise error.Abort(
1423 1438 _('missing file data for %s:%s - run hg verify') %
1424 1439 (f, hex(n)))
1425 1440
1426 1441 return revisions, files
@@ -1,2007 +1,2021 b''
1 1 # scmutil.py - Mercurial core utility functions
2 2 #
3 3 # Copyright Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import glob
12 12 import hashlib
13 13 import os
14 14 import posixpath
15 15 import re
16 16 import subprocess
17 17 import weakref
18 18
19 19 from .i18n import _
20 20 from .node import (
21 21 bin,
22 22 hex,
23 23 nullid,
24 24 nullrev,
25 25 short,
26 26 wdirid,
27 27 wdirrev,
28 28 )
29 29
30 30 from . import (
31 31 copies as copiesmod,
32 32 encoding,
33 33 error,
34 34 match as matchmod,
35 35 obsolete,
36 36 obsutil,
37 37 pathutil,
38 38 phases,
39 39 policy,
40 40 pycompat,
41 41 revsetlang,
42 42 similar,
43 43 smartset,
44 44 url,
45 45 util,
46 46 vfs,
47 47 )
48 48
49 49 from .utils import (
50 50 procutil,
51 51 stringutil,
52 52 )
53 53
54 54 if pycompat.iswindows:
55 55 from . import scmwindows as scmplatform
56 56 else:
57 57 from . import scmposix as scmplatform
58 58
59 59 parsers = policy.importmod(r'parsers')
60 60
61 61 termsize = scmplatform.termsize
62 62
63 63 class status(tuple):
64 64 '''Named tuple with a list of files per status. The 'deleted', 'unknown'
65 65 and 'ignored' properties are only relevant to the working copy.
66 66 '''
67 67
68 68 __slots__ = ()
69 69
70 70 def __new__(cls, modified, added, removed, deleted, unknown, ignored,
71 71 clean):
72 72 return tuple.__new__(cls, (modified, added, removed, deleted, unknown,
73 73 ignored, clean))
74 74
75 75 @property
76 76 def modified(self):
77 77 '''files that have been modified'''
78 78 return self[0]
79 79
80 80 @property
81 81 def added(self):
82 82 '''files that have been added'''
83 83 return self[1]
84 84
85 85 @property
86 86 def removed(self):
87 87 '''files that have been removed'''
88 88 return self[2]
89 89
90 90 @property
91 91 def deleted(self):
92 92 '''files that are in the dirstate, but have been deleted from the
93 93 working copy (aka "missing")
94 94 '''
95 95 return self[3]
96 96
97 97 @property
98 98 def unknown(self):
99 99 '''files not in the dirstate that are not ignored'''
100 100 return self[4]
101 101
102 102 @property
103 103 def ignored(self):
104 104 '''files not in the dirstate that are ignored (by _dirignore())'''
105 105 return self[5]
106 106
107 107 @property
108 108 def clean(self):
109 109 '''files that have not been modified'''
110 110 return self[6]
111 111
112 112 def __repr__(self, *args, **kwargs):
113 113 return ((r'<status modified=%s, added=%s, removed=%s, deleted=%s, '
114 114 r'unknown=%s, ignored=%s, clean=%s>') %
115 115 tuple(pycompat.sysstr(stringutil.pprint(v)) for v in self))
116 116
117 117 def itersubrepos(ctx1, ctx2):
118 118 """find subrepos in ctx1 or ctx2"""
119 119 # Create a (subpath, ctx) mapping where we prefer subpaths from
120 120 # ctx1. The subpaths from ctx2 are important when the .hgsub file
121 121 # has been modified (in ctx2) but not yet committed (in ctx1).
122 122 subpaths = dict.fromkeys(ctx2.substate, ctx2)
123 123 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
124 124
125 125 missing = set()
126 126
127 127 for subpath in ctx2.substate:
128 128 if subpath not in ctx1.substate:
129 129 del subpaths[subpath]
130 130 missing.add(subpath)
131 131
132 132 for subpath, ctx in sorted(subpaths.iteritems()):
133 133 yield subpath, ctx.sub(subpath)
134 134
135 135 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
136 136 # status and diff will have an accurate result when it does
137 137 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
138 138 # against itself.
139 139 for subpath in missing:
140 140 yield subpath, ctx2.nullsub(subpath, ctx1)
141 141
142 142 def nochangesfound(ui, repo, excluded=None):
143 143 '''Report no changes for push/pull, excluded is None or a list of
144 144 nodes excluded from the push/pull.
145 145 '''
146 146 secretlist = []
147 147 if excluded:
148 148 for n in excluded:
149 149 ctx = repo[n]
150 150 if ctx.phase() >= phases.secret and not ctx.extinct():
151 151 secretlist.append(n)
152 152
153 153 if secretlist:
154 154 ui.status(_("no changes found (ignored %d secret changesets)\n")
155 155 % len(secretlist))
156 156 else:
157 157 ui.status(_("no changes found\n"))
158 158
159 159 def callcatch(ui, func):
160 160 """call func() with global exception handling
161 161
162 162 return func() if no exception happens. otherwise do some error handling
163 163 and return an exit code accordingly. does not handle all exceptions.
164 164 """
165 165 try:
166 166 try:
167 167 return func()
168 168 except: # re-raises
169 169 ui.traceback()
170 170 raise
171 171 # Global exception handling, alphabetically
172 172 # Mercurial-specific first, followed by built-in and library exceptions
173 173 except error.LockHeld as inst:
174 174 if inst.errno == errno.ETIMEDOUT:
175 175 reason = _('timed out waiting for lock held by %r') % (
176 176 pycompat.bytestr(inst.locker))
177 177 else:
178 178 reason = _('lock held by %r') % inst.locker
179 179 ui.error(_("abort: %s: %s\n") % (
180 180 inst.desc or stringutil.forcebytestr(inst.filename), reason))
181 181 if not inst.locker:
182 182 ui.error(_("(lock might be very busy)\n"))
183 183 except error.LockUnavailable as inst:
184 184 ui.error(_("abort: could not lock %s: %s\n") %
185 185 (inst.desc or stringutil.forcebytestr(inst.filename),
186 186 encoding.strtolocal(inst.strerror)))
187 187 except error.OutOfBandError as inst:
188 188 if inst.args:
189 189 msg = _("abort: remote error:\n")
190 190 else:
191 191 msg = _("abort: remote error\n")
192 192 ui.error(msg)
193 193 if inst.args:
194 194 ui.error(''.join(inst.args))
195 195 if inst.hint:
196 196 ui.error('(%s)\n' % inst.hint)
197 197 except error.RepoError as inst:
198 198 ui.error(_("abort: %s!\n") % inst)
199 199 if inst.hint:
200 200 ui.error(_("(%s)\n") % inst.hint)
201 201 except error.ResponseError as inst:
202 202 ui.error(_("abort: %s") % inst.args[0])
203 203 msg = inst.args[1]
204 204 if isinstance(msg, type(u'')):
205 205 msg = pycompat.sysbytes(msg)
206 206 if not isinstance(msg, bytes):
207 207 ui.error(" %r\n" % (msg,))
208 208 elif not msg:
209 209 ui.error(_(" empty string\n"))
210 210 else:
211 211 ui.error("\n%r\n" % pycompat.bytestr(stringutil.ellipsis(msg)))
212 212 except error.CensoredNodeError as inst:
213 213 ui.error(_("abort: file censored %s!\n") % inst)
214 214 except error.StorageError as inst:
215 215 ui.error(_("abort: %s!\n") % inst)
216 216 if inst.hint:
217 217 ui.error(_("(%s)\n") % inst.hint)
218 218 except error.InterventionRequired as inst:
219 219 ui.error("%s\n" % inst)
220 220 if inst.hint:
221 221 ui.error(_("(%s)\n") % inst.hint)
222 222 return 1
223 223 except error.WdirUnsupported:
224 224 ui.error(_("abort: working directory revision cannot be specified\n"))
225 225 except error.Abort as inst:
226 226 ui.error(_("abort: %s\n") % inst)
227 227 if inst.hint:
228 228 ui.error(_("(%s)\n") % inst.hint)
229 229 except ImportError as inst:
230 230 ui.error(_("abort: %s!\n") % stringutil.forcebytestr(inst))
231 231 m = stringutil.forcebytestr(inst).split()[-1]
232 232 if m in "mpatch bdiff".split():
233 233 ui.error(_("(did you forget to compile extensions?)\n"))
234 234 elif m in "zlib".split():
235 235 ui.error(_("(is your Python install correct?)\n"))
236 236 except (IOError, OSError) as inst:
237 237 if util.safehasattr(inst, "code"): # HTTPError
238 238 ui.error(_("abort: %s\n") % stringutil.forcebytestr(inst))
239 239 elif util.safehasattr(inst, "reason"): # URLError or SSLError
240 240 try: # usually it is in the form (errno, strerror)
241 241 reason = inst.reason.args[1]
242 242 except (AttributeError, IndexError):
243 243 # it might be anything, for example a string
244 244 reason = inst.reason
245 245 if isinstance(reason, pycompat.unicode):
246 246 # SSLError of Python 2.7.9 contains a unicode
247 247 reason = encoding.unitolocal(reason)
248 248 ui.error(_("abort: error: %s\n") % reason)
249 249 elif (util.safehasattr(inst, "args")
250 250 and inst.args and inst.args[0] == errno.EPIPE):
251 251 pass
252 252 elif getattr(inst, "strerror", None): # common IOError or OSError
253 253 if getattr(inst, "filename", None) is not None:
254 254 ui.error(_("abort: %s: '%s'\n") % (
255 255 encoding.strtolocal(inst.strerror),
256 256 stringutil.forcebytestr(inst.filename)))
257 257 else:
258 258 ui.error(_("abort: %s\n") % encoding.strtolocal(inst.strerror))
259 259 else: # suspicious IOError
260 260 raise
261 261 except MemoryError:
262 262 ui.error(_("abort: out of memory\n"))
263 263 except SystemExit as inst:
264 264 # Commands shouldn't sys.exit directly, but give a return code.
265 265 # Just in case catch this and and pass exit code to caller.
266 266 return inst.code
267 267
268 268 return -1
269 269
270 270 def checknewlabel(repo, lbl, kind):
271 271 # Do not use the "kind" parameter in ui output.
272 272 # It makes strings difficult to translate.
273 273 if lbl in ['tip', '.', 'null']:
274 274 raise error.Abort(_("the name '%s' is reserved") % lbl)
275 275 for c in (':', '\0', '\n', '\r'):
276 276 if c in lbl:
277 277 raise error.Abort(
278 278 _("%r cannot be used in a name") % pycompat.bytestr(c))
279 279 try:
280 280 int(lbl)
281 281 raise error.Abort(_("cannot use an integer as a name"))
282 282 except ValueError:
283 283 pass
284 284 if lbl.strip() != lbl:
285 285 raise error.Abort(_("leading or trailing whitespace in name %r") % lbl)
286 286
287 287 def checkfilename(f):
288 288 '''Check that the filename f is an acceptable filename for a tracked file'''
289 289 if '\r' in f or '\n' in f:
290 290 raise error.Abort(_("'\\n' and '\\r' disallowed in filenames: %r")
291 291 % pycompat.bytestr(f))
292 292
293 293 def checkportable(ui, f):
294 294 '''Check if filename f is portable and warn or abort depending on config'''
295 295 checkfilename(f)
296 296 abort, warn = checkportabilityalert(ui)
297 297 if abort or warn:
298 298 msg = util.checkwinfilename(f)
299 299 if msg:
300 300 msg = "%s: %s" % (msg, procutil.shellquote(f))
301 301 if abort:
302 302 raise error.Abort(msg)
303 303 ui.warn(_("warning: %s\n") % msg)
304 304
305 305 def checkportabilityalert(ui):
306 306 '''check if the user's config requests nothing, a warning, or abort for
307 307 non-portable filenames'''
308 308 val = ui.config('ui', 'portablefilenames')
309 309 lval = val.lower()
310 310 bval = stringutil.parsebool(val)
311 311 abort = pycompat.iswindows or lval == 'abort'
312 312 warn = bval or lval == 'warn'
313 313 if bval is None and not (warn or abort or lval == 'ignore'):
314 314 raise error.ConfigError(
315 315 _("ui.portablefilenames value is invalid ('%s')") % val)
316 316 return abort, warn
317 317
318 318 class casecollisionauditor(object):
319 319 def __init__(self, ui, abort, dirstate):
320 320 self._ui = ui
321 321 self._abort = abort
322 322 allfiles = '\0'.join(dirstate._map)
323 323 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
324 324 self._dirstate = dirstate
325 325 # The purpose of _newfiles is so that we don't complain about
326 326 # case collisions if someone were to call this object with the
327 327 # same filename twice.
328 328 self._newfiles = set()
329 329
330 330 def __call__(self, f):
331 331 if f in self._newfiles:
332 332 return
333 333 fl = encoding.lower(f)
334 334 if fl in self._loweredfiles and f not in self._dirstate:
335 335 msg = _('possible case-folding collision for %s') % f
336 336 if self._abort:
337 337 raise error.Abort(msg)
338 338 self._ui.warn(_("warning: %s\n") % msg)
339 339 self._loweredfiles.add(fl)
340 340 self._newfiles.add(f)
341 341
342 342 def filteredhash(repo, maxrev):
343 343 """build hash of filtered revisions in the current repoview.
344 344
345 345 Multiple caches perform up-to-date validation by checking that the
346 346 tiprev and tipnode stored in the cache file match the current repository.
347 347 However, this is not sufficient for validating repoviews because the set
348 348 of revisions in the view may change without the repository tiprev and
349 349 tipnode changing.
350 350
351 351 This function hashes all the revs filtered from the view and returns
352 352 that SHA-1 digest.
353 353 """
354 354 cl = repo.changelog
355 355 if not cl.filteredrevs:
356 356 return None
357 357 key = None
358 358 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
359 359 if revs:
360 360 s = hashlib.sha1()
361 361 for rev in revs:
362 362 s.update('%d;' % rev)
363 363 key = s.digest()
364 364 return key
365 365
366 366 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
367 367 '''yield every hg repository under path, always recursively.
368 368 The recurse flag will only control recursion into repo working dirs'''
369 369 def errhandler(err):
370 370 if err.filename == path:
371 371 raise err
372 372 samestat = getattr(os.path, 'samestat', None)
373 373 if followsym and samestat is not None:
374 374 def adddir(dirlst, dirname):
375 375 dirstat = os.stat(dirname)
376 376 match = any(samestat(dirstat, lstdirstat) for lstdirstat in dirlst)
377 377 if not match:
378 378 dirlst.append(dirstat)
379 379 return not match
380 380 else:
381 381 followsym = False
382 382
383 383 if (seen_dirs is None) and followsym:
384 384 seen_dirs = []
385 385 adddir(seen_dirs, path)
386 386 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
387 387 dirs.sort()
388 388 if '.hg' in dirs:
389 389 yield root # found a repository
390 390 qroot = os.path.join(root, '.hg', 'patches')
391 391 if os.path.isdir(os.path.join(qroot, '.hg')):
392 392 yield qroot # we have a patch queue repo here
393 393 if recurse:
394 394 # avoid recursing inside the .hg directory
395 395 dirs.remove('.hg')
396 396 else:
397 397 dirs[:] = [] # don't descend further
398 398 elif followsym:
399 399 newdirs = []
400 400 for d in dirs:
401 401 fname = os.path.join(root, d)
402 402 if adddir(seen_dirs, fname):
403 403 if os.path.islink(fname):
404 404 for hgname in walkrepos(fname, True, seen_dirs):
405 405 yield hgname
406 406 else:
407 407 newdirs.append(d)
408 408 dirs[:] = newdirs
409 409
410 410 def binnode(ctx):
411 411 """Return binary node id for a given basectx"""
412 412 node = ctx.node()
413 413 if node is None:
414 414 return wdirid
415 415 return node
416 416
417 417 def intrev(ctx):
418 418 """Return integer for a given basectx that can be used in comparison or
419 419 arithmetic operation"""
420 420 rev = ctx.rev()
421 421 if rev is None:
422 422 return wdirrev
423 423 return rev
424 424
425 425 def formatchangeid(ctx):
426 426 """Format changectx as '{rev}:{node|formatnode}', which is the default
427 427 template provided by logcmdutil.changesettemplater"""
428 428 repo = ctx.repo()
429 429 return formatrevnode(repo.ui, intrev(ctx), binnode(ctx))
430 430
431 431 def formatrevnode(ui, rev, node):
432 432 """Format given revision and node depending on the current verbosity"""
433 433 if ui.debugflag:
434 434 hexfunc = hex
435 435 else:
436 436 hexfunc = short
437 437 return '%d:%s' % (rev, hexfunc(node))
438 438
439 439 def resolvehexnodeidprefix(repo, prefix):
440 440 if (prefix.startswith('x') and
441 441 repo.ui.configbool('experimental', 'revisions.prefixhexnode')):
442 442 prefix = prefix[1:]
443 443 try:
444 444 # Uses unfiltered repo because it's faster when prefix is ambiguous/
445 445 # This matches the shortesthexnodeidprefix() function below.
446 446 node = repo.unfiltered().changelog._partialmatch(prefix)
447 447 except error.AmbiguousPrefixLookupError:
448 448 revset = repo.ui.config('experimental', 'revisions.disambiguatewithin')
449 449 if revset:
450 450 # Clear config to avoid infinite recursion
451 451 configoverrides = {('experimental',
452 452 'revisions.disambiguatewithin'): None}
453 453 with repo.ui.configoverride(configoverrides):
454 454 revs = repo.anyrevs([revset], user=True)
455 455 matches = []
456 456 for rev in revs:
457 457 node = repo.changelog.node(rev)
458 458 if hex(node).startswith(prefix):
459 459 matches.append(node)
460 460 if len(matches) == 1:
461 461 return matches[0]
462 462 raise
463 463 if node is None:
464 464 return
465 465 repo.changelog.rev(node) # make sure node isn't filtered
466 466 return node
467 467
468 468 def mayberevnum(repo, prefix):
469 469 """Checks if the given prefix may be mistaken for a revision number"""
470 470 try:
471 471 i = int(prefix)
472 472 # if we are a pure int, then starting with zero will not be
473 473 # confused as a rev; or, obviously, if the int is larger
474 474 # than the value of the tip rev. We still need to disambiguate if
475 475 # prefix == '0', since that *is* a valid revnum.
476 476 if (prefix != b'0' and prefix[0:1] == b'0') or i >= len(repo):
477 477 return False
478 478 return True
479 479 except ValueError:
480 480 return False
481 481
482 482 def shortesthexnodeidprefix(repo, node, minlength=1, cache=None):
483 483 """Find the shortest unambiguous prefix that matches hexnode.
484 484
485 485 If "cache" is not None, it must be a dictionary that can be used for
486 486 caching between calls to this method.
487 487 """
488 488 # _partialmatch() of filtered changelog could take O(len(repo)) time,
489 489 # which would be unacceptably slow. so we look for hash collision in
490 490 # unfiltered space, which means some hashes may be slightly longer.
491 491
492 492 minlength=max(minlength, 1)
493 493
494 494 def disambiguate(prefix):
495 495 """Disambiguate against revnums."""
496 496 if repo.ui.configbool('experimental', 'revisions.prefixhexnode'):
497 497 if mayberevnum(repo, prefix):
498 498 return 'x' + prefix
499 499 else:
500 500 return prefix
501 501
502 502 hexnode = hex(node)
503 503 for length in range(len(prefix), len(hexnode) + 1):
504 504 prefix = hexnode[:length]
505 505 if not mayberevnum(repo, prefix):
506 506 return prefix
507 507
508 508 cl = repo.unfiltered().changelog
509 509 revset = repo.ui.config('experimental', 'revisions.disambiguatewithin')
510 510 if revset:
511 511 revs = None
512 512 if cache is not None:
513 513 revs = cache.get('disambiguationrevset')
514 514 if revs is None:
515 515 revs = repo.anyrevs([revset], user=True)
516 516 if cache is not None:
517 517 cache['disambiguationrevset'] = revs
518 518 if cl.rev(node) in revs:
519 519 hexnode = hex(node)
520 520 nodetree = None
521 521 if cache is not None:
522 522 nodetree = cache.get('disambiguationnodetree')
523 523 if not nodetree:
524 524 try:
525 525 nodetree = parsers.nodetree(cl.index, len(revs))
526 526 except AttributeError:
527 527 # no native nodetree
528 528 pass
529 529 else:
530 530 for r in revs:
531 531 nodetree.insert(r)
532 532 if cache is not None:
533 533 cache['disambiguationnodetree'] = nodetree
534 534 if nodetree is not None:
535 535 length = max(nodetree.shortest(node), minlength)
536 536 prefix = hexnode[:length]
537 537 return disambiguate(prefix)
538 538 for length in range(minlength, len(hexnode) + 1):
539 539 matches = []
540 540 prefix = hexnode[:length]
541 541 for rev in revs:
542 542 otherhexnode = repo[rev].hex()
543 543 if prefix == otherhexnode[:length]:
544 544 matches.append(otherhexnode)
545 545 if len(matches) == 1:
546 546 return disambiguate(prefix)
547 547
548 548 try:
549 549 return disambiguate(cl.shortest(node, minlength))
550 550 except error.LookupError:
551 551 raise error.RepoLookupError()
552 552
553 553 def isrevsymbol(repo, symbol):
554 554 """Checks if a symbol exists in the repo.
555 555
556 556 See revsymbol() for details. Raises error.AmbiguousPrefixLookupError if the
557 557 symbol is an ambiguous nodeid prefix.
558 558 """
559 559 try:
560 560 revsymbol(repo, symbol)
561 561 return True
562 562 except error.RepoLookupError:
563 563 return False
564 564
565 565 def revsymbol(repo, symbol):
566 566 """Returns a context given a single revision symbol (as string).
567 567
568 568 This is similar to revsingle(), but accepts only a single revision symbol,
569 569 i.e. things like ".", "tip", "1234", "deadbeef", "my-bookmark" work, but
570 570 not "max(public())".
571 571 """
572 572 if not isinstance(symbol, bytes):
573 573 msg = ("symbol (%s of type %s) was not a string, did you mean "
574 574 "repo[symbol]?" % (symbol, type(symbol)))
575 575 raise error.ProgrammingError(msg)
576 576 try:
577 577 if symbol in ('.', 'tip', 'null'):
578 578 return repo[symbol]
579 579
580 580 try:
581 581 r = int(symbol)
582 582 if '%d' % r != symbol:
583 583 raise ValueError
584 584 l = len(repo.changelog)
585 585 if r < 0:
586 586 r += l
587 587 if r < 0 or r >= l and r != wdirrev:
588 588 raise ValueError
589 589 return repo[r]
590 590 except error.FilteredIndexError:
591 591 raise
592 592 except (ValueError, OverflowError, IndexError):
593 593 pass
594 594
595 595 if len(symbol) == 40:
596 596 try:
597 597 node = bin(symbol)
598 598 rev = repo.changelog.rev(node)
599 599 return repo[rev]
600 600 except error.FilteredLookupError:
601 601 raise
602 602 except (TypeError, LookupError):
603 603 pass
604 604
605 605 # look up bookmarks through the name interface
606 606 try:
607 607 node = repo.names.singlenode(repo, symbol)
608 608 rev = repo.changelog.rev(node)
609 609 return repo[rev]
610 610 except KeyError:
611 611 pass
612 612
613 613 node = resolvehexnodeidprefix(repo, symbol)
614 614 if node is not None:
615 615 rev = repo.changelog.rev(node)
616 616 return repo[rev]
617 617
618 618 raise error.RepoLookupError(_("unknown revision '%s'") % symbol)
619 619
620 620 except error.WdirUnsupported:
621 621 return repo[None]
622 622 except (error.FilteredIndexError, error.FilteredLookupError,
623 623 error.FilteredRepoLookupError):
624 624 raise _filterederror(repo, symbol)
625 625
626 626 def _filterederror(repo, changeid):
627 627 """build an exception to be raised about a filtered changeid
628 628
629 629 This is extracted in a function to help extensions (eg: evolve) to
630 630 experiment with various message variants."""
631 631 if repo.filtername.startswith('visible'):
632 632
633 633 # Check if the changeset is obsolete
634 634 unfilteredrepo = repo.unfiltered()
635 635 ctx = revsymbol(unfilteredrepo, changeid)
636 636
637 637 # If the changeset is obsolete, enrich the message with the reason
638 638 # that made this changeset not visible
639 639 if ctx.obsolete():
640 640 msg = obsutil._getfilteredreason(repo, changeid, ctx)
641 641 else:
642 642 msg = _("hidden revision '%s'") % changeid
643 643
644 644 hint = _('use --hidden to access hidden revisions')
645 645
646 646 return error.FilteredRepoLookupError(msg, hint=hint)
647 647 msg = _("filtered revision '%s' (not in '%s' subset)")
648 648 msg %= (changeid, repo.filtername)
649 649 return error.FilteredRepoLookupError(msg)
650 650
651 651 def revsingle(repo, revspec, default='.', localalias=None):
652 652 if not revspec and revspec != 0:
653 653 return repo[default]
654 654
655 655 l = revrange(repo, [revspec], localalias=localalias)
656 656 if not l:
657 657 raise error.Abort(_('empty revision set'))
658 658 return repo[l.last()]
659 659
660 660 def _pairspec(revspec):
661 661 tree = revsetlang.parse(revspec)
662 662 return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall')
663 663
664 664 def revpair(repo, revs):
665 665 if not revs:
666 666 return repo['.'], repo[None]
667 667
668 668 l = revrange(repo, revs)
669 669
670 670 if not l:
671 671 raise error.Abort(_('empty revision range'))
672 672
673 673 first = l.first()
674 674 second = l.last()
675 675
676 676 if (first == second and len(revs) >= 2
677 677 and not all(revrange(repo, [r]) for r in revs)):
678 678 raise error.Abort(_('empty revision on one side of range'))
679 679
680 680 # if top-level is range expression, the result must always be a pair
681 681 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
682 682 return repo[first], repo[None]
683 683
684 684 return repo[first], repo[second]
685 685
686 686 def revrange(repo, specs, localalias=None):
687 687 """Execute 1 to many revsets and return the union.
688 688
689 689 This is the preferred mechanism for executing revsets using user-specified
690 690 config options, such as revset aliases.
691 691
692 692 The revsets specified by ``specs`` will be executed via a chained ``OR``
693 693 expression. If ``specs`` is empty, an empty result is returned.
694 694
695 695 ``specs`` can contain integers, in which case they are assumed to be
696 696 revision numbers.
697 697
698 698 It is assumed the revsets are already formatted. If you have arguments
699 699 that need to be expanded in the revset, call ``revsetlang.formatspec()``
700 700 and pass the result as an element of ``specs``.
701 701
702 702 Specifying a single revset is allowed.
703 703
704 704 Returns a ``revset.abstractsmartset`` which is a list-like interface over
705 705 integer revisions.
706 706 """
707 707 allspecs = []
708 708 for spec in specs:
709 709 if isinstance(spec, int):
710 710 spec = revsetlang.formatspec('%d', spec)
711 711 allspecs.append(spec)
712 712 return repo.anyrevs(allspecs, user=True, localalias=localalias)
713 713
714 714 def meaningfulparents(repo, ctx):
715 715 """Return list of meaningful (or all if debug) parentrevs for rev.
716 716
717 717 For merges (two non-nullrev revisions) both parents are meaningful.
718 718 Otherwise the first parent revision is considered meaningful if it
719 719 is not the preceding revision.
720 720 """
721 721 parents = ctx.parents()
722 722 if len(parents) > 1:
723 723 return parents
724 724 if repo.ui.debugflag:
725 725 return [parents[0], repo[nullrev]]
726 726 if parents[0].rev() >= intrev(ctx) - 1:
727 727 return []
728 728 return parents
729 729
730 730 def getuipathfn(repo, legacyrelativevalue=False, forcerelativevalue=None):
731 731 """Return a function that produced paths for presenting to the user.
732 732
733 733 The returned function takes a repo-relative path and produces a path
734 734 that can be presented in the UI.
735 735
736 736 Depending on the value of ui.relative-paths, either a repo-relative or
737 737 cwd-relative path will be produced.
738 738
739 739 legacyrelativevalue is the value to use if ui.relative-paths=legacy
740 740
741 741 If forcerelativevalue is not None, then that value will be used regardless
742 742 of what ui.relative-paths is set to.
743 743 """
744 744 if forcerelativevalue is not None:
745 745 relative = forcerelativevalue
746 746 else:
747 747 config = repo.ui.config('ui', 'relative-paths')
748 748 if config == 'legacy':
749 749 relative = legacyrelativevalue
750 750 else:
751 751 relative = stringutil.parsebool(config)
752 752 if relative is None:
753 753 raise error.ConfigError(
754 754 _("ui.relative-paths is not a boolean ('%s')") % config)
755 755
756 756 if relative:
757 757 cwd = repo.getcwd()
758 758 pathto = repo.pathto
759 759 return lambda f: pathto(f, cwd)
760 760 elif repo.ui.configbool('ui', 'slash'):
761 761 return lambda f: f
762 762 else:
763 763 return util.localpath
764 764
765 765 def subdiruipathfn(subpath, uipathfn):
766 766 '''Create a new uipathfn that treats the file as relative to subpath.'''
767 767 return lambda f: uipathfn(posixpath.join(subpath, f))
768 768
769 769 def anypats(pats, opts):
770 770 '''Checks if any patterns, including --include and --exclude were given.
771 771
772 772 Some commands (e.g. addremove) use this condition for deciding whether to
773 773 print absolute or relative paths.
774 774 '''
775 775 return bool(pats or opts.get('include') or opts.get('exclude'))
776 776
777 777 def expandpats(pats):
778 778 '''Expand bare globs when running on windows.
779 779 On posix we assume it already has already been done by sh.'''
780 780 if not util.expandglobs:
781 781 return list(pats)
782 782 ret = []
783 783 for kindpat in pats:
784 784 kind, pat = matchmod._patsplit(kindpat, None)
785 785 if kind is None:
786 786 try:
787 787 globbed = glob.glob(pat)
788 788 except re.error:
789 789 globbed = [pat]
790 790 if globbed:
791 791 ret.extend(globbed)
792 792 continue
793 793 ret.append(kindpat)
794 794 return ret
795 795
796 796 def matchandpats(ctx, pats=(), opts=None, globbed=False, default='relpath',
797 797 badfn=None):
798 798 '''Return a matcher and the patterns that were used.
799 799 The matcher will warn about bad matches, unless an alternate badfn callback
800 800 is provided.'''
801 801 if opts is None:
802 802 opts = {}
803 803 if not globbed and default == 'relpath':
804 804 pats = expandpats(pats or [])
805 805
806 806 uipathfn = getuipathfn(ctx.repo(), legacyrelativevalue=True)
807 807 def bad(f, msg):
808 808 ctx.repo().ui.warn("%s: %s\n" % (uipathfn(f), msg))
809 809
810 810 if badfn is None:
811 811 badfn = bad
812 812
813 813 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
814 814 default, listsubrepos=opts.get('subrepos'), badfn=badfn)
815 815
816 816 if m.always():
817 817 pats = []
818 818 return m, pats
819 819
820 820 def match(ctx, pats=(), opts=None, globbed=False, default='relpath',
821 821 badfn=None):
822 822 '''Return a matcher that will warn about bad matches.'''
823 823 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
824 824
825 825 def matchall(repo):
826 826 '''Return a matcher that will efficiently match everything.'''
827 827 return matchmod.always()
828 828
829 829 def matchfiles(repo, files, badfn=None):
830 830 '''Return a matcher that will efficiently match exactly these files.'''
831 831 return matchmod.exact(files, badfn=badfn)
832 832
833 833 def parsefollowlinespattern(repo, rev, pat, msg):
834 834 """Return a file name from `pat` pattern suitable for usage in followlines
835 835 logic.
836 836 """
837 837 if not matchmod.patkind(pat):
838 838 return pathutil.canonpath(repo.root, repo.getcwd(), pat)
839 839 else:
840 840 ctx = repo[rev]
841 841 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=ctx)
842 842 files = [f for f in ctx if m(f)]
843 843 if len(files) != 1:
844 844 raise error.ParseError(msg)
845 845 return files[0]
846 846
847 847 def getorigvfs(ui, repo):
848 848 """return a vfs suitable to save 'orig' file
849 849
850 850 return None if no special directory is configured"""
851 851 origbackuppath = ui.config('ui', 'origbackuppath')
852 852 if not origbackuppath:
853 853 return None
854 854 return vfs.vfs(repo.wvfs.join(origbackuppath))
855 855
856 856 def backuppath(ui, repo, filepath):
857 857 '''customize where working copy backup files (.orig files) are created
858 858
859 859 Fetch user defined path from config file: [ui] origbackuppath = <path>
860 860 Fall back to default (filepath with .orig suffix) if not specified
861 861
862 862 filepath is repo-relative
863 863
864 864 Returns an absolute path
865 865 '''
866 866 origvfs = getorigvfs(ui, repo)
867 867 if origvfs is None:
868 868 return repo.wjoin(filepath + ".orig")
869 869
870 870 origbackupdir = origvfs.dirname(filepath)
871 871 if not origvfs.isdir(origbackupdir) or origvfs.islink(origbackupdir):
872 872 ui.note(_('creating directory: %s\n') % origvfs.join(origbackupdir))
873 873
874 874 # Remove any files that conflict with the backup file's path
875 875 for f in reversed(list(util.finddirs(filepath))):
876 876 if origvfs.isfileorlink(f):
877 877 ui.note(_('removing conflicting file: %s\n')
878 878 % origvfs.join(f))
879 879 origvfs.unlink(f)
880 880 break
881 881
882 882 origvfs.makedirs(origbackupdir)
883 883
884 884 if origvfs.isdir(filepath) and not origvfs.islink(filepath):
885 885 ui.note(_('removing conflicting directory: %s\n')
886 886 % origvfs.join(filepath))
887 887 origvfs.rmtree(filepath, forcibly=True)
888 888
889 889 return origvfs.join(filepath)
890 890
891 891 class _containsnode(object):
892 892 """proxy __contains__(node) to container.__contains__ which accepts revs"""
893 893
894 894 def __init__(self, repo, revcontainer):
895 895 self._torev = repo.changelog.rev
896 896 self._revcontains = revcontainer.__contains__
897 897
898 898 def __contains__(self, node):
899 899 return self._revcontains(self._torev(node))
900 900
901 901 def cleanupnodes(repo, replacements, operation, moves=None, metadata=None,
902 902 fixphase=False, targetphase=None, backup=True):
903 903 """do common cleanups when old nodes are replaced by new nodes
904 904
905 905 That includes writing obsmarkers or stripping nodes, and moving bookmarks.
906 906 (we might also want to move working directory parent in the future)
907 907
908 908 By default, bookmark moves are calculated automatically from 'replacements',
909 909 but 'moves' can be used to override that. Also, 'moves' may include
910 910 additional bookmark moves that should not have associated obsmarkers.
911 911
912 912 replacements is {oldnode: [newnode]} or a iterable of nodes if they do not
913 913 have replacements. operation is a string, like "rebase".
914 914
915 915 metadata is dictionary containing metadata to be stored in obsmarker if
916 916 obsolescence is enabled.
917 917 """
918 918 assert fixphase or targetphase is None
919 919 if not replacements and not moves:
920 920 return
921 921
922 922 # translate mapping's other forms
923 923 if not util.safehasattr(replacements, 'items'):
924 924 replacements = {(n,): () for n in replacements}
925 925 else:
926 926 # upgrading non tuple "source" to tuple ones for BC
927 927 repls = {}
928 928 for key, value in replacements.items():
929 929 if not isinstance(key, tuple):
930 930 key = (key,)
931 931 repls[key] = value
932 932 replacements = repls
933 933
934 934 # Unfiltered repo is needed since nodes in replacements might be hidden.
935 935 unfi = repo.unfiltered()
936 936
937 937 # Calculate bookmark movements
938 938 if moves is None:
939 939 moves = {}
940 940 for oldnodes, newnodes in replacements.items():
941 941 for oldnode in oldnodes:
942 942 if oldnode in moves:
943 943 continue
944 944 if len(newnodes) > 1:
945 945 # usually a split, take the one with biggest rev number
946 946 newnode = next(unfi.set('max(%ln)', newnodes)).node()
947 947 elif len(newnodes) == 0:
948 948 # move bookmark backwards
949 949 allreplaced = []
950 950 for rep in replacements:
951 951 allreplaced.extend(rep)
952 952 roots = list(unfi.set('max((::%n) - %ln)', oldnode,
953 953 allreplaced))
954 954 if roots:
955 955 newnode = roots[0].node()
956 956 else:
957 957 newnode = nullid
958 958 else:
959 959 newnode = newnodes[0]
960 960 moves[oldnode] = newnode
961 961
962 962 allnewnodes = [n for ns in replacements.values() for n in ns]
963 963 toretract = {}
964 964 toadvance = {}
965 965 if fixphase:
966 966 precursors = {}
967 967 for oldnodes, newnodes in replacements.items():
968 968 for oldnode in oldnodes:
969 969 for newnode in newnodes:
970 970 precursors.setdefault(newnode, []).append(oldnode)
971 971
972 972 allnewnodes.sort(key=lambda n: unfi[n].rev())
973 973 newphases = {}
974 974 def phase(ctx):
975 975 return newphases.get(ctx.node(), ctx.phase())
976 976 for newnode in allnewnodes:
977 977 ctx = unfi[newnode]
978 978 parentphase = max(phase(p) for p in ctx.parents())
979 979 if targetphase is None:
980 980 oldphase = max(unfi[oldnode].phase()
981 981 for oldnode in precursors[newnode])
982 982 newphase = max(oldphase, parentphase)
983 983 else:
984 984 newphase = max(targetphase, parentphase)
985 985 newphases[newnode] = newphase
986 986 if newphase > ctx.phase():
987 987 toretract.setdefault(newphase, []).append(newnode)
988 988 elif newphase < ctx.phase():
989 989 toadvance.setdefault(newphase, []).append(newnode)
990 990
991 991 with repo.transaction('cleanup') as tr:
992 992 # Move bookmarks
993 993 bmarks = repo._bookmarks
994 994 bmarkchanges = []
995 995 for oldnode, newnode in moves.items():
996 996 oldbmarks = repo.nodebookmarks(oldnode)
997 997 if not oldbmarks:
998 998 continue
999 999 from . import bookmarks # avoid import cycle
1000 1000 repo.ui.debug('moving bookmarks %r from %s to %s\n' %
1001 1001 (pycompat.rapply(pycompat.maybebytestr, oldbmarks),
1002 1002 hex(oldnode), hex(newnode)))
1003 1003 # Delete divergent bookmarks being parents of related newnodes
1004 1004 deleterevs = repo.revs('parents(roots(%ln & (::%n))) - parents(%n)',
1005 1005 allnewnodes, newnode, oldnode)
1006 1006 deletenodes = _containsnode(repo, deleterevs)
1007 1007 for name in oldbmarks:
1008 1008 bmarkchanges.append((name, newnode))
1009 1009 for b in bookmarks.divergent2delete(repo, deletenodes, name):
1010 1010 bmarkchanges.append((b, None))
1011 1011
1012 1012 if bmarkchanges:
1013 1013 bmarks.applychanges(repo, tr, bmarkchanges)
1014 1014
1015 1015 for phase, nodes in toretract.items():
1016 1016 phases.retractboundary(repo, tr, phase, nodes)
1017 1017 for phase, nodes in toadvance.items():
1018 1018 phases.advanceboundary(repo, tr, phase, nodes)
1019 1019
1020 1020 mayusearchived = repo.ui.config('experimental', 'cleanup-as-archived')
1021 1021 # Obsolete or strip nodes
1022 1022 if obsolete.isenabled(repo, obsolete.createmarkersopt):
1023 1023 # If a node is already obsoleted, and we want to obsolete it
1024 1024 # without a successor, skip that obssolete request since it's
1025 1025 # unnecessary. That's the "if s or not isobs(n)" check below.
1026 1026 # Also sort the node in topology order, that might be useful for
1027 1027 # some obsstore logic.
1028 1028 # NOTE: the sorting might belong to createmarkers.
1029 1029 torev = unfi.changelog.rev
1030 1030 sortfunc = lambda ns: torev(ns[0][0])
1031 1031 rels = []
1032 1032 for ns, s in sorted(replacements.items(), key=sortfunc):
1033 1033 rel = (tuple(unfi[n] for n in ns), tuple(unfi[m] for m in s))
1034 1034 rels.append(rel)
1035 1035 if rels:
1036 1036 obsolete.createmarkers(repo, rels, operation=operation,
1037 1037 metadata=metadata)
1038 1038 elif phases.supportinternal(repo) and mayusearchived:
1039 1039 # this assume we do not have "unstable" nodes above the cleaned ones
1040 1040 allreplaced = set()
1041 1041 for ns in replacements.keys():
1042 1042 allreplaced.update(ns)
1043 1043 if backup:
1044 1044 from . import repair # avoid import cycle
1045 1045 node = min(allreplaced, key=repo.changelog.rev)
1046 1046 repair.backupbundle(repo, allreplaced, allreplaced, node,
1047 1047 operation)
1048 1048 phases.retractboundary(repo, tr, phases.archived, allreplaced)
1049 1049 else:
1050 1050 from . import repair # avoid import cycle
1051 1051 tostrip = list(n for ns in replacements for n in ns)
1052 1052 if tostrip:
1053 1053 repair.delayedstrip(repo.ui, repo, tostrip, operation,
1054 1054 backup=backup)
1055 1055
1056 1056 def addremove(repo, matcher, prefix, uipathfn, opts=None):
1057 1057 if opts is None:
1058 1058 opts = {}
1059 1059 m = matcher
1060 1060 dry_run = opts.get('dry_run')
1061 1061 try:
1062 1062 similarity = float(opts.get('similarity') or 0)
1063 1063 except ValueError:
1064 1064 raise error.Abort(_('similarity must be a number'))
1065 1065 if similarity < 0 or similarity > 100:
1066 1066 raise error.Abort(_('similarity must be between 0 and 100'))
1067 1067 similarity /= 100.0
1068 1068
1069 1069 ret = 0
1070 1070
1071 1071 wctx = repo[None]
1072 1072 for subpath in sorted(wctx.substate):
1073 1073 submatch = matchmod.subdirmatcher(subpath, m)
1074 1074 if opts.get('subrepos') or m.exact(subpath) or any(submatch.files()):
1075 1075 sub = wctx.sub(subpath)
1076 1076 subprefix = repo.wvfs.reljoin(prefix, subpath)
1077 1077 subuipathfn = subdiruipathfn(subpath, uipathfn)
1078 1078 try:
1079 1079 if sub.addremove(submatch, subprefix, subuipathfn, opts):
1080 1080 ret = 1
1081 1081 except error.LookupError:
1082 1082 repo.ui.status(_("skipping missing subrepository: %s\n")
1083 1083 % uipathfn(subpath))
1084 1084
1085 1085 rejected = []
1086 1086 def badfn(f, msg):
1087 1087 if f in m.files():
1088 1088 m.bad(f, msg)
1089 1089 rejected.append(f)
1090 1090
1091 1091 badmatch = matchmod.badmatch(m, badfn)
1092 1092 added, unknown, deleted, removed, forgotten = _interestingfiles(repo,
1093 1093 badmatch)
1094 1094
1095 1095 unknownset = set(unknown + forgotten)
1096 1096 toprint = unknownset.copy()
1097 1097 toprint.update(deleted)
1098 1098 for abs in sorted(toprint):
1099 1099 if repo.ui.verbose or not m.exact(abs):
1100 1100 if abs in unknownset:
1101 1101 status = _('adding %s\n') % uipathfn(abs)
1102 1102 label = 'ui.addremove.added'
1103 1103 else:
1104 1104 status = _('removing %s\n') % uipathfn(abs)
1105 1105 label = 'ui.addremove.removed'
1106 1106 repo.ui.status(status, label=label)
1107 1107
1108 1108 renames = _findrenames(repo, m, added + unknown, removed + deleted,
1109 1109 similarity, uipathfn)
1110 1110
1111 1111 if not dry_run:
1112 1112 _markchanges(repo, unknown + forgotten, deleted, renames)
1113 1113
1114 1114 for f in rejected:
1115 1115 if f in m.files():
1116 1116 return 1
1117 1117 return ret
1118 1118
1119 1119 def marktouched(repo, files, similarity=0.0):
1120 1120 '''Assert that files have somehow been operated upon. files are relative to
1121 1121 the repo root.'''
1122 1122 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
1123 1123 rejected = []
1124 1124
1125 1125 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
1126 1126
1127 1127 if repo.ui.verbose:
1128 1128 unknownset = set(unknown + forgotten)
1129 1129 toprint = unknownset.copy()
1130 1130 toprint.update(deleted)
1131 1131 for abs in sorted(toprint):
1132 1132 if abs in unknownset:
1133 1133 status = _('adding %s\n') % abs
1134 1134 else:
1135 1135 status = _('removing %s\n') % abs
1136 1136 repo.ui.status(status)
1137 1137
1138 1138 # TODO: We should probably have the caller pass in uipathfn and apply it to
1139 1139 # the messages above too. legacyrelativevalue=True is consistent with how
1140 1140 # it used to work.
1141 1141 uipathfn = getuipathfn(repo, legacyrelativevalue=True)
1142 1142 renames = _findrenames(repo, m, added + unknown, removed + deleted,
1143 1143 similarity, uipathfn)
1144 1144
1145 1145 _markchanges(repo, unknown + forgotten, deleted, renames)
1146 1146
1147 1147 for f in rejected:
1148 1148 if f in m.files():
1149 1149 return 1
1150 1150 return 0
1151 1151
1152 1152 def _interestingfiles(repo, matcher):
1153 1153 '''Walk dirstate with matcher, looking for files that addremove would care
1154 1154 about.
1155 1155
1156 1156 This is different from dirstate.status because it doesn't care about
1157 1157 whether files are modified or clean.'''
1158 1158 added, unknown, deleted, removed, forgotten = [], [], [], [], []
1159 1159 audit_path = pathutil.pathauditor(repo.root, cached=True)
1160 1160
1161 1161 ctx = repo[None]
1162 1162 dirstate = repo.dirstate
1163 1163 matcher = repo.narrowmatch(matcher, includeexact=True)
1164 1164 walkresults = dirstate.walk(matcher, subrepos=sorted(ctx.substate),
1165 1165 unknown=True, ignored=False, full=False)
1166 1166 for abs, st in walkresults.iteritems():
1167 1167 dstate = dirstate[abs]
1168 1168 if dstate == '?' and audit_path.check(abs):
1169 1169 unknown.append(abs)
1170 1170 elif dstate != 'r' and not st:
1171 1171 deleted.append(abs)
1172 1172 elif dstate == 'r' and st:
1173 1173 forgotten.append(abs)
1174 1174 # for finding renames
1175 1175 elif dstate == 'r' and not st:
1176 1176 removed.append(abs)
1177 1177 elif dstate == 'a':
1178 1178 added.append(abs)
1179 1179
1180 1180 return added, unknown, deleted, removed, forgotten
1181 1181
1182 1182 def _findrenames(repo, matcher, added, removed, similarity, uipathfn):
1183 1183 '''Find renames from removed files to added ones.'''
1184 1184 renames = {}
1185 1185 if similarity > 0:
1186 1186 for old, new, score in similar.findrenames(repo, added, removed,
1187 1187 similarity):
1188 1188 if (repo.ui.verbose or not matcher.exact(old)
1189 1189 or not matcher.exact(new)):
1190 1190 repo.ui.status(_('recording removal of %s as rename to %s '
1191 1191 '(%d%% similar)\n') %
1192 1192 (uipathfn(old), uipathfn(new),
1193 1193 score * 100))
1194 1194 renames[new] = old
1195 1195 return renames
1196 1196
1197 1197 def _markchanges(repo, unknown, deleted, renames):
1198 1198 '''Marks the files in unknown as added, the files in deleted as removed,
1199 1199 and the files in renames as copied.'''
1200 1200 wctx = repo[None]
1201 1201 with repo.wlock():
1202 1202 wctx.forget(deleted)
1203 1203 wctx.add(unknown)
1204 1204 for new, old in renames.iteritems():
1205 1205 wctx.copy(old, new)
1206 1206
1207 1207 def getrenamedfn(repo, endrev=None):
1208 1208 if copiesmod.usechangesetcentricalgo(repo):
1209 1209 def getrenamed(fn, rev):
1210 1210 ctx = repo[rev]
1211 1211 p1copies = ctx.p1copies()
1212 1212 if fn in p1copies:
1213 1213 return p1copies[fn]
1214 1214 p2copies = ctx.p2copies()
1215 1215 if fn in p2copies:
1216 1216 return p2copies[fn]
1217 1217 return None
1218 1218 return getrenamed
1219 1219
1220 1220 rcache = {}
1221 1221 if endrev is None:
1222 1222 endrev = len(repo)
1223 1223
1224 1224 def getrenamed(fn, rev):
1225 1225 '''looks up all renames for a file (up to endrev) the first
1226 1226 time the file is given. It indexes on the changerev and only
1227 1227 parses the manifest if linkrev != changerev.
1228 1228 Returns rename info for fn at changerev rev.'''
1229 1229 if fn not in rcache:
1230 1230 rcache[fn] = {}
1231 1231 fl = repo.file(fn)
1232 1232 for i in fl:
1233 1233 lr = fl.linkrev(i)
1234 1234 renamed = fl.renamed(fl.node(i))
1235 1235 rcache[fn][lr] = renamed and renamed[0]
1236 1236 if lr >= endrev:
1237 1237 break
1238 1238 if rev in rcache[fn]:
1239 1239 return rcache[fn][rev]
1240 1240
1241 1241 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1242 1242 # filectx logic.
1243 1243 try:
1244 1244 return repo[rev][fn].copysource()
1245 1245 except error.LookupError:
1246 1246 return None
1247 1247
1248 1248 return getrenamed
1249 1249
1250 1250 def getcopiesfn(repo, endrev=None):
1251 1251 if copiesmod.usechangesetcentricalgo(repo):
1252 1252 def copiesfn(ctx):
1253 1253 if ctx.p2copies():
1254 1254 allcopies = ctx.p1copies().copy()
1255 1255 # There should be no overlap
1256 1256 allcopies.update(ctx.p2copies())
1257 1257 return sorted(allcopies.items())
1258 1258 else:
1259 1259 return sorted(ctx.p1copies().items())
1260 1260 else:
1261 1261 getrenamed = getrenamedfn(repo, endrev)
1262 1262 def copiesfn(ctx):
1263 1263 copies = []
1264 1264 for fn in ctx.files():
1265 1265 rename = getrenamed(fn, ctx.rev())
1266 1266 if rename:
1267 1267 copies.append((fn, rename))
1268 1268 return copies
1269 1269
1270 1270 return copiesfn
1271 1271
1272 1272 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
1273 1273 """Update the dirstate to reflect the intent of copying src to dst. For
1274 1274 different reasons it might not end with dst being marked as copied from src.
1275 1275 """
1276 1276 origsrc = repo.dirstate.copied(src) or src
1277 1277 if dst == origsrc: # copying back a copy?
1278 1278 if repo.dirstate[dst] not in 'mn' and not dryrun:
1279 1279 repo.dirstate.normallookup(dst)
1280 1280 else:
1281 1281 if repo.dirstate[origsrc] == 'a' and origsrc == src:
1282 1282 if not ui.quiet:
1283 1283 ui.warn(_("%s has not been committed yet, so no copy "
1284 1284 "data will be stored for %s.\n")
1285 1285 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
1286 1286 if repo.dirstate[dst] in '?r' and not dryrun:
1287 1287 wctx.add([dst])
1288 1288 elif not dryrun:
1289 1289 wctx.copy(origsrc, dst)
1290 1290
1291 1291 def movedirstate(repo, newctx, match=None):
1292 1292 """Move the dirstate to newctx and adjust it as necessary.
1293 1293
1294 1294 A matcher can be provided as an optimization. It is probably a bug to pass
1295 1295 a matcher that doesn't match all the differences between the parent of the
1296 1296 working copy and newctx.
1297 1297 """
1298 1298 oldctx = repo['.']
1299 1299 ds = repo.dirstate
1300 1300 ds.setparents(newctx.node(), nullid)
1301 1301 copies = dict(ds.copies())
1302 1302 s = newctx.status(oldctx, match=match)
1303 1303 for f in s.modified:
1304 1304 if ds[f] == 'r':
1305 1305 # modified + removed -> removed
1306 1306 continue
1307 1307 ds.normallookup(f)
1308 1308
1309 1309 for f in s.added:
1310 1310 if ds[f] == 'r':
1311 1311 # added + removed -> unknown
1312 1312 ds.drop(f)
1313 1313 elif ds[f] != 'a':
1314 1314 ds.add(f)
1315 1315
1316 1316 for f in s.removed:
1317 1317 if ds[f] == 'a':
1318 1318 # removed + added -> normal
1319 1319 ds.normallookup(f)
1320 1320 elif ds[f] != 'r':
1321 1321 ds.remove(f)
1322 1322
1323 1323 # Merge old parent and old working dir copies
1324 1324 oldcopies = copiesmod.pathcopies(newctx, oldctx, match)
1325 1325 oldcopies.update(copies)
1326 1326 copies = dict((dst, oldcopies.get(src, src))
1327 1327 for dst, src in oldcopies.iteritems())
1328 1328 # Adjust the dirstate copies
1329 1329 for dst, src in copies.iteritems():
1330 1330 if (src not in newctx or dst in newctx or ds[dst] != 'a'):
1331 1331 src = None
1332 1332 ds.copy(src, dst)
1333 1333
1334 1334 def writerequires(opener, requirements):
1335 1335 with opener('requires', 'w', atomictemp=True) as fp:
1336 1336 for r in sorted(requirements):
1337 1337 fp.write("%s\n" % r)
1338 1338
1339 1339 class filecachesubentry(object):
1340 1340 def __init__(self, path, stat):
1341 1341 self.path = path
1342 1342 self.cachestat = None
1343 1343 self._cacheable = None
1344 1344
1345 1345 if stat:
1346 1346 self.cachestat = filecachesubentry.stat(self.path)
1347 1347
1348 1348 if self.cachestat:
1349 1349 self._cacheable = self.cachestat.cacheable()
1350 1350 else:
1351 1351 # None means we don't know yet
1352 1352 self._cacheable = None
1353 1353
1354 1354 def refresh(self):
1355 1355 if self.cacheable():
1356 1356 self.cachestat = filecachesubentry.stat(self.path)
1357 1357
1358 1358 def cacheable(self):
1359 1359 if self._cacheable is not None:
1360 1360 return self._cacheable
1361 1361
1362 1362 # we don't know yet, assume it is for now
1363 1363 return True
1364 1364
1365 1365 def changed(self):
1366 1366 # no point in going further if we can't cache it
1367 1367 if not self.cacheable():
1368 1368 return True
1369 1369
1370 1370 newstat = filecachesubentry.stat(self.path)
1371 1371
1372 1372 # we may not know if it's cacheable yet, check again now
1373 1373 if newstat and self._cacheable is None:
1374 1374 self._cacheable = newstat.cacheable()
1375 1375
1376 1376 # check again
1377 1377 if not self._cacheable:
1378 1378 return True
1379 1379
1380 1380 if self.cachestat != newstat:
1381 1381 self.cachestat = newstat
1382 1382 return True
1383 1383 else:
1384 1384 return False
1385 1385
1386 1386 @staticmethod
1387 1387 def stat(path):
1388 1388 try:
1389 1389 return util.cachestat(path)
1390 1390 except OSError as e:
1391 1391 if e.errno != errno.ENOENT:
1392 1392 raise
1393 1393
1394 1394 class filecacheentry(object):
1395 1395 def __init__(self, paths, stat=True):
1396 1396 self._entries = []
1397 1397 for path in paths:
1398 1398 self._entries.append(filecachesubentry(path, stat))
1399 1399
1400 1400 def changed(self):
1401 1401 '''true if any entry has changed'''
1402 1402 for entry in self._entries:
1403 1403 if entry.changed():
1404 1404 return True
1405 1405 return False
1406 1406
1407 1407 def refresh(self):
1408 1408 for entry in self._entries:
1409 1409 entry.refresh()
1410 1410
1411 1411 class filecache(object):
1412 1412 """A property like decorator that tracks files under .hg/ for updates.
1413 1413
1414 1414 On first access, the files defined as arguments are stat()ed and the
1415 1415 results cached. The decorated function is called. The results are stashed
1416 1416 away in a ``_filecache`` dict on the object whose method is decorated.
1417 1417
1418 1418 On subsequent access, the cached result is used as it is set to the
1419 1419 instance dictionary.
1420 1420
1421 1421 On external property set/delete operations, the caller must update the
1422 1422 corresponding _filecache entry appropriately. Use __class__.<attr>.set()
1423 1423 instead of directly setting <attr>.
1424 1424
1425 1425 When using the property API, the cached data is always used if available.
1426 1426 No stat() is performed to check if the file has changed.
1427 1427
1428 1428 Others can muck about with the state of the ``_filecache`` dict. e.g. they
1429 1429 can populate an entry before the property's getter is called. In this case,
1430 1430 entries in ``_filecache`` will be used during property operations,
1431 1431 if available. If the underlying file changes, it is up to external callers
1432 1432 to reflect this by e.g. calling ``delattr(obj, attr)`` to remove the cached
1433 1433 method result as well as possibly calling ``del obj._filecache[attr]`` to
1434 1434 remove the ``filecacheentry``.
1435 1435 """
1436 1436
1437 1437 def __init__(self, *paths):
1438 1438 self.paths = paths
1439 1439
1440 1440 def join(self, obj, fname):
1441 1441 """Used to compute the runtime path of a cached file.
1442 1442
1443 1443 Users should subclass filecache and provide their own version of this
1444 1444 function to call the appropriate join function on 'obj' (an instance
1445 1445 of the class that its member function was decorated).
1446 1446 """
1447 1447 raise NotImplementedError
1448 1448
1449 1449 def __call__(self, func):
1450 1450 self.func = func
1451 1451 self.sname = func.__name__
1452 1452 self.name = pycompat.sysbytes(self.sname)
1453 1453 return self
1454 1454
1455 1455 def __get__(self, obj, type=None):
1456 1456 # if accessed on the class, return the descriptor itself.
1457 1457 if obj is None:
1458 1458 return self
1459 1459
1460 1460 assert self.sname not in obj.__dict__
1461 1461
1462 1462 entry = obj._filecache.get(self.name)
1463 1463
1464 1464 if entry:
1465 1465 if entry.changed():
1466 1466 entry.obj = self.func(obj)
1467 1467 else:
1468 1468 paths = [self.join(obj, path) for path in self.paths]
1469 1469
1470 1470 # We stat -before- creating the object so our cache doesn't lie if
1471 1471 # a writer modified between the time we read and stat
1472 1472 entry = filecacheentry(paths, True)
1473 1473 entry.obj = self.func(obj)
1474 1474
1475 1475 obj._filecache[self.name] = entry
1476 1476
1477 1477 obj.__dict__[self.sname] = entry.obj
1478 1478 return entry.obj
1479 1479
1480 1480 # don't implement __set__(), which would make __dict__ lookup as slow as
1481 1481 # function call.
1482 1482
1483 1483 def set(self, obj, value):
1484 1484 if self.name not in obj._filecache:
1485 1485 # we add an entry for the missing value because X in __dict__
1486 1486 # implies X in _filecache
1487 1487 paths = [self.join(obj, path) for path in self.paths]
1488 1488 ce = filecacheentry(paths, False)
1489 1489 obj._filecache[self.name] = ce
1490 1490 else:
1491 1491 ce = obj._filecache[self.name]
1492 1492
1493 1493 ce.obj = value # update cached copy
1494 1494 obj.__dict__[self.sname] = value # update copy returned by obj.x
1495 1495
1496 1496 def extdatasource(repo, source):
1497 1497 """Gather a map of rev -> value dict from the specified source
1498 1498
1499 1499 A source spec is treated as a URL, with a special case shell: type
1500 1500 for parsing the output from a shell command.
1501 1501
1502 1502 The data is parsed as a series of newline-separated records where
1503 1503 each record is a revision specifier optionally followed by a space
1504 1504 and a freeform string value. If the revision is known locally, it
1505 1505 is converted to a rev, otherwise the record is skipped.
1506 1506
1507 1507 Note that both key and value are treated as UTF-8 and converted to
1508 1508 the local encoding. This allows uniformity between local and
1509 1509 remote data sources.
1510 1510 """
1511 1511
1512 1512 spec = repo.ui.config("extdata", source)
1513 1513 if not spec:
1514 1514 raise error.Abort(_("unknown extdata source '%s'") % source)
1515 1515
1516 1516 data = {}
1517 1517 src = proc = None
1518 1518 try:
1519 1519 if spec.startswith("shell:"):
1520 1520 # external commands should be run relative to the repo root
1521 1521 cmd = spec[6:]
1522 1522 proc = subprocess.Popen(procutil.tonativestr(cmd),
1523 1523 shell=True, bufsize=-1,
1524 1524 close_fds=procutil.closefds,
1525 1525 stdout=subprocess.PIPE,
1526 1526 cwd=procutil.tonativestr(repo.root))
1527 1527 src = proc.stdout
1528 1528 else:
1529 1529 # treat as a URL or file
1530 1530 src = url.open(repo.ui, spec)
1531 1531 for l in src:
1532 1532 if " " in l:
1533 1533 k, v = l.strip().split(" ", 1)
1534 1534 else:
1535 1535 k, v = l.strip(), ""
1536 1536
1537 1537 k = encoding.tolocal(k)
1538 1538 try:
1539 1539 data[revsingle(repo, k).rev()] = encoding.tolocal(v)
1540 1540 except (error.LookupError, error.RepoLookupError):
1541 1541 pass # we ignore data for nodes that don't exist locally
1542 1542 finally:
1543 1543 if proc:
1544 1544 try:
1545 1545 proc.communicate()
1546 1546 except ValueError:
1547 1547 # This happens if we started iterating src and then
1548 1548 # get a parse error on a line. It should be safe to ignore.
1549 1549 pass
1550 1550 if src:
1551 1551 src.close()
1552 1552 if proc and proc.returncode != 0:
1553 1553 raise error.Abort(_("extdata command '%s' failed: %s")
1554 1554 % (cmd, procutil.explainexit(proc.returncode)))
1555 1555
1556 1556 return data
1557 1557
1558 1558 def _locksub(repo, lock, envvar, cmd, environ=None, *args, **kwargs):
1559 1559 if lock is None:
1560 1560 raise error.LockInheritanceContractViolation(
1561 1561 'lock can only be inherited while held')
1562 1562 if environ is None:
1563 1563 environ = {}
1564 1564 with lock.inherit() as locker:
1565 1565 environ[envvar] = locker
1566 1566 return repo.ui.system(cmd, environ=environ, *args, **kwargs)
1567 1567
1568 1568 def wlocksub(repo, cmd, *args, **kwargs):
1569 1569 """run cmd as a subprocess that allows inheriting repo's wlock
1570 1570
1571 1571 This can only be called while the wlock is held. This takes all the
1572 1572 arguments that ui.system does, and returns the exit code of the
1573 1573 subprocess."""
1574 1574 return _locksub(repo, repo.currentwlock(), 'HG_WLOCK_LOCKER', cmd, *args,
1575 1575 **kwargs)
1576 1576
1577 1577 class progress(object):
1578 1578 def __init__(self, ui, updatebar, topic, unit="", total=None):
1579 1579 self.ui = ui
1580 1580 self.pos = 0
1581 1581 self.topic = topic
1582 1582 self.unit = unit
1583 1583 self.total = total
1584 1584 self.debug = ui.configbool('progress', 'debug')
1585 1585 self._updatebar = updatebar
1586 1586
1587 1587 def __enter__(self):
1588 1588 return self
1589 1589
1590 1590 def __exit__(self, exc_type, exc_value, exc_tb):
1591 1591 self.complete()
1592 1592
1593 1593 def update(self, pos, item="", total=None):
1594 1594 assert pos is not None
1595 1595 if total:
1596 1596 self.total = total
1597 1597 self.pos = pos
1598 1598 self._updatebar(self.topic, self.pos, item, self.unit, self.total)
1599 1599 if self.debug:
1600 1600 self._printdebug(item)
1601 1601
1602 1602 def increment(self, step=1, item="", total=None):
1603 1603 self.update(self.pos + step, item, total)
1604 1604
1605 1605 def complete(self):
1606 1606 self.pos = None
1607 1607 self.unit = ""
1608 1608 self.total = None
1609 1609 self._updatebar(self.topic, self.pos, "", self.unit, self.total)
1610 1610
1611 1611 def _printdebug(self, item):
1612 1612 if self.unit:
1613 1613 unit = ' ' + self.unit
1614 1614 if item:
1615 1615 item = ' ' + item
1616 1616
1617 1617 if self.total:
1618 1618 pct = 100.0 * self.pos / self.total
1619 1619 self.ui.debug('%s:%s %d/%d%s (%4.2f%%)\n'
1620 1620 % (self.topic, item, self.pos, self.total, unit, pct))
1621 1621 else:
1622 1622 self.ui.debug('%s:%s %d%s\n' % (self.topic, item, self.pos, unit))
1623 1623
1624 1624 def gdinitconfig(ui):
1625 1625 """helper function to know if a repo should be created as general delta
1626 1626 """
1627 1627 # experimental config: format.generaldelta
1628 1628 return (ui.configbool('format', 'generaldelta')
1629 1629 or ui.configbool('format', 'usegeneraldelta'))
1630 1630
1631 1631 def gddeltaconfig(ui):
1632 1632 """helper function to know if incoming delta should be optimised
1633 1633 """
1634 1634 # experimental config: format.generaldelta
1635 1635 return ui.configbool('format', 'generaldelta')
1636 1636
1637 1637 class simplekeyvaluefile(object):
1638 1638 """A simple file with key=value lines
1639 1639
1640 1640 Keys must be alphanumerics and start with a letter, values must not
1641 1641 contain '\n' characters"""
1642 1642 firstlinekey = '__firstline'
1643 1643
1644 1644 def __init__(self, vfs, path, keys=None):
1645 1645 self.vfs = vfs
1646 1646 self.path = path
1647 1647
1648 1648 def read(self, firstlinenonkeyval=False):
1649 1649 """Read the contents of a simple key-value file
1650 1650
1651 1651 'firstlinenonkeyval' indicates whether the first line of file should
1652 1652 be treated as a key-value pair or reuturned fully under the
1653 1653 __firstline key."""
1654 1654 lines = self.vfs.readlines(self.path)
1655 1655 d = {}
1656 1656 if firstlinenonkeyval:
1657 1657 if not lines:
1658 1658 e = _("empty simplekeyvalue file")
1659 1659 raise error.CorruptedState(e)
1660 1660 # we don't want to include '\n' in the __firstline
1661 1661 d[self.firstlinekey] = lines[0][:-1]
1662 1662 del lines[0]
1663 1663
1664 1664 try:
1665 1665 # the 'if line.strip()' part prevents us from failing on empty
1666 1666 # lines which only contain '\n' therefore are not skipped
1667 1667 # by 'if line'
1668 1668 updatedict = dict(line[:-1].split('=', 1) for line in lines
1669 1669 if line.strip())
1670 1670 if self.firstlinekey in updatedict:
1671 1671 e = _("%r can't be used as a key")
1672 1672 raise error.CorruptedState(e % self.firstlinekey)
1673 1673 d.update(updatedict)
1674 1674 except ValueError as e:
1675 1675 raise error.CorruptedState(str(e))
1676 1676 return d
1677 1677
1678 1678 def write(self, data, firstline=None):
1679 1679 """Write key=>value mapping to a file
1680 1680 data is a dict. Keys must be alphanumerical and start with a letter.
1681 1681 Values must not contain newline characters.
1682 1682
1683 1683 If 'firstline' is not None, it is written to file before
1684 1684 everything else, as it is, not in a key=value form"""
1685 1685 lines = []
1686 1686 if firstline is not None:
1687 1687 lines.append('%s\n' % firstline)
1688 1688
1689 1689 for k, v in data.items():
1690 1690 if k == self.firstlinekey:
1691 1691 e = "key name '%s' is reserved" % self.firstlinekey
1692 1692 raise error.ProgrammingError(e)
1693 1693 if not k[0:1].isalpha():
1694 1694 e = "keys must start with a letter in a key-value file"
1695 1695 raise error.ProgrammingError(e)
1696 1696 if not k.isalnum():
1697 1697 e = "invalid key name in a simple key-value file"
1698 1698 raise error.ProgrammingError(e)
1699 1699 if '\n' in v:
1700 1700 e = "invalid value in a simple key-value file"
1701 1701 raise error.ProgrammingError(e)
1702 1702 lines.append("%s=%s\n" % (k, v))
1703 1703 with self.vfs(self.path, mode='wb', atomictemp=True) as fp:
1704 1704 fp.write(''.join(lines))
1705 1705
1706 1706 _reportobsoletedsource = [
1707 1707 'debugobsolete',
1708 1708 'pull',
1709 1709 'push',
1710 1710 'serve',
1711 1711 'unbundle',
1712 1712 ]
1713 1713
1714 1714 _reportnewcssource = [
1715 1715 'pull',
1716 1716 'unbundle',
1717 1717 ]
1718 1718
1719 1719 def prefetchfiles(repo, revs, match):
1720 1720 """Invokes the registered file prefetch functions, allowing extensions to
1721 1721 ensure the corresponding files are available locally, before the command
1722 1722 uses them."""
1723 1723 if match:
1724 1724 # The command itself will complain about files that don't exist, so
1725 1725 # don't duplicate the message.
1726 1726 match = matchmod.badmatch(match, lambda fn, msg: None)
1727 1727 else:
1728 1728 match = matchall(repo)
1729 1729
1730 1730 fileprefetchhooks(repo, revs, match)
1731 1731
1732 1732 # a list of (repo, revs, match) prefetch functions
1733 1733 fileprefetchhooks = util.hooks()
1734 1734
1735 1735 # A marker that tells the evolve extension to suppress its own reporting
1736 1736 _reportstroubledchangesets = True
1737 1737
1738 1738 def registersummarycallback(repo, otr, txnname=''):
1739 1739 """register a callback to issue a summary after the transaction is closed
1740 1740 """
1741 1741 def txmatch(sources):
1742 1742 return any(txnname.startswith(source) for source in sources)
1743 1743
1744 1744 categories = []
1745 1745
1746 1746 def reportsummary(func):
1747 1747 """decorator for report callbacks."""
1748 1748 # The repoview life cycle is shorter than the one of the actual
1749 1749 # underlying repository. So the filtered object can die before the
1750 1750 # weakref is used leading to troubles. We keep a reference to the
1751 1751 # unfiltered object and restore the filtering when retrieving the
1752 1752 # repository through the weakref.
1753 1753 filtername = repo.filtername
1754 1754 reporef = weakref.ref(repo.unfiltered())
1755 1755 def wrapped(tr):
1756 1756 repo = reporef()
1757 1757 if filtername:
1758 1758 repo = repo.filtered(filtername)
1759 1759 func(repo, tr)
1760 1760 newcat = '%02i-txnreport' % len(categories)
1761 1761 otr.addpostclose(newcat, wrapped)
1762 1762 categories.append(newcat)
1763 1763 return wrapped
1764 1764
1765
1766 @reportsummary
1767 def reportchangegroup(repo, tr):
1768 cgchangesets = tr.changes.get('changegroup-count-changesets', 0)
1769 cgrevisions = tr.changes.get('changegroup-count-revisions', 0)
1770 cgfiles = tr.changes.get('changegroup-count-files', 0)
1771 cgheads = tr.changes.get('changegroup-count-heads', 0)
1772 if cgchangesets or cgrevisions or cgfiles:
1773 htext = ""
1774 if cgheads:
1775 htext = _(" (%+d heads)") % cgheads
1776 msg = _("added %d changesets with %d changes to %d files%s\n")
1777 repo.ui.status(msg % (cgchangesets, cgrevisions, cgfiles, htext))
1778
1765 1779 if txmatch(_reportobsoletedsource):
1766 1780 @reportsummary
1767 1781 def reportobsoleted(repo, tr):
1768 1782 obsoleted = obsutil.getobsoleted(repo, tr)
1769 1783 newmarkers = len(tr.changes.get('obsmarkers', ()))
1770 1784 if newmarkers:
1771 1785 repo.ui.status(_('%i new obsolescence markers\n') % newmarkers)
1772 1786 if obsoleted:
1773 1787 repo.ui.status(_('obsoleted %i changesets\n')
1774 1788 % len(obsoleted))
1775 1789
1776 1790 if (obsolete.isenabled(repo, obsolete.createmarkersopt) and
1777 1791 repo.ui.configbool('experimental', 'evolution.report-instabilities')):
1778 1792 instabilitytypes = [
1779 1793 ('orphan', 'orphan'),
1780 1794 ('phase-divergent', 'phasedivergent'),
1781 1795 ('content-divergent', 'contentdivergent'),
1782 1796 ]
1783 1797
1784 1798 def getinstabilitycounts(repo):
1785 1799 filtered = repo.changelog.filteredrevs
1786 1800 counts = {}
1787 1801 for instability, revset in instabilitytypes:
1788 1802 counts[instability] = len(set(obsolete.getrevs(repo, revset)) -
1789 1803 filtered)
1790 1804 return counts
1791 1805
1792 1806 oldinstabilitycounts = getinstabilitycounts(repo)
1793 1807 @reportsummary
1794 1808 def reportnewinstabilities(repo, tr):
1795 1809 newinstabilitycounts = getinstabilitycounts(repo)
1796 1810 for instability, revset in instabilitytypes:
1797 1811 delta = (newinstabilitycounts[instability] -
1798 1812 oldinstabilitycounts[instability])
1799 1813 msg = getinstabilitymessage(delta, instability)
1800 1814 if msg:
1801 1815 repo.ui.warn(msg)
1802 1816
1803 1817 if txmatch(_reportnewcssource):
1804 1818 @reportsummary
1805 1819 def reportnewcs(repo, tr):
1806 1820 """Report the range of new revisions pulled/unbundled."""
1807 1821 origrepolen = tr.changes.get('origrepolen', len(repo))
1808 1822 unfi = repo.unfiltered()
1809 1823 if origrepolen >= len(unfi):
1810 1824 return
1811 1825
1812 1826 # Compute the bounds of new visible revisions' range.
1813 1827 revs = smartset.spanset(repo, start=origrepolen)
1814 1828 if revs:
1815 1829 minrev, maxrev = repo[revs.min()], repo[revs.max()]
1816 1830
1817 1831 if minrev == maxrev:
1818 1832 revrange = minrev
1819 1833 else:
1820 1834 revrange = '%s:%s' % (minrev, maxrev)
1821 1835 draft = len(repo.revs('%ld and draft()', revs))
1822 1836 secret = len(repo.revs('%ld and secret()', revs))
1823 1837 if not (draft or secret):
1824 1838 msg = _('new changesets %s\n') % revrange
1825 1839 elif draft and secret:
1826 1840 msg = _('new changesets %s (%d drafts, %d secrets)\n')
1827 1841 msg %= (revrange, draft, secret)
1828 1842 elif draft:
1829 1843 msg = _('new changesets %s (%d drafts)\n')
1830 1844 msg %= (revrange, draft)
1831 1845 elif secret:
1832 1846 msg = _('new changesets %s (%d secrets)\n')
1833 1847 msg %= (revrange, secret)
1834 1848 else:
1835 1849 errormsg = 'entered unreachable condition'
1836 1850 raise error.ProgrammingError(errormsg)
1837 1851 repo.ui.status(msg)
1838 1852
1839 1853 # search new changesets directly pulled as obsolete
1840 1854 duplicates = tr.changes.get('revduplicates', ())
1841 1855 obsadded = unfi.revs('(%d: + %ld) and obsolete()',
1842 1856 origrepolen, duplicates)
1843 1857 cl = repo.changelog
1844 1858 extinctadded = [r for r in obsadded if r not in cl]
1845 1859 if extinctadded:
1846 1860 # They are not just obsolete, but obsolete and invisible
1847 1861 # we call them "extinct" internally but the terms have not been
1848 1862 # exposed to users.
1849 1863 msg = '(%d other changesets obsolete on arrival)\n'
1850 1864 repo.ui.status(msg % len(extinctadded))
1851 1865
1852 1866 @reportsummary
1853 1867 def reportphasechanges(repo, tr):
1854 1868 """Report statistics of phase changes for changesets pre-existing
1855 1869 pull/unbundle.
1856 1870 """
1857 1871 origrepolen = tr.changes.get('origrepolen', len(repo))
1858 1872 phasetracking = tr.changes.get('phases', {})
1859 1873 if not phasetracking:
1860 1874 return
1861 1875 published = [
1862 1876 rev for rev, (old, new) in phasetracking.iteritems()
1863 1877 if new == phases.public and rev < origrepolen
1864 1878 ]
1865 1879 if not published:
1866 1880 return
1867 1881 repo.ui.status(_('%d local changesets published\n')
1868 1882 % len(published))
1869 1883
1870 1884 def getinstabilitymessage(delta, instability):
1871 1885 """function to return the message to show warning about new instabilities
1872 1886
1873 1887 exists as a separate function so that extension can wrap to show more
1874 1888 information like how to fix instabilities"""
1875 1889 if delta > 0:
1876 1890 return _('%i new %s changesets\n') % (delta, instability)
1877 1891
1878 1892 def nodesummaries(repo, nodes, maxnumnodes=4):
1879 1893 if len(nodes) <= maxnumnodes or repo.ui.verbose:
1880 1894 return ' '.join(short(h) for h in nodes)
1881 1895 first = ' '.join(short(h) for h in nodes[:maxnumnodes])
1882 1896 return _("%s and %d others") % (first, len(nodes) - maxnumnodes)
1883 1897
1884 1898 def enforcesinglehead(repo, tr, desc):
1885 1899 """check that no named branch has multiple heads"""
1886 1900 if desc in ('strip', 'repair'):
1887 1901 # skip the logic during strip
1888 1902 return
1889 1903 visible = repo.filtered('visible')
1890 1904 # possible improvement: we could restrict the check to affected branch
1891 1905 for name, heads in visible.branchmap().iteritems():
1892 1906 if len(heads) > 1:
1893 1907 msg = _('rejecting multiple heads on branch "%s"')
1894 1908 msg %= name
1895 1909 hint = _('%d heads: %s')
1896 1910 hint %= (len(heads), nodesummaries(repo, heads))
1897 1911 raise error.Abort(msg, hint=hint)
1898 1912
1899 1913 def wrapconvertsink(sink):
1900 1914 """Allow extensions to wrap the sink returned by convcmd.convertsink()
1901 1915 before it is used, whether or not the convert extension was formally loaded.
1902 1916 """
1903 1917 return sink
1904 1918
1905 1919 def unhidehashlikerevs(repo, specs, hiddentype):
1906 1920 """parse the user specs and unhide changesets whose hash or revision number
1907 1921 is passed.
1908 1922
1909 1923 hiddentype can be: 1) 'warn': warn while unhiding changesets
1910 1924 2) 'nowarn': don't warn while unhiding changesets
1911 1925
1912 1926 returns a repo object with the required changesets unhidden
1913 1927 """
1914 1928 if not repo.filtername or not repo.ui.configbool('experimental',
1915 1929 'directaccess'):
1916 1930 return repo
1917 1931
1918 1932 if repo.filtername not in ('visible', 'visible-hidden'):
1919 1933 return repo
1920 1934
1921 1935 symbols = set()
1922 1936 for spec in specs:
1923 1937 try:
1924 1938 tree = revsetlang.parse(spec)
1925 1939 except error.ParseError: # will be reported by scmutil.revrange()
1926 1940 continue
1927 1941
1928 1942 symbols.update(revsetlang.gethashlikesymbols(tree))
1929 1943
1930 1944 if not symbols:
1931 1945 return repo
1932 1946
1933 1947 revs = _getrevsfromsymbols(repo, symbols)
1934 1948
1935 1949 if not revs:
1936 1950 return repo
1937 1951
1938 1952 if hiddentype == 'warn':
1939 1953 unfi = repo.unfiltered()
1940 1954 revstr = ", ".join([pycompat.bytestr(unfi[l]) for l in revs])
1941 1955 repo.ui.warn(_("warning: accessing hidden changesets for write "
1942 1956 "operation: %s\n") % revstr)
1943 1957
1944 1958 # we have to use new filtername to separate branch/tags cache until we can
1945 1959 # disbale these cache when revisions are dynamically pinned.
1946 1960 return repo.filtered('visible-hidden', revs)
1947 1961
1948 1962 def _getrevsfromsymbols(repo, symbols):
1949 1963 """parse the list of symbols and returns a set of revision numbers of hidden
1950 1964 changesets present in symbols"""
1951 1965 revs = set()
1952 1966 unfi = repo.unfiltered()
1953 1967 unficl = unfi.changelog
1954 1968 cl = repo.changelog
1955 1969 tiprev = len(unficl)
1956 1970 allowrevnums = repo.ui.configbool('experimental', 'directaccess.revnums')
1957 1971 for s in symbols:
1958 1972 try:
1959 1973 n = int(s)
1960 1974 if n <= tiprev:
1961 1975 if not allowrevnums:
1962 1976 continue
1963 1977 else:
1964 1978 if n not in cl:
1965 1979 revs.add(n)
1966 1980 continue
1967 1981 except ValueError:
1968 1982 pass
1969 1983
1970 1984 try:
1971 1985 s = resolvehexnodeidprefix(unfi, s)
1972 1986 except (error.LookupError, error.WdirUnsupported):
1973 1987 s = None
1974 1988
1975 1989 if s is not None:
1976 1990 rev = unficl.rev(s)
1977 1991 if rev not in cl:
1978 1992 revs.add(rev)
1979 1993
1980 1994 return revs
1981 1995
1982 1996 def bookmarkrevs(repo, mark):
1983 1997 """
1984 1998 Select revisions reachable by a given bookmark
1985 1999 """
1986 2000 return repo.revs("ancestors(bookmark(%s)) - "
1987 2001 "ancestors(head() and not bookmark(%s)) - "
1988 2002 "ancestors(bookmark() and not bookmark(%s))",
1989 2003 mark, mark, mark)
1990 2004
1991 2005 def computechangesetfilesadded(ctx):
1992 2006 """return the list of files added in a changeset
1993 2007 """
1994 2008 added = []
1995 2009 for f in ctx.files():
1996 2010 if not any(f in p for p in ctx.parents()):
1997 2011 added.append(f)
1998 2012 return added
1999 2013
2000 2014 def computechangesetfilesremoved(ctx):
2001 2015 """return the list of files removed in a changeset
2002 2016 """
2003 2017 removed = []
2004 2018 for f in ctx.files():
2005 2019 if f not in ctx:
2006 2020 removed.append(f)
2007 2021 return removed
@@ -1,2411 +1,2394 b''
1 1 > do_push()
2 2 > {
3 3 > user=$1
4 4 > shift
5 5 > echo "Pushing as user $user"
6 6 > echo 'hgrc = """'
7 7 > sed -n '/\[[ha]/,$p' b/.hg/hgrc | grep -v fakegroups.py
8 8 > echo '"""'
9 9 > if test -f acl.config; then
10 10 > echo 'acl.config = """'
11 11 > cat acl.config
12 12 > echo '"""'
13 13 > fi
14 14 > # On AIX /etc/profile sets LOGNAME read-only. So
15 15 > # LOGNAME=$user hg --cws a --debug push ../b
16 16 > # fails with "This variable is read only."
17 17 > # Use env to work around this.
18 18 > env LOGNAME=$user hg --cwd a --debug push ../b $*
19 19 > hg --cwd b rollback
20 20 > hg --cwd b --quiet tip
21 21 > echo
22 22 > }
23 23
24 24 > cat > posixgetuser.py <<'EOF'
25 25 > import getpass
26 26 > from mercurial import pycompat
27 27 > from mercurial.utils import procutil
28 28 > def posixgetuser():
29 29 > return pycompat.fsencode(getpass.getuser())
30 30 > if not pycompat.isposix:
31 31 > procutil.getuser = posixgetuser # forcibly trust $LOGNAME
32 32 > EOF
33 33
34 34 > init_config()
35 35 > {
36 36 > cat > fakegroups.py <<EOF
37 37 > from hgext import acl
38 38 > def fakegetusers(ui, group):
39 39 > try:
40 40 > return acl._getusersorig(ui, group)
41 41 > except BaseException:
42 42 > return [b"fred", b"betty"]
43 43 > acl._getusersorig = acl._getusers
44 44 > acl._getusers = fakegetusers
45 45 > EOF
46 46 > rm -f acl.config
47 47 > cat > $config <<EOF
48 48 > [hooks]
49 49 > pretxnchangegroup.acl = python:hgext.acl.hook
50 50 > prepushkey.acl = python:hgext.acl.hook
51 51 > [acl]
52 52 > sources = push
53 53 > [extensions]
54 54 > f=`pwd`/fakegroups.py
55 55 > posixgetuser=$TESTTMP/posixgetuser.py
56 56 > EOF
57 57 > }
58 58
59 59 $ hg init a
60 60 $ cd a
61 61 $ mkdir foo foo/Bar quux
62 62 $ echo 'in foo' > foo/file.txt
63 63 $ echo 'in foo/Bar' > foo/Bar/file.txt
64 64 $ echo 'in quux' > quux/file.py
65 65 $ hg add -q
66 66 $ hg ci -m 'add files' -d '1000000 0'
67 67 $ echo >> foo/file.txt
68 68 $ hg ci -m 'change foo/file' -d '1000001 0'
69 69 $ echo >> foo/Bar/file.txt
70 70 $ hg ci -m 'change foo/Bar/file' -d '1000002 0'
71 71 $ echo >> quux/file.py
72 72 $ hg ci -m 'change quux/file' -d '1000003 0'
73 73 $ hg tip --quiet
74 74 3:911600dab2ae
75 75
76 76 $ cd ..
77 77 $ hg clone -r 0 a b
78 78 adding changesets
79 79 adding manifests
80 80 adding file changes
81 81 added 1 changesets with 3 changes to 3 files
82 82 new changesets 6675d58eff77
83 83 updating to branch default
84 84 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
85 85
86 86 $ config=b/.hg/hgrc
87 87 $ cat >> "$config" <<EOF
88 88 > [extensions]
89 89 > posixgetuser=$TESTTMP/posixgetuser.py
90 90 > EOF
91 91
92 92 Extension disabled for lack of a hook
93 93
94 94 $ do_push fred
95 95 Pushing as user fred
96 96 hgrc = """
97 97 """
98 98 pushing to ../b
99 99 query 1; heads
100 100 searching for changes
101 101 all remote heads known locally
102 102 listing keys for "phases"
103 103 checking for updated bookmarks
104 104 listing keys for "bookmarks"
105 105 listing keys for "bookmarks"
106 106 3 changesets found
107 107 list of changesets:
108 108 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
109 109 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
110 110 911600dab2ae7a9baff75958b84fe606851ce955
111 111 bundle2-output-bundle: "HG20", 5 parts total
112 112 bundle2-output-part: "replycaps" 205 bytes payload
113 113 bundle2-output-part: "check:phases" 24 bytes payload
114 114 bundle2-output-part: "check:heads" streamed payload
115 115 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
116 116 bundle2-output-part: "phase-heads" 24 bytes payload
117 117 bundle2-input-bundle: with-transaction
118 118 bundle2-input-part: "replycaps" supported
119 119 bundle2-input-part: total payload size 205
120 120 bundle2-input-part: "check:phases" supported
121 121 bundle2-input-part: total payload size 24
122 122 bundle2-input-part: "check:heads" supported
123 123 bundle2-input-part: total payload size 20
124 124 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
125 125 adding changesets
126 126 add changeset ef1ea85a6374
127 127 add changeset f9cafe1212c8
128 128 add changeset 911600dab2ae
129 129 adding manifests
130 130 adding file changes
131 131 adding foo/Bar/file.txt revisions
132 132 adding foo/file.txt revisions
133 133 adding quux/file.py revisions
134 added 3 changesets with 3 changes to 3 files
135 134 bundle2-input-part: total payload size 1553
136 135 bundle2-input-part: "phase-heads" supported
137 136 bundle2-input-part: total payload size 24
138 137 bundle2-input-bundle: 4 parts total
139 138 updating the branch cache
139 added 3 changesets with 3 changes to 3 files
140 140 bundle2-output-bundle: "HG20", 1 parts total
141 141 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
142 142 bundle2-input-bundle: no-transaction
143 143 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
144 144 bundle2-input-bundle: 0 parts total
145 145 listing keys for "phases"
146 146 repository tip rolled back to revision 0 (undo push)
147 147 0:6675d58eff77
148 148
149 149
150 150 $ echo '[hooks]' >> $config
151 151 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
152 152 $ echo 'prepushkey.acl = python:hgext.acl.hook' >> $config
153 153
154 154 Extension disabled for lack of acl.sources
155 155
156 156 $ do_push fred
157 157 Pushing as user fred
158 158 hgrc = """
159 159 [hooks]
160 160 pretxnchangegroup.acl = python:hgext.acl.hook
161 161 prepushkey.acl = python:hgext.acl.hook
162 162 """
163 163 pushing to ../b
164 164 query 1; heads
165 165 searching for changes
166 166 all remote heads known locally
167 167 listing keys for "phases"
168 168 checking for updated bookmarks
169 169 listing keys for "bookmarks"
170 170 listing keys for "bookmarks"
171 171 3 changesets found
172 172 list of changesets:
173 173 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
174 174 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
175 175 911600dab2ae7a9baff75958b84fe606851ce955
176 176 bundle2-output-bundle: "HG20", 5 parts total
177 177 bundle2-output-part: "replycaps" 205 bytes payload
178 178 bundle2-output-part: "check:phases" 24 bytes payload
179 179 bundle2-output-part: "check:heads" streamed payload
180 180 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
181 181 bundle2-output-part: "phase-heads" 24 bytes payload
182 182 bundle2-input-bundle: with-transaction
183 183 bundle2-input-part: "replycaps" supported
184 184 bundle2-input-part: total payload size 205
185 185 bundle2-input-part: "check:phases" supported
186 186 bundle2-input-part: total payload size 24
187 187 bundle2-input-part: "check:heads" supported
188 188 bundle2-input-part: total payload size 20
189 189 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
190 190 adding changesets
191 191 add changeset ef1ea85a6374
192 192 add changeset f9cafe1212c8
193 193 add changeset 911600dab2ae
194 194 adding manifests
195 195 adding file changes
196 196 adding foo/Bar/file.txt revisions
197 197 adding foo/file.txt revisions
198 198 adding quux/file.py revisions
199 added 3 changesets with 3 changes to 3 files
200 199 calling hook pretxnchangegroup.acl: hgext.acl.hook
201 200 acl: changes have source "push" - skipping
202 201 bundle2-input-part: total payload size 1553
203 202 bundle2-input-part: "phase-heads" supported
204 203 bundle2-input-part: total payload size 24
205 204 bundle2-input-bundle: 4 parts total
206 205 updating the branch cache
206 added 3 changesets with 3 changes to 3 files
207 207 bundle2-output-bundle: "HG20", 1 parts total
208 208 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
209 209 bundle2-input-bundle: no-transaction
210 210 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
211 211 bundle2-input-bundle: 0 parts total
212 212 listing keys for "phases"
213 213 repository tip rolled back to revision 0 (undo push)
214 214 0:6675d58eff77
215 215
216 216
217 217 No [acl.allow]/[acl.deny]
218 218
219 219 $ echo '[acl]' >> $config
220 220 $ echo 'sources = push' >> $config
221 221 $ do_push fred
222 222 Pushing as user fred
223 223 hgrc = """
224 224 [hooks]
225 225 pretxnchangegroup.acl = python:hgext.acl.hook
226 226 prepushkey.acl = python:hgext.acl.hook
227 227 [acl]
228 228 sources = push
229 229 """
230 230 pushing to ../b
231 231 query 1; heads
232 232 searching for changes
233 233 all remote heads known locally
234 234 listing keys for "phases"
235 235 checking for updated bookmarks
236 236 listing keys for "bookmarks"
237 237 listing keys for "bookmarks"
238 238 3 changesets found
239 239 list of changesets:
240 240 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
241 241 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
242 242 911600dab2ae7a9baff75958b84fe606851ce955
243 243 bundle2-output-bundle: "HG20", 5 parts total
244 244 bundle2-output-part: "replycaps" 205 bytes payload
245 245 bundle2-output-part: "check:phases" 24 bytes payload
246 246 bundle2-output-part: "check:heads" streamed payload
247 247 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
248 248 bundle2-output-part: "phase-heads" 24 bytes payload
249 249 bundle2-input-bundle: with-transaction
250 250 bundle2-input-part: "replycaps" supported
251 251 bundle2-input-part: total payload size 205
252 252 bundle2-input-part: "check:phases" supported
253 253 bundle2-input-part: total payload size 24
254 254 bundle2-input-part: "check:heads" supported
255 255 bundle2-input-part: total payload size 20
256 256 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
257 257 adding changesets
258 258 add changeset ef1ea85a6374
259 259 add changeset f9cafe1212c8
260 260 add changeset 911600dab2ae
261 261 adding manifests
262 262 adding file changes
263 263 adding foo/Bar/file.txt revisions
264 264 adding foo/file.txt revisions
265 265 adding quux/file.py revisions
266 added 3 changesets with 3 changes to 3 files
267 266 calling hook pretxnchangegroup.acl: hgext.acl.hook
268 267 acl: checking access for user "fred"
269 268 acl: acl.allow.branches not enabled
270 269 acl: acl.deny.branches not enabled
271 270 acl: acl.allow not enabled
272 271 acl: acl.deny not enabled
273 272 acl: branch access granted: "ef1ea85a6374" on branch "default"
274 273 acl: path access granted: "ef1ea85a6374"
275 274 acl: branch access granted: "f9cafe1212c8" on branch "default"
276 275 acl: path access granted: "f9cafe1212c8"
277 276 acl: branch access granted: "911600dab2ae" on branch "default"
278 277 acl: path access granted: "911600dab2ae"
279 278 bundle2-input-part: total payload size 1553
280 279 bundle2-input-part: "phase-heads" supported
281 280 bundle2-input-part: total payload size 24
282 281 bundle2-input-bundle: 4 parts total
283 282 updating the branch cache
283 added 3 changesets with 3 changes to 3 files
284 284 bundle2-output-bundle: "HG20", 1 parts total
285 285 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
286 286 bundle2-input-bundle: no-transaction
287 287 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
288 288 bundle2-input-bundle: 0 parts total
289 289 listing keys for "phases"
290 290 repository tip rolled back to revision 0 (undo push)
291 291 0:6675d58eff77
292 292
293 293
294 294 Empty [acl.allow]
295 295
296 296 $ echo '[acl.allow]' >> $config
297 297 $ do_push fred
298 298 Pushing as user fred
299 299 hgrc = """
300 300 [hooks]
301 301 pretxnchangegroup.acl = python:hgext.acl.hook
302 302 prepushkey.acl = python:hgext.acl.hook
303 303 [acl]
304 304 sources = push
305 305 [acl.allow]
306 306 """
307 307 pushing to ../b
308 308 query 1; heads
309 309 searching for changes
310 310 all remote heads known locally
311 311 listing keys for "phases"
312 312 checking for updated bookmarks
313 313 listing keys for "bookmarks"
314 314 listing keys for "bookmarks"
315 315 3 changesets found
316 316 list of changesets:
317 317 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
318 318 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
319 319 911600dab2ae7a9baff75958b84fe606851ce955
320 320 bundle2-output-bundle: "HG20", 5 parts total
321 321 bundle2-output-part: "replycaps" 205 bytes payload
322 322 bundle2-output-part: "check:phases" 24 bytes payload
323 323 bundle2-output-part: "check:heads" streamed payload
324 324 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
325 325 bundle2-output-part: "phase-heads" 24 bytes payload
326 326 bundle2-input-bundle: with-transaction
327 327 bundle2-input-part: "replycaps" supported
328 328 bundle2-input-part: total payload size 205
329 329 bundle2-input-part: "check:phases" supported
330 330 bundle2-input-part: total payload size 24
331 331 bundle2-input-part: "check:heads" supported
332 332 bundle2-input-part: total payload size 20
333 333 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
334 334 adding changesets
335 335 add changeset ef1ea85a6374
336 336 add changeset f9cafe1212c8
337 337 add changeset 911600dab2ae
338 338 adding manifests
339 339 adding file changes
340 340 adding foo/Bar/file.txt revisions
341 341 adding foo/file.txt revisions
342 342 adding quux/file.py revisions
343 added 3 changesets with 3 changes to 3 files
344 343 calling hook pretxnchangegroup.acl: hgext.acl.hook
345 344 acl: checking access for user "fred"
346 345 acl: acl.allow.branches not enabled
347 346 acl: acl.deny.branches not enabled
348 347 acl: acl.allow enabled, 0 entries for user fred
349 348 acl: acl.deny not enabled
350 349 acl: branch access granted: "ef1ea85a6374" on branch "default"
351 350 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
352 351 bundle2-input-part: total payload size 1553
353 352 bundle2-input-part: total payload size 24
354 353 bundle2-input-bundle: 4 parts total
355 354 transaction abort!
356 355 rollback completed
357 356 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
358 357 no rollback information available
359 358 0:6675d58eff77
360 359
361 360
362 361 fred is allowed inside foo/
363 362
364 363 $ echo 'foo/** = fred' >> $config
365 364 $ do_push fred
366 365 Pushing as user fred
367 366 hgrc = """
368 367 [hooks]
369 368 pretxnchangegroup.acl = python:hgext.acl.hook
370 369 prepushkey.acl = python:hgext.acl.hook
371 370 [acl]
372 371 sources = push
373 372 [acl.allow]
374 373 foo/** = fred
375 374 """
376 375 pushing to ../b
377 376 query 1; heads
378 377 searching for changes
379 378 all remote heads known locally
380 379 listing keys for "phases"
381 380 checking for updated bookmarks
382 381 listing keys for "bookmarks"
383 382 listing keys for "bookmarks"
384 383 3 changesets found
385 384 list of changesets:
386 385 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
387 386 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
388 387 911600dab2ae7a9baff75958b84fe606851ce955
389 388 bundle2-output-bundle: "HG20", 5 parts total
390 389 bundle2-output-part: "replycaps" 205 bytes payload
391 390 bundle2-output-part: "check:phases" 24 bytes payload
392 391 bundle2-output-part: "check:heads" streamed payload
393 392 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
394 393 bundle2-output-part: "phase-heads" 24 bytes payload
395 394 bundle2-input-bundle: with-transaction
396 395 bundle2-input-part: "replycaps" supported
397 396 bundle2-input-part: total payload size 205
398 397 bundle2-input-part: "check:phases" supported
399 398 bundle2-input-part: total payload size 24
400 399 bundle2-input-part: "check:heads" supported
401 400 bundle2-input-part: total payload size 20
402 401 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
403 402 adding changesets
404 403 add changeset ef1ea85a6374
405 404 add changeset f9cafe1212c8
406 405 add changeset 911600dab2ae
407 406 adding manifests
408 407 adding file changes
409 408 adding foo/Bar/file.txt revisions
410 409 adding foo/file.txt revisions
411 410 adding quux/file.py revisions
412 added 3 changesets with 3 changes to 3 files
413 411 calling hook pretxnchangegroup.acl: hgext.acl.hook
414 412 acl: checking access for user "fred"
415 413 acl: acl.allow.branches not enabled
416 414 acl: acl.deny.branches not enabled
417 415 acl: acl.allow enabled, 1 entries for user fred
418 416 acl: acl.deny not enabled
419 417 acl: branch access granted: "ef1ea85a6374" on branch "default"
420 418 acl: path access granted: "ef1ea85a6374"
421 419 acl: branch access granted: "f9cafe1212c8" on branch "default"
422 420 acl: path access granted: "f9cafe1212c8"
423 421 acl: branch access granted: "911600dab2ae" on branch "default"
424 422 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
425 423 bundle2-input-part: total payload size 1553
426 424 bundle2-input-part: total payload size 24
427 425 bundle2-input-bundle: 4 parts total
428 426 transaction abort!
429 427 rollback completed
430 428 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
431 429 no rollback information available
432 430 0:6675d58eff77
433 431
434 432
435 433 Empty [acl.deny]
436 434
437 435 $ echo '[acl.deny]' >> $config
438 436 $ do_push barney
439 437 Pushing as user barney
440 438 hgrc = """
441 439 [hooks]
442 440 pretxnchangegroup.acl = python:hgext.acl.hook
443 441 prepushkey.acl = python:hgext.acl.hook
444 442 [acl]
445 443 sources = push
446 444 [acl.allow]
447 445 foo/** = fred
448 446 [acl.deny]
449 447 """
450 448 pushing to ../b
451 449 query 1; heads
452 450 searching for changes
453 451 all remote heads known locally
454 452 listing keys for "phases"
455 453 checking for updated bookmarks
456 454 listing keys for "bookmarks"
457 455 listing keys for "bookmarks"
458 456 3 changesets found
459 457 list of changesets:
460 458 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
461 459 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
462 460 911600dab2ae7a9baff75958b84fe606851ce955
463 461 bundle2-output-bundle: "HG20", 5 parts total
464 462 bundle2-output-part: "replycaps" 205 bytes payload
465 463 bundle2-output-part: "check:phases" 24 bytes payload
466 464 bundle2-output-part: "check:heads" streamed payload
467 465 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
468 466 bundle2-output-part: "phase-heads" 24 bytes payload
469 467 bundle2-input-bundle: with-transaction
470 468 bundle2-input-part: "replycaps" supported
471 469 bundle2-input-part: total payload size 205
472 470 bundle2-input-part: "check:phases" supported
473 471 bundle2-input-part: total payload size 24
474 472 bundle2-input-part: "check:heads" supported
475 473 bundle2-input-part: total payload size 20
476 474 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
477 475 adding changesets
478 476 add changeset ef1ea85a6374
479 477 add changeset f9cafe1212c8
480 478 add changeset 911600dab2ae
481 479 adding manifests
482 480 adding file changes
483 481 adding foo/Bar/file.txt revisions
484 482 adding foo/file.txt revisions
485 483 adding quux/file.py revisions
486 added 3 changesets with 3 changes to 3 files
487 484 calling hook pretxnchangegroup.acl: hgext.acl.hook
488 485 acl: checking access for user "barney"
489 486 acl: acl.allow.branches not enabled
490 487 acl: acl.deny.branches not enabled
491 488 acl: acl.allow enabled, 0 entries for user barney
492 489 acl: acl.deny enabled, 0 entries for user barney
493 490 acl: branch access granted: "ef1ea85a6374" on branch "default"
494 491 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
495 492 bundle2-input-part: total payload size 1553
496 493 bundle2-input-part: total payload size 24
497 494 bundle2-input-bundle: 4 parts total
498 495 transaction abort!
499 496 rollback completed
500 497 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
501 498 no rollback information available
502 499 0:6675d58eff77
503 500
504 501
505 502 fred is allowed inside foo/, but not foo/bar/ (case matters)
506 503
507 504 $ echo 'foo/bar/** = fred' >> $config
508 505 $ do_push fred
509 506 Pushing as user fred
510 507 hgrc = """
511 508 [hooks]
512 509 pretxnchangegroup.acl = python:hgext.acl.hook
513 510 prepushkey.acl = python:hgext.acl.hook
514 511 [acl]
515 512 sources = push
516 513 [acl.allow]
517 514 foo/** = fred
518 515 [acl.deny]
519 516 foo/bar/** = fred
520 517 """
521 518 pushing to ../b
522 519 query 1; heads
523 520 searching for changes
524 521 all remote heads known locally
525 522 listing keys for "phases"
526 523 checking for updated bookmarks
527 524 listing keys for "bookmarks"
528 525 listing keys for "bookmarks"
529 526 3 changesets found
530 527 list of changesets:
531 528 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
532 529 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
533 530 911600dab2ae7a9baff75958b84fe606851ce955
534 531 bundle2-output-bundle: "HG20", 5 parts total
535 532 bundle2-output-part: "replycaps" 205 bytes payload
536 533 bundle2-output-part: "check:phases" 24 bytes payload
537 534 bundle2-output-part: "check:heads" streamed payload
538 535 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
539 536 bundle2-output-part: "phase-heads" 24 bytes payload
540 537 bundle2-input-bundle: with-transaction
541 538 bundle2-input-part: "replycaps" supported
542 539 bundle2-input-part: total payload size 205
543 540 bundle2-input-part: "check:phases" supported
544 541 bundle2-input-part: total payload size 24
545 542 bundle2-input-part: "check:heads" supported
546 543 bundle2-input-part: total payload size 20
547 544 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
548 545 adding changesets
549 546 add changeset ef1ea85a6374
550 547 add changeset f9cafe1212c8
551 548 add changeset 911600dab2ae
552 549 adding manifests
553 550 adding file changes
554 551 adding foo/Bar/file.txt revisions
555 552 adding foo/file.txt revisions
556 553 adding quux/file.py revisions
557 added 3 changesets with 3 changes to 3 files
558 554 calling hook pretxnchangegroup.acl: hgext.acl.hook
559 555 acl: checking access for user "fred"
560 556 acl: acl.allow.branches not enabled
561 557 acl: acl.deny.branches not enabled
562 558 acl: acl.allow enabled, 1 entries for user fred
563 559 acl: acl.deny enabled, 1 entries for user fred
564 560 acl: branch access granted: "ef1ea85a6374" on branch "default"
565 561 acl: path access granted: "ef1ea85a6374"
566 562 acl: branch access granted: "f9cafe1212c8" on branch "default"
567 563 acl: path access granted: "f9cafe1212c8"
568 564 acl: branch access granted: "911600dab2ae" on branch "default"
569 565 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
570 566 bundle2-input-part: total payload size 1553
571 567 bundle2-input-part: total payload size 24
572 568 bundle2-input-bundle: 4 parts total
573 569 transaction abort!
574 570 rollback completed
575 571 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
576 572 no rollback information available
577 573 0:6675d58eff77
578 574
579 575
580 576 fred is allowed inside foo/, but not foo/Bar/
581 577
582 578 $ echo 'foo/Bar/** = fred' >> $config
583 579 $ do_push fred
584 580 Pushing as user fred
585 581 hgrc = """
586 582 [hooks]
587 583 pretxnchangegroup.acl = python:hgext.acl.hook
588 584 prepushkey.acl = python:hgext.acl.hook
589 585 [acl]
590 586 sources = push
591 587 [acl.allow]
592 588 foo/** = fred
593 589 [acl.deny]
594 590 foo/bar/** = fred
595 591 foo/Bar/** = fred
596 592 """
597 593 pushing to ../b
598 594 query 1; heads
599 595 searching for changes
600 596 all remote heads known locally
601 597 listing keys for "phases"
602 598 checking for updated bookmarks
603 599 listing keys for "bookmarks"
604 600 listing keys for "bookmarks"
605 601 3 changesets found
606 602 list of changesets:
607 603 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
608 604 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
609 605 911600dab2ae7a9baff75958b84fe606851ce955
610 606 bundle2-output-bundle: "HG20", 5 parts total
611 607 bundle2-output-part: "replycaps" 205 bytes payload
612 608 bundle2-output-part: "check:phases" 24 bytes payload
613 609 bundle2-output-part: "check:heads" streamed payload
614 610 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
615 611 bundle2-output-part: "phase-heads" 24 bytes payload
616 612 bundle2-input-bundle: with-transaction
617 613 bundle2-input-part: "replycaps" supported
618 614 bundle2-input-part: total payload size 205
619 615 bundle2-input-part: "check:phases" supported
620 616 bundle2-input-part: total payload size 24
621 617 bundle2-input-part: "check:heads" supported
622 618 bundle2-input-part: total payload size 20
623 619 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
624 620 adding changesets
625 621 add changeset ef1ea85a6374
626 622 add changeset f9cafe1212c8
627 623 add changeset 911600dab2ae
628 624 adding manifests
629 625 adding file changes
630 626 adding foo/Bar/file.txt revisions
631 627 adding foo/file.txt revisions
632 628 adding quux/file.py revisions
633 added 3 changesets with 3 changes to 3 files
634 629 calling hook pretxnchangegroup.acl: hgext.acl.hook
635 630 acl: checking access for user "fred"
636 631 acl: acl.allow.branches not enabled
637 632 acl: acl.deny.branches not enabled
638 633 acl: acl.allow enabled, 1 entries for user fred
639 634 acl: acl.deny enabled, 2 entries for user fred
640 635 acl: branch access granted: "ef1ea85a6374" on branch "default"
641 636 acl: path access granted: "ef1ea85a6374"
642 637 acl: branch access granted: "f9cafe1212c8" on branch "default"
643 638 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
644 639 bundle2-input-part: total payload size 1553
645 640 bundle2-input-part: total payload size 24
646 641 bundle2-input-bundle: 4 parts total
647 642 transaction abort!
648 643 rollback completed
649 644 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
650 645 no rollback information available
651 646 0:6675d58eff77
652 647
653 648
654 649 $ echo 'barney is not mentioned => not allowed anywhere'
655 650 barney is not mentioned => not allowed anywhere
656 651 $ do_push barney
657 652 Pushing as user barney
658 653 hgrc = """
659 654 [hooks]
660 655 pretxnchangegroup.acl = python:hgext.acl.hook
661 656 prepushkey.acl = python:hgext.acl.hook
662 657 [acl]
663 658 sources = push
664 659 [acl.allow]
665 660 foo/** = fred
666 661 [acl.deny]
667 662 foo/bar/** = fred
668 663 foo/Bar/** = fred
669 664 """
670 665 pushing to ../b
671 666 query 1; heads
672 667 searching for changes
673 668 all remote heads known locally
674 669 listing keys for "phases"
675 670 checking for updated bookmarks
676 671 listing keys for "bookmarks"
677 672 listing keys for "bookmarks"
678 673 3 changesets found
679 674 list of changesets:
680 675 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
681 676 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
682 677 911600dab2ae7a9baff75958b84fe606851ce955
683 678 bundle2-output-bundle: "HG20", 5 parts total
684 679 bundle2-output-part: "replycaps" 205 bytes payload
685 680 bundle2-output-part: "check:phases" 24 bytes payload
686 681 bundle2-output-part: "check:heads" streamed payload
687 682 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
688 683 bundle2-output-part: "phase-heads" 24 bytes payload
689 684 bundle2-input-bundle: with-transaction
690 685 bundle2-input-part: "replycaps" supported
691 686 bundle2-input-part: total payload size 205
692 687 bundle2-input-part: "check:phases" supported
693 688 bundle2-input-part: total payload size 24
694 689 bundle2-input-part: "check:heads" supported
695 690 bundle2-input-part: total payload size 20
696 691 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
697 692 adding changesets
698 693 add changeset ef1ea85a6374
699 694 add changeset f9cafe1212c8
700 695 add changeset 911600dab2ae
701 696 adding manifests
702 697 adding file changes
703 698 adding foo/Bar/file.txt revisions
704 699 adding foo/file.txt revisions
705 700 adding quux/file.py revisions
706 added 3 changesets with 3 changes to 3 files
707 701 calling hook pretxnchangegroup.acl: hgext.acl.hook
708 702 acl: checking access for user "barney"
709 703 acl: acl.allow.branches not enabled
710 704 acl: acl.deny.branches not enabled
711 705 acl: acl.allow enabled, 0 entries for user barney
712 706 acl: acl.deny enabled, 0 entries for user barney
713 707 acl: branch access granted: "ef1ea85a6374" on branch "default"
714 708 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
715 709 bundle2-input-part: total payload size 1553
716 710 bundle2-input-part: total payload size 24
717 711 bundle2-input-bundle: 4 parts total
718 712 transaction abort!
719 713 rollback completed
720 714 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
721 715 no rollback information available
722 716 0:6675d58eff77
723 717
724 718
725 719 fred is not blocked from moving bookmarks
726 720
727 721 $ hg -R a book -q moving-bookmark -r 1
728 722 $ hg -R b book -q moving-bookmark -r 0
729 723 $ cp $config normalconfig
730 724 $ do_push fred -r 1
731 725 Pushing as user fred
732 726 hgrc = """
733 727 [hooks]
734 728 pretxnchangegroup.acl = python:hgext.acl.hook
735 729 prepushkey.acl = python:hgext.acl.hook
736 730 [acl]
737 731 sources = push
738 732 [acl.allow]
739 733 foo/** = fred
740 734 [acl.deny]
741 735 foo/bar/** = fred
742 736 foo/Bar/** = fred
743 737 """
744 738 pushing to ../b
745 739 query 1; heads
746 740 searching for changes
747 741 all remote heads known locally
748 742 listing keys for "phases"
749 743 checking for updated bookmarks
750 744 listing keys for "bookmarks"
751 745 listing keys for "bookmarks"
752 746 1 changesets found
753 747 list of changesets:
754 748 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
755 749 bundle2-output-bundle: "HG20", 7 parts total
756 750 bundle2-output-part: "replycaps" 205 bytes payload
757 751 bundle2-output-part: "check:bookmarks" 37 bytes payload
758 752 bundle2-output-part: "check:phases" 24 bytes payload
759 753 bundle2-output-part: "check:heads" streamed payload
760 754 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
761 755 bundle2-output-part: "phase-heads" 24 bytes payload
762 756 bundle2-output-part: "bookmarks" 37 bytes payload
763 757 bundle2-input-bundle: with-transaction
764 758 bundle2-input-part: "replycaps" supported
765 759 bundle2-input-part: total payload size 205
766 760 bundle2-input-part: "check:bookmarks" supported
767 761 bundle2-input-part: total payload size 37
768 762 bundle2-input-part: "check:phases" supported
769 763 bundle2-input-part: total payload size 24
770 764 bundle2-input-part: "check:heads" supported
771 765 bundle2-input-part: total payload size 20
772 766 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
773 767 adding changesets
774 768 add changeset ef1ea85a6374
775 769 adding manifests
776 770 adding file changes
777 771 adding foo/file.txt revisions
778 added 1 changesets with 1 changes to 1 files
779 772 calling hook pretxnchangegroup.acl: hgext.acl.hook
780 773 acl: checking access for user "fred"
781 774 acl: acl.allow.branches not enabled
782 775 acl: acl.deny.branches not enabled
783 776 acl: acl.allow enabled, 1 entries for user fred
784 777 acl: acl.deny enabled, 2 entries for user fred
785 778 acl: branch access granted: "ef1ea85a6374" on branch "default"
786 779 acl: path access granted: "ef1ea85a6374"
787 780 bundle2-input-part: total payload size 520
788 781 bundle2-input-part: "phase-heads" supported
789 782 bundle2-input-part: total payload size 24
790 783 bundle2-input-part: "bookmarks" supported
791 784 bundle2-input-part: total payload size 37
792 785 calling hook prepushkey.acl: hgext.acl.hook
793 786 acl: checking access for user "fred"
794 787 acl: acl.allow.bookmarks not enabled
795 788 acl: acl.deny.bookmarks not enabled
796 789 acl: bookmark access granted: "ef1ea85a6374b77d6da9dcda9541f498f2d17df7" on bookmark "moving-bookmark"
797 790 bundle2-input-bundle: 6 parts total
798 791 updating the branch cache
792 added 1 changesets with 1 changes to 1 files
799 793 bundle2-output-bundle: "HG20", 1 parts total
800 794 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
801 795 bundle2-input-bundle: no-transaction
802 796 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
803 797 bundle2-input-bundle: 0 parts total
804 798 updating bookmark moving-bookmark
805 799 listing keys for "phases"
806 800 repository tip rolled back to revision 0 (undo push)
807 801 0:6675d58eff77
808 802
809 803
810 804 fred is not allowed to move bookmarks
811 805
812 806 $ echo '[acl.deny.bookmarks]' >> $config
813 807 $ echo '* = fred' >> $config
814 808 $ do_push fred -r 1
815 809 Pushing as user fred
816 810 hgrc = """
817 811 [hooks]
818 812 pretxnchangegroup.acl = python:hgext.acl.hook
819 813 prepushkey.acl = python:hgext.acl.hook
820 814 [acl]
821 815 sources = push
822 816 [acl.allow]
823 817 foo/** = fred
824 818 [acl.deny]
825 819 foo/bar/** = fred
826 820 foo/Bar/** = fred
827 821 [acl.deny.bookmarks]
828 822 * = fred
829 823 """
830 824 pushing to ../b
831 825 query 1; heads
832 826 searching for changes
833 827 all remote heads known locally
834 828 listing keys for "phases"
835 829 checking for updated bookmarks
836 830 listing keys for "bookmarks"
837 831 listing keys for "bookmarks"
838 832 1 changesets found
839 833 list of changesets:
840 834 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
841 835 bundle2-output-bundle: "HG20", 7 parts total
842 836 bundle2-output-part: "replycaps" 205 bytes payload
843 837 bundle2-output-part: "check:bookmarks" 37 bytes payload
844 838 bundle2-output-part: "check:phases" 24 bytes payload
845 839 bundle2-output-part: "check:heads" streamed payload
846 840 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
847 841 bundle2-output-part: "phase-heads" 24 bytes payload
848 842 bundle2-output-part: "bookmarks" 37 bytes payload
849 843 bundle2-input-bundle: with-transaction
850 844 bundle2-input-part: "replycaps" supported
851 845 bundle2-input-part: total payload size 205
852 846 bundle2-input-part: "check:bookmarks" supported
853 847 bundle2-input-part: total payload size 37
854 848 bundle2-input-part: "check:phases" supported
855 849 bundle2-input-part: total payload size 24
856 850 bundle2-input-part: "check:heads" supported
857 851 bundle2-input-part: total payload size 20
858 852 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
859 853 adding changesets
860 854 add changeset ef1ea85a6374
861 855 adding manifests
862 856 adding file changes
863 857 adding foo/file.txt revisions
864 added 1 changesets with 1 changes to 1 files
865 858 calling hook pretxnchangegroup.acl: hgext.acl.hook
866 859 acl: checking access for user "fred"
867 860 acl: acl.allow.branches not enabled
868 861 acl: acl.deny.branches not enabled
869 862 acl: acl.allow enabled, 1 entries for user fred
870 863 acl: acl.deny enabled, 2 entries for user fred
871 864 acl: branch access granted: "ef1ea85a6374" on branch "default"
872 865 acl: path access granted: "ef1ea85a6374"
873 866 bundle2-input-part: total payload size 520
874 867 bundle2-input-part: "phase-heads" supported
875 868 bundle2-input-part: total payload size 24
876 869 bundle2-input-part: "bookmarks" supported
877 870 bundle2-input-part: total payload size 37
878 871 calling hook prepushkey.acl: hgext.acl.hook
879 872 acl: checking access for user "fred"
880 873 acl: acl.allow.bookmarks not enabled
881 874 acl: acl.deny.bookmarks enabled, 1 entries for user fred
882 875 error: prepushkey.acl hook failed: acl: user "fred" denied on bookmark "moving-bookmark" (changeset "ef1ea85a6374b77d6da9dcda9541f498f2d17df7")
883 876 bundle2-input-bundle: 6 parts total
884 877 transaction abort!
885 878 rollback completed
886 879 abort: acl: user "fred" denied on bookmark "moving-bookmark" (changeset "ef1ea85a6374b77d6da9dcda9541f498f2d17df7")
887 880 no rollback information available
888 881 0:6675d58eff77
889 882
890 883
891 884 cleanup bookmark stuff
892 885
893 886 $ hg book -R a -d moving-bookmark
894 887 $ hg book -R b -d moving-bookmark
895 888 $ cp normalconfig $config
896 889
897 890 barney is allowed everywhere
898 891
899 892 $ echo '[acl.allow]' >> $config
900 893 $ echo '** = barney' >> $config
901 894 $ do_push barney
902 895 Pushing as user barney
903 896 hgrc = """
904 897 [hooks]
905 898 pretxnchangegroup.acl = python:hgext.acl.hook
906 899 prepushkey.acl = python:hgext.acl.hook
907 900 [acl]
908 901 sources = push
909 902 [acl.allow]
910 903 foo/** = fred
911 904 [acl.deny]
912 905 foo/bar/** = fred
913 906 foo/Bar/** = fred
914 907 [acl.allow]
915 908 ** = barney
916 909 """
917 910 pushing to ../b
918 911 query 1; heads
919 912 searching for changes
920 913 all remote heads known locally
921 914 listing keys for "phases"
922 915 checking for updated bookmarks
923 916 listing keys for "bookmarks"
924 917 listing keys for "bookmarks"
925 918 3 changesets found
926 919 list of changesets:
927 920 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
928 921 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
929 922 911600dab2ae7a9baff75958b84fe606851ce955
930 923 bundle2-output-bundle: "HG20", 5 parts total
931 924 bundle2-output-part: "replycaps" 205 bytes payload
932 925 bundle2-output-part: "check:phases" 24 bytes payload
933 926 bundle2-output-part: "check:heads" streamed payload
934 927 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
935 928 bundle2-output-part: "phase-heads" 24 bytes payload
936 929 bundle2-input-bundle: with-transaction
937 930 bundle2-input-part: "replycaps" supported
938 931 bundle2-input-part: total payload size 205
939 932 bundle2-input-part: "check:phases" supported
940 933 bundle2-input-part: total payload size 24
941 934 bundle2-input-part: "check:heads" supported
942 935 bundle2-input-part: total payload size 20
943 936 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
944 937 adding changesets
945 938 add changeset ef1ea85a6374
946 939 add changeset f9cafe1212c8
947 940 add changeset 911600dab2ae
948 941 adding manifests
949 942 adding file changes
950 943 adding foo/Bar/file.txt revisions
951 944 adding foo/file.txt revisions
952 945 adding quux/file.py revisions
953 added 3 changesets with 3 changes to 3 files
954 946 calling hook pretxnchangegroup.acl: hgext.acl.hook
955 947 acl: checking access for user "barney"
956 948 acl: acl.allow.branches not enabled
957 949 acl: acl.deny.branches not enabled
958 950 acl: acl.allow enabled, 1 entries for user barney
959 951 acl: acl.deny enabled, 0 entries for user barney
960 952 acl: branch access granted: "ef1ea85a6374" on branch "default"
961 953 acl: path access granted: "ef1ea85a6374"
962 954 acl: branch access granted: "f9cafe1212c8" on branch "default"
963 955 acl: path access granted: "f9cafe1212c8"
964 956 acl: branch access granted: "911600dab2ae" on branch "default"
965 957 acl: path access granted: "911600dab2ae"
966 958 bundle2-input-part: total payload size 1553
967 959 bundle2-input-part: "phase-heads" supported
968 960 bundle2-input-part: total payload size 24
969 961 bundle2-input-bundle: 4 parts total
970 962 updating the branch cache
963 added 3 changesets with 3 changes to 3 files
971 964 bundle2-output-bundle: "HG20", 1 parts total
972 965 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
973 966 bundle2-input-bundle: no-transaction
974 967 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
975 968 bundle2-input-bundle: 0 parts total
976 969 listing keys for "phases"
977 970 repository tip rolled back to revision 0 (undo push)
978 971 0:6675d58eff77
979 972
980 973
981 974 wilma can change files with a .txt extension
982 975
983 976 $ echo '**/*.txt = wilma' >> $config
984 977 $ do_push wilma
985 978 Pushing as user wilma
986 979 hgrc = """
987 980 [hooks]
988 981 pretxnchangegroup.acl = python:hgext.acl.hook
989 982 prepushkey.acl = python:hgext.acl.hook
990 983 [acl]
991 984 sources = push
992 985 [acl.allow]
993 986 foo/** = fred
994 987 [acl.deny]
995 988 foo/bar/** = fred
996 989 foo/Bar/** = fred
997 990 [acl.allow]
998 991 ** = barney
999 992 **/*.txt = wilma
1000 993 """
1001 994 pushing to ../b
1002 995 query 1; heads
1003 996 searching for changes
1004 997 all remote heads known locally
1005 998 listing keys for "phases"
1006 999 checking for updated bookmarks
1007 1000 listing keys for "bookmarks"
1008 1001 listing keys for "bookmarks"
1009 1002 3 changesets found
1010 1003 list of changesets:
1011 1004 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1012 1005 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1013 1006 911600dab2ae7a9baff75958b84fe606851ce955
1014 1007 bundle2-output-bundle: "HG20", 5 parts total
1015 1008 bundle2-output-part: "replycaps" 205 bytes payload
1016 1009 bundle2-output-part: "check:phases" 24 bytes payload
1017 1010 bundle2-output-part: "check:heads" streamed payload
1018 1011 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1019 1012 bundle2-output-part: "phase-heads" 24 bytes payload
1020 1013 bundle2-input-bundle: with-transaction
1021 1014 bundle2-input-part: "replycaps" supported
1022 1015 bundle2-input-part: total payload size 205
1023 1016 bundle2-input-part: "check:phases" supported
1024 1017 bundle2-input-part: total payload size 24
1025 1018 bundle2-input-part: "check:heads" supported
1026 1019 bundle2-input-part: total payload size 20
1027 1020 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1028 1021 adding changesets
1029 1022 add changeset ef1ea85a6374
1030 1023 add changeset f9cafe1212c8
1031 1024 add changeset 911600dab2ae
1032 1025 adding manifests
1033 1026 adding file changes
1034 1027 adding foo/Bar/file.txt revisions
1035 1028 adding foo/file.txt revisions
1036 1029 adding quux/file.py revisions
1037 added 3 changesets with 3 changes to 3 files
1038 1030 calling hook pretxnchangegroup.acl: hgext.acl.hook
1039 1031 acl: checking access for user "wilma"
1040 1032 acl: acl.allow.branches not enabled
1041 1033 acl: acl.deny.branches not enabled
1042 1034 acl: acl.allow enabled, 1 entries for user wilma
1043 1035 acl: acl.deny enabled, 0 entries for user wilma
1044 1036 acl: branch access granted: "ef1ea85a6374" on branch "default"
1045 1037 acl: path access granted: "ef1ea85a6374"
1046 1038 acl: branch access granted: "f9cafe1212c8" on branch "default"
1047 1039 acl: path access granted: "f9cafe1212c8"
1048 1040 acl: branch access granted: "911600dab2ae" on branch "default"
1049 1041 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
1050 1042 bundle2-input-part: total payload size 1553
1051 1043 bundle2-input-part: total payload size 24
1052 1044 bundle2-input-bundle: 4 parts total
1053 1045 transaction abort!
1054 1046 rollback completed
1055 1047 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
1056 1048 no rollback information available
1057 1049 0:6675d58eff77
1058 1050
1059 1051
1060 1052 file specified by acl.config does not exist
1061 1053
1062 1054 $ echo '[acl]' >> $config
1063 1055 $ echo 'config = ../acl.config' >> $config
1064 1056 $ do_push barney
1065 1057 Pushing as user barney
1066 1058 hgrc = """
1067 1059 [hooks]
1068 1060 pretxnchangegroup.acl = python:hgext.acl.hook
1069 1061 prepushkey.acl = python:hgext.acl.hook
1070 1062 [acl]
1071 1063 sources = push
1072 1064 [acl.allow]
1073 1065 foo/** = fred
1074 1066 [acl.deny]
1075 1067 foo/bar/** = fred
1076 1068 foo/Bar/** = fred
1077 1069 [acl.allow]
1078 1070 ** = barney
1079 1071 **/*.txt = wilma
1080 1072 [acl]
1081 1073 config = ../acl.config
1082 1074 """
1083 1075 pushing to ../b
1084 1076 query 1; heads
1085 1077 searching for changes
1086 1078 all remote heads known locally
1087 1079 listing keys for "phases"
1088 1080 checking for updated bookmarks
1089 1081 listing keys for "bookmarks"
1090 1082 listing keys for "bookmarks"
1091 1083 3 changesets found
1092 1084 list of changesets:
1093 1085 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1094 1086 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1095 1087 911600dab2ae7a9baff75958b84fe606851ce955
1096 1088 bundle2-output-bundle: "HG20", 5 parts total
1097 1089 bundle2-output-part: "replycaps" 205 bytes payload
1098 1090 bundle2-output-part: "check:phases" 24 bytes payload
1099 1091 bundle2-output-part: "check:heads" streamed payload
1100 1092 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1101 1093 bundle2-output-part: "phase-heads" 24 bytes payload
1102 1094 bundle2-input-bundle: with-transaction
1103 1095 bundle2-input-part: "replycaps" supported
1104 1096 bundle2-input-part: total payload size 205
1105 1097 bundle2-input-part: "check:phases" supported
1106 1098 bundle2-input-part: total payload size 24
1107 1099 bundle2-input-part: "check:heads" supported
1108 1100 bundle2-input-part: total payload size 20
1109 1101 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1110 1102 adding changesets
1111 1103 add changeset ef1ea85a6374
1112 1104 add changeset f9cafe1212c8
1113 1105 add changeset 911600dab2ae
1114 1106 adding manifests
1115 1107 adding file changes
1116 1108 adding foo/Bar/file.txt revisions
1117 1109 adding foo/file.txt revisions
1118 1110 adding quux/file.py revisions
1119 added 3 changesets with 3 changes to 3 files
1120 1111 calling hook pretxnchangegroup.acl: hgext.acl.hook
1121 1112 acl: checking access for user "barney"
1122 1113 error: pretxnchangegroup.acl hook raised an exception: [Errno *] * (glob)
1123 1114 bundle2-input-part: total payload size 1553
1124 1115 bundle2-input-part: total payload size 24
1125 1116 bundle2-input-bundle: 4 parts total
1126 1117 transaction abort!
1127 1118 rollback completed
1128 1119 abort: $ENOENT$: '../acl.config'
1129 1120 no rollback information available
1130 1121 0:6675d58eff77
1131 1122
1132 1123
1133 1124 betty is allowed inside foo/ by a acl.config file
1134 1125
1135 1126 $ echo '[acl.allow]' >> acl.config
1136 1127 $ echo 'foo/** = betty' >> acl.config
1137 1128 $ do_push betty
1138 1129 Pushing as user betty
1139 1130 hgrc = """
1140 1131 [hooks]
1141 1132 pretxnchangegroup.acl = python:hgext.acl.hook
1142 1133 prepushkey.acl = python:hgext.acl.hook
1143 1134 [acl]
1144 1135 sources = push
1145 1136 [acl.allow]
1146 1137 foo/** = fred
1147 1138 [acl.deny]
1148 1139 foo/bar/** = fred
1149 1140 foo/Bar/** = fred
1150 1141 [acl.allow]
1151 1142 ** = barney
1152 1143 **/*.txt = wilma
1153 1144 [acl]
1154 1145 config = ../acl.config
1155 1146 """
1156 1147 acl.config = """
1157 1148 [acl.allow]
1158 1149 foo/** = betty
1159 1150 """
1160 1151 pushing to ../b
1161 1152 query 1; heads
1162 1153 searching for changes
1163 1154 all remote heads known locally
1164 1155 listing keys for "phases"
1165 1156 checking for updated bookmarks
1166 1157 listing keys for "bookmarks"
1167 1158 listing keys for "bookmarks"
1168 1159 3 changesets found
1169 1160 list of changesets:
1170 1161 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1171 1162 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1172 1163 911600dab2ae7a9baff75958b84fe606851ce955
1173 1164 bundle2-output-bundle: "HG20", 5 parts total
1174 1165 bundle2-output-part: "replycaps" 205 bytes payload
1175 1166 bundle2-output-part: "check:phases" 24 bytes payload
1176 1167 bundle2-output-part: "check:heads" streamed payload
1177 1168 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1178 1169 bundle2-output-part: "phase-heads" 24 bytes payload
1179 1170 bundle2-input-bundle: with-transaction
1180 1171 bundle2-input-part: "replycaps" supported
1181 1172 bundle2-input-part: total payload size 205
1182 1173 bundle2-input-part: "check:phases" supported
1183 1174 bundle2-input-part: total payload size 24
1184 1175 bundle2-input-part: "check:heads" supported
1185 1176 bundle2-input-part: total payload size 20
1186 1177 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1187 1178 adding changesets
1188 1179 add changeset ef1ea85a6374
1189 1180 add changeset f9cafe1212c8
1190 1181 add changeset 911600dab2ae
1191 1182 adding manifests
1192 1183 adding file changes
1193 1184 adding foo/Bar/file.txt revisions
1194 1185 adding foo/file.txt revisions
1195 1186 adding quux/file.py revisions
1196 added 3 changesets with 3 changes to 3 files
1197 1187 calling hook pretxnchangegroup.acl: hgext.acl.hook
1198 1188 acl: checking access for user "betty"
1199 1189 acl: acl.allow.branches not enabled
1200 1190 acl: acl.deny.branches not enabled
1201 1191 acl: acl.allow enabled, 1 entries for user betty
1202 1192 acl: acl.deny enabled, 0 entries for user betty
1203 1193 acl: branch access granted: "ef1ea85a6374" on branch "default"
1204 1194 acl: path access granted: "ef1ea85a6374"
1205 1195 acl: branch access granted: "f9cafe1212c8" on branch "default"
1206 1196 acl: path access granted: "f9cafe1212c8"
1207 1197 acl: branch access granted: "911600dab2ae" on branch "default"
1208 1198 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
1209 1199 bundle2-input-part: total payload size 1553
1210 1200 bundle2-input-part: total payload size 24
1211 1201 bundle2-input-bundle: 4 parts total
1212 1202 transaction abort!
1213 1203 rollback completed
1214 1204 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
1215 1205 no rollback information available
1216 1206 0:6675d58eff77
1217 1207
1218 1208
1219 1209 acl.config can set only [acl.allow]/[acl.deny]
1220 1210
1221 1211 $ echo '[hooks]' >> acl.config
1222 1212 $ echo 'changegroup.acl = false' >> acl.config
1223 1213 $ do_push barney
1224 1214 Pushing as user barney
1225 1215 hgrc = """
1226 1216 [hooks]
1227 1217 pretxnchangegroup.acl = python:hgext.acl.hook
1228 1218 prepushkey.acl = python:hgext.acl.hook
1229 1219 [acl]
1230 1220 sources = push
1231 1221 [acl.allow]
1232 1222 foo/** = fred
1233 1223 [acl.deny]
1234 1224 foo/bar/** = fred
1235 1225 foo/Bar/** = fred
1236 1226 [acl.allow]
1237 1227 ** = barney
1238 1228 **/*.txt = wilma
1239 1229 [acl]
1240 1230 config = ../acl.config
1241 1231 """
1242 1232 acl.config = """
1243 1233 [acl.allow]
1244 1234 foo/** = betty
1245 1235 [hooks]
1246 1236 changegroup.acl = false
1247 1237 """
1248 1238 pushing to ../b
1249 1239 query 1; heads
1250 1240 searching for changes
1251 1241 all remote heads known locally
1252 1242 listing keys for "phases"
1253 1243 checking for updated bookmarks
1254 1244 listing keys for "bookmarks"
1255 1245 listing keys for "bookmarks"
1256 1246 3 changesets found
1257 1247 list of changesets:
1258 1248 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1259 1249 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1260 1250 911600dab2ae7a9baff75958b84fe606851ce955
1261 1251 bundle2-output-bundle: "HG20", 5 parts total
1262 1252 bundle2-output-part: "replycaps" 205 bytes payload
1263 1253 bundle2-output-part: "check:phases" 24 bytes payload
1264 1254 bundle2-output-part: "check:heads" streamed payload
1265 1255 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1266 1256 bundle2-output-part: "phase-heads" 24 bytes payload
1267 1257 bundle2-input-bundle: with-transaction
1268 1258 bundle2-input-part: "replycaps" supported
1269 1259 bundle2-input-part: total payload size 205
1270 1260 bundle2-input-part: "check:phases" supported
1271 1261 bundle2-input-part: total payload size 24
1272 1262 bundle2-input-part: "check:heads" supported
1273 1263 bundle2-input-part: total payload size 20
1274 1264 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1275 1265 adding changesets
1276 1266 add changeset ef1ea85a6374
1277 1267 add changeset f9cafe1212c8
1278 1268 add changeset 911600dab2ae
1279 1269 adding manifests
1280 1270 adding file changes
1281 1271 adding foo/Bar/file.txt revisions
1282 1272 adding foo/file.txt revisions
1283 1273 adding quux/file.py revisions
1284 added 3 changesets with 3 changes to 3 files
1285 1274 calling hook pretxnchangegroup.acl: hgext.acl.hook
1286 1275 acl: checking access for user "barney"
1287 1276 acl: acl.allow.branches not enabled
1288 1277 acl: acl.deny.branches not enabled
1289 1278 acl: acl.allow enabled, 1 entries for user barney
1290 1279 acl: acl.deny enabled, 0 entries for user barney
1291 1280 acl: branch access granted: "ef1ea85a6374" on branch "default"
1292 1281 acl: path access granted: "ef1ea85a6374"
1293 1282 acl: branch access granted: "f9cafe1212c8" on branch "default"
1294 1283 acl: path access granted: "f9cafe1212c8"
1295 1284 acl: branch access granted: "911600dab2ae" on branch "default"
1296 1285 acl: path access granted: "911600dab2ae"
1297 1286 bundle2-input-part: total payload size 1553
1298 1287 bundle2-input-part: "phase-heads" supported
1299 1288 bundle2-input-part: total payload size 24
1300 1289 bundle2-input-bundle: 4 parts total
1301 1290 updating the branch cache
1291 added 3 changesets with 3 changes to 3 files
1302 1292 bundle2-output-bundle: "HG20", 1 parts total
1303 1293 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1304 1294 bundle2-input-bundle: no-transaction
1305 1295 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1306 1296 bundle2-input-bundle: 0 parts total
1307 1297 listing keys for "phases"
1308 1298 repository tip rolled back to revision 0 (undo push)
1309 1299 0:6675d58eff77
1310 1300
1311 1301
1312 1302 asterisk
1313 1303
1314 1304 $ init_config
1315 1305
1316 1306 asterisk test
1317 1307
1318 1308 $ echo '[acl.allow]' >> $config
1319 1309 $ echo "** = fred" >> $config
1320 1310
1321 1311 fred is always allowed
1322 1312
1323 1313 $ do_push fred
1324 1314 Pushing as user fred
1325 1315 hgrc = """
1326 1316 [hooks]
1327 1317 pretxnchangegroup.acl = python:hgext.acl.hook
1328 1318 prepushkey.acl = python:hgext.acl.hook
1329 1319 [acl]
1330 1320 sources = push
1331 1321 [extensions]
1332 1322 posixgetuser=$TESTTMP/posixgetuser.py
1333 1323 [acl.allow]
1334 1324 ** = fred
1335 1325 """
1336 1326 pushing to ../b
1337 1327 query 1; heads
1338 1328 searching for changes
1339 1329 all remote heads known locally
1340 1330 listing keys for "phases"
1341 1331 checking for updated bookmarks
1342 1332 listing keys for "bookmarks"
1343 1333 listing keys for "bookmarks"
1344 1334 3 changesets found
1345 1335 list of changesets:
1346 1336 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1347 1337 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1348 1338 911600dab2ae7a9baff75958b84fe606851ce955
1349 1339 bundle2-output-bundle: "HG20", 5 parts total
1350 1340 bundle2-output-part: "replycaps" 205 bytes payload
1351 1341 bundle2-output-part: "check:phases" 24 bytes payload
1352 1342 bundle2-output-part: "check:heads" streamed payload
1353 1343 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1354 1344 bundle2-output-part: "phase-heads" 24 bytes payload
1355 1345 bundle2-input-bundle: with-transaction
1356 1346 bundle2-input-part: "replycaps" supported
1357 1347 bundle2-input-part: total payload size 205
1358 1348 bundle2-input-part: "check:phases" supported
1359 1349 bundle2-input-part: total payload size 24
1360 1350 bundle2-input-part: "check:heads" supported
1361 1351 bundle2-input-part: total payload size 20
1362 1352 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1363 1353 adding changesets
1364 1354 add changeset ef1ea85a6374
1365 1355 add changeset f9cafe1212c8
1366 1356 add changeset 911600dab2ae
1367 1357 adding manifests
1368 1358 adding file changes
1369 1359 adding foo/Bar/file.txt revisions
1370 1360 adding foo/file.txt revisions
1371 1361 adding quux/file.py revisions
1372 added 3 changesets with 3 changes to 3 files
1373 1362 calling hook pretxnchangegroup.acl: hgext.acl.hook
1374 1363 acl: checking access for user "fred"
1375 1364 acl: acl.allow.branches not enabled
1376 1365 acl: acl.deny.branches not enabled
1377 1366 acl: acl.allow enabled, 1 entries for user fred
1378 1367 acl: acl.deny not enabled
1379 1368 acl: branch access granted: "ef1ea85a6374" on branch "default"
1380 1369 acl: path access granted: "ef1ea85a6374"
1381 1370 acl: branch access granted: "f9cafe1212c8" on branch "default"
1382 1371 acl: path access granted: "f9cafe1212c8"
1383 1372 acl: branch access granted: "911600dab2ae" on branch "default"
1384 1373 acl: path access granted: "911600dab2ae"
1385 1374 bundle2-input-part: total payload size 1553
1386 1375 bundle2-input-part: "phase-heads" supported
1387 1376 bundle2-input-part: total payload size 24
1388 1377 bundle2-input-bundle: 4 parts total
1389 1378 updating the branch cache
1379 added 3 changesets with 3 changes to 3 files
1390 1380 bundle2-output-bundle: "HG20", 1 parts total
1391 1381 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1392 1382 bundle2-input-bundle: no-transaction
1393 1383 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1394 1384 bundle2-input-bundle: 0 parts total
1395 1385 listing keys for "phases"
1396 1386 repository tip rolled back to revision 0 (undo push)
1397 1387 0:6675d58eff77
1398 1388
1399 1389
1400 1390 $ echo '[acl.deny]' >> $config
1401 1391 $ echo "foo/Bar/** = *" >> $config
1402 1392
1403 1393 no one is allowed inside foo/Bar/
1404 1394
1405 1395 $ do_push fred
1406 1396 Pushing as user fred
1407 1397 hgrc = """
1408 1398 [hooks]
1409 1399 pretxnchangegroup.acl = python:hgext.acl.hook
1410 1400 prepushkey.acl = python:hgext.acl.hook
1411 1401 [acl]
1412 1402 sources = push
1413 1403 [extensions]
1414 1404 posixgetuser=$TESTTMP/posixgetuser.py
1415 1405 [acl.allow]
1416 1406 ** = fred
1417 1407 [acl.deny]
1418 1408 foo/Bar/** = *
1419 1409 """
1420 1410 pushing to ../b
1421 1411 query 1; heads
1422 1412 searching for changes
1423 1413 all remote heads known locally
1424 1414 listing keys for "phases"
1425 1415 checking for updated bookmarks
1426 1416 listing keys for "bookmarks"
1427 1417 listing keys for "bookmarks"
1428 1418 3 changesets found
1429 1419 list of changesets:
1430 1420 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1431 1421 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1432 1422 911600dab2ae7a9baff75958b84fe606851ce955
1433 1423 bundle2-output-bundle: "HG20", 5 parts total
1434 1424 bundle2-output-part: "replycaps" 205 bytes payload
1435 1425 bundle2-output-part: "check:phases" 24 bytes payload
1436 1426 bundle2-output-part: "check:heads" streamed payload
1437 1427 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1438 1428 bundle2-output-part: "phase-heads" 24 bytes payload
1439 1429 bundle2-input-bundle: with-transaction
1440 1430 bundle2-input-part: "replycaps" supported
1441 1431 bundle2-input-part: total payload size 205
1442 1432 bundle2-input-part: "check:phases" supported
1443 1433 bundle2-input-part: total payload size 24
1444 1434 bundle2-input-part: "check:heads" supported
1445 1435 bundle2-input-part: total payload size 20
1446 1436 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1447 1437 adding changesets
1448 1438 add changeset ef1ea85a6374
1449 1439 add changeset f9cafe1212c8
1450 1440 add changeset 911600dab2ae
1451 1441 adding manifests
1452 1442 adding file changes
1453 1443 adding foo/Bar/file.txt revisions
1454 1444 adding foo/file.txt revisions
1455 1445 adding quux/file.py revisions
1456 added 3 changesets with 3 changes to 3 files
1457 1446 calling hook pretxnchangegroup.acl: hgext.acl.hook
1458 1447 acl: checking access for user "fred"
1459 1448 acl: acl.allow.branches not enabled
1460 1449 acl: acl.deny.branches not enabled
1461 1450 acl: acl.allow enabled, 1 entries for user fred
1462 1451 acl: acl.deny enabled, 1 entries for user fred
1463 1452 acl: branch access granted: "ef1ea85a6374" on branch "default"
1464 1453 acl: path access granted: "ef1ea85a6374"
1465 1454 acl: branch access granted: "f9cafe1212c8" on branch "default"
1466 1455 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1467 1456 bundle2-input-part: total payload size 1553
1468 1457 bundle2-input-part: total payload size 24
1469 1458 bundle2-input-bundle: 4 parts total
1470 1459 transaction abort!
1471 1460 rollback completed
1472 1461 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1473 1462 no rollback information available
1474 1463 0:6675d58eff77
1475 1464
1476 1465
1477 1466 Groups
1478 1467
1479 1468 $ init_config
1480 1469
1481 1470 OS-level groups
1482 1471
1483 1472 $ echo '[acl.allow]' >> $config
1484 1473 $ echo "** = @group1" >> $config
1485 1474
1486 1475 @group1 is always allowed
1487 1476
1488 1477 $ do_push fred
1489 1478 Pushing as user fred
1490 1479 hgrc = """
1491 1480 [hooks]
1492 1481 pretxnchangegroup.acl = python:hgext.acl.hook
1493 1482 prepushkey.acl = python:hgext.acl.hook
1494 1483 [acl]
1495 1484 sources = push
1496 1485 [extensions]
1497 1486 posixgetuser=$TESTTMP/posixgetuser.py
1498 1487 [acl.allow]
1499 1488 ** = @group1
1500 1489 """
1501 1490 pushing to ../b
1502 1491 query 1; heads
1503 1492 searching for changes
1504 1493 all remote heads known locally
1505 1494 listing keys for "phases"
1506 1495 checking for updated bookmarks
1507 1496 listing keys for "bookmarks"
1508 1497 listing keys for "bookmarks"
1509 1498 3 changesets found
1510 1499 list of changesets:
1511 1500 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1512 1501 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1513 1502 911600dab2ae7a9baff75958b84fe606851ce955
1514 1503 bundle2-output-bundle: "HG20", 5 parts total
1515 1504 bundle2-output-part: "replycaps" 205 bytes payload
1516 1505 bundle2-output-part: "check:phases" 24 bytes payload
1517 1506 bundle2-output-part: "check:heads" streamed payload
1518 1507 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1519 1508 bundle2-output-part: "phase-heads" 24 bytes payload
1520 1509 bundle2-input-bundle: with-transaction
1521 1510 bundle2-input-part: "replycaps" supported
1522 1511 bundle2-input-part: total payload size 205
1523 1512 bundle2-input-part: "check:phases" supported
1524 1513 bundle2-input-part: total payload size 24
1525 1514 bundle2-input-part: "check:heads" supported
1526 1515 bundle2-input-part: total payload size 20
1527 1516 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1528 1517 adding changesets
1529 1518 add changeset ef1ea85a6374
1530 1519 add changeset f9cafe1212c8
1531 1520 add changeset 911600dab2ae
1532 1521 adding manifests
1533 1522 adding file changes
1534 1523 adding foo/Bar/file.txt revisions
1535 1524 adding foo/file.txt revisions
1536 1525 adding quux/file.py revisions
1537 added 3 changesets with 3 changes to 3 files
1538 1526 calling hook pretxnchangegroup.acl: hgext.acl.hook
1539 1527 acl: checking access for user "fred"
1540 1528 acl: acl.allow.branches not enabled
1541 1529 acl: acl.deny.branches not enabled
1542 1530 acl: "group1" not defined in [acl.groups]
1543 1531 acl: acl.allow enabled, 1 entries for user fred
1544 1532 acl: acl.deny not enabled
1545 1533 acl: branch access granted: "ef1ea85a6374" on branch "default"
1546 1534 acl: path access granted: "ef1ea85a6374"
1547 1535 acl: branch access granted: "f9cafe1212c8" on branch "default"
1548 1536 acl: path access granted: "f9cafe1212c8"
1549 1537 acl: branch access granted: "911600dab2ae" on branch "default"
1550 1538 acl: path access granted: "911600dab2ae"
1551 1539 bundle2-input-part: total payload size 1553
1552 1540 bundle2-input-part: "phase-heads" supported
1553 1541 bundle2-input-part: total payload size 24
1554 1542 bundle2-input-bundle: 4 parts total
1555 1543 updating the branch cache
1544 added 3 changesets with 3 changes to 3 files
1556 1545 bundle2-output-bundle: "HG20", 1 parts total
1557 1546 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1558 1547 bundle2-input-bundle: no-transaction
1559 1548 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1560 1549 bundle2-input-bundle: 0 parts total
1561 1550 listing keys for "phases"
1562 1551 repository tip rolled back to revision 0 (undo push)
1563 1552 0:6675d58eff77
1564 1553
1565 1554
1566 1555 $ echo '[acl.deny]' >> $config
1567 1556 $ echo "foo/Bar/** = @group1" >> $config
1568 1557
1569 1558 @group is allowed inside anything but foo/Bar/
1570 1559
1571 1560 $ do_push fred
1572 1561 Pushing as user fred
1573 1562 hgrc = """
1574 1563 [hooks]
1575 1564 pretxnchangegroup.acl = python:hgext.acl.hook
1576 1565 prepushkey.acl = python:hgext.acl.hook
1577 1566 [acl]
1578 1567 sources = push
1579 1568 [extensions]
1580 1569 posixgetuser=$TESTTMP/posixgetuser.py
1581 1570 [acl.allow]
1582 1571 ** = @group1
1583 1572 [acl.deny]
1584 1573 foo/Bar/** = @group1
1585 1574 """
1586 1575 pushing to ../b
1587 1576 query 1; heads
1588 1577 searching for changes
1589 1578 all remote heads known locally
1590 1579 listing keys for "phases"
1591 1580 checking for updated bookmarks
1592 1581 listing keys for "bookmarks"
1593 1582 listing keys for "bookmarks"
1594 1583 3 changesets found
1595 1584 list of changesets:
1596 1585 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1597 1586 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1598 1587 911600dab2ae7a9baff75958b84fe606851ce955
1599 1588 bundle2-output-bundle: "HG20", 5 parts total
1600 1589 bundle2-output-part: "replycaps" 205 bytes payload
1601 1590 bundle2-output-part: "check:phases" 24 bytes payload
1602 1591 bundle2-output-part: "check:heads" streamed payload
1603 1592 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1604 1593 bundle2-output-part: "phase-heads" 24 bytes payload
1605 1594 bundle2-input-bundle: with-transaction
1606 1595 bundle2-input-part: "replycaps" supported
1607 1596 bundle2-input-part: total payload size 205
1608 1597 bundle2-input-part: "check:phases" supported
1609 1598 bundle2-input-part: total payload size 24
1610 1599 bundle2-input-part: "check:heads" supported
1611 1600 bundle2-input-part: total payload size 20
1612 1601 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1613 1602 adding changesets
1614 1603 add changeset ef1ea85a6374
1615 1604 add changeset f9cafe1212c8
1616 1605 add changeset 911600dab2ae
1617 1606 adding manifests
1618 1607 adding file changes
1619 1608 adding foo/Bar/file.txt revisions
1620 1609 adding foo/file.txt revisions
1621 1610 adding quux/file.py revisions
1622 added 3 changesets with 3 changes to 3 files
1623 1611 calling hook pretxnchangegroup.acl: hgext.acl.hook
1624 1612 acl: checking access for user "fred"
1625 1613 acl: acl.allow.branches not enabled
1626 1614 acl: acl.deny.branches not enabled
1627 1615 acl: "group1" not defined in [acl.groups]
1628 1616 acl: acl.allow enabled, 1 entries for user fred
1629 1617 acl: "group1" not defined in [acl.groups]
1630 1618 acl: acl.deny enabled, 1 entries for user fred
1631 1619 acl: branch access granted: "ef1ea85a6374" on branch "default"
1632 1620 acl: path access granted: "ef1ea85a6374"
1633 1621 acl: branch access granted: "f9cafe1212c8" on branch "default"
1634 1622 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1635 1623 bundle2-input-part: total payload size 1553
1636 1624 bundle2-input-part: total payload size 24
1637 1625 bundle2-input-bundle: 4 parts total
1638 1626 transaction abort!
1639 1627 rollback completed
1640 1628 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1641 1629 no rollback information available
1642 1630 0:6675d58eff77
1643 1631
1644 1632
1645 1633 Invalid group
1646 1634
1647 1635 Disable the fakegroups trick to get real failures
1648 1636
1649 1637 $ grep -v fakegroups $config > config.tmp
1650 1638 $ mv config.tmp $config
1651 1639 $ echo '[acl.allow]' >> $config
1652 1640 $ echo "** = @unlikelytoexist" >> $config
1653 1641 $ do_push fred 2>&1 | grep unlikelytoexist
1654 1642 ** = @unlikelytoexist
1655 1643 acl: "unlikelytoexist" not defined in [acl.groups]
1656 1644 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1657 1645 abort: group 'unlikelytoexist' is undefined
1658 1646
1659 1647
1660 1648 Branch acl tests setup
1661 1649
1662 1650 $ init_config
1663 1651 $ cd b
1664 1652 $ hg up
1665 1653 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1666 1654 $ hg branch foobar
1667 1655 marked working directory as branch foobar
1668 1656 (branches are permanent and global, did you want a bookmark?)
1669 1657 $ hg commit -m 'create foobar'
1670 1658 $ echo 'foo contents' > abc.txt
1671 1659 $ hg add abc.txt
1672 1660 $ hg commit -m 'foobar contents'
1673 1661 $ cd ..
1674 1662 $ hg --cwd a pull ../b
1675 1663 pulling from ../b
1676 1664 searching for changes
1677 1665 adding changesets
1678 1666 adding manifests
1679 1667 adding file changes
1680 1668 added 2 changesets with 1 changes to 1 files (+1 heads)
1681 1669 new changesets 81fbf4469322:fb35475503ef
1682 1670 (run 'hg heads' to see heads)
1683 1671
1684 1672 Create additional changeset on foobar branch
1685 1673
1686 1674 $ cd a
1687 1675 $ hg up -C foobar
1688 1676 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1689 1677 $ echo 'foo contents2' > abc.txt
1690 1678 $ hg commit -m 'foobar contents2'
1691 1679 $ cd ..
1692 1680
1693 1681
1694 1682 No branch acls specified
1695 1683
1696 1684 $ do_push astro
1697 1685 Pushing as user astro
1698 1686 hgrc = """
1699 1687 [hooks]
1700 1688 pretxnchangegroup.acl = python:hgext.acl.hook
1701 1689 prepushkey.acl = python:hgext.acl.hook
1702 1690 [acl]
1703 1691 sources = push
1704 1692 [extensions]
1705 1693 posixgetuser=$TESTTMP/posixgetuser.py
1706 1694 """
1707 1695 pushing to ../b
1708 1696 query 1; heads
1709 1697 searching for changes
1710 1698 all remote heads known locally
1711 1699 listing keys for "phases"
1712 1700 checking for updated bookmarks
1713 1701 listing keys for "bookmarks"
1714 1702 listing keys for "bookmarks"
1715 1703 4 changesets found
1716 1704 list of changesets:
1717 1705 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1718 1706 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1719 1707 911600dab2ae7a9baff75958b84fe606851ce955
1720 1708 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1721 1709 bundle2-output-bundle: "HG20", 5 parts total
1722 1710 bundle2-output-part: "replycaps" 205 bytes payload
1723 1711 bundle2-output-part: "check:phases" 48 bytes payload
1724 1712 bundle2-output-part: "check:heads" streamed payload
1725 1713 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1726 1714 bundle2-output-part: "phase-heads" 48 bytes payload
1727 1715 bundle2-input-bundle: with-transaction
1728 1716 bundle2-input-part: "replycaps" supported
1729 1717 bundle2-input-part: total payload size 205
1730 1718 bundle2-input-part: "check:phases" supported
1731 1719 bundle2-input-part: total payload size 48
1732 1720 bundle2-input-part: "check:heads" supported
1733 1721 bundle2-input-part: total payload size 20
1734 1722 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1735 1723 adding changesets
1736 1724 add changeset ef1ea85a6374
1737 1725 add changeset f9cafe1212c8
1738 1726 add changeset 911600dab2ae
1739 1727 add changeset e8fc755d4d82
1740 1728 adding manifests
1741 1729 adding file changes
1742 1730 adding abc.txt revisions
1743 1731 adding foo/Bar/file.txt revisions
1744 1732 adding foo/file.txt revisions
1745 1733 adding quux/file.py revisions
1746 added 4 changesets with 4 changes to 4 files (+1 heads)
1747 1734 calling hook pretxnchangegroup.acl: hgext.acl.hook
1748 1735 acl: checking access for user "astro"
1749 1736 acl: acl.allow.branches not enabled
1750 1737 acl: acl.deny.branches not enabled
1751 1738 acl: acl.allow not enabled
1752 1739 acl: acl.deny not enabled
1753 1740 acl: branch access granted: "ef1ea85a6374" on branch "default"
1754 1741 acl: path access granted: "ef1ea85a6374"
1755 1742 acl: branch access granted: "f9cafe1212c8" on branch "default"
1756 1743 acl: path access granted: "f9cafe1212c8"
1757 1744 acl: branch access granted: "911600dab2ae" on branch "default"
1758 1745 acl: path access granted: "911600dab2ae"
1759 1746 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1760 1747 acl: path access granted: "e8fc755d4d82"
1761 1748 bundle2-input-part: total payload size 2068
1762 1749 bundle2-input-part: "phase-heads" supported
1763 1750 bundle2-input-part: total payload size 48
1764 1751 bundle2-input-bundle: 4 parts total
1765 1752 updating the branch cache
1753 added 4 changesets with 4 changes to 4 files (+1 heads)
1766 1754 bundle2-output-bundle: "HG20", 1 parts total
1767 1755 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1768 1756 bundle2-input-bundle: no-transaction
1769 1757 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1770 1758 bundle2-input-bundle: 0 parts total
1771 1759 listing keys for "phases"
1772 1760 repository tip rolled back to revision 2 (undo push)
1773 1761 2:fb35475503ef
1774 1762
1775 1763
1776 1764 Branch acl deny test
1777 1765
1778 1766 $ echo "[acl.deny.branches]" >> $config
1779 1767 $ echo "foobar = *" >> $config
1780 1768 $ do_push astro
1781 1769 Pushing as user astro
1782 1770 hgrc = """
1783 1771 [hooks]
1784 1772 pretxnchangegroup.acl = python:hgext.acl.hook
1785 1773 prepushkey.acl = python:hgext.acl.hook
1786 1774 [acl]
1787 1775 sources = push
1788 1776 [extensions]
1789 1777 posixgetuser=$TESTTMP/posixgetuser.py
1790 1778 [acl.deny.branches]
1791 1779 foobar = *
1792 1780 """
1793 1781 pushing to ../b
1794 1782 query 1; heads
1795 1783 searching for changes
1796 1784 all remote heads known locally
1797 1785 listing keys for "phases"
1798 1786 checking for updated bookmarks
1799 1787 listing keys for "bookmarks"
1800 1788 listing keys for "bookmarks"
1801 1789 4 changesets found
1802 1790 list of changesets:
1803 1791 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1804 1792 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1805 1793 911600dab2ae7a9baff75958b84fe606851ce955
1806 1794 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1807 1795 bundle2-output-bundle: "HG20", 5 parts total
1808 1796 bundle2-output-part: "replycaps" 205 bytes payload
1809 1797 bundle2-output-part: "check:phases" 48 bytes payload
1810 1798 bundle2-output-part: "check:heads" streamed payload
1811 1799 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1812 1800 bundle2-output-part: "phase-heads" 48 bytes payload
1813 1801 bundle2-input-bundle: with-transaction
1814 1802 bundle2-input-part: "replycaps" supported
1815 1803 bundle2-input-part: total payload size 205
1816 1804 bundle2-input-part: "check:phases" supported
1817 1805 bundle2-input-part: total payload size 48
1818 1806 bundle2-input-part: "check:heads" supported
1819 1807 bundle2-input-part: total payload size 20
1820 1808 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1821 1809 adding changesets
1822 1810 add changeset ef1ea85a6374
1823 1811 add changeset f9cafe1212c8
1824 1812 add changeset 911600dab2ae
1825 1813 add changeset e8fc755d4d82
1826 1814 adding manifests
1827 1815 adding file changes
1828 1816 adding abc.txt revisions
1829 1817 adding foo/Bar/file.txt revisions
1830 1818 adding foo/file.txt revisions
1831 1819 adding quux/file.py revisions
1832 added 4 changesets with 4 changes to 4 files (+1 heads)
1833 1820 calling hook pretxnchangegroup.acl: hgext.acl.hook
1834 1821 acl: checking access for user "astro"
1835 1822 acl: acl.allow.branches not enabled
1836 1823 acl: acl.deny.branches enabled, 1 entries for user astro
1837 1824 acl: acl.allow not enabled
1838 1825 acl: acl.deny not enabled
1839 1826 acl: branch access granted: "ef1ea85a6374" on branch "default"
1840 1827 acl: path access granted: "ef1ea85a6374"
1841 1828 acl: branch access granted: "f9cafe1212c8" on branch "default"
1842 1829 acl: path access granted: "f9cafe1212c8"
1843 1830 acl: branch access granted: "911600dab2ae" on branch "default"
1844 1831 acl: path access granted: "911600dab2ae"
1845 1832 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1846 1833 bundle2-input-part: total payload size 2068
1847 1834 bundle2-input-part: total payload size 48
1848 1835 bundle2-input-bundle: 4 parts total
1849 1836 transaction abort!
1850 1837 rollback completed
1851 1838 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1852 1839 no rollback information available
1853 1840 2:fb35475503ef
1854 1841
1855 1842
1856 1843 Branch acl empty allow test
1857 1844
1858 1845 $ init_config
1859 1846 $ echo "[acl.allow.branches]" >> $config
1860 1847 $ do_push astro
1861 1848 Pushing as user astro
1862 1849 hgrc = """
1863 1850 [hooks]
1864 1851 pretxnchangegroup.acl = python:hgext.acl.hook
1865 1852 prepushkey.acl = python:hgext.acl.hook
1866 1853 [acl]
1867 1854 sources = push
1868 1855 [extensions]
1869 1856 posixgetuser=$TESTTMP/posixgetuser.py
1870 1857 [acl.allow.branches]
1871 1858 """
1872 1859 pushing to ../b
1873 1860 query 1; heads
1874 1861 searching for changes
1875 1862 all remote heads known locally
1876 1863 listing keys for "phases"
1877 1864 checking for updated bookmarks
1878 1865 listing keys for "bookmarks"
1879 1866 listing keys for "bookmarks"
1880 1867 4 changesets found
1881 1868 list of changesets:
1882 1869 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1883 1870 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1884 1871 911600dab2ae7a9baff75958b84fe606851ce955
1885 1872 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1886 1873 bundle2-output-bundle: "HG20", 5 parts total
1887 1874 bundle2-output-part: "replycaps" 205 bytes payload
1888 1875 bundle2-output-part: "check:phases" 48 bytes payload
1889 1876 bundle2-output-part: "check:heads" streamed payload
1890 1877 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1891 1878 bundle2-output-part: "phase-heads" 48 bytes payload
1892 1879 bundle2-input-bundle: with-transaction
1893 1880 bundle2-input-part: "replycaps" supported
1894 1881 bundle2-input-part: total payload size 205
1895 1882 bundle2-input-part: "check:phases" supported
1896 1883 bundle2-input-part: total payload size 48
1897 1884 bundle2-input-part: "check:heads" supported
1898 1885 bundle2-input-part: total payload size 20
1899 1886 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1900 1887 adding changesets
1901 1888 add changeset ef1ea85a6374
1902 1889 add changeset f9cafe1212c8
1903 1890 add changeset 911600dab2ae
1904 1891 add changeset e8fc755d4d82
1905 1892 adding manifests
1906 1893 adding file changes
1907 1894 adding abc.txt revisions
1908 1895 adding foo/Bar/file.txt revisions
1909 1896 adding foo/file.txt revisions
1910 1897 adding quux/file.py revisions
1911 added 4 changesets with 4 changes to 4 files (+1 heads)
1912 1898 calling hook pretxnchangegroup.acl: hgext.acl.hook
1913 1899 acl: checking access for user "astro"
1914 1900 acl: acl.allow.branches enabled, 0 entries for user astro
1915 1901 acl: acl.deny.branches not enabled
1916 1902 acl: acl.allow not enabled
1917 1903 acl: acl.deny not enabled
1918 1904 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1919 1905 bundle2-input-part: total payload size 2068
1920 1906 bundle2-input-part: total payload size 48
1921 1907 bundle2-input-bundle: 4 parts total
1922 1908 transaction abort!
1923 1909 rollback completed
1924 1910 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1925 1911 no rollback information available
1926 1912 2:fb35475503ef
1927 1913
1928 1914
1929 1915 Branch acl allow other
1930 1916
1931 1917 $ init_config
1932 1918 $ echo "[acl.allow.branches]" >> $config
1933 1919 $ echo "* = george" >> $config
1934 1920 $ do_push astro
1935 1921 Pushing as user astro
1936 1922 hgrc = """
1937 1923 [hooks]
1938 1924 pretxnchangegroup.acl = python:hgext.acl.hook
1939 1925 prepushkey.acl = python:hgext.acl.hook
1940 1926 [acl]
1941 1927 sources = push
1942 1928 [extensions]
1943 1929 posixgetuser=$TESTTMP/posixgetuser.py
1944 1930 [acl.allow.branches]
1945 1931 * = george
1946 1932 """
1947 1933 pushing to ../b
1948 1934 query 1; heads
1949 1935 searching for changes
1950 1936 all remote heads known locally
1951 1937 listing keys for "phases"
1952 1938 checking for updated bookmarks
1953 1939 listing keys for "bookmarks"
1954 1940 listing keys for "bookmarks"
1955 1941 4 changesets found
1956 1942 list of changesets:
1957 1943 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1958 1944 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1959 1945 911600dab2ae7a9baff75958b84fe606851ce955
1960 1946 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1961 1947 bundle2-output-bundle: "HG20", 5 parts total
1962 1948 bundle2-output-part: "replycaps" 205 bytes payload
1963 1949 bundle2-output-part: "check:phases" 48 bytes payload
1964 1950 bundle2-output-part: "check:heads" streamed payload
1965 1951 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1966 1952 bundle2-output-part: "phase-heads" 48 bytes payload
1967 1953 bundle2-input-bundle: with-transaction
1968 1954 bundle2-input-part: "replycaps" supported
1969 1955 bundle2-input-part: total payload size 205
1970 1956 bundle2-input-part: "check:phases" supported
1971 1957 bundle2-input-part: total payload size 48
1972 1958 bundle2-input-part: "check:heads" supported
1973 1959 bundle2-input-part: total payload size 20
1974 1960 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1975 1961 adding changesets
1976 1962 add changeset ef1ea85a6374
1977 1963 add changeset f9cafe1212c8
1978 1964 add changeset 911600dab2ae
1979 1965 add changeset e8fc755d4d82
1980 1966 adding manifests
1981 1967 adding file changes
1982 1968 adding abc.txt revisions
1983 1969 adding foo/Bar/file.txt revisions
1984 1970 adding foo/file.txt revisions
1985 1971 adding quux/file.py revisions
1986 added 4 changesets with 4 changes to 4 files (+1 heads)
1987 1972 calling hook pretxnchangegroup.acl: hgext.acl.hook
1988 1973 acl: checking access for user "astro"
1989 1974 acl: acl.allow.branches enabled, 0 entries for user astro
1990 1975 acl: acl.deny.branches not enabled
1991 1976 acl: acl.allow not enabled
1992 1977 acl: acl.deny not enabled
1993 1978 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1994 1979 bundle2-input-part: total payload size 2068
1995 1980 bundle2-input-part: total payload size 48
1996 1981 bundle2-input-bundle: 4 parts total
1997 1982 transaction abort!
1998 1983 rollback completed
1999 1984 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
2000 1985 no rollback information available
2001 1986 2:fb35475503ef
2002 1987
2003 1988 $ do_push george
2004 1989 Pushing as user george
2005 1990 hgrc = """
2006 1991 [hooks]
2007 1992 pretxnchangegroup.acl = python:hgext.acl.hook
2008 1993 prepushkey.acl = python:hgext.acl.hook
2009 1994 [acl]
2010 1995 sources = push
2011 1996 [extensions]
2012 1997 posixgetuser=$TESTTMP/posixgetuser.py
2013 1998 [acl.allow.branches]
2014 1999 * = george
2015 2000 """
2016 2001 pushing to ../b
2017 2002 query 1; heads
2018 2003 searching for changes
2019 2004 all remote heads known locally
2020 2005 listing keys for "phases"
2021 2006 checking for updated bookmarks
2022 2007 listing keys for "bookmarks"
2023 2008 listing keys for "bookmarks"
2024 2009 4 changesets found
2025 2010 list of changesets:
2026 2011 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2027 2012 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2028 2013 911600dab2ae7a9baff75958b84fe606851ce955
2029 2014 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2030 2015 bundle2-output-bundle: "HG20", 5 parts total
2031 2016 bundle2-output-part: "replycaps" 205 bytes payload
2032 2017 bundle2-output-part: "check:phases" 48 bytes payload
2033 2018 bundle2-output-part: "check:heads" streamed payload
2034 2019 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2035 2020 bundle2-output-part: "phase-heads" 48 bytes payload
2036 2021 bundle2-input-bundle: with-transaction
2037 2022 bundle2-input-part: "replycaps" supported
2038 2023 bundle2-input-part: total payload size 205
2039 2024 bundle2-input-part: "check:phases" supported
2040 2025 bundle2-input-part: total payload size 48
2041 2026 bundle2-input-part: "check:heads" supported
2042 2027 bundle2-input-part: total payload size 20
2043 2028 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2044 2029 adding changesets
2045 2030 add changeset ef1ea85a6374
2046 2031 add changeset f9cafe1212c8
2047 2032 add changeset 911600dab2ae
2048 2033 add changeset e8fc755d4d82
2049 2034 adding manifests
2050 2035 adding file changes
2051 2036 adding abc.txt revisions
2052 2037 adding foo/Bar/file.txt revisions
2053 2038 adding foo/file.txt revisions
2054 2039 adding quux/file.py revisions
2055 added 4 changesets with 4 changes to 4 files (+1 heads)
2056 2040 calling hook pretxnchangegroup.acl: hgext.acl.hook
2057 2041 acl: checking access for user "george"
2058 2042 acl: acl.allow.branches enabled, 1 entries for user george
2059 2043 acl: acl.deny.branches not enabled
2060 2044 acl: acl.allow not enabled
2061 2045 acl: acl.deny not enabled
2062 2046 acl: branch access granted: "ef1ea85a6374" on branch "default"
2063 2047 acl: path access granted: "ef1ea85a6374"
2064 2048 acl: branch access granted: "f9cafe1212c8" on branch "default"
2065 2049 acl: path access granted: "f9cafe1212c8"
2066 2050 acl: branch access granted: "911600dab2ae" on branch "default"
2067 2051 acl: path access granted: "911600dab2ae"
2068 2052 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2069 2053 acl: path access granted: "e8fc755d4d82"
2070 2054 bundle2-input-part: total payload size 2068
2071 2055 bundle2-input-part: "phase-heads" supported
2072 2056 bundle2-input-part: total payload size 48
2073 2057 bundle2-input-bundle: 4 parts total
2074 2058 updating the branch cache
2059 added 4 changesets with 4 changes to 4 files (+1 heads)
2075 2060 bundle2-output-bundle: "HG20", 1 parts total
2076 2061 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
2077 2062 bundle2-input-bundle: no-transaction
2078 2063 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
2079 2064 bundle2-input-bundle: 0 parts total
2080 2065 listing keys for "phases"
2081 2066 repository tip rolled back to revision 2 (undo push)
2082 2067 2:fb35475503ef
2083 2068
2084 2069
2085 2070 Branch acl conflicting allow
2086 2071 asterisk ends up applying to all branches and allowing george to
2087 2072 push foobar into the remote
2088 2073
2089 2074 $ init_config
2090 2075 $ echo "[acl.allow.branches]" >> $config
2091 2076 $ echo "foobar = astro" >> $config
2092 2077 $ echo "* = george" >> $config
2093 2078 $ do_push george
2094 2079 Pushing as user george
2095 2080 hgrc = """
2096 2081 [hooks]
2097 2082 pretxnchangegroup.acl = python:hgext.acl.hook
2098 2083 prepushkey.acl = python:hgext.acl.hook
2099 2084 [acl]
2100 2085 sources = push
2101 2086 [extensions]
2102 2087 posixgetuser=$TESTTMP/posixgetuser.py
2103 2088 [acl.allow.branches]
2104 2089 foobar = astro
2105 2090 * = george
2106 2091 """
2107 2092 pushing to ../b
2108 2093 query 1; heads
2109 2094 searching for changes
2110 2095 all remote heads known locally
2111 2096 listing keys for "phases"
2112 2097 checking for updated bookmarks
2113 2098 listing keys for "bookmarks"
2114 2099 listing keys for "bookmarks"
2115 2100 4 changesets found
2116 2101 list of changesets:
2117 2102 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2118 2103 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2119 2104 911600dab2ae7a9baff75958b84fe606851ce955
2120 2105 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2121 2106 bundle2-output-bundle: "HG20", 5 parts total
2122 2107 bundle2-output-part: "replycaps" 205 bytes payload
2123 2108 bundle2-output-part: "check:phases" 48 bytes payload
2124 2109 bundle2-output-part: "check:heads" streamed payload
2125 2110 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2126 2111 bundle2-output-part: "phase-heads" 48 bytes payload
2127 2112 bundle2-input-bundle: with-transaction
2128 2113 bundle2-input-part: "replycaps" supported
2129 2114 bundle2-input-part: total payload size 205
2130 2115 bundle2-input-part: "check:phases" supported
2131 2116 bundle2-input-part: total payload size 48
2132 2117 bundle2-input-part: "check:heads" supported
2133 2118 bundle2-input-part: total payload size 20
2134 2119 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2135 2120 adding changesets
2136 2121 add changeset ef1ea85a6374
2137 2122 add changeset f9cafe1212c8
2138 2123 add changeset 911600dab2ae
2139 2124 add changeset e8fc755d4d82
2140 2125 adding manifests
2141 2126 adding file changes
2142 2127 adding abc.txt revisions
2143 2128 adding foo/Bar/file.txt revisions
2144 2129 adding foo/file.txt revisions
2145 2130 adding quux/file.py revisions
2146 added 4 changesets with 4 changes to 4 files (+1 heads)
2147 2131 calling hook pretxnchangegroup.acl: hgext.acl.hook
2148 2132 acl: checking access for user "george"
2149 2133 acl: acl.allow.branches enabled, 1 entries for user george
2150 2134 acl: acl.deny.branches not enabled
2151 2135 acl: acl.allow not enabled
2152 2136 acl: acl.deny not enabled
2153 2137 acl: branch access granted: "ef1ea85a6374" on branch "default"
2154 2138 acl: path access granted: "ef1ea85a6374"
2155 2139 acl: branch access granted: "f9cafe1212c8" on branch "default"
2156 2140 acl: path access granted: "f9cafe1212c8"
2157 2141 acl: branch access granted: "911600dab2ae" on branch "default"
2158 2142 acl: path access granted: "911600dab2ae"
2159 2143 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2160 2144 acl: path access granted: "e8fc755d4d82"
2161 2145 bundle2-input-part: total payload size 2068
2162 2146 bundle2-input-part: "phase-heads" supported
2163 2147 bundle2-input-part: total payload size 48
2164 2148 bundle2-input-bundle: 4 parts total
2165 2149 updating the branch cache
2150 added 4 changesets with 4 changes to 4 files (+1 heads)
2166 2151 bundle2-output-bundle: "HG20", 1 parts total
2167 2152 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
2168 2153 bundle2-input-bundle: no-transaction
2169 2154 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
2170 2155 bundle2-input-bundle: 0 parts total
2171 2156 listing keys for "phases"
2172 2157 repository tip rolled back to revision 2 (undo push)
2173 2158 2:fb35475503ef
2174 2159
2175 2160 Branch acl conflicting deny
2176 2161
2177 2162 $ init_config
2178 2163 $ echo "[acl.deny.branches]" >> $config
2179 2164 $ echo "foobar = astro" >> $config
2180 2165 $ echo "default = astro" >> $config
2181 2166 $ echo "* = george" >> $config
2182 2167 $ do_push george
2183 2168 Pushing as user george
2184 2169 hgrc = """
2185 2170 [hooks]
2186 2171 pretxnchangegroup.acl = python:hgext.acl.hook
2187 2172 prepushkey.acl = python:hgext.acl.hook
2188 2173 [acl]
2189 2174 sources = push
2190 2175 [extensions]
2191 2176 posixgetuser=$TESTTMP/posixgetuser.py
2192 2177 [acl.deny.branches]
2193 2178 foobar = astro
2194 2179 default = astro
2195 2180 * = george
2196 2181 """
2197 2182 pushing to ../b
2198 2183 query 1; heads
2199 2184 searching for changes
2200 2185 all remote heads known locally
2201 2186 listing keys for "phases"
2202 2187 checking for updated bookmarks
2203 2188 listing keys for "bookmarks"
2204 2189 listing keys for "bookmarks"
2205 2190 4 changesets found
2206 2191 list of changesets:
2207 2192 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2208 2193 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2209 2194 911600dab2ae7a9baff75958b84fe606851ce955
2210 2195 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2211 2196 bundle2-output-bundle: "HG20", 5 parts total
2212 2197 bundle2-output-part: "replycaps" 205 bytes payload
2213 2198 bundle2-output-part: "check:phases" 48 bytes payload
2214 2199 bundle2-output-part: "check:heads" streamed payload
2215 2200 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2216 2201 bundle2-output-part: "phase-heads" 48 bytes payload
2217 2202 bundle2-input-bundle: with-transaction
2218 2203 bundle2-input-part: "replycaps" supported
2219 2204 bundle2-input-part: total payload size 205
2220 2205 bundle2-input-part: "check:phases" supported
2221 2206 bundle2-input-part: total payload size 48
2222 2207 bundle2-input-part: "check:heads" supported
2223 2208 bundle2-input-part: total payload size 20
2224 2209 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2225 2210 adding changesets
2226 2211 add changeset ef1ea85a6374
2227 2212 add changeset f9cafe1212c8
2228 2213 add changeset 911600dab2ae
2229 2214 add changeset e8fc755d4d82
2230 2215 adding manifests
2231 2216 adding file changes
2232 2217 adding abc.txt revisions
2233 2218 adding foo/Bar/file.txt revisions
2234 2219 adding foo/file.txt revisions
2235 2220 adding quux/file.py revisions
2236 added 4 changesets with 4 changes to 4 files (+1 heads)
2237 2221 calling hook pretxnchangegroup.acl: hgext.acl.hook
2238 2222 acl: checking access for user "george"
2239 2223 acl: acl.allow.branches not enabled
2240 2224 acl: acl.deny.branches enabled, 1 entries for user george
2241 2225 acl: acl.allow not enabled
2242 2226 acl: acl.deny not enabled
2243 2227 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2244 2228 bundle2-input-part: total payload size 2068
2245 2229 bundle2-input-part: total payload size 48
2246 2230 bundle2-input-bundle: 4 parts total
2247 2231 transaction abort!
2248 2232 rollback completed
2249 2233 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2250 2234 no rollback information available
2251 2235 2:fb35475503ef
2252 2236
2253 2237 User 'astro' must not be denied
2254 2238
2255 2239 $ init_config
2256 2240 $ echo "[acl.deny.branches]" >> $config
2257 2241 $ echo "default = !astro" >> $config
2258 2242 $ do_push astro
2259 2243 Pushing as user astro
2260 2244 hgrc = """
2261 2245 [hooks]
2262 2246 pretxnchangegroup.acl = python:hgext.acl.hook
2263 2247 prepushkey.acl = python:hgext.acl.hook
2264 2248 [acl]
2265 2249 sources = push
2266 2250 [extensions]
2267 2251 posixgetuser=$TESTTMP/posixgetuser.py
2268 2252 [acl.deny.branches]
2269 2253 default = !astro
2270 2254 """
2271 2255 pushing to ../b
2272 2256 query 1; heads
2273 2257 searching for changes
2274 2258 all remote heads known locally
2275 2259 listing keys for "phases"
2276 2260 checking for updated bookmarks
2277 2261 listing keys for "bookmarks"
2278 2262 listing keys for "bookmarks"
2279 2263 4 changesets found
2280 2264 list of changesets:
2281 2265 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2282 2266 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2283 2267 911600dab2ae7a9baff75958b84fe606851ce955
2284 2268 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2285 2269 bundle2-output-bundle: "HG20", 5 parts total
2286 2270 bundle2-output-part: "replycaps" 205 bytes payload
2287 2271 bundle2-output-part: "check:phases" 48 bytes payload
2288 2272 bundle2-output-part: "check:heads" streamed payload
2289 2273 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2290 2274 bundle2-output-part: "phase-heads" 48 bytes payload
2291 2275 bundle2-input-bundle: with-transaction
2292 2276 bundle2-input-part: "replycaps" supported
2293 2277 bundle2-input-part: total payload size 205
2294 2278 bundle2-input-part: "check:phases" supported
2295 2279 bundle2-input-part: total payload size 48
2296 2280 bundle2-input-part: "check:heads" supported
2297 2281 bundle2-input-part: total payload size 20
2298 2282 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2299 2283 adding changesets
2300 2284 add changeset ef1ea85a6374
2301 2285 add changeset f9cafe1212c8
2302 2286 add changeset 911600dab2ae
2303 2287 add changeset e8fc755d4d82
2304 2288 adding manifests
2305 2289 adding file changes
2306 2290 adding abc.txt revisions
2307 2291 adding foo/Bar/file.txt revisions
2308 2292 adding foo/file.txt revisions
2309 2293 adding quux/file.py revisions
2310 added 4 changesets with 4 changes to 4 files (+1 heads)
2311 2294 calling hook pretxnchangegroup.acl: hgext.acl.hook
2312 2295 acl: checking access for user "astro"
2313 2296 acl: acl.allow.branches not enabled
2314 2297 acl: acl.deny.branches enabled, 0 entries for user astro
2315 2298 acl: acl.allow not enabled
2316 2299 acl: acl.deny not enabled
2317 2300 acl: branch access granted: "ef1ea85a6374" on branch "default"
2318 2301 acl: path access granted: "ef1ea85a6374"
2319 2302 acl: branch access granted: "f9cafe1212c8" on branch "default"
2320 2303 acl: path access granted: "f9cafe1212c8"
2321 2304 acl: branch access granted: "911600dab2ae" on branch "default"
2322 2305 acl: path access granted: "911600dab2ae"
2323 2306 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2324 2307 acl: path access granted: "e8fc755d4d82"
2325 2308 bundle2-input-part: total payload size 2068
2326 2309 bundle2-input-part: "phase-heads" supported
2327 2310 bundle2-input-part: total payload size 48
2328 2311 bundle2-input-bundle: 4 parts total
2329 2312 updating the branch cache
2313 added 4 changesets with 4 changes to 4 files (+1 heads)
2330 2314 bundle2-output-bundle: "HG20", 1 parts total
2331 2315 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
2332 2316 bundle2-input-bundle: no-transaction
2333 2317 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
2334 2318 bundle2-input-bundle: 0 parts total
2335 2319 listing keys for "phases"
2336 2320 repository tip rolled back to revision 2 (undo push)
2337 2321 2:fb35475503ef
2338 2322
2339 2323
2340 2324 Non-astro users must be denied
2341 2325
2342 2326 $ do_push george
2343 2327 Pushing as user george
2344 2328 hgrc = """
2345 2329 [hooks]
2346 2330 pretxnchangegroup.acl = python:hgext.acl.hook
2347 2331 prepushkey.acl = python:hgext.acl.hook
2348 2332 [acl]
2349 2333 sources = push
2350 2334 [extensions]
2351 2335 posixgetuser=$TESTTMP/posixgetuser.py
2352 2336 [acl.deny.branches]
2353 2337 default = !astro
2354 2338 """
2355 2339 pushing to ../b
2356 2340 query 1; heads
2357 2341 searching for changes
2358 2342 all remote heads known locally
2359 2343 listing keys for "phases"
2360 2344 checking for updated bookmarks
2361 2345 listing keys for "bookmarks"
2362 2346 listing keys for "bookmarks"
2363 2347 4 changesets found
2364 2348 list of changesets:
2365 2349 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2366 2350 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2367 2351 911600dab2ae7a9baff75958b84fe606851ce955
2368 2352 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2369 2353 bundle2-output-bundle: "HG20", 5 parts total
2370 2354 bundle2-output-part: "replycaps" 205 bytes payload
2371 2355 bundle2-output-part: "check:phases" 48 bytes payload
2372 2356 bundle2-output-part: "check:heads" streamed payload
2373 2357 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2374 2358 bundle2-output-part: "phase-heads" 48 bytes payload
2375 2359 bundle2-input-bundle: with-transaction
2376 2360 bundle2-input-part: "replycaps" supported
2377 2361 bundle2-input-part: total payload size 205
2378 2362 bundle2-input-part: "check:phases" supported
2379 2363 bundle2-input-part: total payload size 48
2380 2364 bundle2-input-part: "check:heads" supported
2381 2365 bundle2-input-part: total payload size 20
2382 2366 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2383 2367 adding changesets
2384 2368 add changeset ef1ea85a6374
2385 2369 add changeset f9cafe1212c8
2386 2370 add changeset 911600dab2ae
2387 2371 add changeset e8fc755d4d82
2388 2372 adding manifests
2389 2373 adding file changes
2390 2374 adding abc.txt revisions
2391 2375 adding foo/Bar/file.txt revisions
2392 2376 adding foo/file.txt revisions
2393 2377 adding quux/file.py revisions
2394 added 4 changesets with 4 changes to 4 files (+1 heads)
2395 2378 calling hook pretxnchangegroup.acl: hgext.acl.hook
2396 2379 acl: checking access for user "george"
2397 2380 acl: acl.allow.branches not enabled
2398 2381 acl: acl.deny.branches enabled, 1 entries for user george
2399 2382 acl: acl.allow not enabled
2400 2383 acl: acl.deny not enabled
2401 2384 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2402 2385 bundle2-input-part: total payload size 2068
2403 2386 bundle2-input-part: total payload size 48
2404 2387 bundle2-input-bundle: 4 parts total
2405 2388 transaction abort!
2406 2389 rollback completed
2407 2390 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2408 2391 no rollback information available
2409 2392 2:fb35475503ef
2410 2393
2411 2394
@@ -1,292 +1,292 b''
1 1 initialize
2 2 $ make_changes() {
3 3 > d=`pwd`
4 4 > [ ! -z $1 ] && cd $1
5 5 > echo "test `basename \`pwd\``" >> test
6 6 > hg commit -Am"${2:-test}"
7 7 > r=$?
8 8 > cd $d
9 9 > return $r
10 10 > }
11 11 $ ls -1a
12 12 .
13 13 ..
14 14 $ hg init a
15 15 $ cd a
16 16 $ echo 'test' > test; hg commit -Am'test'
17 17 adding test
18 18
19 19 clone to b
20 20
21 21 $ mkdir ../b
22 22 $ cd ../b
23 23 $ hg clone ../a .
24 24 updating to branch default
25 25 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
26 26 $ echo "[extensions]" >> .hg/hgrc
27 27 $ echo "bookflow=" >> .hg/hgrc
28 28 $ hg branch X
29 29 abort: creating named branches is disabled and you should use bookmarks
30 30 (see 'hg help bookflow')
31 31 [255]
32 32 $ hg bookmark X
33 33 $ hg bookmarks
34 34 * X 0:* (glob)
35 35 $ hg bookmark X
36 36 abort: bookmark X already exists, to move use the --rev option
37 37 [255]
38 38 $ make_changes
39 39 $ hg push ../a -q
40 40
41 41 $ hg bookmarks
42 42 \* X 1:* (glob)
43 43
44 44 change a
45 45 $ cd ../a
46 46 $ hg up
47 47 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
48 48 $ echo 'test' >> test; hg commit -Am'test'
49 49
50 50
51 51 pull in b
52 52 $ cd ../b
53 53 $ hg pull -u
54 54 pulling from $TESTTMP/a
55 55 searching for changes
56 56 adding changesets
57 57 adding manifests
58 58 adding file changes
59 59 added 1 changesets with 1 changes to 1 files
60 60 new changesets * (glob)
61 61 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
62 62 (leaving bookmark X)
63 63 $ hg status
64 64 $ hg bookmarks
65 65 X 1:* (glob)
66 66
67 67 check protection of @ bookmark
68 68 $ hg bookmark @
69 69 $ hg bookmarks
70 70 \* @ 2:* (glob)
71 71 X 1:* (glob)
72 72 $ make_changes
73 73 abort: cannot commit, bookmark @ is protected
74 74 [255]
75 75
76 76 $ hg status
77 77 M test
78 78 $ hg bookmarks
79 79 \* @ 2:* (glob)
80 80 X 1:* (glob)
81 81
82 82 $ hg --config bookflow.protect= commit -Am"Updated test"
83 83
84 84 $ hg bookmarks
85 85 \* @ 3:* (glob)
86 86 X 1:* (glob)
87 87
88 88 check requirement for an active bookmark
89 89 $ hg bookmark -i
90 90 $ hg bookmarks
91 91 @ 3:* (glob)
92 92 X 1:* (glob)
93 93 $ make_changes
94 94 abort: cannot commit without an active bookmark
95 95 [255]
96 96 $ hg revert test
97 97 $ rm test.orig
98 98 $ hg status
99 99
100 100
101 101 make the bookmark move by updating it on a, and then pulling
102 102 # add a commit to a
103 103 $ cd ../a
104 104 $ hg bookmark X
105 105 $ hg bookmarks
106 106 \* X 2:* (glob)
107 107 $ make_changes
108 108 $ hg bookmarks
109 109 * X 3:81af7977fdb9
110 110
111 111 # go back to b, and check out X
112 112 $ cd ../b
113 113 $ hg up X
114 114 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
115 115 (activating bookmark X)
116 116 $ hg bookmarks
117 117 @ 3:* (glob)
118 118 \* X 1:* (glob)
119 119
120 120 # pull, this should move the bookmark forward, because it was changed remotely
121 121 $ hg pull -u | grep "updating to active bookmark X"
122 122 updating to active bookmark X
123 123
124 124 $ hg bookmarks
125 125 @ 3:* (glob)
126 126 * X 4:81af7977fdb9
127 127
128 128 the bookmark should not move if it diverged from remote
129 129 $ hg -R ../a status
130 130 $ hg -R ../b status
131 131 $ make_changes ../a
132 132 $ make_changes ../b
133 133 $ hg -R ../a status
134 134 $ hg -R ../b status
135 135 $ hg -R ../a bookmarks
136 136 * X 4:238292f60a57
137 137 $ hg -R ../b bookmarks
138 138 @ 3:* (glob)
139 139 * X 5:096f7e86892d
140 140 $ cd ../b
141 141 $ # make sure we cannot push after bookmarks diverged
142 142 $ hg push -B X | grep abort
143 143 abort: push creates new remote head * with bookmark 'X'! (glob)
144 144 (pull and merge or see 'hg help push' for details about pushing new heads)
145 145 [1]
146 146 $ hg pull -u | grep divergent
147 147 divergent bookmark X stored as X@default
148 148 1 other divergent bookmarks for "X"
149 149 $ hg bookmarks
150 150 @ 3:* (glob)
151 151 * X 5:096f7e86892d
152 152 X@default 6:238292f60a57
153 153 $ hg id -in
154 154 096f7e86892d 5
155 155 $ make_changes
156 156 $ hg status
157 157 $ hg bookmarks
158 158 @ 3:* (glob)
159 159 * X 7:227f941aeb07
160 160 X@default 6:238292f60a57
161 161
162 162 now merge with the remote bookmark
163 163 $ hg merge X@default --tool :local -q
164 164 $ hg status
165 165 M test
166 166 $ hg commit -m"Merged with X@default"
167 167 $ hg bookmarks
168 168 @ 3:* (glob)
169 169 * X 8:26fed9bb3219
170 170 $ hg push -B X | grep bookmark
171 171 pushing to $TESTTMP/a (?)
172 172 updating bookmark X
173 173 $ cd ../a
174 174 $ hg up -q
175 175 $ hg bookmarks
176 176 * X 7:26fed9bb3219
177 177
178 178 test hg pull when there is more than one descendant
179 179 $ cd ../a
180 180 $ hg bookmark Z
181 181 $ hg bookmark Y
182 182 $ make_changes . YY
183 183 $ hg up Z -q
184 184 $ make_changes . ZZ
185 185 created new head
186 186 $ hg bookmarks
187 187 X 7:26fed9bb3219
188 188 Y 8:131e663dbd2a
189 189 * Z 9:b74a4149df25
190 190 $ hg log -r 'p1(Y)' -r 'p1(Z)' -T '{rev}\n' # prove that Y and Z share the same parent
191 191 7
192 192 $ hg log -r 'Y%Z' -T '{rev}\n' # revs in Y but not in Z
193 193 8
194 194 $ hg log -r 'Z%Y' -T '{rev}\n' # revs in Z but not in Y
195 195 9
196 196 $ cd ../b
197 197 $ hg pull -uq
198 198 $ hg id
199 199 b74a4149df25 tip Z
200 200 $ hg bookmarks | grep \* # no active bookmark
201 201 [1]
202 202
203 203
204 204 test shelving
205 205 $ cd ../a
206 206 $ echo anotherfile > anotherfile # this change should not conflict
207 207 $ hg add anotherfile
208 208 $ hg commit -m"Change in a"
209 209 $ cd ../b
210 210 $ hg up Z | grep Z
211 211 (activating bookmark Z)
212 212 $ hg book | grep \* # make sure active bookmark
213 213 \* Z 10:* (glob)
214 214 $ echo "test b" >> test
215 215 $ hg diff --stat
216 216 test | 1 +
217 217 1 files changed, 1 insertions(+), 0 deletions(-)
218 218 $ hg shelve
219 219 shelved as Z
220 220 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
221 221 $ hg pull -uq
222 222 $ hg unshelve
223 223 unshelving change 'Z'
224 224 rebasing shelved changes
225 225 $ hg diff --stat
226 226 test | 1 +
227 227 1 files changed, 1 insertions(+), 0 deletions(-)
228 228
229 229
230 230 make the bookmark move by updating it on a, and then pulling with a local change
231 231 # add a commit to a
232 232 $ cd ../a
233 233 $ hg up -C X |fgrep "activating bookmark X"
234 234 (activating bookmark X)
235 235 # go back to b, and check out X
236 236 $ cd ../b
237 237 $ hg up -C X |fgrep "activating bookmark X"
238 238 (activating bookmark X)
239 239 # update and push from a
240 240 $ make_changes ../a
241 241 created new head
242 242 $ echo "more" >> test
243 243 $ hg pull -u 2>&1 | fgrep -v TESTTMP| fgrep -v "searching for changes" | fgrep -v adding
244 244 pulling from $TESTTMP/a
245 updating bookmark X
245 246 added 1 changesets with 0 changes to 0 files (+1 heads)
246 updating bookmark X
247 247 new changesets * (glob)
248 248 updating to active bookmark X
249 249 merging test
250 250 warning: conflicts while merging test! (edit, then use 'hg resolve --mark')
251 251 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
252 252 use 'hg resolve' to retry unresolved file merges
253 253 $ hg update -Cq
254 254 $ rm test.orig
255 255
256 256 make sure that commits aren't possible if working directory is not pointing to active bookmark
257 257 $ hg -R ../a status
258 258 $ hg -R ../b status
259 259 $ hg -R ../a id -i
260 260 36a6e592ec06
261 261 $ hg -R ../a book | grep X
262 262 \* X \d+:36a6e592ec06 (re)
263 263 $ hg -R ../b id -i
264 264 36a6e592ec06
265 265 $ hg -R ../b book | grep X
266 266 \* X \d+:36a6e592ec06 (re)
267 267 $ make_changes ../a
268 268 $ hg -R ../a book | grep X
269 269 \* X \d+:f73a71c992b8 (re)
270 270 $ cd ../b
271 271 $ hg pull 2>&1 | grep -v add | grep -v pulling | grep -v searching | grep -v changeset
272 272 updating bookmark X
273 273 (run 'hg update' to get a working copy)
274 274 working directory out of sync with active bookmark, run 'hg up X'
275 275 $ hg id -i # we're still on the old commit
276 276 36a6e592ec06
277 277 $ hg book | grep X # while the bookmark moved
278 278 \* X \d+:f73a71c992b8 (re)
279 279 $ make_changes
280 280 abort: cannot commit, working directory out of sync with active bookmark
281 281 (run 'hg up X')
282 282 [255]
283 283 $ hg up -Cq -r . # cleanup local changes
284 284 $ hg status
285 285 $ hg id -i # we're still on the old commit
286 286 36a6e592ec06
287 287 $ hg up X -q
288 288 $ hg id -i # now we're on X
289 289 f73a71c992b8
290 290 $ hg book | grep X
291 291 \* X \d+:f73a71c992b8 (re)
292 292
@@ -1,1355 +1,1355 b''
1 1 #testcases b2-pushkey b2-binary
2 2
3 3 #if b2-pushkey
4 4 $ cat << EOF >> $HGRCPATH
5 5 > [devel]
6 6 > legacy.exchange=bookmarks
7 7 > EOF
8 8 #endif
9 9
10 10 #require serve
11 11
12 12 $ cat << EOF >> $HGRCPATH
13 13 > [ui]
14 14 > logtemplate={rev}:{node|short} {desc|firstline}
15 15 > [phases]
16 16 > publish=False
17 17 > [experimental]
18 18 > evolution.createmarkers=True
19 19 > evolution.exchange=True
20 20 > EOF
21 21
22 22 $ cat > $TESTTMP/hook.sh <<'EOF'
23 23 > echo "test-hook-bookmark: $HG_BOOKMARK: $HG_OLDNODE -> $HG_NODE"
24 24 > EOF
25 25 $ TESTHOOK="hooks.txnclose-bookmark.test=sh $TESTTMP/hook.sh"
26 26
27 27 initialize
28 28
29 29 $ hg init a
30 30 $ cd a
31 31 $ echo 'test' > test
32 32 $ hg commit -Am'test'
33 33 adding test
34 34
35 35 set bookmarks
36 36
37 37 $ hg bookmark X
38 38 $ hg bookmark Y
39 39 $ hg bookmark Z
40 40
41 41 import bookmark by name
42 42
43 43 $ hg init ../b
44 44 $ cd ../b
45 45 $ hg book Y
46 46 $ hg book
47 47 * Y -1:000000000000
48 48 $ hg pull ../a --config "$TESTHOOK"
49 49 pulling from ../a
50 50 requesting all changes
51 51 adding changesets
52 52 adding manifests
53 53 adding file changes
54 added 1 changesets with 1 changes to 1 files
55 54 adding remote bookmark X
56 55 updating bookmark Y
57 56 adding remote bookmark Z
57 added 1 changesets with 1 changes to 1 files
58 58 new changesets 4e3505fd9583 (1 drafts)
59 59 test-hook-bookmark: X: -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
60 60 test-hook-bookmark: Y: 0000000000000000000000000000000000000000 -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
61 61 test-hook-bookmark: Z: -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
62 62 (run 'hg update' to get a working copy)
63 63 $ hg bookmarks
64 64 X 0:4e3505fd9583
65 65 * Y 0:4e3505fd9583
66 66 Z 0:4e3505fd9583
67 67 $ hg debugpushkey ../a namespaces
68 68 bookmarks
69 69 namespaces
70 70 obsolete
71 71 phases
72 72 $ hg debugpushkey ../a bookmarks
73 73 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
74 74 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
75 75 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
76 76
77 77 delete the bookmark to re-pull it
78 78
79 79 $ hg book -d X
80 80 $ hg pull -B X ../a
81 81 pulling from ../a
82 82 no changes found
83 83 adding remote bookmark X
84 84
85 85 finally no-op pull
86 86
87 87 $ hg pull -B X ../a
88 88 pulling from ../a
89 89 no changes found
90 90 $ hg bookmark
91 91 X 0:4e3505fd9583
92 92 * Y 0:4e3505fd9583
93 93 Z 0:4e3505fd9583
94 94
95 95 export bookmark by name
96 96
97 97 $ hg bookmark W
98 98 $ hg bookmark foo
99 99 $ hg bookmark foobar
100 100 $ hg push -B W ../a
101 101 pushing to ../a
102 102 searching for changes
103 103 no changes found
104 104 exporting bookmark W
105 105 [1]
106 106 $ hg -R ../a bookmarks
107 107 W -1:000000000000
108 108 X 0:4e3505fd9583
109 109 Y 0:4e3505fd9583
110 110 * Z 0:4e3505fd9583
111 111
112 112 delete a remote bookmark
113 113
114 114 $ hg book -d W
115 115
116 116 #if b2-pushkey
117 117
118 118 $ hg push -B W ../a --config "$TESTHOOK" --debug --config devel.bundle2.debug=yes
119 119 pushing to ../a
120 120 query 1; heads
121 121 searching for changes
122 122 all remote heads known locally
123 123 listing keys for "phases"
124 124 checking for updated bookmarks
125 125 listing keys for "bookmarks"
126 126 no changes found
127 127 bundle2-output-bundle: "HG20", 4 parts total
128 128 bundle2-output: start emission of HG20 stream
129 129 bundle2-output: bundle parameter:
130 130 bundle2-output: start of parts
131 131 bundle2-output: bundle part: "replycaps"
132 132 bundle2-output-part: "replycaps" 222 bytes payload
133 133 bundle2-output: part 0: "REPLYCAPS"
134 134 bundle2-output: header chunk size: 16
135 135 bundle2-output: payload chunk size: 222
136 136 bundle2-output: closing payload chunk
137 137 bundle2-output: bundle part: "check:bookmarks"
138 138 bundle2-output-part: "check:bookmarks" 23 bytes payload
139 139 bundle2-output: part 1: "CHECK:BOOKMARKS"
140 140 bundle2-output: header chunk size: 22
141 141 bundle2-output: payload chunk size: 23
142 142 bundle2-output: closing payload chunk
143 143 bundle2-output: bundle part: "check:phases"
144 144 bundle2-output-part: "check:phases" 24 bytes payload
145 145 bundle2-output: part 2: "CHECK:PHASES"
146 146 bundle2-output: header chunk size: 19
147 147 bundle2-output: payload chunk size: 24
148 148 bundle2-output: closing payload chunk
149 149 bundle2-output: bundle part: "pushkey"
150 150 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
151 151 bundle2-output: part 3: "PUSHKEY"
152 152 bundle2-output: header chunk size: 90
153 153 bundle2-output: closing payload chunk
154 154 bundle2-output: end of bundle
155 155 bundle2-input: start processing of HG20 stream
156 156 bundle2-input: reading bundle2 stream parameters
157 157 bundle2-input-bundle: with-transaction
158 158 bundle2-input: start extraction of bundle2 parts
159 159 bundle2-input: part header size: 16
160 160 bundle2-input: part type: "REPLYCAPS"
161 161 bundle2-input: part id: "0"
162 162 bundle2-input: part parameters: 0
163 163 bundle2-input: found a handler for part replycaps
164 164 bundle2-input-part: "replycaps" supported
165 165 bundle2-input: payload chunk size: 222
166 166 bundle2-input: payload chunk size: 0
167 167 bundle2-input-part: total payload size 222
168 168 bundle2-input: part header size: 22
169 169 bundle2-input: part type: "CHECK:BOOKMARKS"
170 170 bundle2-input: part id: "1"
171 171 bundle2-input: part parameters: 0
172 172 bundle2-input: found a handler for part check:bookmarks
173 173 bundle2-input-part: "check:bookmarks" supported
174 174 bundle2-input: payload chunk size: 23
175 175 bundle2-input: payload chunk size: 0
176 176 bundle2-input-part: total payload size 23
177 177 bundle2-input: part header size: 19
178 178 bundle2-input: part type: "CHECK:PHASES"
179 179 bundle2-input: part id: "2"
180 180 bundle2-input: part parameters: 0
181 181 bundle2-input: found a handler for part check:phases
182 182 bundle2-input-part: "check:phases" supported
183 183 bundle2-input: payload chunk size: 24
184 184 bundle2-input: payload chunk size: 0
185 185 bundle2-input-part: total payload size 24
186 186 bundle2-input: part header size: 90
187 187 bundle2-input: part type: "PUSHKEY"
188 188 bundle2-input: part id: "3"
189 189 bundle2-input: part parameters: 4
190 190 bundle2-input: found a handler for part pushkey
191 191 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
192 192 pushing key for "bookmarks:W"
193 193 bundle2-input: payload chunk size: 0
194 194 bundle2-input: part header size: 0
195 195 bundle2-input: end of bundle2 stream
196 196 bundle2-input-bundle: 3 parts total
197 197 running hook txnclose-bookmark.test: sh $TESTTMP/hook.sh
198 198 test-hook-bookmark: W: 0000000000000000000000000000000000000000 ->
199 199 bundle2-output-bundle: "HG20", 1 parts total
200 200 bundle2-output: start emission of HG20 stream
201 201 bundle2-output: bundle parameter:
202 202 bundle2-output: start of parts
203 203 bundle2-output: bundle part: "reply:pushkey"
204 204 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
205 205 bundle2-output: part 0: "REPLY:PUSHKEY"
206 206 bundle2-output: header chunk size: 43
207 207 bundle2-output: closing payload chunk
208 208 bundle2-output: end of bundle
209 209 bundle2-input: start processing of HG20 stream
210 210 bundle2-input: reading bundle2 stream parameters
211 211 bundle2-input-bundle: no-transaction
212 212 bundle2-input: start extraction of bundle2 parts
213 213 bundle2-input: part header size: 43
214 214 bundle2-input: part type: "REPLY:PUSHKEY"
215 215 bundle2-input: part id: "0"
216 216 bundle2-input: part parameters: 2
217 217 bundle2-input: found a handler for part reply:pushkey
218 218 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
219 219 bundle2-input: payload chunk size: 0
220 220 bundle2-input: part header size: 0
221 221 bundle2-input: end of bundle2 stream
222 222 bundle2-input-bundle: 0 parts total
223 223 deleting remote bookmark W
224 224 listing keys for "phases"
225 225 [1]
226 226
227 227 #endif
228 228 #if b2-binary
229 229
230 230 $ hg push -B W ../a --config "$TESTHOOK" --debug --config devel.bundle2.debug=yes
231 231 pushing to ../a
232 232 query 1; heads
233 233 searching for changes
234 234 all remote heads known locally
235 235 listing keys for "phases"
236 236 checking for updated bookmarks
237 237 listing keys for "bookmarks"
238 238 no changes found
239 239 bundle2-output-bundle: "HG20", 4 parts total
240 240 bundle2-output: start emission of HG20 stream
241 241 bundle2-output: bundle parameter:
242 242 bundle2-output: start of parts
243 243 bundle2-output: bundle part: "replycaps"
244 244 bundle2-output-part: "replycaps" 222 bytes payload
245 245 bundle2-output: part 0: "REPLYCAPS"
246 246 bundle2-output: header chunk size: 16
247 247 bundle2-output: payload chunk size: 222
248 248 bundle2-output: closing payload chunk
249 249 bundle2-output: bundle part: "check:bookmarks"
250 250 bundle2-output-part: "check:bookmarks" 23 bytes payload
251 251 bundle2-output: part 1: "CHECK:BOOKMARKS"
252 252 bundle2-output: header chunk size: 22
253 253 bundle2-output: payload chunk size: 23
254 254 bundle2-output: closing payload chunk
255 255 bundle2-output: bundle part: "check:phases"
256 256 bundle2-output-part: "check:phases" 24 bytes payload
257 257 bundle2-output: part 2: "CHECK:PHASES"
258 258 bundle2-output: header chunk size: 19
259 259 bundle2-output: payload chunk size: 24
260 260 bundle2-output: closing payload chunk
261 261 bundle2-output: bundle part: "bookmarks"
262 262 bundle2-output-part: "bookmarks" 23 bytes payload
263 263 bundle2-output: part 3: "BOOKMARKS"
264 264 bundle2-output: header chunk size: 16
265 265 bundle2-output: payload chunk size: 23
266 266 bundle2-output: closing payload chunk
267 267 bundle2-output: end of bundle
268 268 bundle2-input: start processing of HG20 stream
269 269 bundle2-input: reading bundle2 stream parameters
270 270 bundle2-input-bundle: with-transaction
271 271 bundle2-input: start extraction of bundle2 parts
272 272 bundle2-input: part header size: 16
273 273 bundle2-input: part type: "REPLYCAPS"
274 274 bundle2-input: part id: "0"
275 275 bundle2-input: part parameters: 0
276 276 bundle2-input: found a handler for part replycaps
277 277 bundle2-input-part: "replycaps" supported
278 278 bundle2-input: payload chunk size: 222
279 279 bundle2-input: payload chunk size: 0
280 280 bundle2-input-part: total payload size 222
281 281 bundle2-input: part header size: 22
282 282 bundle2-input: part type: "CHECK:BOOKMARKS"
283 283 bundle2-input: part id: "1"
284 284 bundle2-input: part parameters: 0
285 285 bundle2-input: found a handler for part check:bookmarks
286 286 bundle2-input-part: "check:bookmarks" supported
287 287 bundle2-input: payload chunk size: 23
288 288 bundle2-input: payload chunk size: 0
289 289 bundle2-input-part: total payload size 23
290 290 bundle2-input: part header size: 19
291 291 bundle2-input: part type: "CHECK:PHASES"
292 292 bundle2-input: part id: "2"
293 293 bundle2-input: part parameters: 0
294 294 bundle2-input: found a handler for part check:phases
295 295 bundle2-input-part: "check:phases" supported
296 296 bundle2-input: payload chunk size: 24
297 297 bundle2-input: payload chunk size: 0
298 298 bundle2-input-part: total payload size 24
299 299 bundle2-input: part header size: 16
300 300 bundle2-input: part type: "BOOKMARKS"
301 301 bundle2-input: part id: "3"
302 302 bundle2-input: part parameters: 0
303 303 bundle2-input: found a handler for part bookmarks
304 304 bundle2-input-part: "bookmarks" supported
305 305 bundle2-input: payload chunk size: 23
306 306 bundle2-input: payload chunk size: 0
307 307 bundle2-input-part: total payload size 23
308 308 bundle2-input: part header size: 0
309 309 bundle2-input: end of bundle2 stream
310 310 bundle2-input-bundle: 3 parts total
311 311 running hook txnclose-bookmark.test: sh $TESTTMP/hook.sh
312 312 test-hook-bookmark: W: 0000000000000000000000000000000000000000 ->
313 313 bundle2-output-bundle: "HG20", 0 parts total
314 314 bundle2-output: start emission of HG20 stream
315 315 bundle2-output: bundle parameter:
316 316 bundle2-output: start of parts
317 317 bundle2-output: end of bundle
318 318 bundle2-input: start processing of HG20 stream
319 319 bundle2-input: reading bundle2 stream parameters
320 320 bundle2-input-bundle: no-transaction
321 321 bundle2-input: start extraction of bundle2 parts
322 322 bundle2-input: part header size: 0
323 323 bundle2-input: end of bundle2 stream
324 324 bundle2-input-bundle: 0 parts total
325 325 deleting remote bookmark W
326 326 listing keys for "phases"
327 327 [1]
328 328
329 329 #endif
330 330
331 331 export the active bookmark
332 332
333 333 $ hg bookmark V
334 334 $ hg push -B . ../a
335 335 pushing to ../a
336 336 searching for changes
337 337 no changes found
338 338 exporting bookmark V
339 339 [1]
340 340
341 341 exporting the active bookmark with 'push -B .'
342 342 demand that one of the bookmarks is activated
343 343
344 344 $ hg update -r default
345 345 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
346 346 (leaving bookmark V)
347 347 $ hg push -B . ../a
348 348 abort: no active bookmark!
349 349 [255]
350 350 $ hg update -r V
351 351 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
352 352 (activating bookmark V)
353 353
354 354 delete the bookmark
355 355
356 356 $ hg book -d V
357 357 $ hg push -B V ../a
358 358 pushing to ../a
359 359 searching for changes
360 360 no changes found
361 361 deleting remote bookmark V
362 362 [1]
363 363 $ hg up foobar
364 364 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 365 (activating bookmark foobar)
366 366
367 367 push/pull name that doesn't exist
368 368
369 369 $ hg push -B badname ../a
370 370 pushing to ../a
371 371 searching for changes
372 372 bookmark badname does not exist on the local or remote repository!
373 373 no changes found
374 374 [2]
375 375 $ hg pull -B anotherbadname ../a
376 376 pulling from ../a
377 377 abort: remote bookmark anotherbadname not found!
378 378 [255]
379 379
380 380 divergent bookmarks
381 381
382 382 $ cd ../a
383 383 $ echo c1 > f1
384 384 $ hg ci -Am1
385 385 adding f1
386 386 $ hg book -f @
387 387 $ hg book -f X
388 388 $ hg book
389 389 @ 1:0d2164f0ce0d
390 390 * X 1:0d2164f0ce0d
391 391 Y 0:4e3505fd9583
392 392 Z 1:0d2164f0ce0d
393 393
394 394 $ cd ../b
395 395 $ hg up
396 396 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
397 397 updating bookmark foobar
398 398 $ echo c2 > f2
399 399 $ hg ci -Am2
400 400 adding f2
401 401 $ hg book -if @
402 402 $ hg book -if X
403 403 $ hg book
404 404 @ 1:9b140be10808
405 405 X 1:9b140be10808
406 406 Y 0:4e3505fd9583
407 407 Z 0:4e3505fd9583
408 408 foo -1:000000000000
409 409 * foobar 1:9b140be10808
410 410
411 411 $ hg pull --config paths.foo=../a foo --config "$TESTHOOK"
412 412 pulling from $TESTTMP/a
413 413 searching for changes
414 414 adding changesets
415 415 adding manifests
416 416 adding file changes
417 added 1 changesets with 1 changes to 1 files (+1 heads)
418 417 divergent bookmark @ stored as @foo
419 418 divergent bookmark X stored as X@foo
420 419 updating bookmark Z
420 added 1 changesets with 1 changes to 1 files (+1 heads)
421 421 new changesets 0d2164f0ce0d (1 drafts)
422 422 test-hook-bookmark: @foo: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
423 423 test-hook-bookmark: X@foo: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
424 424 test-hook-bookmark: Z: 4e3505fd95835d721066b76e75dbb8cc554d7f77 -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
425 425 (run 'hg heads' to see heads, 'hg merge' to merge)
426 426 $ hg book
427 427 @ 1:9b140be10808
428 428 @foo 2:0d2164f0ce0d
429 429 X 1:9b140be10808
430 430 X@foo 2:0d2164f0ce0d
431 431 Y 0:4e3505fd9583
432 432 Z 2:0d2164f0ce0d
433 433 foo -1:000000000000
434 434 * foobar 1:9b140be10808
435 435
436 436 (test that too many divergence of bookmark)
437 437
438 438 $ "$PYTHON" $TESTDIR/seq.py 1 100 | while read i; do hg bookmarks -r 000000000000 "X@${i}"; done
439 439 $ hg pull ../a
440 440 pulling from ../a
441 441 searching for changes
442 442 no changes found
443 443 warning: failed to assign numbered name to divergent bookmark X
444 444 divergent bookmark @ stored as @1
445 445 $ hg bookmarks | grep '^ X' | grep -v ':000000000000'
446 446 X 1:9b140be10808
447 447 X@foo 2:0d2164f0ce0d
448 448
449 449 (test that remotely diverged bookmarks are reused if they aren't changed)
450 450
451 451 $ hg bookmarks | grep '^ @'
452 452 @ 1:9b140be10808
453 453 @1 2:0d2164f0ce0d
454 454 @foo 2:0d2164f0ce0d
455 455 $ hg pull ../a
456 456 pulling from ../a
457 457 searching for changes
458 458 no changes found
459 459 warning: failed to assign numbered name to divergent bookmark X
460 460 divergent bookmark @ stored as @1
461 461 $ hg bookmarks | grep '^ @'
462 462 @ 1:9b140be10808
463 463 @1 2:0d2164f0ce0d
464 464 @foo 2:0d2164f0ce0d
465 465
466 466 $ "$PYTHON" $TESTDIR/seq.py 1 100 | while read i; do hg bookmarks -d "X@${i}"; done
467 467 $ hg bookmarks -d "@1"
468 468
469 469 $ hg push -f ../a
470 470 pushing to ../a
471 471 searching for changes
472 472 adding changesets
473 473 adding manifests
474 474 adding file changes
475 475 added 1 changesets with 1 changes to 1 files (+1 heads)
476 476 $ hg -R ../a book
477 477 @ 1:0d2164f0ce0d
478 478 * X 1:0d2164f0ce0d
479 479 Y 0:4e3505fd9583
480 480 Z 1:0d2164f0ce0d
481 481
482 482 explicit pull should overwrite the local version (issue4439)
483 483
484 484 $ hg update -r X
485 485 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
486 486 (activating bookmark X)
487 487 $ hg pull --config paths.foo=../a foo -B . --config "$TESTHOOK"
488 488 pulling from $TESTTMP/a
489 489 no changes found
490 490 divergent bookmark @ stored as @foo
491 491 importing bookmark X
492 492 test-hook-bookmark: @foo: 0d2164f0ce0d8f1d6f94351eba04b794909be66c -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
493 493 test-hook-bookmark: X: 9b140be1080824d768c5a4691a564088eede71f9 -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
494 494
495 495 reinstall state for further testing:
496 496
497 497 $ hg book -fr 9b140be10808 X
498 498
499 499 revsets should not ignore divergent bookmarks
500 500
501 501 $ hg bookmark -fr 1 Z
502 502 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
503 503 0:4e3505fd9583 Y
504 504 1:9b140be10808 @ X Z foobar
505 505 2:0d2164f0ce0d @foo X@foo
506 506 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
507 507 2:0d2164f0ce0d @foo X@foo
508 508 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
509 509 2:0d2164f0ce0d @foo X@foo
510 510
511 511 update a remote bookmark from a non-head to a head
512 512
513 513 $ hg up -q Y
514 514 $ echo c3 > f2
515 515 $ hg ci -Am3
516 516 adding f2
517 517 created new head
518 518 $ hg push ../a --config "$TESTHOOK"
519 519 pushing to ../a
520 520 searching for changes
521 521 adding changesets
522 522 adding manifests
523 523 adding file changes
524 524 added 1 changesets with 1 changes to 1 files (+1 heads)
525 525 test-hook-bookmark: Y: 4e3505fd95835d721066b76e75dbb8cc554d7f77 -> f6fc62dde3c0771e29704af56ba4d8af77abcc2f
526 526 updating bookmark Y
527 527 $ hg -R ../a book
528 528 @ 1:0d2164f0ce0d
529 529 * X 1:0d2164f0ce0d
530 530 Y 3:f6fc62dde3c0
531 531 Z 1:0d2164f0ce0d
532 532
533 533 update a bookmark in the middle of a client pulling changes
534 534
535 535 $ cd ..
536 536 $ hg clone -q a pull-race
537 537
538 538 We want to use http because it is stateless and therefore more susceptible to
539 539 race conditions
540 540
541 541 $ hg serve -R pull-race -p $HGPORT -d --pid-file=pull-race.pid -E main-error.log
542 542 $ cat pull-race.pid >> $DAEMON_PIDS
543 543
544 544 $ cat <<EOF > $TESTTMP/out_makecommit.sh
545 545 > #!/bin/sh
546 546 > hg ci -Am5
547 547 > echo committed in pull-race
548 548 > EOF
549 549
550 550 $ hg clone -q http://localhost:$HGPORT/ pull-race2 --config "$TESTHOOK"
551 551 test-hook-bookmark: @: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
552 552 test-hook-bookmark: X: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
553 553 test-hook-bookmark: Y: -> f6fc62dde3c0771e29704af56ba4d8af77abcc2f
554 554 test-hook-bookmark: Z: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
555 555 $ cd pull-race
556 556 $ hg up -q Y
557 557 $ echo c4 > f2
558 558 $ hg ci -Am4
559 559 $ echo c5 > f3
560 560 $ cat <<EOF > .hg/hgrc
561 561 > [hooks]
562 562 > outgoing.makecommit = sh $TESTTMP/out_makecommit.sh
563 563 > EOF
564 564
565 565 (new config needs a server restart)
566 566
567 567 $ cd ..
568 568 $ killdaemons.py
569 569 $ hg serve -R pull-race -p $HGPORT -d --pid-file=pull-race.pid -E main-error.log
570 570 $ cat pull-race.pid >> $DAEMON_PIDS
571 571 $ cd pull-race2
572 572 $ hg -R $TESTTMP/pull-race book
573 573 @ 1:0d2164f0ce0d
574 574 X 1:0d2164f0ce0d
575 575 * Y 4:b0a5eff05604
576 576 Z 1:0d2164f0ce0d
577 577 $ hg pull
578 578 pulling from http://localhost:$HGPORT/
579 579 searching for changes
580 580 adding changesets
581 581 adding manifests
582 582 adding file changes
583 updating bookmark Y
583 584 added 1 changesets with 1 changes to 1 files
584 updating bookmark Y
585 585 new changesets b0a5eff05604 (1 drafts)
586 586 (run 'hg update' to get a working copy)
587 587 $ hg book
588 588 * @ 1:0d2164f0ce0d
589 589 X 1:0d2164f0ce0d
590 590 Y 4:b0a5eff05604
591 591 Z 1:0d2164f0ce0d
592 592
593 593 Update a bookmark right after the initial lookup -B (issue4689)
594 594
595 595 $ echo c6 > ../pull-race/f3 # to be committed during the race
596 596 $ cat <<EOF > $TESTTMP/listkeys_makecommit.sh
597 597 > #!/bin/sh
598 598 > if hg st | grep -q M; then
599 599 > hg commit -m race
600 600 > echo committed in pull-race
601 601 > else
602 602 > exit 0
603 603 > fi
604 604 > EOF
605 605 $ cat <<EOF > ../pull-race/.hg/hgrc
606 606 > [hooks]
607 607 > # If anything to commit, commit it right after the first key listing used
608 608 > # during lookup. This makes the commit appear before the actual getbundle
609 609 > # call.
610 610 > listkeys.makecommit= sh $TESTTMP/listkeys_makecommit.sh
611 611 > EOF
612 612 $ restart_server() {
613 613 > "$TESTDIR/killdaemons.py" $DAEMON_PIDS
614 614 > hg serve -R ../pull-race -p $HGPORT -d --pid-file=../pull-race.pid -E main-error.log
615 615 > cat ../pull-race.pid >> $DAEMON_PIDS
616 616 > }
617 617 $ restart_server # new config need server restart
618 618 $ hg -R $TESTTMP/pull-race book
619 619 @ 1:0d2164f0ce0d
620 620 X 1:0d2164f0ce0d
621 621 * Y 5:35d1ef0a8d1b
622 622 Z 1:0d2164f0ce0d
623 623 $ hg update -r Y
624 624 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
625 625 (activating bookmark Y)
626 626 $ hg pull -B .
627 627 pulling from http://localhost:$HGPORT/
628 628 searching for changes
629 629 adding changesets
630 630 adding manifests
631 631 adding file changes
632 updating bookmark Y
632 633 added 1 changesets with 1 changes to 1 files
633 updating bookmark Y
634 634 new changesets 35d1ef0a8d1b (1 drafts)
635 635 (run 'hg update' to get a working copy)
636 636 $ hg book
637 637 @ 1:0d2164f0ce0d
638 638 X 1:0d2164f0ce0d
639 639 * Y 5:35d1ef0a8d1b
640 640 Z 1:0d2164f0ce0d
641 641
642 642 Update a bookmark right after the initial lookup -r (issue4700)
643 643
644 644 $ echo c7 > ../pull-race/f3 # to be committed during the race
645 645 $ cat <<EOF > ../lookuphook.py
646 646 > """small extensions adding a hook after wireprotocol lookup to test race"""
647 647 > import functools
648 648 > from mercurial import wireprotov1server, wireprotov2server
649 649 >
650 650 > def wrappedlookup(orig, repo, *args, **kwargs):
651 651 > ret = orig(repo, *args, **kwargs)
652 652 > repo.hook(b'lookup')
653 653 > return ret
654 654 > for table in [wireprotov1server.commands, wireprotov2server.COMMANDS]:
655 655 > table[b'lookup'].func = functools.partial(wrappedlookup, table[b'lookup'].func)
656 656 > EOF
657 657 $ cat <<EOF > ../pull-race/.hg/hgrc
658 658 > [extensions]
659 659 > lookuphook=$TESTTMP/lookuphook.py
660 660 > [hooks]
661 661 > lookup.makecommit= sh $TESTTMP/listkeys_makecommit.sh
662 662 > EOF
663 663 $ restart_server # new config need server restart
664 664 $ hg -R $TESTTMP/pull-race book
665 665 @ 1:0d2164f0ce0d
666 666 X 1:0d2164f0ce0d
667 667 * Y 6:0d60821d2197
668 668 Z 1:0d2164f0ce0d
669 669 $ hg pull -r Y
670 670 pulling from http://localhost:$HGPORT/
671 671 searching for changes
672 672 adding changesets
673 673 adding manifests
674 674 adding file changes
675 updating bookmark Y
675 676 added 1 changesets with 1 changes to 1 files
676 updating bookmark Y
677 677 new changesets 0d60821d2197 (1 drafts)
678 678 (run 'hg update' to get a working copy)
679 679 $ hg book
680 680 @ 1:0d2164f0ce0d
681 681 X 1:0d2164f0ce0d
682 682 * Y 6:0d60821d2197
683 683 Z 1:0d2164f0ce0d
684 684 $ hg -R $TESTTMP/pull-race book
685 685 @ 1:0d2164f0ce0d
686 686 X 1:0d2164f0ce0d
687 687 * Y 7:714424d9e8b8
688 688 Z 1:0d2164f0ce0d
689 689
690 690 (done with this section of the test)
691 691
692 692 $ killdaemons.py
693 693 $ cd ../b
694 694
695 695 diverging a remote bookmark fails
696 696
697 697 $ hg up -q 4e3505fd9583
698 698 $ echo c4 > f2
699 699 $ hg ci -Am4
700 700 adding f2
701 701 created new head
702 702 $ echo c5 > f2
703 703 $ hg ci -Am5
704 704 $ hg log -G
705 705 @ 5:c922c0139ca0 5
706 706 |
707 707 o 4:4efff6d98829 4
708 708 |
709 709 | o 3:f6fc62dde3c0 3
710 710 |/
711 711 | o 2:0d2164f0ce0d 1
712 712 |/
713 713 | o 1:9b140be10808 2
714 714 |/
715 715 o 0:4e3505fd9583 test
716 716
717 717
718 718 $ hg book -f Y
719 719
720 720 $ cat <<EOF > ../a/.hg/hgrc
721 721 > [web]
722 722 > push_ssl = false
723 723 > allow_push = *
724 724 > EOF
725 725
726 726 $ hg serve -R ../a -p $HGPORT2 -d --pid-file=../hg2.pid
727 727 $ cat ../hg2.pid >> $DAEMON_PIDS
728 728
729 729 $ hg push http://localhost:$HGPORT2/
730 730 pushing to http://localhost:$HGPORT2/
731 731 searching for changes
732 732 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
733 733 (merge or see 'hg help push' for details about pushing new heads)
734 734 [255]
735 735 $ hg -R ../a book
736 736 @ 1:0d2164f0ce0d
737 737 * X 1:0d2164f0ce0d
738 738 Y 3:f6fc62dde3c0
739 739 Z 1:0d2164f0ce0d
740 740
741 741
742 742 Unrelated marker does not alter the decision
743 743
744 744 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
745 745 1 new obsolescence markers
746 746 $ hg push http://localhost:$HGPORT2/
747 747 pushing to http://localhost:$HGPORT2/
748 748 searching for changes
749 749 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
750 750 (merge or see 'hg help push' for details about pushing new heads)
751 751 [255]
752 752 $ hg -R ../a book
753 753 @ 1:0d2164f0ce0d
754 754 * X 1:0d2164f0ce0d
755 755 Y 3:f6fc62dde3c0
756 756 Z 1:0d2164f0ce0d
757 757
758 758 Update to a successor works
759 759
760 760 $ hg id --debug -r 3
761 761 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
762 762 $ hg id --debug -r 4
763 763 4efff6d98829d9c824c621afd6e3f01865f5439f
764 764 $ hg id --debug -r 5
765 765 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
766 766 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
767 767 1 new obsolescence markers
768 768 obsoleted 1 changesets
769 769 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
770 770 1 new obsolescence markers
771 771 $ hg push http://localhost:$HGPORT2/
772 772 pushing to http://localhost:$HGPORT2/
773 773 searching for changes
774 774 remote: adding changesets
775 775 remote: adding manifests
776 776 remote: adding file changes
777 777 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
778 778 remote: 2 new obsolescence markers
779 779 remote: obsoleted 1 changesets
780 780 updating bookmark Y
781 781 $ hg -R ../a book
782 782 @ 1:0d2164f0ce0d
783 783 * X 1:0d2164f0ce0d
784 784 Y 5:c922c0139ca0
785 785 Z 1:0d2164f0ce0d
786 786
787 787 hgweb
788 788
789 789 $ cat <<EOF > .hg/hgrc
790 790 > [web]
791 791 > push_ssl = false
792 792 > allow_push = *
793 793 > EOF
794 794
795 795 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
796 796 $ cat ../hg.pid >> $DAEMON_PIDS
797 797 $ cd ../a
798 798
799 799 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
800 800 bookmarks
801 801 namespaces
802 802 obsolete
803 803 phases
804 804 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
805 805 @ 9b140be1080824d768c5a4691a564088eede71f9
806 806 X 9b140be1080824d768c5a4691a564088eede71f9
807 807 Y c922c0139ca03858f655e4a2af4dd02796a63969
808 808 Z 9b140be1080824d768c5a4691a564088eede71f9
809 809 foo 0000000000000000000000000000000000000000
810 810 foobar 9b140be1080824d768c5a4691a564088eede71f9
811 811 $ hg out -B http://localhost:$HGPORT/
812 812 comparing with http://localhost:$HGPORT/
813 813 searching for changed bookmarks
814 814 @ 0d2164f0ce0d
815 815 X 0d2164f0ce0d
816 816 Z 0d2164f0ce0d
817 817 foo
818 818 foobar
819 819 $ hg push -B Z http://localhost:$HGPORT/
820 820 pushing to http://localhost:$HGPORT/
821 821 searching for changes
822 822 no changes found
823 823 updating bookmark Z
824 824 [1]
825 825 $ hg book -d Z
826 826 $ hg in -B http://localhost:$HGPORT/
827 827 comparing with http://localhost:$HGPORT/
828 828 searching for changed bookmarks
829 829 @ 9b140be10808
830 830 X 9b140be10808
831 831 Z 0d2164f0ce0d
832 832 foo 000000000000
833 833 foobar 9b140be10808
834 834 $ hg pull -B Z http://localhost:$HGPORT/
835 835 pulling from http://localhost:$HGPORT/
836 836 no changes found
837 837 divergent bookmark @ stored as @1
838 838 divergent bookmark X stored as X@1
839 839 adding remote bookmark Z
840 840 adding remote bookmark foo
841 841 adding remote bookmark foobar
842 842 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
843 843 requesting all changes
844 844 adding changesets
845 845 adding manifests
846 846 adding file changes
847 847 added 5 changesets with 5 changes to 3 files (+2 heads)
848 848 2 new obsolescence markers
849 849 new changesets 4e3505fd9583:c922c0139ca0 (5 drafts)
850 850 updating to bookmark @
851 851 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
852 852 $ hg -R cloned-bookmarks bookmarks
853 853 * @ 1:9b140be10808
854 854 X 1:9b140be10808
855 855 Y 4:c922c0139ca0
856 856 Z 2:0d2164f0ce0d
857 857 foo -1:000000000000
858 858 foobar 1:9b140be10808
859 859
860 860 $ cd ..
861 861
862 862 Test to show result of bookmarks comparison
863 863
864 864 $ mkdir bmcomparison
865 865 $ cd bmcomparison
866 866
867 867 $ hg init source
868 868 $ hg -R source debugbuilddag '+2*2*3*4'
869 869 $ hg -R source log -G --template '{rev}:{node|short}'
870 870 o 4:e7bd5218ca15
871 871 |
872 872 | o 3:6100d3090acf
873 873 |/
874 874 | o 2:fa942426a6fd
875 875 |/
876 876 | o 1:66f7d451a68b
877 877 |/
878 878 o 0:1ea73414a91b
879 879
880 880 $ hg -R source bookmarks -r 0 SAME
881 881 $ hg -R source bookmarks -r 0 ADV_ON_REPO1
882 882 $ hg -R source bookmarks -r 0 ADV_ON_REPO2
883 883 $ hg -R source bookmarks -r 0 DIFF_ADV_ON_REPO1
884 884 $ hg -R source bookmarks -r 0 DIFF_ADV_ON_REPO2
885 885 $ hg -R source bookmarks -r 1 DIVERGED
886 886
887 887 $ hg clone -U source repo1
888 888
889 889 (test that incoming/outgoing exit with 1, if there is no bookmark to
890 890 be exchanged)
891 891
892 892 $ hg -R repo1 incoming -B
893 893 comparing with $TESTTMP/bmcomparison/source
894 894 searching for changed bookmarks
895 895 no changed bookmarks found
896 896 [1]
897 897 $ hg -R repo1 outgoing -B
898 898 comparing with $TESTTMP/bmcomparison/source
899 899 searching for changed bookmarks
900 900 no changed bookmarks found
901 901 [1]
902 902
903 903 $ hg -R repo1 bookmarks -f -r 1 ADD_ON_REPO1
904 904 $ hg -R repo1 bookmarks -f -r 2 ADV_ON_REPO1
905 905 $ hg -R repo1 bookmarks -f -r 3 DIFF_ADV_ON_REPO1
906 906 $ hg -R repo1 bookmarks -f -r 3 DIFF_DIVERGED
907 907 $ hg -R repo1 -q --config extensions.mq= strip 4
908 908 $ hg -R repo1 log -G --template '{node|short} ({bookmarks})'
909 909 o 6100d3090acf (DIFF_ADV_ON_REPO1 DIFF_DIVERGED)
910 910 |
911 911 | o fa942426a6fd (ADV_ON_REPO1)
912 912 |/
913 913 | o 66f7d451a68b (ADD_ON_REPO1 DIVERGED)
914 914 |/
915 915 o 1ea73414a91b (ADV_ON_REPO2 DIFF_ADV_ON_REPO2 SAME)
916 916
917 917
918 918 $ hg clone -U source repo2
919 919 $ hg -R repo2 bookmarks -f -r 1 ADD_ON_REPO2
920 920 $ hg -R repo2 bookmarks -f -r 1 ADV_ON_REPO2
921 921 $ hg -R repo2 bookmarks -f -r 2 DIVERGED
922 922 $ hg -R repo2 bookmarks -f -r 4 DIFF_ADV_ON_REPO2
923 923 $ hg -R repo2 bookmarks -f -r 4 DIFF_DIVERGED
924 924 $ hg -R repo2 -q --config extensions.mq= strip 3
925 925 $ hg -R repo2 log -G --template '{node|short} ({bookmarks})'
926 926 o e7bd5218ca15 (DIFF_ADV_ON_REPO2 DIFF_DIVERGED)
927 927 |
928 928 | o fa942426a6fd (DIVERGED)
929 929 |/
930 930 | o 66f7d451a68b (ADD_ON_REPO2 ADV_ON_REPO2)
931 931 |/
932 932 o 1ea73414a91b (ADV_ON_REPO1 DIFF_ADV_ON_REPO1 SAME)
933 933
934 934
935 935 (test that difference of bookmarks between repositories are fully shown)
936 936
937 937 $ hg -R repo1 incoming -B repo2 -v
938 938 comparing with repo2
939 939 searching for changed bookmarks
940 940 ADD_ON_REPO2 66f7d451a68b added
941 941 ADV_ON_REPO2 66f7d451a68b advanced
942 942 DIFF_ADV_ON_REPO2 e7bd5218ca15 changed
943 943 DIFF_DIVERGED e7bd5218ca15 changed
944 944 DIVERGED fa942426a6fd diverged
945 945 $ hg -R repo1 outgoing -B repo2 -v
946 946 comparing with repo2
947 947 searching for changed bookmarks
948 948 ADD_ON_REPO1 66f7d451a68b added
949 949 ADD_ON_REPO2 deleted
950 950 ADV_ON_REPO1 fa942426a6fd advanced
951 951 DIFF_ADV_ON_REPO1 6100d3090acf advanced
952 952 DIFF_ADV_ON_REPO2 1ea73414a91b changed
953 953 DIFF_DIVERGED 6100d3090acf changed
954 954 DIVERGED 66f7d451a68b diverged
955 955
956 956 $ hg -R repo2 incoming -B repo1 -v
957 957 comparing with repo1
958 958 searching for changed bookmarks
959 959 ADD_ON_REPO1 66f7d451a68b added
960 960 ADV_ON_REPO1 fa942426a6fd advanced
961 961 DIFF_ADV_ON_REPO1 6100d3090acf changed
962 962 DIFF_DIVERGED 6100d3090acf changed
963 963 DIVERGED 66f7d451a68b diverged
964 964 $ hg -R repo2 outgoing -B repo1 -v
965 965 comparing with repo1
966 966 searching for changed bookmarks
967 967 ADD_ON_REPO1 deleted
968 968 ADD_ON_REPO2 66f7d451a68b added
969 969 ADV_ON_REPO2 66f7d451a68b advanced
970 970 DIFF_ADV_ON_REPO1 1ea73414a91b changed
971 971 DIFF_ADV_ON_REPO2 e7bd5218ca15 advanced
972 972 DIFF_DIVERGED e7bd5218ca15 changed
973 973 DIVERGED fa942426a6fd diverged
974 974
975 975 $ cd ..
976 976
977 977 Pushing a bookmark should only push the changes required by that
978 978 bookmark, not all outgoing changes:
979 979 $ hg clone http://localhost:$HGPORT/ addmarks
980 980 requesting all changes
981 981 adding changesets
982 982 adding manifests
983 983 adding file changes
984 984 added 5 changesets with 5 changes to 3 files (+2 heads)
985 985 2 new obsolescence markers
986 986 new changesets 4e3505fd9583:c922c0139ca0 (5 drafts)
987 987 updating to bookmark @
988 988 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
989 989 $ cd addmarks
990 990 $ echo foo > foo
991 991 $ hg add foo
992 992 $ hg commit -m 'add foo'
993 993 $ echo bar > bar
994 994 $ hg add bar
995 995 $ hg commit -m 'add bar'
996 996 $ hg co "tip^"
997 997 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
998 998 (leaving bookmark @)
999 999 $ hg book add-foo
1000 1000 $ hg book -r tip add-bar
1001 1001 Note: this push *must* push only a single changeset, as that's the point
1002 1002 of this test.
1003 1003 $ hg push -B add-foo --traceback
1004 1004 pushing to http://localhost:$HGPORT/
1005 1005 searching for changes
1006 1006 remote: adding changesets
1007 1007 remote: adding manifests
1008 1008 remote: adding file changes
1009 1009 remote: added 1 changesets with 1 changes to 1 files
1010 1010 exporting bookmark add-foo
1011 1011
1012 1012 pushing a new bookmark on a new head does not require -f if -B is specified
1013 1013
1014 1014 $ hg up -q X
1015 1015 $ hg book W
1016 1016 $ echo c5 > f2
1017 1017 $ hg ci -Am5
1018 1018 created new head
1019 1019 $ hg push -B .
1020 1020 pushing to http://localhost:$HGPORT/
1021 1021 searching for changes
1022 1022 remote: adding changesets
1023 1023 remote: adding manifests
1024 1024 remote: adding file changes
1025 1025 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
1026 1026 exporting bookmark W
1027 1027 $ hg -R ../b id -r W
1028 1028 cc978a373a53 tip W
1029 1029
1030 1030 pushing an existing but divergent bookmark with -B still requires -f
1031 1031
1032 1032 $ hg clone -q . ../r
1033 1033 $ hg up -q X
1034 1034 $ echo 1 > f2
1035 1035 $ hg ci -qAml
1036 1036
1037 1037 $ cd ../r
1038 1038 $ hg up -q X
1039 1039 $ echo 2 > f2
1040 1040 $ hg ci -qAmr
1041 1041 $ hg push -B X
1042 1042 pushing to $TESTTMP/addmarks
1043 1043 searching for changes
1044 1044 remote has heads on branch 'default' that are not known locally: a2a606d9ff1b
1045 1045 abort: push creates new remote head 54694f811df9 with bookmark 'X'!
1046 1046 (pull and merge or see 'hg help push' for details about pushing new heads)
1047 1047 [255]
1048 1048 $ cd ../addmarks
1049 1049
1050 1050 Check summary output for incoming/outgoing bookmarks
1051 1051
1052 1052 $ hg bookmarks -d X
1053 1053 $ hg bookmarks -d Y
1054 1054 $ hg summary --remote | grep '^remote:'
1055 1055 remote: *, 2 incoming bookmarks, 1 outgoing bookmarks (glob)
1056 1056
1057 1057 $ cd ..
1058 1058
1059 1059 pushing an unchanged bookmark should result in no changes
1060 1060
1061 1061 $ hg init unchanged-a
1062 1062 $ hg init unchanged-b
1063 1063 $ cd unchanged-a
1064 1064 $ echo initial > foo
1065 1065 $ hg commit -A -m initial
1066 1066 adding foo
1067 1067 $ hg bookmark @
1068 1068 $ hg push -B @ ../unchanged-b
1069 1069 pushing to ../unchanged-b
1070 1070 searching for changes
1071 1071 adding changesets
1072 1072 adding manifests
1073 1073 adding file changes
1074 1074 added 1 changesets with 1 changes to 1 files
1075 1075 exporting bookmark @
1076 1076
1077 1077 $ hg push -B @ ../unchanged-b
1078 1078 pushing to ../unchanged-b
1079 1079 searching for changes
1080 1080 no changes found
1081 1081 [1]
1082 1082
1083 1083 Pushing a really long bookmark should work fine (issue5165)
1084 1084 ===============================================
1085 1085
1086 1086 #if b2-binary
1087 1087 >>> with open('longname', 'w') as f:
1088 1088 ... f.write('wat' * 100) and None
1089 1089 $ hg book `cat longname`
1090 1090 $ hg push -B `cat longname` ../unchanged-b
1091 1091 pushing to ../unchanged-b
1092 1092 searching for changes
1093 1093 no changes found
1094 1094 exporting bookmark (wat){100} (re)
1095 1095 [1]
1096 1096 $ hg -R ../unchanged-b book --delete `cat longname`
1097 1097
1098 1098 Test again but forcing bundle2 exchange to make sure that doesn't regress.
1099 1099
1100 1100 $ hg push -B `cat longname` ../unchanged-b --config devel.legacy.exchange=bundle1
1101 1101 pushing to ../unchanged-b
1102 1102 searching for changes
1103 1103 no changes found
1104 1104 exporting bookmark (wat){100} (re)
1105 1105 [1]
1106 1106 $ hg -R ../unchanged-b book --delete `cat longname`
1107 1107 $ hg book --delete `cat longname`
1108 1108 $ hg co @
1109 1109 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1110 1110 (activating bookmark @)
1111 1111 #endif
1112 1112
1113 1113 Check hook preventing push (issue4455)
1114 1114 ======================================
1115 1115
1116 1116 $ hg bookmarks
1117 1117 * @ 0:55482a6fb4b1
1118 1118 $ hg log -G
1119 1119 @ 0:55482a6fb4b1 initial
1120 1120
1121 1121 $ hg init ../issue4455-dest
1122 1122 $ hg push ../issue4455-dest # changesets only
1123 1123 pushing to ../issue4455-dest
1124 1124 searching for changes
1125 1125 adding changesets
1126 1126 adding manifests
1127 1127 adding file changes
1128 1128 added 1 changesets with 1 changes to 1 files
1129 1129 $ cat >> .hg/hgrc << EOF
1130 1130 > [paths]
1131 1131 > local=../issue4455-dest/
1132 1132 > ssh=ssh://user@dummy/issue4455-dest
1133 1133 > http=http://localhost:$HGPORT/
1134 1134 > [ui]
1135 1135 > ssh="$PYTHON" "$TESTDIR/dummyssh"
1136 1136 > EOF
1137 1137 $ cat >> ../issue4455-dest/.hg/hgrc << EOF
1138 1138 > [hooks]
1139 1139 > prepushkey=false
1140 1140 > [web]
1141 1141 > push_ssl = false
1142 1142 > allow_push = *
1143 1143 > EOF
1144 1144 $ killdaemons.py
1145 1145 $ hg serve -R ../issue4455-dest -p $HGPORT -d --pid-file=../issue4455.pid -E ../issue4455-error.log
1146 1146 $ cat ../issue4455.pid >> $DAEMON_PIDS
1147 1147
1148 1148 Local push
1149 1149 ----------
1150 1150
1151 1151 #if b2-pushkey
1152 1152
1153 1153 $ hg push -B @ local
1154 1154 pushing to $TESTTMP/issue4455-dest
1155 1155 searching for changes
1156 1156 no changes found
1157 1157 pushkey-abort: prepushkey hook exited with status 1
1158 1158 abort: exporting bookmark @ failed!
1159 1159 [255]
1160 1160
1161 1161 #endif
1162 1162 #if b2-binary
1163 1163
1164 1164 $ hg push -B @ local
1165 1165 pushing to $TESTTMP/issue4455-dest
1166 1166 searching for changes
1167 1167 no changes found
1168 1168 abort: prepushkey hook exited with status 1
1169 1169 [255]
1170 1170
1171 1171 #endif
1172 1172
1173 1173 $ hg -R ../issue4455-dest/ bookmarks
1174 1174 no bookmarks set
1175 1175
1176 1176 Using ssh
1177 1177 ---------
1178 1178
1179 1179 #if b2-pushkey
1180 1180
1181 1181 $ hg push -B @ ssh # bundle2+
1182 1182 pushing to ssh://user@dummy/issue4455-dest
1183 1183 searching for changes
1184 1184 no changes found
1185 1185 remote: pushkey-abort: prepushkey hook exited with status 1
1186 1186 abort: exporting bookmark @ failed!
1187 1187 [255]
1188 1188
1189 1189 $ hg -R ../issue4455-dest/ bookmarks
1190 1190 no bookmarks set
1191 1191
1192 1192 $ hg push -B @ ssh --config devel.legacy.exchange=bundle1
1193 1193 pushing to ssh://user@dummy/issue4455-dest
1194 1194 searching for changes
1195 1195 no changes found
1196 1196 remote: pushkey-abort: prepushkey hook exited with status 1
1197 1197 exporting bookmark @ failed!
1198 1198 [1]
1199 1199
1200 1200 #endif
1201 1201 #if b2-binary
1202 1202
1203 1203 $ hg push -B @ ssh # bundle2+
1204 1204 pushing to ssh://user@dummy/issue4455-dest
1205 1205 searching for changes
1206 1206 no changes found
1207 1207 remote: prepushkey hook exited with status 1
1208 1208 abort: push failed on remote
1209 1209 [255]
1210 1210
1211 1211 #endif
1212 1212
1213 1213 $ hg -R ../issue4455-dest/ bookmarks
1214 1214 no bookmarks set
1215 1215
1216 1216 Using http
1217 1217 ----------
1218 1218
1219 1219 #if b2-pushkey
1220 1220 $ hg push -B @ http # bundle2+
1221 1221 pushing to http://localhost:$HGPORT/
1222 1222 searching for changes
1223 1223 no changes found
1224 1224 remote: pushkey-abort: prepushkey hook exited with status 1
1225 1225 abort: exporting bookmark @ failed!
1226 1226 [255]
1227 1227
1228 1228 $ hg -R ../issue4455-dest/ bookmarks
1229 1229 no bookmarks set
1230 1230
1231 1231 $ hg push -B @ http --config devel.legacy.exchange=bundle1
1232 1232 pushing to http://localhost:$HGPORT/
1233 1233 searching for changes
1234 1234 no changes found
1235 1235 remote: pushkey-abort: prepushkey hook exited with status 1
1236 1236 exporting bookmark @ failed!
1237 1237 [1]
1238 1238
1239 1239 #endif
1240 1240
1241 1241 #if b2-binary
1242 1242
1243 1243 $ hg push -B @ ssh # bundle2+
1244 1244 pushing to ssh://user@dummy/issue4455-dest
1245 1245 searching for changes
1246 1246 no changes found
1247 1247 remote: prepushkey hook exited with status 1
1248 1248 abort: push failed on remote
1249 1249 [255]
1250 1250
1251 1251 #endif
1252 1252
1253 1253 $ hg -R ../issue4455-dest/ bookmarks
1254 1254 no bookmarks set
1255 1255
1256 1256 $ cd ..
1257 1257
1258 1258 Test that pre-pushkey compat for bookmark works as expected (issue5777)
1259 1259
1260 1260 $ cat << EOF >> $HGRCPATH
1261 1261 > [ui]
1262 1262 > ssh="$PYTHON" "$TESTDIR/dummyssh"
1263 1263 > [server]
1264 1264 > bookmarks-pushkey-compat = yes
1265 1265 > EOF
1266 1266
1267 1267 $ hg init server
1268 1268 $ echo foo > server/a
1269 1269 $ hg -R server book foo
1270 1270 $ hg -R server commit -Am a
1271 1271 adding a
1272 1272 $ hg clone ssh://user@dummy/server client
1273 1273 requesting all changes
1274 1274 adding changesets
1275 1275 adding manifests
1276 1276 adding file changes
1277 1277 added 1 changesets with 1 changes to 1 files
1278 1278 new changesets 79513d0d7716 (1 drafts)
1279 1279 updating to branch default
1280 1280 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1281 1281
1282 1282 Forbid bookmark move on the server
1283 1283
1284 1284 $ cat << EOF >> $TESTTMP/no-bm-move.sh
1285 1285 > #!/bin/sh
1286 1286 > echo \$HG_NAMESPACE | grep -v bookmarks
1287 1287 > EOF
1288 1288 $ cat << EOF >> server/.hg/hgrc
1289 1289 > [hooks]
1290 1290 > prepushkey.no-bm-move= sh $TESTTMP/no-bm-move.sh
1291 1291 > EOF
1292 1292
1293 1293 pushing changeset is okay
1294 1294
1295 1295 $ echo bar >> client/a
1296 1296 $ hg -R client commit -m b
1297 1297 $ hg -R client push
1298 1298 pushing to ssh://user@dummy/server
1299 1299 searching for changes
1300 1300 remote: adding changesets
1301 1301 remote: adding manifests
1302 1302 remote: adding file changes
1303 1303 remote: added 1 changesets with 1 changes to 1 files
1304 1304
1305 1305 attempt to move the bookmark is rejected
1306 1306
1307 1307 $ hg -R client book foo -r .
1308 1308 moving bookmark 'foo' forward from 79513d0d7716
1309 1309
1310 1310 #if b2-pushkey
1311 1311 $ hg -R client push
1312 1312 pushing to ssh://user@dummy/server
1313 1313 searching for changes
1314 1314 no changes found
1315 1315 remote: pushkey-abort: prepushkey.no-bm-move hook exited with status 1
1316 1316 abort: updating bookmark foo failed!
1317 1317 [255]
1318 1318 #endif
1319 1319 #if b2-binary
1320 1320 $ hg -R client push
1321 1321 pushing to ssh://user@dummy/server
1322 1322 searching for changes
1323 1323 no changes found
1324 1324 remote: prepushkey.no-bm-move hook exited with status 1
1325 1325 abort: push failed on remote
1326 1326 [255]
1327 1327 #endif
1328 1328
1329 1329 -- test for pushing bookmarks pointing to secret changesets
1330 1330
1331 1331 Set up a "remote" repo
1332 1332 $ hg init issue6159remote
1333 1333 $ cd issue6159remote
1334 1334 $ echo a > a
1335 1335 $ hg add a
1336 1336 $ hg commit -m_
1337 1337 $ hg bookmark foo
1338 1338 $ cd ..
1339 1339
1340 1340 Clone a local repo
1341 1341 $ hg clone -q issue6159remote issue6159local
1342 1342 $ cd issue6159local
1343 1343 $ hg up -qr foo
1344 1344 $ echo b > b
1345 1345
1346 1346 Move the bookmark "foo" to point at a secret changeset
1347 1347 $ hg commit -qAm_ --config phases.new-commit=secret
1348 1348
1349 1349 Pushing the bookmark "foo" now fails as it contains a secret changeset
1350 1350 $ hg push -r foo
1351 1351 pushing to $TESTTMP/issue6159remote
1352 1352 searching for changes
1353 1353 no changes found (ignored 1 secret changesets)
1354 1354 abort: cannot push bookmark foo as it points to a secret changeset
1355 1355 [255]
@@ -1,1236 +1,1236 b''
1 1
2 2 $ hg init repo
3 3 $ cd repo
4 4
5 5 $ cat > $TESTTMP/hook.sh <<'EOF'
6 6 > echo "test-hook-bookmark: $HG_BOOKMARK: $HG_OLDNODE -> $HG_NODE"
7 7 > EOF
8 8 $ TESTHOOK="hooks.txnclose-bookmark.test=sh $TESTTMP/hook.sh"
9 9
10 10 no bookmarks
11 11
12 12 $ hg bookmarks
13 13 no bookmarks set
14 14
15 15 $ hg bookmarks -Tjson
16 16 [
17 17 ]
18 18
19 19 bookmark rev -1
20 20
21 21 $ hg bookmark X --config "$TESTHOOK"
22 22 test-hook-bookmark: X: -> 0000000000000000000000000000000000000000
23 23
24 24 list bookmarks
25 25
26 26 $ hg bookmarks
27 27 * X -1:000000000000
28 28
29 29 list bookmarks with color
30 30
31 31 $ hg --config extensions.color= --config color.mode=ansi \
32 32 > bookmarks --color=always
33 33 \x1b[0;32m * \x1b[0m\x1b[0;32mX\x1b[0m\x1b[0;32m -1:000000000000\x1b[0m (esc)
34 34
35 35 $ echo a > a
36 36 $ hg add a
37 37 $ hg commit -m 0 --config "$TESTHOOK"
38 38 test-hook-bookmark: X: 0000000000000000000000000000000000000000 -> f7b1eb17ad24730a1651fccd46c43826d1bbc2ac
39 39
40 40 bookmark X moved to rev 0
41 41
42 42 $ hg bookmarks
43 43 * X 0:f7b1eb17ad24
44 44
45 45 look up bookmark
46 46
47 47 $ hg log -r X
48 48 changeset: 0:f7b1eb17ad24
49 49 bookmark: X
50 50 tag: tip
51 51 user: test
52 52 date: Thu Jan 01 00:00:00 1970 +0000
53 53 summary: 0
54 54
55 55
56 56 second bookmark for rev 0, command should work even with ui.strict on
57 57
58 58 $ hg --config ui.strict=1 bookmark X2 --config "$TESTHOOK"
59 59 test-hook-bookmark: X2: -> f7b1eb17ad24730a1651fccd46c43826d1bbc2ac
60 60
61 61 bookmark rev -1 again
62 62
63 63 $ hg bookmark -r null Y
64 64
65 65 list bookmarks
66 66
67 67 $ hg bookmarks
68 68 X 0:f7b1eb17ad24
69 69 * X2 0:f7b1eb17ad24
70 70 Y -1:000000000000
71 71 $ hg bookmarks -l
72 72 X 0:f7b1eb17ad24
73 73 * X2 0:f7b1eb17ad24
74 74 Y -1:000000000000
75 75 $ hg bookmarks -l X Y
76 76 X 0:f7b1eb17ad24
77 77 Y -1:000000000000
78 78 $ hg bookmarks -l .
79 79 * X2 0:f7b1eb17ad24
80 80 $ hg bookmarks -l X A Y
81 81 abort: bookmark 'A' does not exist
82 82 [255]
83 83 $ hg bookmarks -l -r0
84 84 abort: --rev is incompatible with --list
85 85 [255]
86 86 $ hg bookmarks -l --inactive
87 87 abort: --inactive is incompatible with --list
88 88 [255]
89 89
90 90 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
91 91 0 X
92 92 0 X2
93 93
94 94 $ echo b > b
95 95 $ hg add b
96 96 $ hg commit -m 1 --config "$TESTHOOK"
97 97 test-hook-bookmark: X2: f7b1eb17ad24730a1651fccd46c43826d1bbc2ac -> 925d80f479bb026b0fb3deb27503780b13f74123
98 98
99 99 $ hg bookmarks -T '{rev}:{node|shortest} {bookmark} {desc|firstline}\n'
100 100 0:f7b1 X 0
101 101 1:925d X2 1
102 102 -1:0000 Y
103 103
104 104 $ hg bookmarks -Tjson
105 105 [
106 106 {
107 107 "active": false,
108 108 "bookmark": "X",
109 109 "node": "f7b1eb17ad24730a1651fccd46c43826d1bbc2ac",
110 110 "rev": 0
111 111 },
112 112 {
113 113 "active": true,
114 114 "bookmark": "X2",
115 115 "node": "925d80f479bb026b0fb3deb27503780b13f74123",
116 116 "rev": 1
117 117 },
118 118 {
119 119 "active": false,
120 120 "bookmark": "Y",
121 121 "node": "0000000000000000000000000000000000000000",
122 122 "rev": -1
123 123 }
124 124 ]
125 125
126 126 bookmarks revset
127 127
128 128 $ hg log -r 'bookmark()'
129 129 changeset: 0:f7b1eb17ad24
130 130 bookmark: X
131 131 user: test
132 132 date: Thu Jan 01 00:00:00 1970 +0000
133 133 summary: 0
134 134
135 135 changeset: 1:925d80f479bb
136 136 bookmark: X2
137 137 tag: tip
138 138 user: test
139 139 date: Thu Jan 01 00:00:00 1970 +0000
140 140 summary: 1
141 141
142 142 $ hg log -r 'bookmark(Y)'
143 143 $ hg log -r 'bookmark(X2)'
144 144 changeset: 1:925d80f479bb
145 145 bookmark: X2
146 146 tag: tip
147 147 user: test
148 148 date: Thu Jan 01 00:00:00 1970 +0000
149 149 summary: 1
150 150
151 151 $ hg log -r 'bookmark("re:X")'
152 152 changeset: 0:f7b1eb17ad24
153 153 bookmark: X
154 154 user: test
155 155 date: Thu Jan 01 00:00:00 1970 +0000
156 156 summary: 0
157 157
158 158 changeset: 1:925d80f479bb
159 159 bookmark: X2
160 160 tag: tip
161 161 user: test
162 162 date: Thu Jan 01 00:00:00 1970 +0000
163 163 summary: 1
164 164
165 165 $ hg log -r 'bookmark("literal:X")'
166 166 changeset: 0:f7b1eb17ad24
167 167 bookmark: X
168 168 user: test
169 169 date: Thu Jan 01 00:00:00 1970 +0000
170 170 summary: 0
171 171
172 172
173 173 "." is expanded to the active bookmark:
174 174
175 175 $ hg log -r 'bookmark(.)'
176 176 changeset: 1:925d80f479bb
177 177 bookmark: X2
178 178 tag: tip
179 179 user: test
180 180 date: Thu Jan 01 00:00:00 1970 +0000
181 181 summary: 1
182 182
183 183
184 184 but "literal:." is not since "." seems not a literal bookmark:
185 185
186 186 $ hg log -r 'bookmark("literal:.")'
187 187 abort: bookmark '.' does not exist!
188 188 [255]
189 189
190 190 "." should fail if there's no active bookmark:
191 191
192 192 $ hg bookmark --inactive
193 193 $ hg log -r 'bookmark(.)'
194 194 abort: no active bookmark!
195 195 [255]
196 196 $ hg log -r 'present(bookmark(.))'
197 197
198 198 $ hg log -r 'bookmark(unknown)'
199 199 abort: bookmark 'unknown' does not exist!
200 200 [255]
201 201 $ hg log -r 'bookmark("literal:unknown")'
202 202 abort: bookmark 'unknown' does not exist!
203 203 [255]
204 204 $ hg log -r 'bookmark("re:unknown")'
205 205 $ hg log -r 'present(bookmark("literal:unknown"))'
206 206 $ hg log -r 'present(bookmark("re:unknown"))'
207 207
208 208 $ hg help revsets | grep 'bookmark('
209 209 "bookmark([name])"
210 210
211 211 reactivate "X2"
212 212
213 213 $ hg update X2
214 214 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
215 215 (activating bookmark X2)
216 216
217 217 bookmarks X and X2 moved to rev 1, Y at rev -1
218 218
219 219 $ hg bookmarks
220 220 X 0:f7b1eb17ad24
221 221 * X2 1:925d80f479bb
222 222 Y -1:000000000000
223 223
224 224 bookmark rev 0 again
225 225
226 226 $ hg bookmark -r 0 Z
227 227
228 228 $ hg update X
229 229 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
230 230 (activating bookmark X)
231 231 $ echo c > c
232 232 $ hg add c
233 233 $ hg commit -m 2
234 234 created new head
235 235
236 236 bookmarks X moved to rev 2, Y at rev -1, Z at rev 0
237 237
238 238 $ hg bookmarks
239 239 * X 2:db815d6d32e6
240 240 X2 1:925d80f479bb
241 241 Y -1:000000000000
242 242 Z 0:f7b1eb17ad24
243 243
244 244 rename nonexistent bookmark
245 245
246 246 $ hg bookmark -m A B
247 247 abort: bookmark 'A' does not exist
248 248 [255]
249 249
250 250 rename to existent bookmark
251 251
252 252 $ hg bookmark -m X Y
253 253 abort: bookmark 'Y' already exists (use -f to force)
254 254 [255]
255 255
256 256 force rename to existent bookmark
257 257
258 258 $ hg bookmark -f -m X Y
259 259
260 260 rename bookmark using .
261 261
262 262 $ hg book rename-me
263 263 $ hg book -m . renamed --config "$TESTHOOK"
264 264 test-hook-bookmark: rename-me: db815d6d32e69058eadefc8cffbad37675707975 ->
265 265 test-hook-bookmark: renamed: -> db815d6d32e69058eadefc8cffbad37675707975
266 266 $ hg bookmark
267 267 X2 1:925d80f479bb
268 268 Y 2:db815d6d32e6
269 269 Z 0:f7b1eb17ad24
270 270 * renamed 2:db815d6d32e6
271 271 $ hg up -q Y
272 272 $ hg book -d renamed --config "$TESTHOOK"
273 273 test-hook-bookmark: renamed: db815d6d32e69058eadefc8cffbad37675707975 ->
274 274
275 275 rename bookmark using . with no active bookmark
276 276
277 277 $ hg book rename-me
278 278 $ hg book -i rename-me
279 279 $ hg book -m . renamed
280 280 abort: no active bookmark!
281 281 [255]
282 282 $ hg up -q Y
283 283 $ hg book -d rename-me
284 284
285 285 delete bookmark using .
286 286
287 287 $ hg book delete-me
288 288 $ hg book -d .
289 289 $ hg bookmark
290 290 X2 1:925d80f479bb
291 291 Y 2:db815d6d32e6
292 292 Z 0:f7b1eb17ad24
293 293 $ hg up -q Y
294 294
295 295 delete bookmark using . with no active bookmark
296 296
297 297 $ hg book delete-me
298 298 $ hg book -i delete-me
299 299 $ hg book -d .
300 300 abort: no active bookmark!
301 301 [255]
302 302 $ hg up -q Y
303 303 $ hg book -d delete-me
304 304
305 305 list bookmarks
306 306
307 307 $ hg bookmark
308 308 X2 1:925d80f479bb
309 309 * Y 2:db815d6d32e6
310 310 Z 0:f7b1eb17ad24
311 311
312 312 bookmarks from a revset
313 313 $ hg bookmark -r '.^1' REVSET
314 314 $ hg bookmark -r ':tip' TIP
315 315 $ hg up -q TIP
316 316 $ hg bookmarks
317 317 REVSET 0:f7b1eb17ad24
318 318 * TIP 2:db815d6d32e6
319 319 X2 1:925d80f479bb
320 320 Y 2:db815d6d32e6
321 321 Z 0:f7b1eb17ad24
322 322
323 323 $ hg bookmark -d REVSET
324 324 $ hg bookmark -d TIP
325 325
326 326 rename without new name or multiple names
327 327
328 328 $ hg bookmark -m Y
329 329 abort: new bookmark name required
330 330 [255]
331 331 $ hg bookmark -m Y Y2 Y3
332 332 abort: only one new bookmark name allowed
333 333 [255]
334 334
335 335 delete without name
336 336
337 337 $ hg bookmark -d
338 338 abort: bookmark name required
339 339 [255]
340 340
341 341 delete nonexistent bookmark
342 342
343 343 $ hg bookmark -d A
344 344 abort: bookmark 'A' does not exist
345 345 [255]
346 346
347 347 delete with --inactive
348 348
349 349 $ hg bookmark -d --inactive Y
350 350 abort: --inactive is incompatible with --delete
351 351 [255]
352 352
353 353 bookmark name with spaces should be stripped
354 354
355 355 $ hg bookmark ' x y '
356 356
357 357 list bookmarks
358 358
359 359 $ hg bookmarks
360 360 X2 1:925d80f479bb
361 361 Y 2:db815d6d32e6
362 362 Z 0:f7b1eb17ad24
363 363 * x y 2:db815d6d32e6
364 364 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
365 365 2 Y
366 366 2 x y
367 367 1 X2
368 368 0 Z
369 369
370 370 look up stripped bookmark name
371 371
372 372 $ hg log -r '"x y"'
373 373 changeset: 2:db815d6d32e6
374 374 bookmark: Y
375 375 bookmark: x y
376 376 tag: tip
377 377 parent: 0:f7b1eb17ad24
378 378 user: test
379 379 date: Thu Jan 01 00:00:00 1970 +0000
380 380 summary: 2
381 381
382 382
383 383 reject bookmark name with newline
384 384
385 385 $ hg bookmark '
386 386 > '
387 387 abort: bookmark names cannot consist entirely of whitespace
388 388 [255]
389 389
390 390 $ hg bookmark -m Z '
391 391 > '
392 392 abort: bookmark names cannot consist entirely of whitespace
393 393 [255]
394 394
395 395 bookmark with reserved name
396 396
397 397 $ hg bookmark tip
398 398 abort: the name 'tip' is reserved
399 399 [255]
400 400
401 401 $ hg bookmark .
402 402 abort: the name '.' is reserved
403 403 [255]
404 404
405 405 $ hg bookmark null
406 406 abort: the name 'null' is reserved
407 407 [255]
408 408
409 409
410 410 bookmark with existing name
411 411
412 412 $ hg bookmark X2
413 413 abort: bookmark 'X2' already exists (use -f to force)
414 414 [255]
415 415
416 416 $ hg bookmark -m Y Z
417 417 abort: bookmark 'Z' already exists (use -f to force)
418 418 [255]
419 419
420 420 bookmark with name of branch
421 421
422 422 $ hg bookmark default
423 423 abort: a bookmark cannot have the name of an existing branch
424 424 [255]
425 425
426 426 $ hg bookmark -m Y default
427 427 abort: a bookmark cannot have the name of an existing branch
428 428 [255]
429 429
430 430 bookmark with integer name
431 431
432 432 $ hg bookmark 10
433 433 abort: cannot use an integer as a name
434 434 [255]
435 435
436 436 bookmark with a name that matches a node id
437 437 $ hg bookmark 925d80f479bb db815d6d32e6 --config "$TESTHOOK"
438 438 bookmark 925d80f479bb matches a changeset hash
439 439 (did you leave a -r out of an 'hg bookmark' command?)
440 440 bookmark db815d6d32e6 matches a changeset hash
441 441 (did you leave a -r out of an 'hg bookmark' command?)
442 442 test-hook-bookmark: 925d80f479bb: -> db815d6d32e69058eadefc8cffbad37675707975
443 443 test-hook-bookmark: db815d6d32e6: -> db815d6d32e69058eadefc8cffbad37675707975
444 444 $ hg bookmark -d 925d80f479bb
445 445 $ hg bookmark -d db815d6d32e6
446 446
447 447 $ cd ..
448 448
449 449 bookmark with a name that matches an ambiguous node id
450 450
451 451 $ hg init ambiguous
452 452 $ cd ambiguous
453 453 $ echo 0 > a
454 454 $ hg ci -qAm 0
455 455 $ for i in 1057 2857 4025; do
456 456 > hg up -q 0
457 457 > echo $i > a
458 458 > hg ci -qm $i
459 459 > done
460 460 $ hg up -q null
461 461 $ hg log -r0: -T '{rev}:{node}\n'
462 462 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
463 463 1:c56256a09cd28e5764f32e8e2810d0f01e2e357a
464 464 2:c5623987d205cd6d9d8389bfc40fff9dbb670b48
465 465 3:c562ddd9c94164376c20b86b0b4991636a3bf84f
466 466
467 467 $ hg bookmark -r0 c562
468 468 $ hg bookmarks
469 469 c562 0:b4e73ffab476
470 470
471 471 $ cd ..
472 472
473 473 incompatible options
474 474
475 475 $ cd repo
476 476
477 477 $ hg bookmark -m Y -d Z
478 478 abort: --delete and --rename are incompatible
479 479 [255]
480 480
481 481 $ hg bookmark -r 1 -d Z
482 482 abort: --rev is incompatible with --delete
483 483 [255]
484 484
485 485 $ hg bookmark -r 1 -m Z Y
486 486 abort: --rev is incompatible with --rename
487 487 [255]
488 488
489 489 force bookmark with existing name
490 490
491 491 $ hg bookmark -f X2 --config "$TESTHOOK"
492 492 test-hook-bookmark: X2: 925d80f479bb026b0fb3deb27503780b13f74123 -> db815d6d32e69058eadefc8cffbad37675707975
493 493
494 494 force bookmark back to where it was, should deactivate it
495 495
496 496 $ hg bookmark -fr1 X2
497 497 $ hg bookmarks
498 498 X2 1:925d80f479bb
499 499 Y 2:db815d6d32e6
500 500 Z 0:f7b1eb17ad24
501 501 x y 2:db815d6d32e6
502 502
503 503 forward bookmark to descendant without --force
504 504
505 505 $ hg bookmark Z
506 506 moving bookmark 'Z' forward from f7b1eb17ad24
507 507
508 508 list bookmarks
509 509
510 510 $ hg bookmark
511 511 X2 1:925d80f479bb
512 512 Y 2:db815d6d32e6
513 513 * Z 2:db815d6d32e6
514 514 x y 2:db815d6d32e6
515 515 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
516 516 2 Y
517 517 2 Z
518 518 2 x y
519 519 1 X2
520 520
521 521 revision but no bookmark name
522 522
523 523 $ hg bookmark -r .
524 524 abort: bookmark name required
525 525 [255]
526 526
527 527 bookmark name with whitespace only
528 528
529 529 $ hg bookmark ' '
530 530 abort: bookmark names cannot consist entirely of whitespace
531 531 [255]
532 532
533 533 $ hg bookmark -m Y ' '
534 534 abort: bookmark names cannot consist entirely of whitespace
535 535 [255]
536 536
537 537 invalid bookmark
538 538
539 539 $ hg bookmark 'foo:bar'
540 540 abort: ':' cannot be used in a name
541 541 [255]
542 542
543 543 $ hg bookmark 'foo
544 544 > bar'
545 545 abort: '\n' cannot be used in a name
546 546 [255]
547 547
548 548 the bookmark extension should be ignored now that it is part of core
549 549
550 550 $ echo "[extensions]" >> $HGRCPATH
551 551 $ echo "bookmarks=" >> $HGRCPATH
552 552 $ hg bookmarks
553 553 X2 1:925d80f479bb
554 554 Y 2:db815d6d32e6
555 555 * Z 2:db815d6d32e6
556 556 x y 2:db815d6d32e6
557 557
558 558 test summary
559 559
560 560 $ hg summary
561 561 parent: 2:db815d6d32e6 tip
562 562 2
563 563 branch: default
564 564 bookmarks: *Z Y x y
565 565 commit: (clean)
566 566 update: 1 new changesets, 2 branch heads (merge)
567 567 phases: 3 draft
568 568
569 569 test id
570 570
571 571 $ hg id
572 572 db815d6d32e6 tip Y/Z/x y
573 573
574 574 test rollback
575 575
576 576 $ echo foo > f1
577 577 $ hg bookmark tmp-rollback
578 578 $ hg ci -Amr
579 579 adding f1
580 580 $ hg bookmarks
581 581 X2 1:925d80f479bb
582 582 Y 2:db815d6d32e6
583 583 Z 2:db815d6d32e6
584 584 * tmp-rollback 3:2bf5cfec5864
585 585 x y 2:db815d6d32e6
586 586 $ hg rollback
587 587 repository tip rolled back to revision 2 (undo commit)
588 588 working directory now based on revision 2
589 589 $ hg bookmarks
590 590 X2 1:925d80f479bb
591 591 Y 2:db815d6d32e6
592 592 Z 2:db815d6d32e6
593 593 * tmp-rollback 2:db815d6d32e6
594 594 x y 2:db815d6d32e6
595 595 $ hg bookmark -f Z -r 1
596 596 $ hg rollback
597 597 repository tip rolled back to revision 2 (undo bookmark)
598 598 $ hg bookmarks
599 599 X2 1:925d80f479bb
600 600 Y 2:db815d6d32e6
601 601 Z 2:db815d6d32e6
602 602 * tmp-rollback 2:db815d6d32e6
603 603 x y 2:db815d6d32e6
604 604 $ hg bookmark -d tmp-rollback
605 605
606 606 activate bookmark on working dir parent without --force
607 607
608 608 $ hg bookmark --inactive Z
609 609 $ hg bookmark Z
610 610
611 611 test clone
612 612
613 613 $ hg bookmark -r 2 -i @
614 614 $ hg bookmark -r 2 -i a@
615 615 $ hg bookmarks
616 616 @ 2:db815d6d32e6
617 617 X2 1:925d80f479bb
618 618 Y 2:db815d6d32e6
619 619 * Z 2:db815d6d32e6
620 620 a@ 2:db815d6d32e6
621 621 x y 2:db815d6d32e6
622 622 $ hg clone . cloned-bookmarks
623 623 updating to bookmark @
624 624 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
625 625 $ hg -R cloned-bookmarks bookmarks
626 626 * @ 2:db815d6d32e6
627 627 X2 1:925d80f479bb
628 628 Y 2:db815d6d32e6
629 629 Z 2:db815d6d32e6
630 630 a@ 2:db815d6d32e6
631 631 x y 2:db815d6d32e6
632 632
633 633 test clone with pull protocol
634 634
635 635 $ hg clone --pull . cloned-bookmarks-pull
636 636 requesting all changes
637 637 adding changesets
638 638 adding manifests
639 639 adding file changes
640 640 added 3 changesets with 3 changes to 3 files (+1 heads)
641 641 new changesets f7b1eb17ad24:db815d6d32e6
642 642 updating to bookmark @
643 643 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
644 644 $ hg -R cloned-bookmarks-pull bookmarks
645 645 * @ 2:db815d6d32e6
646 646 X2 1:925d80f479bb
647 647 Y 2:db815d6d32e6
648 648 Z 2:db815d6d32e6
649 649 a@ 2:db815d6d32e6
650 650 x y 2:db815d6d32e6
651 651
652 652 delete multiple bookmarks at once
653 653
654 654 $ hg bookmark -d @ a@
655 655
656 656 test clone with a bookmark named "default" (issue3677)
657 657
658 658 $ hg bookmark -r 1 -f -i default
659 659 $ hg clone . cloned-bookmark-default
660 660 updating to branch default
661 661 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
662 662 $ hg -R cloned-bookmark-default bookmarks
663 663 X2 1:925d80f479bb
664 664 Y 2:db815d6d32e6
665 665 Z 2:db815d6d32e6
666 666 default 1:925d80f479bb
667 667 x y 2:db815d6d32e6
668 668 $ hg -R cloned-bookmark-default parents -q
669 669 2:db815d6d32e6
670 670 $ hg bookmark -d default
671 671
672 672 test clone with a specific revision
673 673
674 674 $ hg clone -r 925d80 . cloned-bookmarks-rev
675 675 adding changesets
676 676 adding manifests
677 677 adding file changes
678 678 added 2 changesets with 2 changes to 2 files
679 679 new changesets f7b1eb17ad24:925d80f479bb
680 680 updating to branch default
681 681 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
682 682 $ hg -R cloned-bookmarks-rev bookmarks
683 683 X2 1:925d80f479bb
684 684
685 685 test clone with update to a bookmark
686 686
687 687 $ hg clone -u Z . ../cloned-bookmarks-update
688 688 updating to branch default
689 689 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
690 690 $ hg -R ../cloned-bookmarks-update bookmarks
691 691 X2 1:925d80f479bb
692 692 Y 2:db815d6d32e6
693 693 * Z 2:db815d6d32e6
694 694 x y 2:db815d6d32e6
695 695
696 696 create bundle with two heads
697 697
698 698 $ hg clone . tobundle
699 699 updating to branch default
700 700 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
701 701 $ echo x > tobundle/x
702 702 $ hg -R tobundle add tobundle/x
703 703 $ hg -R tobundle commit -m'x'
704 704 $ hg -R tobundle update -r -2
705 705 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
706 706 $ echo y > tobundle/y
707 707 $ hg -R tobundle branch test
708 708 marked working directory as branch test
709 709 (branches are permanent and global, did you want a bookmark?)
710 710 $ hg -R tobundle add tobundle/y
711 711 $ hg -R tobundle commit -m'y'
712 712 $ hg -R tobundle bundle tobundle.hg
713 713 searching for changes
714 714 2 changesets found
715 715 $ hg unbundle tobundle.hg
716 716 adding changesets
717 717 adding manifests
718 718 adding file changes
719 719 added 2 changesets with 2 changes to 2 files (+1 heads)
720 720 new changesets 125c9a1d6df6:9ba5f110a0b3 (2 drafts)
721 721 (run 'hg heads' to see heads, 'hg merge' to merge)
722 722
723 723 update to active bookmark if it's not the parent
724 724
725 725 (it is known issue that fsmonitor can't handle nested repositories. In
726 726 this test scenario, cloned-bookmark-default and tobundle exist in the
727 727 working directory of current repository)
728 728
729 729 $ hg summary
730 730 parent: 2:db815d6d32e6
731 731 2
732 732 branch: default
733 733 bookmarks: *Z Y x y
734 734 commit: 1 added, 1 unknown (new branch head) (no-fsmonitor !)
735 735 commit: 1 added, * unknown (new branch head) (glob) (fsmonitor !)
736 736 update: 2 new changesets (update)
737 737 phases: 5 draft
738 738 $ hg update
739 739 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
740 740 updating bookmark Z
741 741 $ hg bookmarks
742 742 X2 1:925d80f479bb
743 743 Y 2:db815d6d32e6
744 744 * Z 3:125c9a1d6df6
745 745 x y 2:db815d6d32e6
746 746
747 747 pull --update works the same as pull && update
748 748
749 749 $ hg bookmark -r3 Y
750 750 moving bookmark 'Y' forward from db815d6d32e6
751 751 $ cp -R ../cloned-bookmarks-update ../cloned-bookmarks-manual-update
752 752 $ cp -R ../cloned-bookmarks-update ../cloned-bookmarks-manual-update-with-divergence
753 753
754 754 (manual version)
755 755
756 756 $ hg -R ../cloned-bookmarks-manual-update update Y
757 757 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
758 758 (activating bookmark Y)
759 759 $ hg -R ../cloned-bookmarks-manual-update pull .
760 760 pulling from .
761 761 searching for changes
762 762 adding changesets
763 763 adding manifests
764 764 adding file changes
765 added 2 changesets with 2 changes to 2 files (+1 heads)
766 765 updating bookmark Y
767 766 updating bookmark Z
767 added 2 changesets with 2 changes to 2 files (+1 heads)
768 768 new changesets 125c9a1d6df6:9ba5f110a0b3
769 769 (run 'hg heads' to see heads, 'hg merge' to merge)
770 770
771 771 (# tests strange but with --date crashing when bookmark have to move)
772 772
773 773 $ hg -R ../cloned-bookmarks-manual-update update -d 1986
774 774 abort: revision matching date not found
775 775 [255]
776 776 $ hg -R ../cloned-bookmarks-manual-update update
777 777 updating to active bookmark Y
778 778 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
779 779
780 780 (all in one version)
781 781
782 782 $ hg -R ../cloned-bookmarks-update update Y
783 783 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
784 784 (activating bookmark Y)
785 785 $ hg -R ../cloned-bookmarks-update pull --update .
786 786 pulling from .
787 787 searching for changes
788 788 adding changesets
789 789 adding manifests
790 790 adding file changes
791 added 2 changesets with 2 changes to 2 files (+1 heads)
792 791 updating bookmark Y
793 792 updating bookmark Z
793 added 2 changesets with 2 changes to 2 files (+1 heads)
794 794 new changesets 125c9a1d6df6:9ba5f110a0b3
795 795 updating to active bookmark Y
796 796 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
797 797
798 798 We warn about divergent during bare update to the active bookmark
799 799
800 800 $ hg -R ../cloned-bookmarks-manual-update-with-divergence update Y
801 801 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
802 802 (activating bookmark Y)
803 803 $ hg -R ../cloned-bookmarks-manual-update-with-divergence bookmarks -r X2 Y@1
804 804 $ hg -R ../cloned-bookmarks-manual-update-with-divergence bookmarks
805 805 X2 1:925d80f479bb
806 806 * Y 2:db815d6d32e6
807 807 Y@1 1:925d80f479bb
808 808 Z 2:db815d6d32e6
809 809 x y 2:db815d6d32e6
810 810 $ hg -R ../cloned-bookmarks-manual-update-with-divergence pull
811 811 pulling from $TESTTMP/repo
812 812 searching for changes
813 813 adding changesets
814 814 adding manifests
815 815 adding file changes
816 added 2 changesets with 2 changes to 2 files (+1 heads)
817 816 updating bookmark Y
818 817 updating bookmark Z
818 added 2 changesets with 2 changes to 2 files (+1 heads)
819 819 new changesets 125c9a1d6df6:9ba5f110a0b3
820 820 (run 'hg heads' to see heads, 'hg merge' to merge)
821 821 $ hg -R ../cloned-bookmarks-manual-update-with-divergence update
822 822 updating to active bookmark Y
823 823 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
824 824 1 other divergent bookmarks for "Y"
825 825
826 826 test wrongly formated bookmark
827 827
828 828 $ echo '' >> .hg/bookmarks
829 829 $ hg bookmarks
830 830 X2 1:925d80f479bb
831 831 Y 3:125c9a1d6df6
832 832 * Z 3:125c9a1d6df6
833 833 x y 2:db815d6d32e6
834 834 $ echo "Ican'thasformatedlines" >> .hg/bookmarks
835 835 $ hg bookmarks
836 836 malformed line in .hg/bookmarks: "Ican'thasformatedlines"
837 837 X2 1:925d80f479bb
838 838 Y 3:125c9a1d6df6
839 839 * Z 3:125c9a1d6df6
840 840 x y 2:db815d6d32e6
841 841
842 842 test missing revisions
843 843
844 844 $ echo "925d80f479b925d80f479bc925d80f479bccabab z" > .hg/bookmarks
845 845 $ hg book
846 846 no bookmarks set
847 847
848 848 test stripping a non-checked-out but bookmarked revision
849 849
850 850 $ hg log --graph
851 851 o changeset: 4:9ba5f110a0b3
852 852 | branch: test
853 853 | tag: tip
854 854 | parent: 2:db815d6d32e6
855 855 | user: test
856 856 | date: Thu Jan 01 00:00:00 1970 +0000
857 857 | summary: y
858 858 |
859 859 | @ changeset: 3:125c9a1d6df6
860 860 |/ user: test
861 861 | date: Thu Jan 01 00:00:00 1970 +0000
862 862 | summary: x
863 863 |
864 864 o changeset: 2:db815d6d32e6
865 865 | parent: 0:f7b1eb17ad24
866 866 | user: test
867 867 | date: Thu Jan 01 00:00:00 1970 +0000
868 868 | summary: 2
869 869 |
870 870 | o changeset: 1:925d80f479bb
871 871 |/ user: test
872 872 | date: Thu Jan 01 00:00:00 1970 +0000
873 873 | summary: 1
874 874 |
875 875 o changeset: 0:f7b1eb17ad24
876 876 user: test
877 877 date: Thu Jan 01 00:00:00 1970 +0000
878 878 summary: 0
879 879
880 880 $ hg book should-end-on-two
881 881 $ hg co --clean 4
882 882 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
883 883 (leaving bookmark should-end-on-two)
884 884 $ hg book four
885 885 $ hg --config extensions.mq= strip 3
886 886 saved backup bundle to * (glob)
887 887 should-end-on-two should end up pointing to revision 2, as that's the
888 888 tipmost surviving ancestor of the stripped revision.
889 889 $ hg log --graph
890 890 @ changeset: 3:9ba5f110a0b3
891 891 | branch: test
892 892 | bookmark: four
893 893 | tag: tip
894 894 | user: test
895 895 | date: Thu Jan 01 00:00:00 1970 +0000
896 896 | summary: y
897 897 |
898 898 o changeset: 2:db815d6d32e6
899 899 | bookmark: should-end-on-two
900 900 | parent: 0:f7b1eb17ad24
901 901 | user: test
902 902 | date: Thu Jan 01 00:00:00 1970 +0000
903 903 | summary: 2
904 904 |
905 905 | o changeset: 1:925d80f479bb
906 906 |/ user: test
907 907 | date: Thu Jan 01 00:00:00 1970 +0000
908 908 | summary: 1
909 909 |
910 910 o changeset: 0:f7b1eb17ad24
911 911 user: test
912 912 date: Thu Jan 01 00:00:00 1970 +0000
913 913 summary: 0
914 914
915 915
916 916 no-op update doesn't deactivate bookmarks
917 917
918 918 (it is known issue that fsmonitor can't handle nested repositories. In
919 919 this test scenario, cloned-bookmark-default and tobundle exist in the
920 920 working directory of current repository)
921 921
922 922 $ hg bookmarks
923 923 * four 3:9ba5f110a0b3
924 924 should-end-on-two 2:db815d6d32e6
925 925 $ hg up four
926 926 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
927 927 $ hg up
928 928 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
929 929 $ hg sum
930 930 parent: 3:9ba5f110a0b3 tip
931 931 y
932 932 branch: test
933 933 bookmarks: *four
934 934 commit: 2 unknown (clean) (no-fsmonitor !)
935 935 commit: * unknown (clean) (glob) (fsmonitor !)
936 936 update: (current)
937 937 phases: 4 draft
938 938
939 939 test clearing divergent bookmarks of linear ancestors
940 940
941 941 $ hg bookmark Z -r 0
942 942 $ hg bookmark Z@1 -r 1
943 943 $ hg bookmark Z@2 -r 2
944 944 $ hg bookmark Z@3 -r 3
945 945 $ hg book
946 946 Z 0:f7b1eb17ad24
947 947 Z@1 1:925d80f479bb
948 948 Z@2 2:db815d6d32e6
949 949 Z@3 3:9ba5f110a0b3
950 950 * four 3:9ba5f110a0b3
951 951 should-end-on-two 2:db815d6d32e6
952 952 $ hg bookmark Z
953 953 moving bookmark 'Z' forward from f7b1eb17ad24
954 954 $ hg book
955 955 * Z 3:9ba5f110a0b3
956 956 Z@1 1:925d80f479bb
957 957 four 3:9ba5f110a0b3
958 958 should-end-on-two 2:db815d6d32e6
959 959
960 960 test clearing only a single divergent bookmark across branches
961 961
962 962 $ hg book foo -r 1
963 963 $ hg book foo@1 -r 0
964 964 $ hg book foo@2 -r 2
965 965 $ hg book foo@3 -r 3
966 966 $ hg book foo -r foo@3
967 967 $ hg book
968 968 * Z 3:9ba5f110a0b3
969 969 Z@1 1:925d80f479bb
970 970 foo 3:9ba5f110a0b3
971 971 foo@1 0:f7b1eb17ad24
972 972 foo@2 2:db815d6d32e6
973 973 four 3:9ba5f110a0b3
974 974 should-end-on-two 2:db815d6d32e6
975 975
976 976 pull --update works the same as pull && update (case #2)
977 977
978 978 It is assumed that "hg pull" itself doesn't update current active
979 979 bookmark ('Y' in tests below).
980 980
981 981 $ hg pull -q ../cloned-bookmarks-update
982 982 divergent bookmark Z stored as Z@2
983 983
984 984 (pulling revision on another named branch with --update updates
985 985 neither the working directory nor current active bookmark: "no-op"
986 986 case)
987 987
988 988 $ echo yy >> y
989 989 $ hg commit -m yy
990 990
991 991 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
992 992 * Y 3:125c9a1d6df6
993 993 $ hg -R ../cloned-bookmarks-update pull . --update
994 994 pulling from .
995 995 searching for changes
996 996 adding changesets
997 997 adding manifests
998 998 adding file changes
999 added 1 changesets with 1 changes to 1 files
1000 999 divergent bookmark Z stored as Z@default
1001 1000 adding remote bookmark foo
1002 1001 adding remote bookmark four
1003 1002 adding remote bookmark should-end-on-two
1003 added 1 changesets with 1 changes to 1 files
1004 1004 new changesets 5fb12f0f2d51
1005 1005 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1006 1006 $ hg -R ../cloned-bookmarks-update parents -T "{rev}:{node|short}\n"
1007 1007 3:125c9a1d6df6
1008 1008 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
1009 1009 * Y 3:125c9a1d6df6
1010 1010
1011 1011 (pulling revision on current named/topological branch with --update
1012 1012 updates the working directory and current active bookmark)
1013 1013
1014 1014 $ hg update -C -q 125c9a1d6df6
1015 1015 $ echo xx >> x
1016 1016 $ hg commit -m xx
1017 1017
1018 1018 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
1019 1019 * Y 3:125c9a1d6df6
1020 1020 $ hg -R ../cloned-bookmarks-update pull . --update
1021 1021 pulling from .
1022 1022 searching for changes
1023 1023 adding changesets
1024 1024 adding manifests
1025 1025 adding file changes
1026 divergent bookmark Z stored as Z@default
1026 1027 added 1 changesets with 1 changes to 1 files
1027 divergent bookmark Z stored as Z@default
1028 1028 new changesets 81dcce76aa0b
1029 1029 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1030 1030 updating bookmark Y
1031 1031 $ hg -R ../cloned-bookmarks-update parents -T "{rev}:{node|short}\n"
1032 1032 6:81dcce76aa0b
1033 1033 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
1034 1034 * Y 6:81dcce76aa0b
1035 1035
1036 1036 $ cd ..
1037 1037
1038 1038 ensure changelog is written before bookmarks
1039 1039 $ hg init orderrepo
1040 1040 $ cd orderrepo
1041 1041 $ touch a
1042 1042 $ hg commit -Aqm one
1043 1043 $ hg book mybook
1044 1044 $ echo a > a
1045 1045
1046 1046 $ cat > $TESTTMP/pausefinalize.py <<EOF
1047 1047 > from __future__ import absolute_import
1048 1048 > import os
1049 1049 > import time
1050 1050 > from mercurial import extensions, localrepo
1051 1051 > def transaction(orig, self, desc, report=None):
1052 1052 > tr = orig(self, desc, report)
1053 1053 > def sleep(*args, **kwargs):
1054 1054 > retry = 20
1055 1055 > while retry > 0 and not os.path.exists(b"$TESTTMP/unpause"):
1056 1056 > retry -= 1
1057 1057 > time.sleep(0.5)
1058 1058 > if os.path.exists(b"$TESTTMP/unpause"):
1059 1059 > os.remove(b"$TESTTMP/unpause")
1060 1060 > # It is important that this finalizer start with 'a', so it runs before
1061 1061 > # the changelog finalizer appends to the changelog.
1062 1062 > tr.addfinalize(b'a-sleep', sleep)
1063 1063 > return tr
1064 1064 >
1065 1065 > def extsetup(ui):
1066 1066 > # This extension inserts an artifical pause during the transaction
1067 1067 > # finalizer, so we can run commands mid-transaction-close.
1068 1068 > extensions.wrapfunction(localrepo.localrepository, 'transaction',
1069 1069 > transaction)
1070 1070 > EOF
1071 1071 $ hg commit -qm two --config extensions.pausefinalize=$TESTTMP/pausefinalize.py &
1072 1072 $ sleep 2
1073 1073 $ hg log -r .
1074 1074 changeset: 0:867bc5792c8c
1075 1075 bookmark: mybook
1076 1076 tag: tip
1077 1077 user: test
1078 1078 date: Thu Jan 01 00:00:00 1970 +0000
1079 1079 summary: one
1080 1080
1081 1081 $ hg bookmarks
1082 1082 * mybook 0:867bc5792c8c
1083 1083 $ touch $TESTTMP/unpause
1084 1084
1085 1085 $ cd ..
1086 1086
1087 1087 check whether HG_PENDING makes pending changes only in related
1088 1088 repositories visible to an external hook.
1089 1089
1090 1090 (emulate a transaction running concurrently by copied
1091 1091 .hg/bookmarks.pending in subsequent test)
1092 1092
1093 1093 $ cat > $TESTTMP/savepending.sh <<EOF
1094 1094 > cp .hg/bookmarks.pending .hg/bookmarks.pending.saved
1095 1095 > exit 1 # to avoid adding new bookmark for subsequent tests
1096 1096 > EOF
1097 1097
1098 1098 $ hg init unrelated
1099 1099 $ cd unrelated
1100 1100 $ echo a > a
1101 1101 $ hg add a
1102 1102 $ hg commit -m '#0'
1103 1103 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" bookmarks INVISIBLE
1104 1104 transaction abort!
1105 1105 rollback completed
1106 1106 abort: pretxnclose hook exited with status 1
1107 1107 [255]
1108 1108 $ cp .hg/bookmarks.pending.saved .hg/bookmarks.pending
1109 1109
1110 1110 (check visible bookmarks while transaction running in repo)
1111 1111
1112 1112 $ cat > $TESTTMP/checkpending.sh <<EOF
1113 1113 > echo "@repo"
1114 1114 > hg -R "$TESTTMP/repo" bookmarks
1115 1115 > echo "@unrelated"
1116 1116 > hg -R "$TESTTMP/unrelated" bookmarks
1117 1117 > exit 1 # to avoid adding new bookmark for subsequent tests
1118 1118 > EOF
1119 1119
1120 1120 $ cd ../repo
1121 1121 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" bookmarks NEW
1122 1122 @repo
1123 1123 * NEW 6:81dcce76aa0b
1124 1124 X2 1:925d80f479bb
1125 1125 Y 4:125c9a1d6df6
1126 1126 Z 5:5fb12f0f2d51
1127 1127 Z@1 1:925d80f479bb
1128 1128 Z@2 4:125c9a1d6df6
1129 1129 foo 3:9ba5f110a0b3
1130 1130 foo@1 0:f7b1eb17ad24
1131 1131 foo@2 2:db815d6d32e6
1132 1132 four 3:9ba5f110a0b3
1133 1133 should-end-on-two 2:db815d6d32e6
1134 1134 x y 2:db815d6d32e6
1135 1135 @unrelated
1136 1136 no bookmarks set
1137 1137 transaction abort!
1138 1138 rollback completed
1139 1139 abort: pretxnclose hook exited with status 1
1140 1140 [255]
1141 1141
1142 1142 Check pretxnclose-bookmark can abort a transaction
1143 1143 --------------------------------------------------
1144 1144
1145 1145 add hooks:
1146 1146
1147 1147 * to prevent NEW bookmark on a non-public changeset
1148 1148 * to prevent non-forward move of NEW bookmark
1149 1149
1150 1150 $ cat << EOF >> .hg/hgrc
1151 1151 > [hooks]
1152 1152 > pretxnclose-bookmark.force-public = sh -c "(echo \$HG_BOOKMARK| grep -v NEW > /dev/null) || [ -z \"\$HG_NODE\" ] || (hg log -r \"\$HG_NODE\" -T '{phase}' | grep public > /dev/null)"
1153 1153 > pretxnclose-bookmark.force-forward = sh -c "(echo \$HG_BOOKMARK| grep -v NEW > /dev/null) || [ -z \"\$HG_NODE\" ] || (hg log -r \"max(\$HG_OLDNODE::\$HG_NODE)\" -T 'MATCH' | grep MATCH > /dev/null)"
1154 1154 > EOF
1155 1155
1156 1156 $ hg log -G -T phases
1157 1157 @ changeset: 6:81dcce76aa0b
1158 1158 | tag: tip
1159 1159 | phase: draft
1160 1160 | parent: 4:125c9a1d6df6
1161 1161 | user: test
1162 1162 | date: Thu Jan 01 00:00:00 1970 +0000
1163 1163 | summary: xx
1164 1164 |
1165 1165 | o changeset: 5:5fb12f0f2d51
1166 1166 | | branch: test
1167 1167 | | bookmark: Z
1168 1168 | | phase: draft
1169 1169 | | parent: 3:9ba5f110a0b3
1170 1170 | | user: test
1171 1171 | | date: Thu Jan 01 00:00:00 1970 +0000
1172 1172 | | summary: yy
1173 1173 | |
1174 1174 o | changeset: 4:125c9a1d6df6
1175 1175 | | bookmark: Y
1176 1176 | | bookmark: Z@2
1177 1177 | | phase: public
1178 1178 | | parent: 2:db815d6d32e6
1179 1179 | | user: test
1180 1180 | | date: Thu Jan 01 00:00:00 1970 +0000
1181 1181 | | summary: x
1182 1182 | |
1183 1183 | o changeset: 3:9ba5f110a0b3
1184 1184 |/ branch: test
1185 1185 | bookmark: foo
1186 1186 | bookmark: four
1187 1187 | phase: public
1188 1188 | user: test
1189 1189 | date: Thu Jan 01 00:00:00 1970 +0000
1190 1190 | summary: y
1191 1191 |
1192 1192 o changeset: 2:db815d6d32e6
1193 1193 | bookmark: foo@2
1194 1194 | bookmark: should-end-on-two
1195 1195 | bookmark: x y
1196 1196 | phase: public
1197 1197 | parent: 0:f7b1eb17ad24
1198 1198 | user: test
1199 1199 | date: Thu Jan 01 00:00:00 1970 +0000
1200 1200 | summary: 2
1201 1201 |
1202 1202 | o changeset: 1:925d80f479bb
1203 1203 |/ bookmark: X2
1204 1204 | bookmark: Z@1
1205 1205 | phase: public
1206 1206 | user: test
1207 1207 | date: Thu Jan 01 00:00:00 1970 +0000
1208 1208 | summary: 1
1209 1209 |
1210 1210 o changeset: 0:f7b1eb17ad24
1211 1211 bookmark: foo@1
1212 1212 phase: public
1213 1213 user: test
1214 1214 date: Thu Jan 01 00:00:00 1970 +0000
1215 1215 summary: 0
1216 1216
1217 1217
1218 1218 attempt to create on a default changeset
1219 1219
1220 1220 $ hg bookmark -r 81dcce76aa0b NEW
1221 1221 transaction abort!
1222 1222 rollback completed
1223 1223 abort: pretxnclose-bookmark.force-public hook exited with status 1
1224 1224 [255]
1225 1225
1226 1226 create on a public changeset
1227 1227
1228 1228 $ hg bookmark -r 9ba5f110a0b3 NEW
1229 1229
1230 1230 move to the other branch
1231 1231
1232 1232 $ hg bookmark -f -r 125c9a1d6df6 NEW
1233 1233 transaction abort!
1234 1234 rollback completed
1235 1235 abort: pretxnclose-bookmark.force-forward hook exited with status 1
1236 1236 [255]
@@ -1,1166 +1,1151 b''
1 1 #testcases sshv1 sshv2
2 2
3 3 #if sshv2
4 4 $ cat >> $HGRCPATH << EOF
5 5 > [experimental]
6 6 > sshpeer.advertise-v2 = true
7 7 > sshserver.support-v2 = true
8 8 > EOF
9 9 #endif
10 10
11 11 Test exchange of common information using bundle2
12 12
13 13
14 14 $ getmainid() {
15 15 > hg -R main log --template '{node}\n' --rev "$1"
16 16 > }
17 17
18 18 enable obsolescence
19 19
20 20 $ cp $HGRCPATH $TESTTMP/hgrc.orig
21 21 $ cat > $TESTTMP/bundle2-pushkey-hook.sh << EOF
22 22 > echo pushkey: lock state after \"\$HG_NAMESPACE\"
23 23 > hg debuglock
24 24 > EOF
25 25
26 26 $ cat >> $HGRCPATH << EOF
27 27 > [experimental]
28 28 > evolution.createmarkers=True
29 29 > evolution.exchange=True
30 30 > bundle2-output-capture=True
31 31 > [ui]
32 32 > ssh="$PYTHON" "$TESTDIR/dummyssh"
33 33 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
34 34 > [web]
35 35 > push_ssl = false
36 36 > allow_push = *
37 37 > [phases]
38 38 > publish=False
39 39 > [hooks]
40 40 > pretxnclose.tip = hg log -r tip -T "pre-close-tip:{node|short} {phase} {bookmarks}\n"
41 41 > txnclose.tip = hg log -r tip -T "postclose-tip:{node|short} {phase} {bookmarks}\n"
42 42 > txnclose.env = sh -c "HG_LOCAL= printenv.py txnclose"
43 43 > pushkey= sh "$TESTTMP/bundle2-pushkey-hook.sh"
44 44 > EOF
45 45
46 46 The extension requires a repo (currently unused)
47 47
48 48 $ hg init main
49 49 $ cd main
50 50 $ touch a
51 51 $ hg add a
52 52 $ hg commit -m 'a'
53 53 pre-close-tip:3903775176ed draft
54 54 postclose-tip:3903775176ed draft
55 55 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
56 56
57 57 $ hg unbundle $TESTDIR/bundles/rebase.hg
58 58 adding changesets
59 59 adding manifests
60 60 adding file changes
61 pre-close-tip:02de42196ebe draft
61 62 added 8 changesets with 7 changes to 7 files (+3 heads)
62 pre-close-tip:02de42196ebe draft
63 63 new changesets cd010b8cd998:02de42196ebe (8 drafts)
64 64 postclose-tip:02de42196ebe draft
65 65 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_NODE_LAST=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=unbundle HG_TXNID=TXN:$ID$ HG_TXNNAME=unbundle
66 66 bundle:*/tests/bundles/rebase.hg HG_URL=bundle:*/tests/bundles/rebase.hg (glob)
67 67 (run 'hg heads' to see heads, 'hg merge' to merge)
68 68
69 69 $ cd ..
70 70
71 71 Real world exchange
72 72 =====================
73 73
74 74 Add more obsolescence information
75 75
76 76 $ hg -R main debugobsolete -d '0 0' 1111111111111111111111111111111111111111 `getmainid 9520eea781bc`
77 77 pre-close-tip:02de42196ebe draft
78 78 1 new obsolescence markers
79 79 postclose-tip:02de42196ebe draft
80 80 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
81 81 $ hg -R main debugobsolete -d '0 0' 2222222222222222222222222222222222222222 `getmainid 24b6387c8c8c`
82 82 pre-close-tip:02de42196ebe draft
83 83 1 new obsolescence markers
84 84 postclose-tip:02de42196ebe draft
85 85 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
86 86
87 87 clone --pull
88 88
89 89 $ hg -R main phase --public cd010b8cd998
90 90 pre-close-tip:02de42196ebe draft
91 91 postclose-tip:02de42196ebe draft
92 92 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
93 93 $ hg clone main other --pull --rev 9520eea781bc
94 94 adding changesets
95 95 adding manifests
96 96 adding file changes
97 pre-close-tip:9520eea781bc draft
97 98 added 2 changesets with 2 changes to 2 files
98 pre-close-tip:9520eea781bc draft
99 99 1 new obsolescence markers
100 100 new changesets cd010b8cd998:9520eea781bc (1 drafts)
101 101 postclose-tip:9520eea781bc draft
102 102 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_NODE_LAST=9520eea781bcca16c1e15acc0ba14335a0e8e5ba HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
103 103 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
104 104 updating to branch default
105 105 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 106 $ hg -R other log -G
107 107 @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
108 108 |
109 109 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
110 110
111 111 $ hg -R other debugobsolete
112 112 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
113 113
114 114 pull
115 115
116 116 $ hg -R main phase --public 9520eea781bc
117 117 pre-close-tip:02de42196ebe draft
118 118 postclose-tip:02de42196ebe draft
119 119 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
120 120 $ hg -R other pull -r 24b6387c8c8c
121 121 pulling from $TESTTMP/main
122 122 searching for changes
123 123 adding changesets
124 124 adding manifests
125 125 adding file changes
126 pre-close-tip:24b6387c8c8c draft
126 127 added 1 changesets with 1 changes to 1 files (+1 heads)
127 pre-close-tip:24b6387c8c8c draft
128 128 1 new obsolescence markers
129 129 new changesets 24b6387c8c8c (1 drafts)
130 130 postclose-tip:24b6387c8c8c draft
131 131 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_NODE_LAST=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
132 132 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
133 133 (run 'hg heads' to see heads, 'hg merge' to merge)
134 134 $ hg -R other log -G
135 135 o 2:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
136 136 |
137 137 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
138 138 |/
139 139 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
140 140
141 141 $ hg -R other debugobsolete
142 142 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
143 143 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
144 144
145 145 pull empty (with phase movement)
146 146
147 147 $ hg -R main phase --public 24b6387c8c8c
148 148 pre-close-tip:02de42196ebe draft
149 149 postclose-tip:02de42196ebe draft
150 150 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
151 151 $ hg -R other pull -r 24b6387c8c8c
152 152 pulling from $TESTTMP/main
153 153 no changes found
154 154 pre-close-tip:24b6387c8c8c public
155 155 1 local changesets published
156 156 postclose-tip:24b6387c8c8c public
157 157 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=0 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
158 158 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
159 159 $ hg -R other log -G
160 160 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
161 161 |
162 162 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
163 163 |/
164 164 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
165 165
166 166 $ hg -R other debugobsolete
167 167 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
168 168 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
169 169
170 170 pull empty
171 171
172 172 $ hg -R other pull -r 24b6387c8c8c
173 173 pulling from $TESTTMP/main
174 174 no changes found
175 175 pre-close-tip:24b6387c8c8c public
176 176 postclose-tip:24b6387c8c8c public
177 177 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=0 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
178 178 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
179 179 $ hg -R other log -G
180 180 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
181 181 |
182 182 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
183 183 |/
184 184 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
185 185
186 186 $ hg -R other debugobsolete
187 187 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
188 188 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
189 189
190 190 add extra data to test their exchange during push
191 191
192 192 $ hg -R main bookmark --rev eea13746799a book_eea1
193 193 pre-close-tip:02de42196ebe draft
194 194 postclose-tip:02de42196ebe draft
195 195 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
196 196 $ hg -R main debugobsolete -d '0 0' 3333333333333333333333333333333333333333 `getmainid eea13746799a`
197 197 pre-close-tip:02de42196ebe draft
198 198 1 new obsolescence markers
199 199 postclose-tip:02de42196ebe draft
200 200 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
201 201 $ hg -R main bookmark --rev 02de42196ebe book_02de
202 202 pre-close-tip:02de42196ebe draft book_02de
203 203 postclose-tip:02de42196ebe draft book_02de
204 204 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
205 205 $ hg -R main debugobsolete -d '0 0' 4444444444444444444444444444444444444444 `getmainid 02de42196ebe`
206 206 pre-close-tip:02de42196ebe draft book_02de
207 207 1 new obsolescence markers
208 208 postclose-tip:02de42196ebe draft book_02de
209 209 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
210 210 $ hg -R main bookmark --rev 42ccdea3bb16 book_42cc
211 211 pre-close-tip:02de42196ebe draft book_02de
212 212 postclose-tip:02de42196ebe draft book_02de
213 213 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
214 214 $ hg -R main debugobsolete -d '0 0' 5555555555555555555555555555555555555555 `getmainid 42ccdea3bb16`
215 215 pre-close-tip:02de42196ebe draft book_02de
216 216 1 new obsolescence markers
217 217 postclose-tip:02de42196ebe draft book_02de
218 218 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
219 219 $ hg -R main bookmark --rev 5fddd98957c8 book_5fdd
220 220 pre-close-tip:02de42196ebe draft book_02de
221 221 postclose-tip:02de42196ebe draft book_02de
222 222 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
223 223 $ hg -R main debugobsolete -d '0 0' 6666666666666666666666666666666666666666 `getmainid 5fddd98957c8`
224 224 pre-close-tip:02de42196ebe draft book_02de
225 225 1 new obsolescence markers
226 226 postclose-tip:02de42196ebe draft book_02de
227 227 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
228 228 $ hg -R main bookmark --rev 32af7686d403 book_32af
229 229 pre-close-tip:02de42196ebe draft book_02de
230 230 postclose-tip:02de42196ebe draft book_02de
231 231 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
232 232 $ hg -R main debugobsolete -d '0 0' 7777777777777777777777777777777777777777 `getmainid 32af7686d403`
233 233 pre-close-tip:02de42196ebe draft book_02de
234 234 1 new obsolescence markers
235 235 postclose-tip:02de42196ebe draft book_02de
236 236 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
237 237
238 238 $ hg -R other bookmark --rev cd010b8cd998 book_eea1
239 239 pre-close-tip:24b6387c8c8c public
240 240 postclose-tip:24b6387c8c8c public
241 241 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
242 242 $ hg -R other bookmark --rev cd010b8cd998 book_02de
243 243 pre-close-tip:24b6387c8c8c public
244 244 postclose-tip:24b6387c8c8c public
245 245 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
246 246 $ hg -R other bookmark --rev cd010b8cd998 book_42cc
247 247 pre-close-tip:24b6387c8c8c public
248 248 postclose-tip:24b6387c8c8c public
249 249 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
250 250 $ hg -R other bookmark --rev cd010b8cd998 book_5fdd
251 251 pre-close-tip:24b6387c8c8c public
252 252 postclose-tip:24b6387c8c8c public
253 253 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
254 254 $ hg -R other bookmark --rev cd010b8cd998 book_32af
255 255 pre-close-tip:24b6387c8c8c public
256 256 postclose-tip:24b6387c8c8c public
257 257 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
258 258
259 259 $ hg -R main phase --public eea13746799a
260 260 pre-close-tip:02de42196ebe draft book_02de
261 261 postclose-tip:02de42196ebe draft book_02de
262 262 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
263 263
264 264 push
265 265 $ hg -R main push other --rev eea13746799a --bookmark book_eea1
266 266 pushing to other
267 267 searching for changes
268 268 remote: adding changesets
269 269 remote: adding manifests
270 270 remote: adding file changes
271 remote: pre-close-tip:eea13746799a public book_eea1
271 272 remote: added 1 changesets with 0 changes to 0 files (-1 heads)
272 remote: pre-close-tip:eea13746799a public book_eea1
273 273 remote: 1 new obsolescence markers
274 274 remote: pushkey: lock state after "bookmarks"
275 275 remote: lock: free
276 276 remote: wlock: free
277 277 remote: postclose-tip:eea13746799a public book_eea1
278 278 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_NODE_LAST=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_PHASES_MOVED=1 HG_SOURCE=push HG_TXNID=TXN:$ID$ HG_TXNNAME=push HG_URL=file:$TESTTMP/other
279 279 updating bookmark book_eea1
280 280 pre-close-tip:02de42196ebe draft book_02de
281 281 postclose-tip:02de42196ebe draft book_02de
282 282 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_SOURCE=push-response HG_TXNID=TXN:$ID$ HG_TXNNAME=push-response
283 283 file:/*/$TESTTMP/other HG_URL=file:$TESTTMP/other (glob)
284 284 $ hg -R other log -G
285 285 o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
286 286 |\
287 287 | o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
288 288 | |
289 289 @ | 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
290 290 |/
291 291 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de book_32af book_42cc book_5fdd A
292 292
293 293 $ hg -R other debugobsolete
294 294 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
295 295 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
296 296 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
297 297
298 298 pull over ssh
299 299
300 300 $ hg -R other pull ssh://user@dummy/main -r 02de42196ebe --bookmark book_02de
301 301 pulling from ssh://user@dummy/main
302 302 searching for changes
303 303 adding changesets
304 304 adding manifests
305 305 adding file changes
306 added 1 changesets with 1 changes to 1 files (+1 heads)
307 306 updating bookmark book_02de
308 307 pre-close-tip:02de42196ebe draft book_02de
308 added 1 changesets with 1 changes to 1 files (+1 heads)
309 309 1 new obsolescence markers
310 310 new changesets 02de42196ebe (1 drafts)
311 311 postclose-tip:02de42196ebe draft book_02de
312 312 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_NODE_LAST=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
313 313 ssh://user@dummy/main HG_URL=ssh://user@dummy/main
314 314 (run 'hg heads' to see heads, 'hg merge' to merge)
315 315 $ hg -R other debugobsolete
316 316 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
317 317 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
318 318 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
319 319 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
320 320
321 321 pull over http
322 322
323 323 $ hg serve -R main -p $HGPORT -d --pid-file=main.pid -E main-error.log
324 324 $ cat main.pid >> $DAEMON_PIDS
325 325
326 326 $ hg -R other pull http://localhost:$HGPORT/ -r 42ccdea3bb16 --bookmark book_42cc
327 327 pulling from http://localhost:$HGPORT/
328 328 searching for changes
329 329 adding changesets
330 330 adding manifests
331 331 adding file changes
332 added 1 changesets with 1 changes to 1 files (+1 heads)
333 332 updating bookmark book_42cc
334 333 pre-close-tip:42ccdea3bb16 draft book_42cc
334 added 1 changesets with 1 changes to 1 files (+1 heads)
335 335 1 new obsolescence markers
336 336 new changesets 42ccdea3bb16 (1 drafts)
337 337 postclose-tip:42ccdea3bb16 draft book_42cc
338 338 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_NODE_LAST=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
339 339 http://localhost:$HGPORT/ HG_URL=http://localhost:$HGPORT/
340 340 (run 'hg heads .' to see heads, 'hg merge' to merge)
341 341 $ cat main-error.log
342 342 $ hg -R other debugobsolete
343 343 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
344 344 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
345 345 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
346 346 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
347 347 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
348 348
349 349 push over ssh
350 350
351 351 $ hg -R main push ssh://user@dummy/other -r 5fddd98957c8 --bookmark book_5fdd
352 352 pushing to ssh://user@dummy/other
353 353 searching for changes
354 354 remote: adding changesets
355 355 remote: adding manifests
356 356 remote: adding file changes
357 remote: pre-close-tip:5fddd98957c8 draft book_5fdd
357 358 remote: added 1 changesets with 1 changes to 1 files
358 remote: pre-close-tip:5fddd98957c8 draft book_5fdd
359 359 remote: 1 new obsolescence markers
360 360 remote: pushkey: lock state after "bookmarks"
361 361 remote: lock: free
362 362 remote: wlock: free
363 363 remote: postclose-tip:5fddd98957c8 draft book_5fdd
364 364 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_NODE_LAST=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_TXNNAME=serve HG_URL=remote:ssh:$LOCALIP
365 365 updating bookmark book_5fdd
366 366 pre-close-tip:02de42196ebe draft book_02de
367 367 postclose-tip:02de42196ebe draft book_02de
368 368 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_SOURCE=push-response HG_TXNID=TXN:$ID$ HG_TXNNAME=push-response
369 369 ssh://user@dummy/other HG_URL=ssh://user@dummy/other
370 370 $ hg -R other log -G
371 371 o 6:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
372 372 |
373 373 o 5:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
374 374 |
375 375 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
376 376 | |
377 377 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
378 378 | |/|
379 379 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
380 380 |/ /
381 381 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
382 382 |/
383 383 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af A
384 384
385 385 $ hg -R other debugobsolete
386 386 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
387 387 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
388 388 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
389 389 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
390 390 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
391 391 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
392 392
393 393 push over http
394 394
395 395 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
396 396 $ cat other.pid >> $DAEMON_PIDS
397 397
398 398 $ hg -R main phase --public 32af7686d403
399 399 pre-close-tip:02de42196ebe draft book_02de
400 400 postclose-tip:02de42196ebe draft book_02de
401 401 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
402 402 $ hg -R main push http://localhost:$HGPORT2/ -r 32af7686d403 --bookmark book_32af
403 403 pushing to http://localhost:$HGPORT2/
404 404 searching for changes
405 405 remote: adding changesets
406 406 remote: adding manifests
407 407 remote: adding file changes
408 remote: pre-close-tip:32af7686d403 public book_32af
408 409 remote: added 1 changesets with 1 changes to 1 files
409 remote: pre-close-tip:32af7686d403 public book_32af
410 410 remote: 1 new obsolescence markers
411 411 remote: pushkey: lock state after "bookmarks"
412 412 remote: lock: free
413 413 remote: wlock: free
414 414 remote: postclose-tip:32af7686d403 public book_32af
415 415 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=32af7686d403cf45b5d95f2d70cebea587ac806a HG_NODE_LAST=32af7686d403cf45b5d95f2d70cebea587ac806a HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_TXNNAME=serve HG_URL=remote:http:$LOCALIP: (glob)
416 416 updating bookmark book_32af
417 417 pre-close-tip:02de42196ebe draft book_02de
418 418 postclose-tip:02de42196ebe draft book_02de
419 419 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_SOURCE=push-response HG_TXNID=TXN:$ID$ HG_TXNNAME=push-response
420 420 http://localhost:$HGPORT2/ HG_URL=http://localhost:$HGPORT2/
421 421 $ cat other-error.log
422 422
423 423 Check final content.
424 424
425 425 $ hg -R other log -G
426 426 o 7:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af D
427 427 |
428 428 o 6:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
429 429 |
430 430 o 5:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
431 431 |
432 432 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
433 433 | |
434 434 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
435 435 | |/|
436 436 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
437 437 |/ /
438 438 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
439 439 |/
440 440 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
441 441
442 442 $ hg -R other debugobsolete
443 443 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
444 444 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
445 445 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
446 446 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
447 447 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
448 448 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
449 449 7777777777777777777777777777777777777777 32af7686d403cf45b5d95f2d70cebea587ac806a 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
450 450
451 451 (check that no 'pending' files remain)
452 452
453 453 $ ls -1 other/.hg/bookmarks*
454 454 other/.hg/bookmarks
455 455 $ ls -1 other/.hg/store/phaseroots*
456 456 other/.hg/store/phaseroots
457 457 $ ls -1 other/.hg/store/00changelog.i*
458 458 other/.hg/store/00changelog.i
459 459
460 460 Error Handling
461 461 ==============
462 462
463 463 Check that errors are properly returned to the client during push.
464 464
465 465 Setting up
466 466
467 467 $ cat > failpush.py << EOF
468 468 > """A small extension that makes push fails when using bundle2
469 469 >
470 470 > used to test error handling in bundle2
471 471 > """
472 472 >
473 473 > from mercurial import error
474 474 > from mercurial import bundle2
475 475 > from mercurial import exchange
476 476 > from mercurial import extensions
477 477 > from mercurial import registrar
478 478 > cmdtable = {}
479 479 > command = registrar.command(cmdtable)
480 480 >
481 481 > configtable = {}
482 482 > configitem = registrar.configitem(configtable)
483 483 > configitem(b'failpush', b'reason',
484 484 > default=None,
485 485 > )
486 486 >
487 487 > def _pushbundle2failpart(pushop, bundler):
488 488 > reason = pushop.ui.config(b'failpush', b'reason')
489 489 > part = None
490 490 > if reason == b'abort':
491 491 > bundler.newpart(b'test:abort')
492 492 > if reason == b'unknown':
493 493 > bundler.newpart(b'test:unknown')
494 494 > if reason == b'race':
495 495 > # 20 Bytes of crap
496 496 > bundler.newpart(b'check:heads', data=b'01234567890123456789')
497 497 >
498 498 > @bundle2.parthandler(b"test:abort")
499 499 > def handleabort(op, part):
500 500 > raise error.Abort(b'Abandon ship!', hint=b"don't panic")
501 501 >
502 502 > def uisetup(ui):
503 503 > exchange.b2partsgenmapping[b'failpart'] = _pushbundle2failpart
504 504 > exchange.b2partsgenorder.insert(0, b'failpart')
505 505 >
506 506 > EOF
507 507
508 508 $ cd main
509 509 $ hg up tip
510 510 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
511 511 $ echo 'I' > I
512 512 $ hg add I
513 513 $ hg ci -m 'I'
514 514 pre-close-tip:e7ec4e813ba6 draft
515 515 postclose-tip:e7ec4e813ba6 draft
516 516 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
517 517 $ hg id
518 518 e7ec4e813ba6 tip
519 519 $ cd ..
520 520
521 521 $ cat << EOF >> $HGRCPATH
522 522 > [extensions]
523 523 > failpush=$TESTTMP/failpush.py
524 524 > EOF
525 525
526 526 $ killdaemons.py
527 527 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
528 528 $ cat other.pid >> $DAEMON_PIDS
529 529
530 530 Doing the actual push: Abort error
531 531
532 532 $ cat << EOF >> $HGRCPATH
533 533 > [failpush]
534 534 > reason = abort
535 535 > EOF
536 536
537 537 $ hg -R main push other -r e7ec4e813ba6
538 538 pushing to other
539 539 searching for changes
540 540 abort: Abandon ship!
541 541 (don't panic)
542 542 [255]
543 543
544 544 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
545 545 pushing to ssh://user@dummy/other
546 546 searching for changes
547 547 remote: Abandon ship!
548 548 remote: (don't panic)
549 549 abort: push failed on remote
550 550 [255]
551 551
552 552 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
553 553 pushing to http://localhost:$HGPORT2/
554 554 searching for changes
555 555 remote: Abandon ship!
556 556 remote: (don't panic)
557 557 abort: push failed on remote
558 558 [255]
559 559
560 560
561 561 Doing the actual push: unknown mandatory parts
562 562
563 563 $ cat << EOF >> $HGRCPATH
564 564 > [failpush]
565 565 > reason = unknown
566 566 > EOF
567 567
568 568 $ hg -R main push other -r e7ec4e813ba6
569 569 pushing to other
570 570 searching for changes
571 571 abort: missing support for test:unknown
572 572 [255]
573 573
574 574 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
575 575 pushing to ssh://user@dummy/other
576 576 searching for changes
577 577 abort: missing support for test:unknown
578 578 [255]
579 579
580 580 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
581 581 pushing to http://localhost:$HGPORT2/
582 582 searching for changes
583 583 abort: missing support for test:unknown
584 584 [255]
585 585
586 586 Doing the actual push: race
587 587
588 588 $ cat << EOF >> $HGRCPATH
589 589 > [failpush]
590 590 > reason = race
591 591 > EOF
592 592
593 593 $ hg -R main push other -r e7ec4e813ba6
594 594 pushing to other
595 595 searching for changes
596 596 abort: push failed:
597 597 'remote repository changed while pushing - please try again'
598 598 [255]
599 599
600 600 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
601 601 pushing to ssh://user@dummy/other
602 602 searching for changes
603 603 abort: push failed:
604 604 'remote repository changed while pushing - please try again'
605 605 [255]
606 606
607 607 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
608 608 pushing to http://localhost:$HGPORT2/
609 609 searching for changes
610 610 abort: push failed:
611 611 'remote repository changed while pushing - please try again'
612 612 [255]
613 613
614 614 Doing the actual push: hook abort
615 615
616 616 $ cat << EOF >> $HGRCPATH
617 617 > [failpush]
618 618 > reason =
619 619 > [hooks]
620 620 > pretxnclose.failpush = sh -c "echo 'You shall not pass!'; false"
621 621 > txnabort.failpush = sh -c "echo 'Cleaning up the mess...'"
622 622 > EOF
623 623
624 624 $ killdaemons.py
625 625 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
626 626 $ cat other.pid >> $DAEMON_PIDS
627 627
628 628 $ hg -R main push other -r e7ec4e813ba6
629 629 pushing to other
630 630 searching for changes
631 631 remote: adding changesets
632 632 remote: adding manifests
633 633 remote: adding file changes
634 remote: added 1 changesets with 1 changes to 1 files
635 634 remote: pre-close-tip:e7ec4e813ba6 draft
636 635 remote: You shall not pass!
637 636 remote: transaction abort!
638 637 remote: Cleaning up the mess...
639 638 remote: rollback completed
640 639 abort: pretxnclose.failpush hook exited with status 1
641 640 [255]
642 641
643 642 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
644 643 pushing to ssh://user@dummy/other
645 644 searching for changes
646 645 remote: adding changesets
647 646 remote: adding manifests
648 647 remote: adding file changes
649 remote: added 1 changesets with 1 changes to 1 files
650 648 remote: pre-close-tip:e7ec4e813ba6 draft
651 649 remote: You shall not pass!
652 650 remote: transaction abort!
653 651 remote: Cleaning up the mess...
654 652 remote: rollback completed
655 653 remote: pretxnclose.failpush hook exited with status 1
656 654 abort: push failed on remote
657 655 [255]
658 656
659 657 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
660 658 pushing to http://localhost:$HGPORT2/
661 659 searching for changes
662 660 remote: adding changesets
663 661 remote: adding manifests
664 662 remote: adding file changes
665 remote: added 1 changesets with 1 changes to 1 files
666 663 remote: pre-close-tip:e7ec4e813ba6 draft
667 664 remote: You shall not pass!
668 665 remote: transaction abort!
669 666 remote: Cleaning up the mess...
670 667 remote: rollback completed
671 668 remote: pretxnclose.failpush hook exited with status 1
672 669 abort: push failed on remote
673 670 [255]
674 671
675 672 (check that no 'pending' files remain)
676 673
677 674 $ ls -1 other/.hg/bookmarks*
678 675 other/.hg/bookmarks
679 676 $ ls -1 other/.hg/store/phaseroots*
680 677 other/.hg/store/phaseroots
681 678 $ ls -1 other/.hg/store/00changelog.i*
682 679 other/.hg/store/00changelog.i
683 680
684 681 Check error from hook during the unbundling process itself
685 682
686 683 $ cat << EOF >> $HGRCPATH
687 684 > pretxnchangegroup = sh -c "echo 'Fail early!'; false"
688 685 > EOF
689 686 $ killdaemons.py # reload http config
690 687 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
691 688 $ cat other.pid >> $DAEMON_PIDS
692 689
693 690 $ hg -R main push other -r e7ec4e813ba6
694 691 pushing to other
695 692 searching for changes
696 693 remote: adding changesets
697 694 remote: adding manifests
698 695 remote: adding file changes
699 remote: added 1 changesets with 1 changes to 1 files
700 696 remote: Fail early!
701 697 remote: transaction abort!
702 698 remote: Cleaning up the mess...
703 699 remote: rollback completed
704 700 abort: pretxnchangegroup hook exited with status 1
705 701 [255]
706 702 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
707 703 pushing to ssh://user@dummy/other
708 704 searching for changes
709 705 remote: adding changesets
710 706 remote: adding manifests
711 707 remote: adding file changes
712 remote: added 1 changesets with 1 changes to 1 files
713 708 remote: Fail early!
714 709 remote: transaction abort!
715 710 remote: Cleaning up the mess...
716 711 remote: rollback completed
717 712 remote: pretxnchangegroup hook exited with status 1
718 713 abort: push failed on remote
719 714 [255]
720 715 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
721 716 pushing to http://localhost:$HGPORT2/
722 717 searching for changes
723 718 remote: adding changesets
724 719 remote: adding manifests
725 720 remote: adding file changes
726 remote: added 1 changesets with 1 changes to 1 files
727 721 remote: Fail early!
728 722 remote: transaction abort!
729 723 remote: Cleaning up the mess...
730 724 remote: rollback completed
731 725 remote: pretxnchangegroup hook exited with status 1
732 726 abort: push failed on remote
733 727 [255]
734 728
735 729 Check output capture control.
736 730
737 731 (should be still forced for http, disabled for local and ssh)
738 732
739 733 $ cat >> $HGRCPATH << EOF
740 734 > [experimental]
741 735 > bundle2-output-capture=False
742 736 > EOF
743 737
744 738 $ hg -R main push other -r e7ec4e813ba6
745 739 pushing to other
746 740 searching for changes
747 741 adding changesets
748 742 adding manifests
749 743 adding file changes
750 added 1 changesets with 1 changes to 1 files
751 744 Fail early!
752 745 transaction abort!
753 746 Cleaning up the mess...
754 747 rollback completed
755 748 abort: pretxnchangegroup hook exited with status 1
756 749 [255]
757 750 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
758 751 pushing to ssh://user@dummy/other
759 752 searching for changes
760 753 remote: adding changesets
761 754 remote: adding manifests
762 755 remote: adding file changes
763 remote: added 1 changesets with 1 changes to 1 files
764 756 remote: Fail early!
765 757 remote: transaction abort!
766 758 remote: Cleaning up the mess...
767 759 remote: rollback completed
768 760 remote: pretxnchangegroup hook exited with status 1
769 761 abort: push failed on remote
770 762 [255]
771 763 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
772 764 pushing to http://localhost:$HGPORT2/
773 765 searching for changes
774 766 remote: adding changesets
775 767 remote: adding manifests
776 768 remote: adding file changes
777 remote: added 1 changesets with 1 changes to 1 files
778 769 remote: Fail early!
779 770 remote: transaction abort!
780 771 remote: Cleaning up the mess...
781 772 remote: rollback completed
782 773 remote: pretxnchangegroup hook exited with status 1
783 774 abort: push failed on remote
784 775 [255]
785 776
786 777 Check abort from mandatory pushkey
787 778
788 779 $ cat > mandatorypart.py << EOF
789 780 > from mercurial import exchange
790 781 > from mercurial import pushkey
791 782 > from mercurial import node
792 783 > from mercurial import error
793 784 > @exchange.b2partsgenerator(b'failingpuskey')
794 785 > def addfailingpushey(pushop, bundler):
795 786 > enc = pushkey.encode
796 787 > part = bundler.newpart(b'pushkey')
797 788 > part.addparam(b'namespace', enc(b'phases'))
798 789 > part.addparam(b'key', enc(b'cd010b8cd998f3981a5a8115f94f8da4ab506089'))
799 790 > part.addparam(b'old', enc(b'0')) # successful update
800 791 > part.addparam(b'new', enc(b'0'))
801 792 > def fail(pushop, exc):
802 793 > raise error.Abort(b'Correct phase push failed (because hooks)')
803 794 > pushop.pkfailcb[part.id] = fail
804 795 > EOF
805 796 $ cat >> $HGRCPATH << EOF
806 797 > [hooks]
807 798 > pretxnchangegroup=
808 799 > pretxnclose.failpush=
809 800 > prepushkey.failpush = sh -c "echo 'do not push the key !'; false"
810 801 > [extensions]
811 802 > mandatorypart=$TESTTMP/mandatorypart.py
812 803 > EOF
813 804 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
814 805 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
815 806 $ cat other.pid >> $DAEMON_PIDS
816 807
817 808 (Failure from a hook)
818 809
819 810 $ hg -R main push other -r e7ec4e813ba6
820 811 pushing to other
821 812 searching for changes
822 813 adding changesets
823 814 adding manifests
824 815 adding file changes
825 added 1 changesets with 1 changes to 1 files
826 816 do not push the key !
827 817 pushkey-abort: prepushkey.failpush hook exited with status 1
828 818 transaction abort!
829 819 Cleaning up the mess...
830 820 rollback completed
831 821 abort: Correct phase push failed (because hooks)
832 822 [255]
833 823 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
834 824 pushing to ssh://user@dummy/other
835 825 searching for changes
836 826 remote: adding changesets
837 827 remote: adding manifests
838 828 remote: adding file changes
839 remote: added 1 changesets with 1 changes to 1 files
840 829 remote: do not push the key !
841 830 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
842 831 remote: transaction abort!
843 832 remote: Cleaning up the mess...
844 833 remote: rollback completed
845 834 abort: Correct phase push failed (because hooks)
846 835 [255]
847 836 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
848 837 pushing to http://localhost:$HGPORT2/
849 838 searching for changes
850 839 remote: adding changesets
851 840 remote: adding manifests
852 841 remote: adding file changes
853 remote: added 1 changesets with 1 changes to 1 files
854 842 remote: do not push the key !
855 843 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
856 844 remote: transaction abort!
857 845 remote: Cleaning up the mess...
858 846 remote: rollback completed
859 847 abort: Correct phase push failed (because hooks)
860 848 [255]
861 849
862 850 (Failure from a the pushkey)
863 851
864 852 $ cat > mandatorypart.py << EOF
865 853 > from mercurial import exchange
866 854 > from mercurial import pushkey
867 855 > from mercurial import node
868 856 > from mercurial import error
869 857 > @exchange.b2partsgenerator(b'failingpuskey')
870 858 > def addfailingpushey(pushop, bundler):
871 859 > enc = pushkey.encode
872 860 > part = bundler.newpart(b'pushkey')
873 861 > part.addparam(b'namespace', enc(b'phases'))
874 862 > part.addparam(b'key', enc(b'cd010b8cd998f3981a5a8115f94f8da4ab506089'))
875 863 > part.addparam(b'old', enc(b'4')) # will fail
876 864 > part.addparam(b'new', enc(b'3'))
877 865 > def fail(pushop, exc):
878 866 > raise error.Abort(b'Clown phase push failed')
879 867 > pushop.pkfailcb[part.id] = fail
880 868 > EOF
881 869 $ cat >> $HGRCPATH << EOF
882 870 > [hooks]
883 871 > prepushkey.failpush =
884 872 > EOF
885 873 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
886 874 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
887 875 $ cat other.pid >> $DAEMON_PIDS
888 876
889 877 $ hg -R main push other -r e7ec4e813ba6
890 878 pushing to other
891 879 searching for changes
892 880 adding changesets
893 881 adding manifests
894 882 adding file changes
895 added 1 changesets with 1 changes to 1 files
896 883 transaction abort!
897 884 Cleaning up the mess...
898 885 rollback completed
899 886 pushkey: lock state after "phases"
900 887 lock: free
901 888 wlock: free
902 889 abort: Clown phase push failed
903 890 [255]
904 891 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
905 892 pushing to ssh://user@dummy/other
906 893 searching for changes
907 894 remote: adding changesets
908 895 remote: adding manifests
909 896 remote: adding file changes
910 remote: added 1 changesets with 1 changes to 1 files
911 897 remote: transaction abort!
912 898 remote: Cleaning up the mess...
913 899 remote: rollback completed
914 900 remote: pushkey: lock state after "phases"
915 901 remote: lock: free
916 902 remote: wlock: free
917 903 abort: Clown phase push failed
918 904 [255]
919 905 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
920 906 pushing to http://localhost:$HGPORT2/
921 907 searching for changes
922 908 remote: adding changesets
923 909 remote: adding manifests
924 910 remote: adding file changes
925 remote: added 1 changesets with 1 changes to 1 files
926 911 remote: transaction abort!
927 912 remote: Cleaning up the mess...
928 913 remote: rollback completed
929 914 remote: pushkey: lock state after "phases"
930 915 remote: lock: free
931 916 remote: wlock: free
932 917 abort: Clown phase push failed
933 918 [255]
934 919
935 920 Test lazily acquiring the lock during unbundle
936 921 $ cp $TESTTMP/hgrc.orig $HGRCPATH
937 922 $ cat >> $HGRCPATH <<EOF
938 923 > [ui]
939 924 > ssh="$PYTHON" "$TESTDIR/dummyssh"
940 925 > EOF
941 926
942 927 $ cat >> $TESTTMP/locktester.py <<EOF
943 928 > import os
944 929 > from mercurial import bundle2, error, extensions
945 930 > def checklock(orig, repo, *args, **kwargs):
946 931 > if repo.svfs.lexists(b"lock"):
947 932 > raise error.Abort(b"Lock should not be taken")
948 933 > return orig(repo, *args, **kwargs)
949 934 > def extsetup(ui):
950 935 > extensions.wrapfunction(bundle2, b'processbundle', checklock)
951 936 > EOF
952 937
953 938 $ hg init lazylock
954 939 $ cat >> lazylock/.hg/hgrc <<EOF
955 940 > [extensions]
956 941 > locktester=$TESTTMP/locktester.py
957 942 > EOF
958 943
959 944 $ hg clone -q ssh://user@dummy/lazylock lazylockclient
960 945 $ cd lazylockclient
961 946 $ touch a && hg ci -Aqm a
962 947 $ hg push
963 948 pushing to ssh://user@dummy/lazylock
964 949 searching for changes
965 950 remote: Lock should not be taken
966 951 abort: push failed on remote
967 952 [255]
968 953
969 954 $ cat >> ../lazylock/.hg/hgrc <<EOF
970 955 > [experimental]
971 956 > bundle2lazylocking=True
972 957 > EOF
973 958 $ hg push
974 959 pushing to ssh://user@dummy/lazylock
975 960 searching for changes
976 961 remote: adding changesets
977 962 remote: adding manifests
978 963 remote: adding file changes
979 964 remote: added 1 changesets with 1 changes to 1 files
980 965
981 966 $ cd ..
982 967
983 968 Servers can disable bundle1 for clone/pull operations
984 969
985 970 $ killdaemons.py
986 971 $ hg init bundle2onlyserver
987 972 $ cd bundle2onlyserver
988 973 $ cat > .hg/hgrc << EOF
989 974 > [server]
990 975 > bundle1.pull = false
991 976 > EOF
992 977
993 978 $ touch foo
994 979 $ hg -q commit -A -m initial
995 980
996 981 $ hg serve -p $HGPORT -d --pid-file=hg.pid
997 982 $ cat hg.pid >> $DAEMON_PIDS
998 983
999 984 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
1000 985 requesting all changes
1001 986 abort: remote error:
1002 987 incompatible Mercurial client; bundle2 required
1003 988 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1004 989 [255]
1005 990 $ killdaemons.py
1006 991 $ cd ..
1007 992
1008 993 bundle1 can still pull non-generaldelta repos when generaldelta bundle1 disabled
1009 994
1010 995 $ hg --config format.usegeneraldelta=false init notgdserver
1011 996 $ cd notgdserver
1012 997 $ cat > .hg/hgrc << EOF
1013 998 > [server]
1014 999 > bundle1gd.pull = false
1015 1000 > EOF
1016 1001
1017 1002 $ touch foo
1018 1003 $ hg -q commit -A -m initial
1019 1004 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1020 1005 $ cat hg.pid >> $DAEMON_PIDS
1021 1006
1022 1007 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2-1
1023 1008 requesting all changes
1024 1009 adding changesets
1025 1010 adding manifests
1026 1011 adding file changes
1027 1012 added 1 changesets with 1 changes to 1 files
1028 1013 new changesets 96ee1d7354c4
1029 1014 updating to branch default
1030 1015 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1031 1016
1032 1017 $ killdaemons.py
1033 1018 $ cd ../bundle2onlyserver
1034 1019
1035 1020 bundle1 pull can be disabled for generaldelta repos only
1036 1021
1037 1022 $ cat > .hg/hgrc << EOF
1038 1023 > [server]
1039 1024 > bundle1gd.pull = false
1040 1025 > EOF
1041 1026
1042 1027 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1043 1028 $ cat hg.pid >> $DAEMON_PIDS
1044 1029 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
1045 1030 requesting all changes
1046 1031 abort: remote error:
1047 1032 incompatible Mercurial client; bundle2 required
1048 1033 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1049 1034 [255]
1050 1035
1051 1036 $ killdaemons.py
1052 1037
1053 1038 Verify the global server.bundle1 option works
1054 1039
1055 1040 $ cd ..
1056 1041 $ cat > bundle2onlyserver/.hg/hgrc << EOF
1057 1042 > [server]
1058 1043 > bundle1 = false
1059 1044 > EOF
1060 1045 $ hg serve -R bundle2onlyserver -p $HGPORT -d --pid-file=hg.pid
1061 1046 $ cat hg.pid >> $DAEMON_PIDS
1062 1047 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT not-bundle2
1063 1048 requesting all changes
1064 1049 abort: remote error:
1065 1050 incompatible Mercurial client; bundle2 required
1066 1051 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1067 1052 [255]
1068 1053 $ killdaemons.py
1069 1054
1070 1055 $ hg --config devel.legacy.exchange=bundle1 clone ssh://user@dummy/bundle2onlyserver not-bundle2-ssh
1071 1056 requesting all changes
1072 1057 adding changesets
1073 1058 remote: abort: incompatible Mercurial client; bundle2 required
1074 1059 remote: (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1075 1060 transaction abort!
1076 1061 rollback completed
1077 1062 abort: stream ended unexpectedly (got 0 bytes, expected 4)
1078 1063 [255]
1079 1064
1080 1065 $ cat > bundle2onlyserver/.hg/hgrc << EOF
1081 1066 > [server]
1082 1067 > bundle1gd = false
1083 1068 > EOF
1084 1069 $ hg serve -R bundle2onlyserver -p $HGPORT -d --pid-file=hg.pid
1085 1070 $ cat hg.pid >> $DAEMON_PIDS
1086 1071
1087 1072 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
1088 1073 requesting all changes
1089 1074 abort: remote error:
1090 1075 incompatible Mercurial client; bundle2 required
1091 1076 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1092 1077 [255]
1093 1078
1094 1079 $ killdaemons.py
1095 1080
1096 1081 $ cd notgdserver
1097 1082 $ cat > .hg/hgrc << EOF
1098 1083 > [server]
1099 1084 > bundle1gd = false
1100 1085 > EOF
1101 1086 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1102 1087 $ cat hg.pid >> $DAEMON_PIDS
1103 1088
1104 1089 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2-2
1105 1090 requesting all changes
1106 1091 adding changesets
1107 1092 adding manifests
1108 1093 adding file changes
1109 1094 added 1 changesets with 1 changes to 1 files
1110 1095 new changesets 96ee1d7354c4
1111 1096 updating to branch default
1112 1097 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1113 1098
1114 1099 $ killdaemons.py
1115 1100 $ cd ../bundle2onlyserver
1116 1101
1117 1102 Verify bundle1 pushes can be disabled
1118 1103
1119 1104 $ cat > .hg/hgrc << EOF
1120 1105 > [server]
1121 1106 > bundle1.push = false
1122 1107 > [web]
1123 1108 > allow_push = *
1124 1109 > push_ssl = false
1125 1110 > EOF
1126 1111
1127 1112 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E error.log
1128 1113 $ cat hg.pid >> $DAEMON_PIDS
1129 1114 $ cd ..
1130 1115
1131 1116 $ hg clone http://localhost:$HGPORT bundle2-only
1132 1117 requesting all changes
1133 1118 adding changesets
1134 1119 adding manifests
1135 1120 adding file changes
1136 1121 added 1 changesets with 1 changes to 1 files
1137 1122 new changesets 96ee1d7354c4
1138 1123 updating to branch default
1139 1124 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1140 1125 $ cd bundle2-only
1141 1126 $ echo commit > foo
1142 1127 $ hg commit -m commit
1143 1128 $ hg --config devel.legacy.exchange=bundle1 push
1144 1129 pushing to http://localhost:$HGPORT/
1145 1130 searching for changes
1146 1131 abort: remote error:
1147 1132 incompatible Mercurial client; bundle2 required
1148 1133 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1149 1134 [255]
1150 1135
1151 1136 (also check with ssh)
1152 1137
1153 1138 $ hg --config devel.legacy.exchange=bundle1 push ssh://user@dummy/bundle2onlyserver
1154 1139 pushing to ssh://user@dummy/bundle2onlyserver
1155 1140 searching for changes
1156 1141 remote: abort: incompatible Mercurial client; bundle2 required
1157 1142 remote: (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1158 1143 [1]
1159 1144
1160 1145 $ hg push
1161 1146 pushing to http://localhost:$HGPORT/
1162 1147 searching for changes
1163 1148 remote: adding changesets
1164 1149 remote: adding manifests
1165 1150 remote: adding file changes
1166 1151 remote: added 1 changesets with 1 changes to 1 files
@@ -1,1237 +1,1236 b''
1 1 This test is dedicated to test the bundle2 container format
2 2
3 3 It test multiple existing parts to test different feature of the container. You
4 4 probably do not need to touch this test unless you change the binary encoding
5 5 of the bundle2 format itself.
6 6
7 7 Create an extension to test bundle2 API
8 8
9 9 $ cat > bundle2.py << EOF
10 10 > """A small extension to test bundle2 implementation
11 11 >
12 12 > This extension allows detailed testing of the various bundle2 API and
13 13 > behaviors.
14 14 > """
15 15 > import gc
16 16 > import os
17 17 > import sys
18 18 > from mercurial import util
19 19 > from mercurial import bundle2
20 20 > from mercurial import scmutil
21 21 > from mercurial import discovery
22 22 > from mercurial import changegroup
23 23 > from mercurial import error
24 24 > from mercurial import obsolete
25 25 > from mercurial import pycompat
26 26 > from mercurial import registrar
27 27 >
28 28 >
29 29 > try:
30 30 > import msvcrt
31 31 > msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
32 32 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
33 33 > msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
34 34 > except ImportError:
35 35 > pass
36 36 >
37 37 > cmdtable = {}
38 38 > command = registrar.command(cmdtable)
39 39 >
40 40 > ELEPHANTSSONG = b"""Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
41 41 > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
42 42 > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
43 43 > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
44 44 >
45 45 > @bundle2.parthandler(b'test:song')
46 46 > def songhandler(op, part):
47 47 > """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
48 48 > op.ui.write(b'The choir starts singing:\n')
49 49 > verses = 0
50 50 > for line in part.read().split(b'\n'):
51 51 > op.ui.write(b' %s\n' % line)
52 52 > verses += 1
53 53 > op.records.add(b'song', {b'verses': verses})
54 54 >
55 55 > @bundle2.parthandler(b'test:ping')
56 56 > def pinghandler(op, part):
57 57 > op.ui.write(b'received ping request (id %i)\n' % part.id)
58 58 > if op.reply is not None and b'ping-pong' in op.reply.capabilities:
59 59 > op.ui.write_err(b'replying to ping request (id %i)\n' % part.id)
60 60 > op.reply.newpart(b'test:pong', [(b'in-reply-to', b'%d' % part.id)],
61 61 > mandatory=False)
62 62 >
63 63 > @bundle2.parthandler(b'test:debugreply')
64 64 > def debugreply(op, part):
65 65 > """print data about the capacity of the bundle reply"""
66 66 > if op.reply is None:
67 67 > op.ui.write(b'debugreply: no reply\n')
68 68 > else:
69 69 > op.ui.write(b'debugreply: capabilities:\n')
70 70 > for cap in sorted(op.reply.capabilities):
71 71 > op.ui.write(b"debugreply: '%s'\n" % cap)
72 72 > for val in op.reply.capabilities[cap]:
73 73 > op.ui.write(b"debugreply: '%s'\n" % val)
74 74 >
75 75 > @command(b'bundle2',
76 76 > [(b'', b'param', [], b'stream level parameter'),
77 77 > (b'', b'unknown', False, b'include an unknown mandatory part in the bundle'),
78 78 > (b'', b'unknownparams', False, b'include an unknown part parameters in the bundle'),
79 79 > (b'', b'parts', False, b'include some arbitrary parts to the bundle'),
80 80 > (b'', b'reply', False, b'produce a reply bundle'),
81 81 > (b'', b'pushrace', False, b'includes a check:head part with unknown nodes'),
82 82 > (b'', b'genraise', False, b'includes a part that raise an exception during generation'),
83 83 > (b'', b'timeout', False, b'emulate a timeout during bundle generation'),
84 84 > (b'r', b'rev', [], b'includes those changeset in the bundle'),
85 85 > (b'', b'compress', b'', b'compress the stream'),
86 86 > ],
87 87 > b'[OUTPUTFILE]')
88 88 > def cmdbundle2(ui, repo, path=None, **opts):
89 89 > """write a bundle2 container on standard output"""
90 90 > bundler = bundle2.bundle20(ui)
91 91 > for p in opts['param']:
92 92 > p = p.split(b'=', 1)
93 93 > try:
94 94 > bundler.addparam(*p)
95 95 > except error.ProgrammingError as exc:
96 96 > raise error.Abort(b'%s' % exc)
97 97 >
98 98 > if opts['compress']:
99 99 > bundler.setcompression(opts['compress'])
100 100 >
101 101 > if opts['reply']:
102 102 > capsstring = b'ping-pong\nelephants=babar,celeste\ncity%3D%21=celeste%2Cville'
103 103 > bundler.newpart(b'replycaps', data=capsstring)
104 104 >
105 105 > if opts['pushrace']:
106 106 > # also serve to test the assignement of data outside of init
107 107 > part = bundler.newpart(b'check:heads')
108 108 > part.data = b'01234567890123456789'
109 109 >
110 110 > revs = opts['rev']
111 111 > if 'rev' in opts:
112 112 > revs = scmutil.revrange(repo, opts['rev'])
113 113 > if revs:
114 114 > # very crude version of a changegroup part creation
115 115 > bundled = repo.revs('%ld::%ld', revs, revs)
116 116 > headmissing = [c.node() for c in repo.set('heads(%ld)', revs)]
117 117 > headcommon = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)]
118 118 > outgoing = discovery.outgoing(repo, headcommon, headmissing)
119 119 > cg = changegroup.makechangegroup(repo, outgoing, b'01',
120 120 > b'test:bundle2')
121 121 > bundler.newpart(b'changegroup', data=cg.getchunks(),
122 122 > mandatory=False)
123 123 >
124 124 > if opts['parts']:
125 125 > bundler.newpart(b'test:empty', mandatory=False)
126 126 > # add a second one to make sure we handle multiple parts
127 127 > bundler.newpart(b'test:empty', mandatory=False)
128 128 > bundler.newpart(b'test:song', data=ELEPHANTSSONG, mandatory=False)
129 129 > bundler.newpart(b'test:debugreply', mandatory=False)
130 130 > mathpart = bundler.newpart(b'test:math')
131 131 > mathpart.addparam(b'pi', b'3.14')
132 132 > mathpart.addparam(b'e', b'2.72')
133 133 > mathpart.addparam(b'cooking', b'raw', mandatory=False)
134 134 > mathpart.data = b'42'
135 135 > mathpart.mandatory = False
136 136 > # advisory known part with unknown mandatory param
137 137 > bundler.newpart(b'test:song', [(b'randomparam', b'')], mandatory=False)
138 138 > if opts['unknown']:
139 139 > bundler.newpart(b'test:unknown', data=b'some random content')
140 140 > if opts['unknownparams']:
141 141 > bundler.newpart(b'test:song', [(b'randomparams', b'')])
142 142 > if opts['parts']:
143 143 > bundler.newpart(b'test:ping', mandatory=False)
144 144 > if opts['genraise']:
145 145 > def genraise():
146 146 > yield b'first line\n'
147 147 > raise RuntimeError('Someone set up us the bomb!')
148 148 > bundler.newpart(b'output', data=genraise(), mandatory=False)
149 149 >
150 150 > if path is None:
151 151 > file = pycompat.stdout
152 152 > else:
153 153 > file = open(path, 'wb')
154 154 >
155 155 > if opts['timeout']:
156 156 > bundler.newpart(b'test:song', data=ELEPHANTSSONG, mandatory=False)
157 157 > for idx, junk in enumerate(bundler.getchunks()):
158 158 > ui.write(b'%d chunk\n' % idx)
159 159 > if idx > 4:
160 160 > # This throws a GeneratorExit inside the generator, which
161 161 > # can cause problems if the exception-recovery code is
162 162 > # too zealous. It's important for this test that the break
163 163 > # occur while we're in the middle of a part.
164 164 > break
165 165 > gc.collect()
166 166 > ui.write(b'fake timeout complete.\n')
167 167 > return
168 168 > try:
169 169 > for chunk in bundler.getchunks():
170 170 > file.write(chunk)
171 171 > except RuntimeError as exc:
172 172 > raise error.Abort(exc)
173 173 > finally:
174 174 > file.flush()
175 175 >
176 176 > @command(b'unbundle2', [], b'')
177 177 > def cmdunbundle2(ui, repo, replypath=None):
178 178 > """process a bundle2 stream from stdin on the current repo"""
179 179 > try:
180 180 > tr = None
181 181 > lock = repo.lock()
182 182 > tr = repo.transaction(b'processbundle')
183 183 > try:
184 184 > unbundler = bundle2.getunbundler(ui, pycompat.stdin)
185 185 > op = bundle2.processbundle(repo, unbundler, lambda: tr)
186 186 > tr.close()
187 187 > except error.BundleValueError as exc:
188 188 > raise error.Abort(b'missing support for %s' % exc)
189 189 > except error.PushRaced as exc:
190 190 > raise error.Abort(b'push race: %s' % exc)
191 191 > finally:
192 192 > if tr is not None:
193 193 > tr.release()
194 194 > lock.release()
195 195 > remains = pycompat.stdin.read()
196 196 > ui.write(b'%i unread bytes\n' % len(remains))
197 197 > if op.records[b'song']:
198 198 > totalverses = sum(r[b'verses'] for r in op.records[b'song'])
199 199 > ui.write(b'%i total verses sung\n' % totalverses)
200 200 > for rec in op.records[b'changegroup']:
201 201 > ui.write(b'addchangegroup return: %i\n' % rec[b'return'])
202 202 > if op.reply is not None and replypath is not None:
203 203 > with open(replypath, 'wb') as file:
204 204 > for chunk in op.reply.getchunks():
205 205 > file.write(chunk)
206 206 >
207 207 > @command(b'statbundle2', [], b'')
208 208 > def cmdstatbundle2(ui, repo):
209 209 > """print statistic on the bundle2 container read from stdin"""
210 210 > unbundler = bundle2.getunbundler(ui, pycompat.stdin)
211 211 > try:
212 212 > params = unbundler.params
213 213 > except error.BundleValueError as exc:
214 214 > raise error.Abort(b'unknown parameters: %s' % exc)
215 215 > ui.write(b'options count: %i\n' % len(params))
216 216 > for key in sorted(params):
217 217 > ui.write(b'- %s\n' % key)
218 218 > value = params[key]
219 219 > if value is not None:
220 220 > ui.write(b' %s\n' % value)
221 221 > count = 0
222 222 > for p in unbundler.iterparts():
223 223 > count += 1
224 224 > ui.write(b' :%s:\n' % p.type)
225 225 > ui.write(b' mandatory: %i\n' % len(p.mandatoryparams))
226 226 > ui.write(b' advisory: %i\n' % len(p.advisoryparams))
227 227 > ui.write(b' payload: %i bytes\n' % len(p.read()))
228 228 > ui.write(b'parts count: %i\n' % count)
229 229 > EOF
230 230 $ cat >> $HGRCPATH << EOF
231 231 > [extensions]
232 232 > bundle2=$TESTTMP/bundle2.py
233 233 > [experimental]
234 234 > evolution.createmarkers=True
235 235 > [ui]
236 236 > ssh="$PYTHON" "$TESTDIR/dummyssh"
237 237 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
238 238 > [web]
239 239 > push_ssl = false
240 240 > allow_push = *
241 241 > [phases]
242 242 > publish=False
243 243 > EOF
244 244
245 245 The extension requires a repo (currently unused)
246 246
247 247 $ hg init main
248 248 $ cd main
249 249 $ touch a
250 250 $ hg add a
251 251 $ hg commit -m 'a'
252 252
253 253
254 254 Empty bundle
255 255 =================
256 256
257 257 - no option
258 258 - no parts
259 259
260 260 Test bundling
261 261
262 262 $ hg bundle2 | f --hexdump
263 263
264 264 0000: 48 47 32 30 00 00 00 00 00 00 00 00 |HG20........|
265 265
266 266 Test timeouts during bundling
267 267 $ hg bundle2 --timeout --debug --config devel.bundle2.debug=yes
268 268 bundle2-output-bundle: "HG20", 1 parts total
269 269 bundle2-output: start emission of HG20 stream
270 270 0 chunk
271 271 bundle2-output: bundle parameter:
272 272 1 chunk
273 273 bundle2-output: start of parts
274 274 bundle2-output: bundle part: "test:song"
275 275 bundle2-output-part: "test:song" (advisory) 178 bytes payload
276 276 bundle2-output: part 0: "test:song"
277 277 bundle2-output: header chunk size: 16
278 278 2 chunk
279 279 3 chunk
280 280 bundle2-output: payload chunk size: 178
281 281 4 chunk
282 282 5 chunk
283 283 bundle2-generatorexit
284 284 fake timeout complete.
285 285
286 286 Test unbundling
287 287
288 288 $ hg bundle2 | hg statbundle2
289 289 options count: 0
290 290 parts count: 0
291 291
292 292 Test old style bundle are detected and refused
293 293
294 294 $ hg bundle --all --type v1 ../bundle.hg
295 295 1 changesets found
296 296 $ hg statbundle2 < ../bundle.hg
297 297 abort: unknown bundle version 10
298 298 [255]
299 299
300 300 Test parameters
301 301 =================
302 302
303 303 - some options
304 304 - no parts
305 305
306 306 advisory parameters, no value
307 307 -------------------------------
308 308
309 309 Simplest possible parameters form
310 310
311 311 Test generation simple option
312 312
313 313 $ hg bundle2 --param 'caution' | f --hexdump
314 314
315 315 0000: 48 47 32 30 00 00 00 07 63 61 75 74 69 6f 6e 00 |HG20....caution.|
316 316 0010: 00 00 00 |...|
317 317
318 318 Test unbundling
319 319
320 320 $ hg bundle2 --param 'caution' | hg statbundle2
321 321 options count: 1
322 322 - caution
323 323 parts count: 0
324 324
325 325 Test generation multiple option
326 326
327 327 $ hg bundle2 --param 'caution' --param 'meal' | f --hexdump
328 328
329 329 0000: 48 47 32 30 00 00 00 0c 63 61 75 74 69 6f 6e 20 |HG20....caution |
330 330 0010: 6d 65 61 6c 00 00 00 00 |meal....|
331 331
332 332 Test unbundling
333 333
334 334 $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
335 335 options count: 2
336 336 - caution
337 337 - meal
338 338 parts count: 0
339 339
340 340 advisory parameters, with value
341 341 -------------------------------
342 342
343 343 Test generation
344 344
345 345 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | f --hexdump
346 346
347 347 0000: 48 47 32 30 00 00 00 1c 63 61 75 74 69 6f 6e 20 |HG20....caution |
348 348 0010: 6d 65 61 6c 3d 76 65 67 61 6e 20 65 6c 65 70 68 |meal=vegan eleph|
349 349 0020: 61 6e 74 73 00 00 00 00 |ants....|
350 350
351 351 Test unbundling
352 352
353 353 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
354 354 options count: 3
355 355 - caution
356 356 - elephants
357 357 - meal
358 358 vegan
359 359 parts count: 0
360 360
361 361 parameter with special char in value
362 362 ---------------------------------------------------
363 363
364 364 Test generation
365 365
366 366 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | f --hexdump
367 367
368 368 0000: 48 47 32 30 00 00 00 29 65 25 37 43 25 32 31 25 |HG20...)e%7C%21%|
369 369 0010: 32 30 37 2f 3d 62 61 62 61 72 25 32 35 25 32 33 |207/=babar%25%23|
370 370 0020: 25 33 44 25 33 44 74 75 74 75 20 73 69 6d 70 6c |%3D%3Dtutu simpl|
371 371 0030: 65 00 00 00 00 |e....|
372 372
373 373 Test unbundling
374 374
375 375 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
376 376 options count: 2
377 377 - e|! 7/
378 378 babar%#==tutu
379 379 - simple
380 380 parts count: 0
381 381
382 382 Test unknown mandatory option
383 383 ---------------------------------------------------
384 384
385 385 $ hg bundle2 --param 'Gravity' | hg statbundle2
386 386 abort: unknown parameters: Stream Parameter - Gravity
387 387 [255]
388 388
389 389 Test debug output
390 390 ---------------------------------------------------
391 391
392 392 bundling debug
393 393
394 394 $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2 --config progress.debug=true --config devel.bundle2.debug=true
395 395 bundle2-output-bundle: "HG20", (2 params) 0 parts total
396 396 bundle2-output: start emission of HG20 stream
397 397 bundle2-output: bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
398 398 bundle2-output: start of parts
399 399 bundle2-output: end of bundle
400 400
401 401 file content is ok
402 402
403 403 $ f --hexdump ../out.hg2
404 404 ../out.hg2:
405 405 0000: 48 47 32 30 00 00 00 29 65 25 37 43 25 32 31 25 |HG20...)e%7C%21%|
406 406 0010: 32 30 37 2f 3d 62 61 62 61 72 25 32 35 25 32 33 |207/=babar%25%23|
407 407 0020: 25 33 44 25 33 44 74 75 74 75 20 73 69 6d 70 6c |%3D%3Dtutu simpl|
408 408 0030: 65 00 00 00 00 |e....|
409 409
410 410 unbundling debug
411 411
412 412 $ hg statbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../out.hg2
413 413 bundle2-input: start processing of HG20 stream
414 414 bundle2-input: reading bundle2 stream parameters
415 415 bundle2-input: ignoring unknown parameter e|! 7/
416 416 bundle2-input: ignoring unknown parameter simple
417 417 options count: 2
418 418 - e|! 7/
419 419 babar%#==tutu
420 420 - simple
421 421 bundle2-input: start extraction of bundle2 parts
422 422 bundle2-input: part header size: 0
423 423 bundle2-input: end of bundle2 stream
424 424 parts count: 0
425 425
426 426
427 427 Test buggy input
428 428 ---------------------------------------------------
429 429
430 430 empty parameter name
431 431
432 432 $ hg bundle2 --param '' --quiet
433 433 abort: empty parameter name
434 434 [255]
435 435
436 436 bad parameter name
437 437
438 438 $ hg bundle2 --param 42babar
439 439 abort: non letter first character: 42babar
440 440 [255]
441 441
442 442
443 443 Test part
444 444 =================
445 445
446 446 $ hg bundle2 --parts ../parts.hg2 --debug --config progress.debug=true --config devel.bundle2.debug=true
447 447 bundle2-output-bundle: "HG20", 7 parts total
448 448 bundle2-output: start emission of HG20 stream
449 449 bundle2-output: bundle parameter:
450 450 bundle2-output: start of parts
451 451 bundle2-output: bundle part: "test:empty"
452 452 bundle2-output-part: "test:empty" (advisory) empty payload
453 453 bundle2-output: part 0: "test:empty"
454 454 bundle2-output: header chunk size: 17
455 455 bundle2-output: closing payload chunk
456 456 bundle2-output: bundle part: "test:empty"
457 457 bundle2-output-part: "test:empty" (advisory) empty payload
458 458 bundle2-output: part 1: "test:empty"
459 459 bundle2-output: header chunk size: 17
460 460 bundle2-output: closing payload chunk
461 461 bundle2-output: bundle part: "test:song"
462 462 bundle2-output-part: "test:song" (advisory) 178 bytes payload
463 463 bundle2-output: part 2: "test:song"
464 464 bundle2-output: header chunk size: 16
465 465 bundle2-output: payload chunk size: 178
466 466 bundle2-output: closing payload chunk
467 467 bundle2-output: bundle part: "test:debugreply"
468 468 bundle2-output-part: "test:debugreply" (advisory) empty payload
469 469 bundle2-output: part 3: "test:debugreply"
470 470 bundle2-output: header chunk size: 22
471 471 bundle2-output: closing payload chunk
472 472 bundle2-output: bundle part: "test:math"
473 473 bundle2-output-part: "test:math" (advisory) (params: 2 mandatory 2 advisory) 2 bytes payload
474 474 bundle2-output: part 4: "test:math"
475 475 bundle2-output: header chunk size: 43
476 476 bundle2-output: payload chunk size: 2
477 477 bundle2-output: closing payload chunk
478 478 bundle2-output: bundle part: "test:song"
479 479 bundle2-output-part: "test:song" (advisory) (params: 1 mandatory) empty payload
480 480 bundle2-output: part 5: "test:song"
481 481 bundle2-output: header chunk size: 29
482 482 bundle2-output: closing payload chunk
483 483 bundle2-output: bundle part: "test:ping"
484 484 bundle2-output-part: "test:ping" (advisory) empty payload
485 485 bundle2-output: part 6: "test:ping"
486 486 bundle2-output: header chunk size: 16
487 487 bundle2-output: closing payload chunk
488 488 bundle2-output: end of bundle
489 489
490 490 $ f --hexdump ../parts.hg2
491 491 ../parts.hg2:
492 492 0000: 48 47 32 30 00 00 00 00 00 00 00 11 0a 74 65 73 |HG20.........tes|
493 493 0010: 74 3a 65 6d 70 74 79 00 00 00 00 00 00 00 00 00 |t:empty.........|
494 494 0020: 00 00 00 00 11 0a 74 65 73 74 3a 65 6d 70 74 79 |......test:empty|
495 495 0030: 00 00 00 01 00 00 00 00 00 00 00 00 00 10 09 74 |...............t|
496 496 0040: 65 73 74 3a 73 6f 6e 67 00 00 00 02 00 00 00 00 |est:song........|
497 497 0050: 00 b2 50 61 74 61 6c 69 20 44 69 72 61 70 61 74 |..Patali Dirapat|
498 498 0060: 61 2c 20 43 72 6f 6d 64 61 20 43 72 6f 6d 64 61 |a, Cromda Cromda|
499 499 0070: 20 52 69 70 61 6c 6f 2c 20 50 61 74 61 20 50 61 | Ripalo, Pata Pa|
500 500 0080: 74 61 2c 20 4b 6f 20 4b 6f 20 4b 6f 0a 42 6f 6b |ta, Ko Ko Ko.Bok|
501 501 0090: 6f 72 6f 20 44 69 70 6f 75 6c 69 74 6f 2c 20 52 |oro Dipoulito, R|
502 502 00a0: 6f 6e 64 69 20 52 6f 6e 64 69 20 50 65 70 69 6e |ondi Rondi Pepin|
503 503 00b0: 6f 2c 20 50 61 74 61 20 50 61 74 61 2c 20 4b 6f |o, Pata Pata, Ko|
504 504 00c0: 20 4b 6f 20 4b 6f 0a 45 6d 61 6e 61 20 4b 61 72 | Ko Ko.Emana Kar|
505 505 00d0: 61 73 73 6f 6c 69 2c 20 4c 6f 75 63 72 61 20 4c |assoli, Loucra L|
506 506 00e0: 6f 75 63 72 61 20 50 6f 6e 70 6f 6e 74 6f 2c 20 |oucra Ponponto, |
507 507 00f0: 50 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f |Pata Pata, Ko Ko|
508 508 0100: 20 4b 6f 2e 00 00 00 00 00 00 00 16 0f 74 65 73 | Ko..........tes|
509 509 0110: 74 3a 64 65 62 75 67 72 65 70 6c 79 00 00 00 03 |t:debugreply....|
510 510 0120: 00 00 00 00 00 00 00 00 00 2b 09 74 65 73 74 3a |.........+.test:|
511 511 0130: 6d 61 74 68 00 00 00 04 02 01 02 04 01 04 07 03 |math............|
512 512 0140: 70 69 33 2e 31 34 65 32 2e 37 32 63 6f 6f 6b 69 |pi3.14e2.72cooki|
513 513 0150: 6e 67 72 61 77 00 00 00 02 34 32 00 00 00 00 00 |ngraw....42.....|
514 514 0160: 00 00 1d 09 74 65 73 74 3a 73 6f 6e 67 00 00 00 |....test:song...|
515 515 0170: 05 01 00 0b 00 72 61 6e 64 6f 6d 70 61 72 61 6d |.....randomparam|
516 516 0180: 00 00 00 00 00 00 00 10 09 74 65 73 74 3a 70 69 |.........test:pi|
517 517 0190: 6e 67 00 00 00 06 00 00 00 00 00 00 00 00 00 00 |ng..............|
518 518
519 519
520 520 $ hg statbundle2 < ../parts.hg2
521 521 options count: 0
522 522 :test:empty:
523 523 mandatory: 0
524 524 advisory: 0
525 525 payload: 0 bytes
526 526 :test:empty:
527 527 mandatory: 0
528 528 advisory: 0
529 529 payload: 0 bytes
530 530 :test:song:
531 531 mandatory: 0
532 532 advisory: 0
533 533 payload: 178 bytes
534 534 :test:debugreply:
535 535 mandatory: 0
536 536 advisory: 0
537 537 payload: 0 bytes
538 538 :test:math:
539 539 mandatory: 2
540 540 advisory: 1
541 541 payload: 2 bytes
542 542 :test:song:
543 543 mandatory: 1
544 544 advisory: 0
545 545 payload: 0 bytes
546 546 :test:ping:
547 547 mandatory: 0
548 548 advisory: 0
549 549 payload: 0 bytes
550 550 parts count: 7
551 551
552 552 $ hg statbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../parts.hg2
553 553 bundle2-input: start processing of HG20 stream
554 554 bundle2-input: reading bundle2 stream parameters
555 555 options count: 0
556 556 bundle2-input: start extraction of bundle2 parts
557 557 bundle2-input: part header size: 17
558 558 bundle2-input: part type: "test:empty"
559 559 bundle2-input: part id: "0"
560 560 bundle2-input: part parameters: 0
561 561 :test:empty:
562 562 mandatory: 0
563 563 advisory: 0
564 564 bundle2-input: payload chunk size: 0
565 565 payload: 0 bytes
566 566 bundle2-input: part header size: 17
567 567 bundle2-input: part type: "test:empty"
568 568 bundle2-input: part id: "1"
569 569 bundle2-input: part parameters: 0
570 570 :test:empty:
571 571 mandatory: 0
572 572 advisory: 0
573 573 bundle2-input: payload chunk size: 0
574 574 payload: 0 bytes
575 575 bundle2-input: part header size: 16
576 576 bundle2-input: part type: "test:song"
577 577 bundle2-input: part id: "2"
578 578 bundle2-input: part parameters: 0
579 579 :test:song:
580 580 mandatory: 0
581 581 advisory: 0
582 582 bundle2-input: payload chunk size: 178
583 583 bundle2-input: payload chunk size: 0
584 584 bundle2-input-part: total payload size 178
585 585 payload: 178 bytes
586 586 bundle2-input: part header size: 22
587 587 bundle2-input: part type: "test:debugreply"
588 588 bundle2-input: part id: "3"
589 589 bundle2-input: part parameters: 0
590 590 :test:debugreply:
591 591 mandatory: 0
592 592 advisory: 0
593 593 bundle2-input: payload chunk size: 0
594 594 payload: 0 bytes
595 595 bundle2-input: part header size: 43
596 596 bundle2-input: part type: "test:math"
597 597 bundle2-input: part id: "4"
598 598 bundle2-input: part parameters: 3
599 599 :test:math:
600 600 mandatory: 2
601 601 advisory: 1
602 602 bundle2-input: payload chunk size: 2
603 603 bundle2-input: payload chunk size: 0
604 604 bundle2-input-part: total payload size 2
605 605 payload: 2 bytes
606 606 bundle2-input: part header size: 29
607 607 bundle2-input: part type: "test:song"
608 608 bundle2-input: part id: "5"
609 609 bundle2-input: part parameters: 1
610 610 :test:song:
611 611 mandatory: 1
612 612 advisory: 0
613 613 bundle2-input: payload chunk size: 0
614 614 payload: 0 bytes
615 615 bundle2-input: part header size: 16
616 616 bundle2-input: part type: "test:ping"
617 617 bundle2-input: part id: "6"
618 618 bundle2-input: part parameters: 0
619 619 :test:ping:
620 620 mandatory: 0
621 621 advisory: 0
622 622 bundle2-input: payload chunk size: 0
623 623 payload: 0 bytes
624 624 bundle2-input: part header size: 0
625 625 bundle2-input: end of bundle2 stream
626 626 parts count: 7
627 627
628 628 Test actual unbundling of test part
629 629 =======================================
630 630
631 631 Process the bundle
632 632
633 633 $ hg unbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../parts.hg2
634 634 bundle2-input: start processing of HG20 stream
635 635 bundle2-input: reading bundle2 stream parameters
636 636 bundle2-input-bundle: with-transaction
637 637 bundle2-input: start extraction of bundle2 parts
638 638 bundle2-input: part header size: 17
639 639 bundle2-input: part type: "test:empty"
640 640 bundle2-input: part id: "0"
641 641 bundle2-input: part parameters: 0
642 642 bundle2-input: ignoring unsupported advisory part test:empty
643 643 bundle2-input-part: "test:empty" (advisory) unsupported-type
644 644 bundle2-input: payload chunk size: 0
645 645 bundle2-input: part header size: 17
646 646 bundle2-input: part type: "test:empty"
647 647 bundle2-input: part id: "1"
648 648 bundle2-input: part parameters: 0
649 649 bundle2-input: ignoring unsupported advisory part test:empty
650 650 bundle2-input-part: "test:empty" (advisory) unsupported-type
651 651 bundle2-input: payload chunk size: 0
652 652 bundle2-input: part header size: 16
653 653 bundle2-input: part type: "test:song"
654 654 bundle2-input: part id: "2"
655 655 bundle2-input: part parameters: 0
656 656 bundle2-input: found a handler for part test:song
657 657 bundle2-input-part: "test:song" (advisory) supported
658 658 The choir starts singing:
659 659 bundle2-input: payload chunk size: 178
660 660 bundle2-input: payload chunk size: 0
661 661 bundle2-input-part: total payload size 178
662 662 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
663 663 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
664 664 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
665 665 bundle2-input: part header size: 22
666 666 bundle2-input: part type: "test:debugreply"
667 667 bundle2-input: part id: "3"
668 668 bundle2-input: part parameters: 0
669 669 bundle2-input: found a handler for part test:debugreply
670 670 bundle2-input-part: "test:debugreply" (advisory) supported
671 671 debugreply: no reply
672 672 bundle2-input: payload chunk size: 0
673 673 bundle2-input: part header size: 43
674 674 bundle2-input: part type: "test:math"
675 675 bundle2-input: part id: "4"
676 676 bundle2-input: part parameters: 3
677 677 bundle2-input: ignoring unsupported advisory part test:math
678 678 bundle2-input-part: "test:math" (advisory) (params: 2 mandatory 2 advisory) unsupported-type
679 679 bundle2-input: payload chunk size: 2
680 680 bundle2-input: payload chunk size: 0
681 681 bundle2-input-part: total payload size 2
682 682 bundle2-input: part header size: 29
683 683 bundle2-input: part type: "test:song"
684 684 bundle2-input: part id: "5"
685 685 bundle2-input: part parameters: 1
686 686 bundle2-input: found a handler for part test:song
687 687 bundle2-input: ignoring unsupported advisory part test:song - randomparam
688 688 bundle2-input-part: "test:song" (advisory) (params: 1 mandatory) unsupported-params (randomparam)
689 689 bundle2-input: payload chunk size: 0
690 690 bundle2-input: part header size: 16
691 691 bundle2-input: part type: "test:ping"
692 692 bundle2-input: part id: "6"
693 693 bundle2-input: part parameters: 0
694 694 bundle2-input: found a handler for part test:ping
695 695 bundle2-input-part: "test:ping" (advisory) supported
696 696 received ping request (id 6)
697 697 bundle2-input: payload chunk size: 0
698 698 bundle2-input: part header size: 0
699 699 bundle2-input: end of bundle2 stream
700 700 bundle2-input-bundle: 6 parts total
701 701 0 unread bytes
702 702 3 total verses sung
703 703
704 704 Unbundle with an unknown mandatory part
705 705 (should abort)
706 706
707 707 $ hg bundle2 --parts --unknown ../unknown.hg2
708 708
709 709 $ hg unbundle2 < ../unknown.hg2
710 710 The choir starts singing:
711 711 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
712 712 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
713 713 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
714 714 debugreply: no reply
715 715 0 unread bytes
716 716 abort: missing support for test:unknown
717 717 [255]
718 718
719 719 Unbundle with an unknown mandatory part parameters
720 720 (should abort)
721 721
722 722 $ hg bundle2 --unknownparams ../unknown.hg2
723 723
724 724 $ hg unbundle2 < ../unknown.hg2
725 725 0 unread bytes
726 726 abort: missing support for test:song - randomparams
727 727 [255]
728 728
729 729 unbundle with a reply
730 730
731 731 $ hg bundle2 --parts --reply ../parts-reply.hg2
732 732 $ hg unbundle2 ../reply.hg2 < ../parts-reply.hg2
733 733 0 unread bytes
734 734 3 total verses sung
735 735
736 736 The reply is a bundle
737 737
738 738 $ f --hexdump ../reply.hg2
739 739 ../reply.hg2:
740 740 0000: 48 47 32 30 00 00 00 00 00 00 00 1b 06 6f 75 74 |HG20.........out|
741 741 0010: 70 75 74 00 00 00 00 00 01 0b 01 69 6e 2d 72 65 |put........in-re|
742 742 0020: 70 6c 79 2d 74 6f 33 00 00 00 d9 54 68 65 20 63 |ply-to3....The c|
743 743 0030: 68 6f 69 72 20 73 74 61 72 74 73 20 73 69 6e 67 |hoir starts sing|
744 744 0040: 69 6e 67 3a 0a 20 20 20 20 50 61 74 61 6c 69 20 |ing:. Patali |
745 745 0050: 44 69 72 61 70 61 74 61 2c 20 43 72 6f 6d 64 61 |Dirapata, Cromda|
746 746 0060: 20 43 72 6f 6d 64 61 20 52 69 70 61 6c 6f 2c 20 | Cromda Ripalo, |
747 747 0070: 50 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f |Pata Pata, Ko Ko|
748 748 0080: 20 4b 6f 0a 20 20 20 20 42 6f 6b 6f 72 6f 20 44 | Ko. Bokoro D|
749 749 0090: 69 70 6f 75 6c 69 74 6f 2c 20 52 6f 6e 64 69 20 |ipoulito, Rondi |
750 750 00a0: 52 6f 6e 64 69 20 50 65 70 69 6e 6f 2c 20 50 61 |Rondi Pepino, Pa|
751 751 00b0: 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f 20 4b |ta Pata, Ko Ko K|
752 752 00c0: 6f 0a 20 20 20 20 45 6d 61 6e 61 20 4b 61 72 61 |o. Emana Kara|
753 753 00d0: 73 73 6f 6c 69 2c 20 4c 6f 75 63 72 61 20 4c 6f |ssoli, Loucra Lo|
754 754 00e0: 75 63 72 61 20 50 6f 6e 70 6f 6e 74 6f 2c 20 50 |ucra Ponponto, P|
755 755 00f0: 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f 20 |ata Pata, Ko Ko |
756 756 0100: 4b 6f 2e 0a 00 00 00 00 00 00 00 1b 06 6f 75 74 |Ko...........out|
757 757 0110: 70 75 74 00 00 00 01 00 01 0b 01 69 6e 2d 72 65 |put........in-re|
758 758 0120: 70 6c 79 2d 74 6f 34 00 00 00 c9 64 65 62 75 67 |ply-to4....debug|
759 759 0130: 72 65 70 6c 79 3a 20 63 61 70 61 62 69 6c 69 74 |reply: capabilit|
760 760 0140: 69 65 73 3a 0a 64 65 62 75 67 72 65 70 6c 79 3a |ies:.debugreply:|
761 761 0150: 20 20 20 20 20 27 63 69 74 79 3d 21 27 0a 64 65 | 'city=!'.de|
762 762 0160: 62 75 67 72 65 70 6c 79 3a 20 20 20 20 20 20 20 |bugreply: |
763 763 0170: 20 20 27 63 65 6c 65 73 74 65 2c 76 69 6c 6c 65 | 'celeste,ville|
764 764 0180: 27 0a 64 65 62 75 67 72 65 70 6c 79 3a 20 20 20 |'.debugreply: |
765 765 0190: 20 20 27 65 6c 65 70 68 61 6e 74 73 27 0a 64 65 | 'elephants'.de|
766 766 01a0: 62 75 67 72 65 70 6c 79 3a 20 20 20 20 20 20 20 |bugreply: |
767 767 01b0: 20 20 27 62 61 62 61 72 27 0a 64 65 62 75 67 72 | 'babar'.debugr|
768 768 01c0: 65 70 6c 79 3a 20 20 20 20 20 20 20 20 20 27 63 |eply: 'c|
769 769 01d0: 65 6c 65 73 74 65 27 0a 64 65 62 75 67 72 65 70 |eleste'.debugrep|
770 770 01e0: 6c 79 3a 20 20 20 20 20 27 70 69 6e 67 2d 70 6f |ly: 'ping-po|
771 771 01f0: 6e 67 27 0a 00 00 00 00 00 00 00 1e 09 74 65 73 |ng'..........tes|
772 772 0200: 74 3a 70 6f 6e 67 00 00 00 02 01 00 0b 01 69 6e |t:pong........in|
773 773 0210: 2d 72 65 70 6c 79 2d 74 6f 37 00 00 00 00 00 00 |-reply-to7......|
774 774 0220: 00 1b 06 6f 75 74 70 75 74 00 00 00 03 00 01 0b |...output.......|
775 775 0230: 01 69 6e 2d 72 65 70 6c 79 2d 74 6f 37 00 00 00 |.in-reply-to7...|
776 776 0240: 3d 72 65 63 65 69 76 65 64 20 70 69 6e 67 20 72 |=received ping r|
777 777 0250: 65 71 75 65 73 74 20 28 69 64 20 37 29 0a 72 65 |equest (id 7).re|
778 778 0260: 70 6c 79 69 6e 67 20 74 6f 20 70 69 6e 67 20 72 |plying to ping r|
779 779 0270: 65 71 75 65 73 74 20 28 69 64 20 37 29 0a 00 00 |equest (id 7)...|
780 780 0280: 00 00 00 00 00 00 |......|
781 781
782 782 The reply is valid
783 783
784 784 $ hg statbundle2 < ../reply.hg2
785 785 options count: 0
786 786 :output:
787 787 mandatory: 0
788 788 advisory: 1
789 789 payload: 217 bytes
790 790 :output:
791 791 mandatory: 0
792 792 advisory: 1
793 793 payload: 201 bytes
794 794 :test:pong:
795 795 mandatory: 1
796 796 advisory: 0
797 797 payload: 0 bytes
798 798 :output:
799 799 mandatory: 0
800 800 advisory: 1
801 801 payload: 61 bytes
802 802 parts count: 4
803 803
804 804 Unbundle the reply to get the output:
805 805
806 806 $ hg unbundle2 < ../reply.hg2
807 807 remote: The choir starts singing:
808 808 remote: Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
809 809 remote: Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
810 810 remote: Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
811 811 remote: debugreply: capabilities:
812 812 remote: debugreply: 'city=!'
813 813 remote: debugreply: 'celeste,ville'
814 814 remote: debugreply: 'elephants'
815 815 remote: debugreply: 'babar'
816 816 remote: debugreply: 'celeste'
817 817 remote: debugreply: 'ping-pong'
818 818 remote: received ping request (id 7)
819 819 remote: replying to ping request (id 7)
820 820 0 unread bytes
821 821
822 822 Test push race detection
823 823
824 824 $ hg bundle2 --pushrace ../part-race.hg2
825 825
826 826 $ hg unbundle2 < ../part-race.hg2
827 827 0 unread bytes
828 828 abort: push race: remote repository changed while pushing - please try again
829 829 [255]
830 830
831 831 Support for changegroup
832 832 ===================================
833 833
834 834 $ hg unbundle $TESTDIR/bundles/rebase.hg
835 835 adding changesets
836 836 adding manifests
837 837 adding file changes
838 838 added 8 changesets with 7 changes to 7 files (+3 heads)
839 839 new changesets cd010b8cd998:02de42196ebe (8 drafts)
840 840 (run 'hg heads' to see heads, 'hg merge' to merge)
841 841
842 842 $ hg log -G
843 843 o 8:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> H
844 844 |
845 845 | o 7:eea13746799a draft Nicolas Dumazet <nicdumz.commits@gmail.com> G
846 846 |/|
847 847 o | 6:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
848 848 | |
849 849 | o 5:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
850 850 |/
851 851 | o 4:32af7686d403 draft Nicolas Dumazet <nicdumz.commits@gmail.com> D
852 852 | |
853 853 | o 3:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> C
854 854 | |
855 855 | o 2:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> B
856 856 |/
857 857 o 1:cd010b8cd998 draft Nicolas Dumazet <nicdumz.commits@gmail.com> A
858 858
859 859 @ 0:3903775176ed draft test a
860 860
861 861
862 862 $ hg bundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true --rev '8+7+5+4' ../rev.hg2
863 863 4 changesets found
864 864 list of changesets:
865 865 32af7686d403cf45b5d95f2d70cebea587ac806a
866 866 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
867 867 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
868 868 02de42196ebee42ef284b6780a87cdc96e8eaab6
869 869 bundle2-output-bundle: "HG20", 1 parts total
870 870 bundle2-output: start emission of HG20 stream
871 871 bundle2-output: bundle parameter:
872 872 bundle2-output: start of parts
873 873 bundle2-output: bundle part: "changegroup"
874 874 bundle2-output-part: "changegroup" (advisory) streamed payload
875 875 bundle2-output: part 0: "changegroup"
876 876 bundle2-output: header chunk size: 18
877 877 changesets: 1/4 chunks (25.00%)
878 878 changesets: 2/4 chunks (50.00%)
879 879 changesets: 3/4 chunks (75.00%)
880 880 changesets: 4/4 chunks (100.00%)
881 881 manifests: 1/4 chunks (25.00%)
882 882 manifests: 2/4 chunks (50.00%)
883 883 manifests: 3/4 chunks (75.00%)
884 884 manifests: 4/4 chunks (100.00%)
885 885 files: D 1/3 files (33.33%)
886 886 files: E 2/3 files (66.67%)
887 887 files: H 3/3 files (100.00%)
888 888 bundle2-output: payload chunk size: 1555
889 889 bundle2-output: closing payload chunk
890 890 bundle2-output: end of bundle
891 891
892 892 $ f --hexdump ../rev.hg2
893 893 ../rev.hg2:
894 894 0000: 48 47 32 30 00 00 00 00 00 00 00 12 0b 63 68 61 |HG20.........cha|
895 895 0010: 6e 67 65 67 72 6f 75 70 00 00 00 00 00 00 00 00 |ngegroup........|
896 896 0020: 06 13 00 00 00 a4 32 af 76 86 d4 03 cf 45 b5 d9 |......2.v....E..|
897 897 0030: 5f 2d 70 ce be a5 87 ac 80 6a 5f dd d9 89 57 c8 |_-p......j_...W.|
898 898 0040: a5 4a 4d 43 6d fe 1d a9 d8 7f 21 a1 b9 7b 00 00 |.JMCm.....!..{..|
899 899 0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
900 900 0060: 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f 2d 70 ce |..2.v....E.._-p.|
901 901 0070: be a5 87 ac 80 6a 00 00 00 00 00 00 00 29 00 00 |.....j.......)..|
902 902 0080: 00 29 36 65 31 66 34 63 34 37 65 63 62 35 33 33 |.)6e1f4c47ecb533|
903 903 0090: 66 66 64 30 63 38 65 35 32 63 64 63 38 38 61 66 |ffd0c8e52cdc88af|
904 904 00a0: 62 36 63 64 33 39 65 32 30 63 0a 00 00 00 66 00 |b6cd39e20c....f.|
905 905 00b0: 00 00 68 00 00 00 02 44 0a 00 00 00 69 00 00 00 |..h....D....i...|
906 906 00c0: 6a 00 00 00 01 44 00 00 00 a4 95 20 ee a7 81 bc |j....D..... ....|
907 907 00d0: ca 16 c1 e1 5a cc 0b a1 43 35 a0 e8 e5 ba cd 01 |....Z...C5......|
908 908 00e0: 0b 8c d9 98 f3 98 1a 5a 81 15 f9 4f 8d a4 ab 50 |.......Z...O...P|
909 909 00f0: 60 89 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |`...............|
910 910 0100: 00 00 00 00 00 00 95 20 ee a7 81 bc ca 16 c1 e1 |....... ........|
911 911 0110: 5a cc 0b a1 43 35 a0 e8 e5 ba 00 00 00 00 00 00 |Z...C5..........|
912 912 0120: 00 29 00 00 00 29 34 64 65 63 65 39 63 38 32 36 |.)...)4dece9c826|
913 913 0130: 66 36 39 34 39 30 35 30 37 62 39 38 63 36 33 38 |f69490507b98c638|
914 914 0140: 33 61 33 30 30 39 62 32 39 35 38 33 37 64 0a 00 |3a3009b295837d..|
915 915 0150: 00 00 66 00 00 00 68 00 00 00 02 45 0a 00 00 00 |..f...h....E....|
916 916 0160: 69 00 00 00 6a 00 00 00 01 45 00 00 00 a2 ee a1 |i...j....E......|
917 917 0170: 37 46 79 9a 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f |7Fy.......<...8.|
918 918 0180: 52 4f 24 b6 38 7c 8c 8c ae 37 17 88 80 f3 fa 95 |RO$.8|...7......|
919 919 0190: de d3 cb 1c f7 85 95 20 ee a7 81 bc ca 16 c1 e1 |....... ........|
920 920 01a0: 5a cc 0b a1 43 35 a0 e8 e5 ba ee a1 37 46 79 9a |Z...C5......7Fy.|
921 921 01b0: 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f 52 4f 00 00 |......<...8.RO..|
922 922 01c0: 00 00 00 00 00 29 00 00 00 29 33 36 35 62 39 33 |.....)...)365b93|
923 923 01d0: 64 35 37 66 64 66 34 38 31 34 65 32 62 35 39 31 |d57fdf4814e2b591|
924 924 01e0: 31 64 36 62 61 63 66 66 32 62 31 32 30 31 34 34 |1d6bacff2b120144|
925 925 01f0: 34 31 0a 00 00 00 66 00 00 00 68 00 00 00 00 00 |41....f...h.....|
926 926 0200: 00 00 69 00 00 00 6a 00 00 00 01 47 00 00 00 a4 |..i...j....G....|
927 927 0210: 02 de 42 19 6e be e4 2e f2 84 b6 78 0a 87 cd c9 |..B.n......x....|
928 928 0220: 6e 8e aa b6 24 b6 38 7c 8c 8c ae 37 17 88 80 f3 |n...$.8|...7....|
929 929 0230: fa 95 de d3 cb 1c f7 85 00 00 00 00 00 00 00 00 |................|
930 930 0240: 00 00 00 00 00 00 00 00 00 00 00 00 02 de 42 19 |..............B.|
931 931 0250: 6e be e4 2e f2 84 b6 78 0a 87 cd c9 6e 8e aa b6 |n......x....n...|
932 932 0260: 00 00 00 00 00 00 00 29 00 00 00 29 38 62 65 65 |.......)...)8bee|
933 933 0270: 34 38 65 64 63 37 33 31 38 35 34 31 66 63 30 30 |48edc7318541fc00|
934 934 0280: 31 33 65 65 34 31 62 30 38 39 32 37 36 61 38 63 |13ee41b089276a8c|
935 935 0290: 32 34 62 66 0a 00 00 00 66 00 00 00 66 00 00 00 |24bf....f...f...|
936 936 02a0: 02 48 0a 00 00 00 67 00 00 00 68 00 00 00 01 48 |.H....g...h....H|
937 937 02b0: 00 00 00 00 00 00 00 8b 6e 1f 4c 47 ec b5 33 ff |........n.LG..3.|
938 938 02c0: d0 c8 e5 2c dc 88 af b6 cd 39 e2 0c 66 a5 a0 18 |...,.....9..f...|
939 939 02d0: 17 fd f5 23 9c 27 38 02 b5 b7 61 8d 05 1c 89 e4 |...#.'8...a.....|
940 940 02e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
941 941 02f0: 00 00 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f 2d |....2.v....E.._-|
942 942 0300: 70 ce be a5 87 ac 80 6a 00 00 00 81 00 00 00 81 |p......j........|
943 943 0310: 00 00 00 2b 44 00 63 33 66 31 63 61 32 39 32 34 |...+D.c3f1ca2924|
944 944 0320: 63 31 36 61 31 39 62 30 36 35 36 61 38 34 39 30 |c16a19b0656a8490|
945 945 0330: 30 65 35 30 34 65 35 62 30 61 65 63 32 64 0a 00 |0e504e5b0aec2d..|
946 946 0340: 00 00 8b 4d ec e9 c8 26 f6 94 90 50 7b 98 c6 38 |...M...&...P{..8|
947 947 0350: 3a 30 09 b2 95 83 7d 00 7d 8c 9d 88 84 13 25 f5 |:0....}.}.....%.|
948 948 0360: c6 b0 63 71 b3 5b 4e 8a 2b 1a 83 00 00 00 00 00 |..cq.[N.+.......|
949 949 0370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 95 |................|
950 950 0380: 20 ee a7 81 bc ca 16 c1 e1 5a cc 0b a1 43 35 a0 | ........Z...C5.|
951 951 0390: e8 e5 ba 00 00 00 2b 00 00 00 ac 00 00 00 2b 45 |......+.......+E|
952 952 03a0: 00 39 63 36 66 64 30 33 35 30 61 36 63 30 64 30 |.9c6fd0350a6c0d0|
953 953 03b0: 63 34 39 64 34 61 39 63 35 30 31 37 63 66 30 37 |c49d4a9c5017cf07|
954 954 03c0: 30 34 33 66 35 34 65 35 38 0a 00 00 00 8b 36 5b |043f54e58.....6[|
955 955 03d0: 93 d5 7f df 48 14 e2 b5 91 1d 6b ac ff 2b 12 01 |....H.....k..+..|
956 956 03e0: 44 41 28 a5 84 c6 5e f1 21 f8 9e b6 6a b7 d0 bc |DA(...^.!...j...|
957 957 03f0: 15 3d 80 99 e7 ce 4d ec e9 c8 26 f6 94 90 50 7b |.=....M...&...P{|
958 958 0400: 98 c6 38 3a 30 09 b2 95 83 7d ee a1 37 46 79 9a |..8:0....}..7Fy.|
959 959 0410: 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f 52 4f 00 00 |......<...8.RO..|
960 960 0420: 00 56 00 00 00 56 00 00 00 2b 46 00 32 32 62 66 |.V...V...+F.22bf|
961 961 0430: 63 66 64 36 32 61 32 31 61 33 32 38 37 65 64 62 |cfd62a21a3287edb|
962 962 0440: 64 34 64 36 35 36 32 31 38 64 30 66 35 32 35 65 |d4d656218d0f525e|
963 963 0450: 64 37 36 61 0a 00 00 00 97 8b ee 48 ed c7 31 85 |d76a.......H..1.|
964 964 0460: 41 fc 00 13 ee 41 b0 89 27 6a 8c 24 bf 28 a5 84 |A....A..'j.$.(..|
965 965 0470: c6 5e f1 21 f8 9e b6 6a b7 d0 bc 15 3d 80 99 e7 |.^.!...j....=...|
966 966 0480: ce 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
967 967 0490: 00 00 00 00 00 02 de 42 19 6e be e4 2e f2 84 b6 |.......B.n......|
968 968 04a0: 78 0a 87 cd c9 6e 8e aa b6 00 00 00 2b 00 00 00 |x....n......+...|
969 969 04b0: 56 00 00 00 00 00 00 00 81 00 00 00 81 00 00 00 |V...............|
970 970 04c0: 2b 48 00 38 35 30 30 31 38 39 65 37 34 61 39 65 |+H.8500189e74a9e|
971 971 04d0: 30 34 37 35 65 38 32 32 30 39 33 62 63 37 64 62 |0475e822093bc7db|
972 972 04e0: 30 64 36 33 31 61 65 62 30 62 34 0a 00 00 00 00 |0d631aeb0b4.....|
973 973 04f0: 00 00 00 05 44 00 00 00 62 c3 f1 ca 29 24 c1 6a |....D...b...)$.j|
974 974 0500: 19 b0 65 6a 84 90 0e 50 4e 5b 0a ec 2d 00 00 00 |..ej...PN[..-...|
975 975 0510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
976 976 0520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
977 977 0530: 00 00 00 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f |.....2.v....E.._|
978 978 0540: 2d 70 ce be a5 87 ac 80 6a 00 00 00 00 00 00 00 |-p......j.......|
979 979 0550: 00 00 00 00 02 44 0a 00 00 00 00 00 00 00 05 45 |.....D.........E|
980 980 0560: 00 00 00 62 9c 6f d0 35 0a 6c 0d 0c 49 d4 a9 c5 |...b.o.5.l..I...|
981 981 0570: 01 7c f0 70 43 f5 4e 58 00 00 00 00 00 00 00 00 |.|.pC.NX........|
982 982 0580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
983 983 0590: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
984 984 05a0: 95 20 ee a7 81 bc ca 16 c1 e1 5a cc 0b a1 43 35 |. ........Z...C5|
985 985 05b0: a0 e8 e5 ba 00 00 00 00 00 00 00 00 00 00 00 02 |................|
986 986 05c0: 45 0a 00 00 00 00 00 00 00 05 48 00 00 00 62 85 |E.........H...b.|
987 987 05d0: 00 18 9e 74 a9 e0 47 5e 82 20 93 bc 7d b0 d6 31 |...t..G^. ..}..1|
988 988 05e0: ae b0 b4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
989 989 05f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
990 990 0600: 00 00 00 00 00 00 00 00 00 00 00 02 de 42 19 6e |.............B.n|
991 991 0610: be e4 2e f2 84 b6 78 0a 87 cd c9 6e 8e aa b6 00 |......x....n....|
992 992 0620: 00 00 00 00 00 00 00 00 00 00 02 48 0a 00 00 00 |...........H....|
993 993 0630: 00 00 00 00 00 00 00 00 00 00 00 00 00 |.............|
994 994
995 995 $ hg debugbundle ../rev.hg2
996 996 Stream params: {}
997 997 changegroup -- {} (mandatory: False)
998 998 32af7686d403cf45b5d95f2d70cebea587ac806a
999 999 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
1000 1000 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
1001 1001 02de42196ebee42ef284b6780a87cdc96e8eaab6
1002 1002 $ hg unbundle ../rev.hg2
1003 1003 adding changesets
1004 1004 adding manifests
1005 1005 adding file changes
1006 1006 added 0 changesets with 0 changes to 3 files
1007 1007 (run 'hg update' to get a working copy)
1008 1008
1009 1009 with reply
1010 1010
1011 1011 $ hg bundle2 --rev '8+7+5+4' --reply ../rev-rr.hg2
1012 1012 $ hg unbundle2 ../rev-reply.hg2 < ../rev-rr.hg2
1013 added 0 changesets with 0 changes to 3 files
1013 1014 0 unread bytes
1014 1015 addchangegroup return: 1
1015 1016
1016 1017 $ f --hexdump ../rev-reply.hg2
1017 1018 ../rev-reply.hg2:
1018 1019 0000: 48 47 32 30 00 00 00 00 00 00 00 2f 11 72 65 70 |HG20......./.rep|
1019 1020 0010: 6c 79 3a 63 68 61 6e 67 65 67 72 6f 75 70 00 00 |ly:changegroup..|
1020 1021 0020: 00 00 00 02 0b 01 06 01 69 6e 2d 72 65 70 6c 79 |........in-reply|
1021 1022 0030: 2d 74 6f 31 72 65 74 75 72 6e 31 00 00 00 00 00 |-to1return1.....|
1022 1023 0040: 00 00 1b 06 6f 75 74 70 75 74 00 00 00 01 00 01 |....output......|
1023 1024 0050: 0b 01 69 6e 2d 72 65 70 6c 79 2d 74 6f 31 00 00 |..in-reply-to1..|
1024 0060: 00 64 61 64 64 69 6e 67 20 63 68 61 6e 67 65 73 |.dadding changes|
1025 0060: 00 37 61 64 64 69 6e 67 20 63 68 61 6e 67 65 73 |.7adding changes|
1025 1026 0070: 65 74 73 0a 61 64 64 69 6e 67 20 6d 61 6e 69 66 |ets.adding manif|
1026 1027 0080: 65 73 74 73 0a 61 64 64 69 6e 67 20 66 69 6c 65 |ests.adding file|
1027 0090: 20 63 68 61 6e 67 65 73 0a 61 64 64 65 64 20 30 | changes.added 0|
1028 00a0: 20 63 68 61 6e 67 65 73 65 74 73 20 77 69 74 68 | changesets with|
1029 00b0: 20 30 20 63 68 61 6e 67 65 73 20 74 6f 20 33 20 | 0 changes to 3 |
1030 00c0: 66 69 6c 65 73 0a 00 00 00 00 00 00 00 00 |files.........|
1028 0090: 20 63 68 61 6e 67 65 73 0a 00 00 00 00 00 00 00 | changes........|
1029 00a0: 00 |.|
1031 1030
1032 1031 Check handling of exception during generation.
1033 1032 ----------------------------------------------
1034 1033
1035 1034 $ hg bundle2 --genraise > ../genfailed.hg2
1036 1035 abort: Someone set up us the bomb!
1037 1036 [255]
1038 1037
1039 1038 Should still be a valid bundle
1040 1039
1041 1040 $ f --hexdump ../genfailed.hg2
1042 1041 ../genfailed.hg2:
1043 1042 0000: 48 47 32 30 00 00 00 00 00 00 00 0d 06 6f 75 74 |HG20.........out|
1044 1043 0010: 70 75 74 00 00 00 00 00 00 ff ff ff ff 00 00 00 |put.............|
1045 1044 0020: 48 0b 65 72 72 6f 72 3a 61 62 6f 72 74 00 00 00 |H.error:abort...|
1046 1045 0030: 00 01 00 07 2d 6d 65 73 73 61 67 65 75 6e 65 78 |....-messageunex|
1047 1046 0040: 70 65 63 74 65 64 20 65 72 72 6f 72 3a 20 53 6f |pected error: So|
1048 1047 0050: 6d 65 6f 6e 65 20 73 65 74 20 75 70 20 75 73 20 |meone set up us |
1049 1048 0060: 74 68 65 20 62 6f 6d 62 21 00 00 00 00 00 00 00 |the bomb!.......|
1050 1049 0070: 00 |.|
1051 1050
1052 1051 And its handling on the other size raise a clean exception
1053 1052
1054 1053 $ cat ../genfailed.hg2 | hg unbundle2
1055 1054 0 unread bytes
1056 1055 abort: unexpected error: Someone set up us the bomb!
1057 1056 [255]
1058 1057
1059 1058 Test compression
1060 1059 ================
1061 1060
1062 1061 Simple case where it just work: GZ
1063 1062 ----------------------------------
1064 1063
1065 1064 $ hg bundle2 --compress GZ --rev '8+7+5+4' ../rev.hg2.bz
1066 1065 $ f --hexdump ../rev.hg2.bz
1067 1066 ../rev.hg2.bz:
1068 1067 0000: 48 47 32 30 00 00 00 0e 43 6f 6d 70 72 65 73 73 |HG20....Compress|
1069 1068 0010: 69 6f 6e 3d 47 5a 78 9c 95 94 7d 68 95 55 1c c7 |ion=GZx...}h.U..|
1070 1069 0020: 9f 3b 31 e8 ce fa c3 65 be a0 a4 b4 52 b9 29 e7 |.;1....e....R.).|
1071 1070 0030: f5 79 ce 89 fa 63 ed 5e 77 8b 9c c3 3f 2a 1c 68 |.y...c.^w...?*.h|
1072 1071 0040: cf 79 9b dd 6a ae b0 28 74 b8 e5 96 5b bb 86 61 |.y..j..(t...[..a|
1073 1072 0050: a3 15 6e 3a 71 c8 6a e8 a5 da 95 64 28 22 ce 69 |..n:q.j....d(".i|
1074 1073 0060: cd 06 59 34 28 2b 51 2a 58 c3 17 56 2a 9a 9d 67 |..Y4(+Q*X..V*..g|
1075 1074 0070: dc c6 35 9e c4 1d f8 9e 87 f3 9c f3 3b bf 0f bf |..5.........;...|
1076 1075 0080: 97 e3 38 ce f4 42 b9 d6 af ae d2 55 af ae 7b ad |..8..B.....U..{.|
1077 1076 0090: c6 c9 8d bb 8a ec b4 07 ed 7f fd ed d3 53 be 4e |.............S.N|
1078 1077 00a0: f4 0e af 59 52 73 ea 50 d7 96 9e ba d4 9a 1f 87 |...YRs.P........|
1079 1078 00b0: 9b 9f 1d e8 7a 6a 79 e9 cb 7f cf eb fe 7e d3 82 |....zjy......~..|
1080 1079 00c0: ce 2f 36 38 21 23 cc 36 b7 b5 38 90 ab a1 21 92 |./68!#.6..8...!.|
1081 1080 00d0: 78 5a 0a 8a b1 31 0a 48 a6 29 92 4a 32 e6 1b e1 |xZ...1.H.).J2...|
1082 1081 00e0: 4a 85 b9 46 40 46 ed 61 63 b5 d6 aa 20 1e ac 5e |J..F@F.ac... ..^|
1083 1082 00f0: b0 0a ae 8a c4 03 c6 d6 f9 a3 7b eb fb 4e de 7f |..........{..N..|
1084 1083 0100: e4 97 55 5f 15 76 96 d2 5d bf 9d 3f 38 18 29 4c |..U_.v..]..?8.)L|
1085 1084 0110: 0f b7 5d 6e 9b b3 aa 7e c6 d5 15 5b f7 7c 52 f1 |..]n...~...[.|R.|
1086 1085 0120: 7c 73 18 63 98 6d 3e 23 51 5a 6a 2e 19 72 8d cb ||s.c.m>#QZj..r..|
1087 1086 0130: 09 07 14 78 82 33 e9 62 86 7d 0c 00 17 88 53 86 |...x.3.b.}....S.|
1088 1087 0140: 3d 75 0b 63 e2 16 c6 84 9d 76 8f 76 7a cb de fc |=u.c.....v.vz...|
1089 1088 0150: a8 a3 f0 46 d3 a5 f6 c7 96 b6 9f 60 3b 57 ae 28 |...F.......`;W.(|
1090 1089 0160: ce b2 8d e9 f4 3e 6f 66 53 dd e5 6b ad 67 be f9 |.....>ofS..k.g..|
1091 1090 0170: 72 ee 5f 8d 61 3c 61 b6 f9 8c d8 a5 82 63 45 3d |r._.a<a......cE=|
1092 1091 0180: a3 0c 61 90 68 24 28 87 50 b9 c2 97 c6 20 01 11 |..a.h$(.P.... ..|
1093 1092 0190: 80 84 10 98 cf e8 e4 13 96 05 51 2c 38 f3 c4 ec |..........Q,8...|
1094 1093 01a0: ea 43 e7 96 5e 6a c8 be 11 dd 32 78 a2 fa dd 8f |.C..^j....2x....|
1095 1094 01b0: b3 61 84 61 51 0c b3 cd 27 64 42 6b c2 b4 92 1e |.a.aQ...'dBk....|
1096 1095 01c0: 86 8c 12 68 24 00 10 db 7f 50 00 c6 91 e7 fa 4c |...h$....P.....L|
1097 1096 01d0: 22 22 cc bf 84 81 0a 92 c1 aa 2a c7 1b 49 e6 ee |""........*..I..|
1098 1097 01e0: 6b a9 7e e0 e9 b2 91 5e 7c 73 68 e0 fc 23 3f 34 |k.~....^|sh..#?4|
1099 1098 01f0: ed cf 0e f2 b3 d3 4c d7 ae 59 33 6f 8c 3d b8 63 |......L..Y3o.=.c|
1100 1099 0200: 21 2b e8 3d e0 6f 9d 3a b7 f9 dc 24 2a b2 3e a7 |!+.=.o.:...$*.>.|
1101 1100 0210: 58 dc 91 d8 40 e9 23 8e 88 84 ae 0f b9 00 2e b5 |X...@.#.........|
1102 1101 0220: 74 36 f3 40 53 40 34 15 c0 d7 12 8d e7 bb 65 f9 |t6.@S@4.......e.|
1103 1102 0230: c8 ef 03 0f ff f9 fe b6 8a 0d 6d fd ec 51 70 f7 |..........m..Qp.|
1104 1103 0240: a7 ad 9b 6b 9d da 74 7b 53 43 d1 43 63 fd 19 f9 |...k..t{SC.Cc...|
1105 1104 0250: ca 67 95 e5 ef c4 e6 6c 9e 44 e1 c5 ac 7a 82 6f |.g.....l.D...z.o|
1106 1105 0260: c2 e1 d2 b5 2d 81 29 f0 5d 09 6c 6f 10 ae 88 cf |....-.).].lo....|
1107 1106 0270: 25 05 d0 93 06 78 80 60 43 2d 10 1b 47 71 2b b7 |%....x.`C-..Gq+.|
1108 1107 0280: 7f bb e9 a7 e4 7d 67 7b df 9b f7 62 cf cd d8 f4 |.....}g{...b....|
1109 1108 0290: 48 bc 64 51 57 43 ff ea 8b 0b ae 74 64 53 07 86 |H.dQWC.....tdS..|
1110 1109 02a0: fa 66 3c 5e f7 e1 af a7 c2 90 ff a7 be 9e c9 29 |.f<^...........)|
1111 1110 02b0: b6 cc 41 48 18 69 94 8b 7c 04 7d 8c 98 a7 95 50 |..AH.i..|.}....P|
1112 1111 02c0: 44 d9 d0 20 c8 14 30 14 51 ad 6c 16 03 94 0f 5a |D.. ..0.Q.l....Z|
1113 1112 02d0: 46 93 7f 1c 87 8d 25 d7 9d a2 d1 92 4c f3 c2 54 |F.....%.....L..T|
1114 1113 02e0: ba f8 70 18 ca 24 0a 29 96 43 71 f2 93 95 74 18 |..p..$.).Cq...t.|
1115 1114 02f0: b5 65 c4 b8 f6 6c 5c 34 20 1e d5 0c 21 c0 b1 90 |.e...l\4 ...!...|
1116 1115 0300: 9e 12 40 b9 18 fa 5a 00 41 a2 39 d3 a9 c1 73 21 |..@...Z.A.9...s!|
1117 1116 0310: 8e 5e 3c b9 b8 f8 48 6a 76 46 a7 1a b6 dd 5b 51 |.^<...HjvF....[Q|
1118 1117 0320: 5e 19 1d 59 12 c6 32 89 02 9a c0 8f 4f b8 0a ba |^..Y..2.....O...|
1119 1118 0330: 5e ec 58 37 44 a3 2f dd 33 ed c9 d3 dd c7 22 1b |^.X7D./.3.....".|
1120 1119 0340: 2f d4 94 8e 95 3f 77 a7 ae 6e f3 32 8d bb 4a 4c |/....?w..n.2..JL|
1121 1120 0350: b8 0a 5a 43 34 3a b3 3a d6 77 ff 5c b6 fa ad f9 |..ZC4:.:.w.\....|
1122 1121 0360: db fb 6a 33 df c1 7d 99 cf ef d4 d5 6d da 77 7c |..j3..}.....m.w||
1123 1122 0370: 3b 19 fd af c5 3f f1 60 c3 17 |;....?.`..|
1124 1123 $ hg debugbundle ../rev.hg2.bz
1125 1124 Stream params: {Compression: GZ}
1126 1125 changegroup -- {} (mandatory: False)
1127 1126 32af7686d403cf45b5d95f2d70cebea587ac806a
1128 1127 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
1129 1128 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
1130 1129 02de42196ebee42ef284b6780a87cdc96e8eaab6
1131 1130 $ hg unbundle ../rev.hg2.bz
1132 1131 adding changesets
1133 1132 adding manifests
1134 1133 adding file changes
1135 1134 added 0 changesets with 0 changes to 3 files
1136 1135 (run 'hg update' to get a working copy)
1137 1136 Simple case where it just work: BZ
1138 1137 ----------------------------------
1139 1138
1140 1139 $ hg bundle2 --compress BZ --rev '8+7+5+4' ../rev.hg2.bz
1141 1140 $ f --hexdump ../rev.hg2.bz
1142 1141 ../rev.hg2.bz:
1143 1142 0000: 48 47 32 30 00 00 00 0e 43 6f 6d 70 72 65 73 73 |HG20....Compress|
1144 1143 0010: 69 6f 6e 3d 42 5a 42 5a 68 39 31 41 59 26 53 59 |ion=BZBZh91AY&SY|
1145 1144 0020: a3 4b 18 3d 00 00 1a 7f ff ff bf 5f f6 ef ef 7f |.K.=......._....|
1146 1145 0030: f6 3f f7 d1 d9 ff ff f7 6e ff ff 6e f7 f6 bd df |.?......n..n....|
1147 1146 0040: b5 ab ff cf 67 f6 e7 7b f7 c0 02 d7 33 82 8b 51 |....g..{....3..Q|
1148 1147 0050: 04 a5 53 d5 3d 27 a0 99 18 4d 0d 34 00 d1 a1 e8 |..S.='...M.4....|
1149 1148 0060: 80 c8 7a 87 a9 a3 43 6a 3d 46 86 26 80 34 3d 40 |..z...Cj=F.&.4=@|
1150 1149 0070: c8 c9 b5 34 f4 8f 48 0f 51 ea 34 34 fd 4d aa 19 |...4..H.Q.44.M..|
1151 1150 0080: 03 40 0c 08 da 86 43 d4 f5 0f 42 1e a0 f3 54 33 |.@....C...B...T3|
1152 1151 0090: 54 d3 13 4d 03 40 32 00 00 32 03 26 80 0d 00 0d |T..M.@2..2.&....|
1153 1152 00a0: 00 68 c8 c8 03 20 32 30 98 8c 80 00 00 03 4d 00 |.h... 20......M.|
1154 1153 00b0: c8 00 00 0d 00 00 22 99 a1 34 c2 64 a6 d5 34 1a |......"..4.d..4.|
1155 1154 00c0: 00 00 06 86 83 4d 07 a8 d1 a0 68 01 a0 00 00 00 |.....M....h.....|
1156 1155 00d0: 00 0d 06 80 00 00 00 0d 00 03 40 00 00 04 a4 a1 |..........@.....|
1157 1156 00e0: 4d a9 89 89 b4 9a 32 0c 43 46 86 87 a9 8d 41 9a |M.....2.CF....A.|
1158 1157 00f0: 98 46 9a 0d 31 32 1a 34 0d 0c 8d a2 0c 98 4d 06 |.F..12.4......M.|
1159 1158 0100: 8c 40 c2 60 8d 0d 0c 20 c9 89 fa a0 d0 d3 21 a1 |.@.`... ......!.|
1160 1159 0110: ea 34 d3 68 9e a6 d1 74 05 33 cb 66 96 93 28 64 |.4.h...t.3.f..(d|
1161 1160 0120: 40 91 22 ac 55 9b ea 40 7b 38 94 e2 f8 06 00 cb |@.".U..@{8......|
1162 1161 0130: 28 02 00 4d ab 40 24 10 43 18 cf 64 b4 06 83 0c |(..M.@$.C..d....|
1163 1162 0140: 34 6c b4 a3 d4 0a 0a e4 a8 5c 4e 23 c0 c9 7a 31 |4l.......\N#..z1|
1164 1163 0150: 97 87 77 7a 64 88 80 8e 60 97 20 93 0f 8e eb c4 |..wzd...`. .....|
1165 1164 0160: 62 a4 44 a3 52 20 b2 99 a9 2e e1 d7 29 4a 54 ac |b.D.R ......)JT.|
1166 1165 0170: 44 7a bb cc 04 3d e0 aa bd 6a 33 5e 9b a2 57 36 |Dz...=...j3^..W6|
1167 1166 0180: fa cb 45 bb 6d 3e c1 d9 d9 f5 83 69 8a d0 e0 e2 |..E.m>.....i....|
1168 1167 0190: e7 ae 90 55 24 da 3f ab 78 c0 4c b4 56 a3 9e a4 |...U$.?.x.L.V...|
1169 1168 01a0: af 9c 65 74 86 ec 6d dc 62 dc 33 ca c8 50 dd 9d |..et..m.b.3..P..|
1170 1169 01b0: 98 8e 9e 59 20 f3 f0 42 91 4a 09 f5 75 8d 3d a5 |...Y ..B.J..u.=.|
1171 1170 01c0: a5 15 cb 8d 10 63 b0 c2 2e b2 81 f7 c1 76 0e 53 |.....c.......v.S|
1172 1171 01d0: 6c 0e 46 73 b5 ae 67 f9 4c 0b 45 6b a8 32 2a 2f |l.Fs..g.L.Ek.2*/|
1173 1172 01e0: a2 54 a4 44 05 20 a1 38 d1 a4 c6 09 a8 2b 08 99 |.T.D. .8.....+..|
1174 1173 01f0: a4 14 ae 8d a3 e3 aa 34 27 d8 44 ca c3 5d 21 8b |.......4'.D..]!.|
1175 1174 0200: 1a 1e 97 29 71 2b 09 4a 4a 55 55 94 58 65 b2 bc |...)q+.JJUU.Xe..|
1176 1175 0210: f3 a5 90 26 36 76 67 7a 51 98 d6 8a 4a 99 50 b5 |...&6vgzQ...J.P.|
1177 1176 0220: 99 8f 94 21 17 a9 8b f3 ad 4c 33 d4 2e 40 c8 0c |...!.....L3..@..|
1178 1177 0230: 3b 90 53 39 db 48 02 34 83 48 d6 b3 99 13 d2 58 |;.S9.H.4.H.....X|
1179 1178 0240: 65 8e 71 ac a9 06 95 f2 c4 8e b4 08 6b d3 0c ae |e.q.........k...|
1180 1179 0250: d9 90 56 71 43 a7 a2 62 16 3e 50 63 d3 57 3c 2d |..VqC..b.>Pc.W<-|
1181 1180 0260: 9f 0f 34 05 08 d8 a6 4b 59 31 54 66 3a 45 0c 8a |..4....KY1Tf:E..|
1182 1181 0270: c7 90 3a f0 6a 83 1b f5 ca fb 80 2b 50 06 fb 51 |..:.j......+P..Q|
1183 1182 0280: 7e a6 a4 d4 81 44 82 21 54 00 5b 1a 30 83 62 a3 |~....D.!T.[.0.b.|
1184 1183 0290: 18 b6 24 19 1e 45 df 4d 5c db a6 af 5b ac 90 fa |..$..E.M\...[...|
1185 1184 02a0: 3e ed f9 ec 4c ba 36 ee d8 60 20 a7 c7 3b cb d1 |>...L.6..` ..;..|
1186 1185 02b0: 90 43 7d 27 16 50 5d ad f4 14 07 0b 90 5c cc 6b |.C}'.P]......\.k|
1187 1186 02c0: 8d 3f a6 88 f4 34 37 a8 cf 14 63 36 19 f7 3e 28 |.?...47...c6..>(|
1188 1187 02d0: de 99 e8 16 a4 9d 0d 40 a1 a7 24 52 14 a6 72 62 |.......@..$R..rb|
1189 1188 02e0: 59 5a ca 2d e5 51 90 78 88 d9 c6 c7 21 d0 f7 46 |YZ.-.Q.x....!..F|
1190 1189 02f0: b2 04 46 44 4e 20 9c 12 b1 03 4e 25 e0 a9 0c 58 |..FDN ....N%...X|
1191 1190 0300: 5b 1d 3c 93 20 01 51 de a9 1c 69 23 32 46 14 b4 |[.<. .Q...i#2F..|
1192 1191 0310: 90 db 17 98 98 50 03 90 29 aa 40 b0 13 d8 43 d2 |.....P..).@...C.|
1193 1192 0320: 5f c5 9d eb f3 f2 ad 41 e8 7a a9 ed a1 58 84 a6 |_......A.z...X..|
1194 1193 0330: 42 bf d6 fc 24 82 c1 20 32 26 4a 15 a6 1d 29 7f |B...$.. 2&J...).|
1195 1194 0340: 7e f4 3d 07 bc 62 9a 5b ec 44 3d 72 1d 41 8b 5c |~.=..b.[.D=r.A.\|
1196 1195 0350: 80 de 0e 62 9a 2e f8 83 00 d5 07 a0 9c c6 74 98 |...b..........t.|
1197 1196 0360: 11 b2 5e a9 38 02 03 ee fd 86 5c f4 86 b3 ae da |..^.8.....\.....|
1198 1197 0370: 05 94 01 c5 c6 ea 18 e6 ba 2a ba b3 04 5c 96 89 |.........*...\..|
1199 1198 0380: 72 63 5b 10 11 f6 67 34 98 cb e4 c0 4e fa e6 99 |rc[...g4....N...|
1200 1199 0390: 19 6e 50 e8 26 8d 0c 17 e0 be ef e1 8e 02 6f 32 |.nP.&.........o2|
1201 1200 03a0: 82 dc 26 f8 a1 08 f3 8a 0d f3 c4 75 00 48 73 b8 |..&........u.Hs.|
1202 1201 03b0: be 3b 0d 7f d0 fd c7 78 96 ec e0 03 80 68 4d 8d |.;.....x.....hM.|
1203 1202 03c0: 43 8c d7 68 58 f9 50 f0 18 cb 21 58 1b 60 cd 1f |C..hX.P...!X.`..|
1204 1203 03d0: 84 36 2e 16 1f 0a f7 4e 8f eb df 01 2d c2 79 0b |.6.....N....-.y.|
1205 1204 03e0: f7 24 ea 0d e8 59 86 51 6e 1c 30 a3 ad 2f ee 8c |.$...Y.Qn.0../..|
1206 1205 03f0: 90 c8 84 d5 e8 34 c1 95 b2 c9 f6 4d 87 1c 7d 19 |.....4.....M..}.|
1207 1206 0400: d6 41 58 56 7a e0 6c ba 10 c7 e8 33 39 36 96 e7 |.AXVz.l....396..|
1208 1207 0410: d2 f9 59 9a 08 95 48 38 e7 0b b7 0a 24 67 c4 39 |..Y...H8....$g.9|
1209 1208 0420: 8b 43 88 57 9c 01 f5 61 b5 e1 27 41 7e af 83 fe |.C.W...a..'A~...|
1210 1209 0430: 2e e4 8a 70 a1 21 46 96 30 7a |...p.!F.0z|
1211 1210 $ hg debugbundle ../rev.hg2.bz
1212 1211 Stream params: {Compression: BZ}
1213 1212 changegroup -- {} (mandatory: False)
1214 1213 32af7686d403cf45b5d95f2d70cebea587ac806a
1215 1214 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
1216 1215 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
1217 1216 02de42196ebee42ef284b6780a87cdc96e8eaab6
1218 1217 $ hg unbundle ../rev.hg2.bz
1219 1218 adding changesets
1220 1219 adding manifests
1221 1220 adding file changes
1222 1221 added 0 changesets with 0 changes to 3 files
1223 1222 (run 'hg update' to get a working copy)
1224 1223
1225 1224 unknown compression while unbundling
1226 1225 -----------------------------
1227 1226
1228 1227 $ hg bundle2 --param Compression=FooBarUnknown --rev '8+7+5+4' ../rev.hg2.bz
1229 1228 $ cat ../rev.hg2.bz | hg statbundle2
1230 1229 abort: unknown parameters: Stream Parameter - Compression='FooBarUnknown'
1231 1230 [255]
1232 1231 $ hg unbundle ../rev.hg2.bz
1233 1232 abort: ../rev.hg2.bz: unknown bundle feature, Stream Parameter - Compression='FooBarUnknown'
1234 1233 (see https://mercurial-scm.org/wiki/BundleFeature for more information)
1235 1234 [255]
1236 1235
1237 1236 $ cd ..
@@ -1,460 +1,457 b''
1 1 Create an extension to test bundle2 with multiple changegroups
2 2
3 3 $ cat > bundle2.py <<EOF
4 4 > """
5 5 > """
6 6 > from mercurial import changegroup, discovery, exchange
7 7 >
8 8 > def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None,
9 9 > b2caps=None, heads=None, common=None,
10 10 > **kwargs):
11 11 > # Create two changegroups given the common changesets and heads for the
12 12 > # changegroup part we are being requested. Use the parent of each head
13 13 > # in 'heads' as intermediate heads for the first changegroup.
14 14 > intermediates = [repo[r].p1().node() for r in heads]
15 15 > outgoing = discovery.outgoing(repo, common, intermediates)
16 16 > cg = changegroup.makechangegroup(repo, outgoing, b'01',
17 17 > source, bundlecaps=bundlecaps)
18 18 > bundler.newpart(b'output', data=b'changegroup1')
19 19 > bundler.newpart(b'changegroup', data=cg.getchunks())
20 20 > outgoing = discovery.outgoing(repo, common + intermediates, heads)
21 21 > cg = changegroup.makechangegroup(repo, outgoing, b'01',
22 22 > source, bundlecaps=bundlecaps)
23 23 > bundler.newpart(b'output', data=b'changegroup2')
24 24 > bundler.newpart(b'changegroup', data=cg.getchunks())
25 25 >
26 26 > def _pull(repo, *args, **kwargs):
27 27 > pullop = _orig_pull(repo, *args, **kwargs)
28 28 > repo.ui.write(b'pullop.cgresult is %d\n' % pullop.cgresult)
29 29 > return pullop
30 30 >
31 31 > _orig_pull = exchange.pull
32 32 > exchange.pull = _pull
33 33 > exchange.getbundle2partsmapping[b'changegroup'] = _getbundlechangegrouppart
34 34 > EOF
35 35
36 36 $ cat >> $HGRCPATH << EOF
37 37 > [ui]
38 38 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
39 39 > EOF
40 40
41 41 Start with a simple repository with a single commit
42 42
43 43 $ hg init repo
44 44 $ cd repo
45 45 $ cat > .hg/hgrc << EOF
46 46 > [extensions]
47 47 > bundle2=$TESTTMP/bundle2.py
48 48 > EOF
49 49
50 50 $ echo A > A
51 51 $ hg commit -A -m A -q
52 52 $ cd ..
53 53
54 54 Clone
55 55
56 56 $ hg clone -q repo clone
57 57
58 58 Add two linear commits
59 59
60 60 $ cd repo
61 61 $ echo B > B
62 62 $ hg commit -A -m B -q
63 63 $ echo C > C
64 64 $ hg commit -A -m C -q
65 65
66 66 $ cd ../clone
67 67 $ cat >> .hg/hgrc <<EOF
68 68 > [hooks]
69 69 > pretxnchangegroup = sh -c "printenv.py --line pretxnchangegroup"
70 70 > changegroup = sh -c "printenv.py --line changegroup"
71 71 > incoming = sh -c "printenv.py --line incoming"
72 72 > EOF
73 73
74 74 Pull the new commits in the clone
75 75
76 76 $ hg pull
77 77 pulling from $TESTTMP/repo
78 78 searching for changes
79 79 remote: changegroup1
80 80 adding changesets
81 81 adding manifests
82 82 adding file changes
83 added 1 changesets with 1 changes to 1 files
84 83 pretxnchangegroup hook: HG_HOOKNAME=pretxnchangegroup
85 84 HG_HOOKTYPE=pretxnchangegroup
86 85 HG_NODE=27547f69f25460a52fff66ad004e58da7ad3fb56
87 86 HG_NODE_LAST=27547f69f25460a52fff66ad004e58da7ad3fb56
88 87 HG_PENDING=$TESTTMP/clone
89 88 HG_SOURCE=pull
90 89 HG_TXNID=TXN:$ID$
91 90 HG_TXNNAME=pull
92 91 file:/*/$TESTTMP/repo (glob)
93 92 HG_URL=file:$TESTTMP/repo
94 93
95 94 remote: changegroup2
96 95 adding changesets
97 96 adding manifests
98 97 adding file changes
99 added 1 changesets with 1 changes to 1 files
100 98 pretxnchangegroup hook: HG_HOOKNAME=pretxnchangegroup
101 99 HG_HOOKTYPE=pretxnchangegroup
102 100 HG_NODE=f838bfaca5c7226600ebcfd84f3c3c13a28d3757
103 101 HG_NODE_LAST=f838bfaca5c7226600ebcfd84f3c3c13a28d3757
104 102 HG_PENDING=$TESTTMP/clone
105 103 HG_PHASES_MOVED=1
106 104 HG_SOURCE=pull
107 105 HG_TXNID=TXN:$ID$
108 106 HG_TXNNAME=pull
109 107 file:/*/$TESTTMP/repo (glob)
110 108 HG_URL=file:$TESTTMP/repo
111 109
110 added 2 changesets with 2 changes to 2 files
112 111 new changesets 27547f69f254:f838bfaca5c7
113 112 changegroup hook: HG_HOOKNAME=changegroup
114 113 HG_HOOKTYPE=changegroup
115 114 HG_NODE=27547f69f25460a52fff66ad004e58da7ad3fb56
116 115 HG_NODE_LAST=27547f69f25460a52fff66ad004e58da7ad3fb56
117 116 HG_SOURCE=pull
118 117 HG_TXNID=TXN:$ID$
119 118 HG_TXNNAME=pull
120 119 file:/*/$TESTTMP/repo (glob)
121 120 HG_URL=file:$TESTTMP/repo
122 121
123 122 incoming hook: HG_HOOKNAME=incoming
124 123 HG_HOOKTYPE=incoming
125 124 HG_NODE=27547f69f25460a52fff66ad004e58da7ad3fb56
126 125 HG_SOURCE=pull
127 126 HG_TXNID=TXN:$ID$
128 127 HG_TXNNAME=pull
129 128 file:/*/$TESTTMP/repo (glob)
130 129 HG_URL=file:$TESTTMP/repo
131 130
132 131 changegroup hook: HG_HOOKNAME=changegroup
133 132 HG_HOOKTYPE=changegroup
134 133 HG_NODE=f838bfaca5c7226600ebcfd84f3c3c13a28d3757
135 134 HG_NODE_LAST=f838bfaca5c7226600ebcfd84f3c3c13a28d3757
136 135 HG_PHASES_MOVED=1
137 136 HG_SOURCE=pull
138 137 HG_TXNID=TXN:$ID$
139 138 HG_TXNNAME=pull
140 139 file:/*/$TESTTMP/repo (glob)
141 140 HG_URL=file:$TESTTMP/repo
142 141
143 142 incoming hook: HG_HOOKNAME=incoming
144 143 HG_HOOKTYPE=incoming
145 144 HG_NODE=f838bfaca5c7226600ebcfd84f3c3c13a28d3757
146 145 HG_PHASES_MOVED=1
147 146 HG_SOURCE=pull
148 147 HG_TXNID=TXN:$ID$
149 148 HG_TXNNAME=pull
150 149 file:/*/$TESTTMP/repo (glob)
151 150 HG_URL=file:$TESTTMP/repo
152 151
153 152 pullop.cgresult is 1
154 153 (run 'hg update' to get a working copy)
155 154 $ hg update
156 155 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
157 156 $ hg log -G
158 157 @ 2:f838bfaca5c7 public test C
159 158 |
160 159 o 1:27547f69f254 public test B
161 160 |
162 161 o 0:4a2df7238c3b public test A
163 162
164 163 Add more changesets with multiple heads to the original repository
165 164
166 165 $ cd ../repo
167 166 $ echo D > D
168 167 $ hg commit -A -m D -q
169 168 $ hg up -r 1
170 169 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
171 170 $ echo E > E
172 171 $ hg commit -A -m E -q
173 172 $ echo F > F
174 173 $ hg commit -A -m F -q
175 174 $ hg up -r 1
176 175 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
177 176 $ echo G > G
178 177 $ hg commit -A -m G -q
179 178 $ hg up -r 3
180 179 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
181 180 $ echo H > H
182 181 $ hg commit -A -m H -q
183 182 $ hg log -G
184 183 @ 7:5cd59d311f65 draft test H
185 184 |
186 185 | o 6:1d14c3ce6ac0 draft test G
187 186 | |
188 187 | | o 5:7f219660301f draft test F
189 188 | | |
190 189 | | o 4:8a5212ebc852 draft test E
191 190 | |/
192 191 o | 3:b3325c91a4d9 draft test D
193 192 | |
194 193 o | 2:f838bfaca5c7 draft test C
195 194 |/
196 195 o 1:27547f69f254 draft test B
197 196 |
198 197 o 0:4a2df7238c3b draft test A
199 198
200 199 New heads are reported during transfer and properly accounted for in
201 200 pullop.cgresult
202 201
203 202 $ cd ../clone
204 203 $ hg pull
205 204 pulling from $TESTTMP/repo
206 205 searching for changes
207 206 remote: changegroup1
208 207 adding changesets
209 208 adding manifests
210 209 adding file changes
211 added 2 changesets with 2 changes to 2 files (+1 heads)
212 210 pretxnchangegroup hook: HG_HOOKNAME=pretxnchangegroup
213 211 HG_HOOKTYPE=pretxnchangegroup
214 212 HG_NODE=b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
215 213 HG_NODE_LAST=8a5212ebc8527f9fb821601504794e3eb11a1ed3
216 214 HG_PENDING=$TESTTMP/clone
217 215 HG_SOURCE=pull
218 216 HG_TXNID=TXN:$ID$
219 217 HG_TXNNAME=pull
220 218 file:/*/$TESTTMP/repo (glob)
221 219 HG_URL=file:$TESTTMP/repo
222 220
223 221 remote: changegroup2
224 222 adding changesets
225 223 adding manifests
226 224 adding file changes
227 added 3 changesets with 3 changes to 3 files (+1 heads)
228 225 pretxnchangegroup hook: HG_HOOKNAME=pretxnchangegroup
229 226 HG_HOOKTYPE=pretxnchangegroup
230 227 HG_NODE=7f219660301fe4c8a116f714df5e769695cc2b46
231 228 HG_NODE_LAST=5cd59d311f6508b8e0ed28a266756c859419c9f1
232 229 HG_PENDING=$TESTTMP/clone
233 230 HG_PHASES_MOVED=1
234 231 HG_SOURCE=pull
235 232 HG_TXNID=TXN:$ID$
236 233 HG_TXNNAME=pull
237 234 file:/*/$TESTTMP/repo (glob)
238 235 HG_URL=file:$TESTTMP/repo
239 236
237 added 5 changesets with 5 changes to 5 files (+2 heads)
240 238 new changesets b3325c91a4d9:5cd59d311f65
241 239 changegroup hook: HG_HOOKNAME=changegroup
242 240 HG_HOOKTYPE=changegroup
243 241 HG_NODE=b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
244 242 HG_NODE_LAST=8a5212ebc8527f9fb821601504794e3eb11a1ed3
245 243 HG_SOURCE=pull
246 244 HG_TXNID=TXN:$ID$
247 245 HG_TXNNAME=pull
248 246 file:/*/$TESTTMP/repo (glob)
249 247 HG_URL=file:$TESTTMP/repo
250 248
251 249 incoming hook: HG_HOOKNAME=incoming
252 250 HG_HOOKTYPE=incoming
253 251 HG_NODE=b3325c91a4d916bcc4cdc83ea3fe4ece46a42f6e
254 252 HG_SOURCE=pull
255 253 HG_TXNID=TXN:$ID$
256 254 HG_TXNNAME=pull
257 255 file:/*/$TESTTMP/repo (glob)
258 256 HG_URL=file:$TESTTMP/repo
259 257
260 258 incoming hook: HG_HOOKNAME=incoming
261 259 HG_HOOKTYPE=incoming
262 260 HG_NODE=8a5212ebc8527f9fb821601504794e3eb11a1ed3
263 261 HG_SOURCE=pull
264 262 HG_TXNID=TXN:$ID$
265 263 HG_TXNNAME=pull
266 264 file:/*/$TESTTMP/repo (glob)
267 265 HG_URL=file:$TESTTMP/repo
268 266
269 267 changegroup hook: HG_HOOKNAME=changegroup
270 268 HG_HOOKTYPE=changegroup
271 269 HG_NODE=7f219660301fe4c8a116f714df5e769695cc2b46
272 270 HG_NODE_LAST=5cd59d311f6508b8e0ed28a266756c859419c9f1
273 271 HG_PHASES_MOVED=1
274 272 HG_SOURCE=pull
275 273 HG_TXNID=TXN:$ID$
276 274 HG_TXNNAME=pull
277 275 file:/*/$TESTTMP/repo (glob)
278 276 HG_URL=file:$TESTTMP/repo
279 277
280 278 incoming hook: HG_HOOKNAME=incoming
281 279 HG_HOOKTYPE=incoming
282 280 HG_NODE=7f219660301fe4c8a116f714df5e769695cc2b46
283 281 HG_PHASES_MOVED=1
284 282 HG_SOURCE=pull
285 283 HG_TXNID=TXN:$ID$
286 284 HG_TXNNAME=pull
287 285 file:/*/$TESTTMP/repo (glob)
288 286 HG_URL=file:$TESTTMP/repo
289 287
290 288 incoming hook: HG_HOOKNAME=incoming
291 289 HG_HOOKTYPE=incoming
292 290 HG_NODE=1d14c3ce6ac0582d2809220d33e8cd7a696e0156
293 291 HG_PHASES_MOVED=1
294 292 HG_SOURCE=pull
295 293 HG_TXNID=TXN:$ID$
296 294 HG_TXNNAME=pull
297 295 file:/*/$TESTTMP/repo (glob)
298 296 HG_URL=file:$TESTTMP/repo
299 297
300 298 incoming hook: HG_HOOKNAME=incoming
301 299 HG_HOOKTYPE=incoming
302 300 HG_NODE=5cd59d311f6508b8e0ed28a266756c859419c9f1
303 301 HG_PHASES_MOVED=1
304 302 HG_SOURCE=pull
305 303 HG_TXNID=TXN:$ID$
306 304 HG_TXNNAME=pull
307 305 file:/*/$TESTTMP/repo (glob)
308 306 HG_URL=file:$TESTTMP/repo
309 307
310 308 pullop.cgresult is 3
311 309 (run 'hg heads' to see heads, 'hg merge' to merge)
312 310 $ hg log -G
313 311 o 7:5cd59d311f65 public test H
314 312 |
315 313 | o 6:1d14c3ce6ac0 public test G
316 314 | |
317 315 | | o 5:7f219660301f public test F
318 316 | | |
319 317 | | o 4:8a5212ebc852 public test E
320 318 | |/
321 319 o | 3:b3325c91a4d9 public test D
322 320 | |
323 321 @ | 2:f838bfaca5c7 public test C
324 322 |/
325 323 o 1:27547f69f254 public test B
326 324 |
327 325 o 0:4a2df7238c3b public test A
328 326
329 327 Removing a head from the original repository by merging it
330 328
331 329 $ cd ../repo
332 330 $ hg merge -r 6 -q
333 331 $ hg commit -m Merge
334 332 $ echo I > I
335 333 $ hg commit -A -m H -q
336 334 $ hg log -G
337 335 @ 9:9d18e5bd9ab0 draft test H
338 336 |
339 337 o 8:71bd7b46de72 draft test Merge
340 338 |\
341 339 | o 7:5cd59d311f65 draft test H
342 340 | |
343 341 o | 6:1d14c3ce6ac0 draft test G
344 342 | |
345 343 | | o 5:7f219660301f draft test F
346 344 | | |
347 345 +---o 4:8a5212ebc852 draft test E
348 346 | |
349 347 | o 3:b3325c91a4d9 draft test D
350 348 | |
351 349 | o 2:f838bfaca5c7 draft test C
352 350 |/
353 351 o 1:27547f69f254 draft test B
354 352 |
355 353 o 0:4a2df7238c3b draft test A
356 354
357 355 Removed heads are reported during transfer and properly accounted for in
358 356 pullop.cgresult
359 357
360 358 $ cd ../clone
361 359 $ hg pull
362 360 pulling from $TESTTMP/repo
363 361 searching for changes
364 362 remote: changegroup1
365 363 adding changesets
366 364 adding manifests
367 365 adding file changes
368 added 1 changesets with 0 changes to 0 files (-1 heads)
369 366 pretxnchangegroup hook: HG_HOOKNAME=pretxnchangegroup
370 367 HG_HOOKTYPE=pretxnchangegroup
371 368 HG_NODE=71bd7b46de72e69a32455bf88d04757d542e6cf4
372 369 HG_NODE_LAST=71bd7b46de72e69a32455bf88d04757d542e6cf4
373 370 HG_PENDING=$TESTTMP/clone
374 371 HG_SOURCE=pull
375 372 HG_TXNID=TXN:$ID$
376 373 HG_TXNNAME=pull
377 374 file:/*/$TESTTMP/repo (glob)
378 375 HG_URL=file:$TESTTMP/repo
379 376
380 377 remote: changegroup2
381 378 adding changesets
382 379 adding manifests
383 380 adding file changes
384 added 1 changesets with 1 changes to 1 files
385 381 pretxnchangegroup hook: HG_HOOKNAME=pretxnchangegroup
386 382 HG_HOOKTYPE=pretxnchangegroup
387 383 HG_NODE=9d18e5bd9ab09337802595d49f1dad0c98df4d84
388 384 HG_NODE_LAST=9d18e5bd9ab09337802595d49f1dad0c98df4d84
389 385 HG_PENDING=$TESTTMP/clone
390 386 HG_PHASES_MOVED=1
391 387 HG_SOURCE=pull
392 388 HG_TXNID=TXN:$ID$
393 389 HG_TXNNAME=pull
394 390 file:/*/$TESTTMP/repo (glob)
395 391 HG_URL=file:$TESTTMP/repo
396 392
393 added 2 changesets with 1 changes to 1 files (-1 heads)
397 394 new changesets 71bd7b46de72:9d18e5bd9ab0
398 395 changegroup hook: HG_HOOKNAME=changegroup
399 396 HG_HOOKTYPE=changegroup
400 397 HG_NODE=71bd7b46de72e69a32455bf88d04757d542e6cf4
401 398 HG_NODE_LAST=71bd7b46de72e69a32455bf88d04757d542e6cf4
402 399 HG_SOURCE=pull
403 400 HG_TXNID=TXN:$ID$
404 401 HG_TXNNAME=pull
405 402 file:/*/$TESTTMP/repo (glob)
406 403 HG_URL=file:$TESTTMP/repo
407 404
408 405 incoming hook: HG_HOOKNAME=incoming
409 406 HG_HOOKTYPE=incoming
410 407 HG_NODE=71bd7b46de72e69a32455bf88d04757d542e6cf4
411 408 HG_SOURCE=pull
412 409 HG_TXNID=TXN:$ID$
413 410 HG_TXNNAME=pull
414 411 file:/*/$TESTTMP/repo (glob)
415 412 HG_URL=file:$TESTTMP/repo
416 413
417 414 changegroup hook: HG_HOOKNAME=changegroup
418 415 HG_HOOKTYPE=changegroup
419 416 HG_NODE=9d18e5bd9ab09337802595d49f1dad0c98df4d84
420 417 HG_NODE_LAST=9d18e5bd9ab09337802595d49f1dad0c98df4d84
421 418 HG_PHASES_MOVED=1
422 419 HG_SOURCE=pull
423 420 HG_TXNID=TXN:$ID$
424 421 HG_TXNNAME=pull
425 422 file:/*/$TESTTMP/repo (glob)
426 423 HG_URL=file:$TESTTMP/repo
427 424
428 425 incoming hook: HG_HOOKNAME=incoming
429 426 HG_HOOKTYPE=incoming
430 427 HG_NODE=9d18e5bd9ab09337802595d49f1dad0c98df4d84
431 428 HG_PHASES_MOVED=1
432 429 HG_SOURCE=pull
433 430 HG_TXNID=TXN:$ID$
434 431 HG_TXNNAME=pull
435 432 file:/*/$TESTTMP/repo (glob)
436 433 HG_URL=file:$TESTTMP/repo
437 434
438 435 pullop.cgresult is -2
439 436 (run 'hg update' to get a working copy)
440 437 $ hg log -G
441 438 o 9:9d18e5bd9ab0 public test H
442 439 |
443 440 o 8:71bd7b46de72 public test Merge
444 441 |\
445 442 | o 7:5cd59d311f65 public test H
446 443 | |
447 444 o | 6:1d14c3ce6ac0 public test G
448 445 | |
449 446 | | o 5:7f219660301f public test F
450 447 | | |
451 448 +---o 4:8a5212ebc852 public test E
452 449 | |
453 450 | o 3:b3325c91a4d9 public test D
454 451 | |
455 452 | @ 2:f838bfaca5c7 public test C
456 453 |/
457 454 o 1:27547f69f254 public test B
458 455 |
459 456 o 0:4a2df7238c3b public test A
460 457
@@ -1,619 +1,609 b''
1 1 #testcases sshv1 sshv2
2 2
3 3 #if sshv2
4 4 $ cat >> $HGRCPATH << EOF
5 5 > [experimental]
6 6 > sshpeer.advertise-v2 = true
7 7 > sshserver.support-v2 = true
8 8 > EOF
9 9 #endif
10 10
11 11 Create an extension to test bundle2 remote-changegroup parts
12 12
13 13 $ cat > bundle2.py << EOF
14 14 > """A small extension to test bundle2 remote-changegroup parts.
15 15 >
16 16 > Current bundle2 implementation doesn't provide a way to generate those
17 17 > parts, so they must be created by extensions.
18 18 > """
19 19 > from mercurial import (
20 20 > bundle2,
21 21 > changegroup,
22 22 > discovery,
23 23 > exchange,
24 24 > pycompat,
25 25 > util,
26 26 > )
27 27 >
28 28 > def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None,
29 29 > b2caps=None, heads=None, common=None,
30 30 > **kwargs):
31 31 > """this function replaces the changegroup part handler for getbundle.
32 32 > It allows to create a set of arbitrary parts containing changegroups
33 33 > and remote-changegroups, as described in a bundle2maker file in the
34 34 > repository .hg/ directory.
35 35 >
36 36 > Each line of that bundle2maker file contain a description of the
37 37 > part to add:
38 38 > - changegroup common_revset heads_revset
39 39 > Creates a changegroup part based, using common_revset and
40 40 > heads_revset for outgoing
41 41 > - remote-changegroup url file
42 42 > Creates a remote-changegroup part for a bundle at the given
43 43 > url. Size and digest, as required by the client, are computed
44 44 > from the given file.
45 45 > - raw-remote-changegroup <python expression>
46 46 > Creates a remote-changegroup part with the data given in the
47 47 > Python expression as parameters. The Python expression is
48 48 > evaluated with eval, and is expected to be a dict.
49 49 > """
50 50 > def newpart(name, data=b''):
51 51 > """wrapper around bundler.newpart adding an extra part making the
52 52 > client output information about each processed part"""
53 53 > bundler.newpart(b'output', data=name)
54 54 > part = bundler.newpart(name, data=data)
55 55 > return part
56 56 >
57 57 > for line in open(repo.vfs.join(b'bundle2maker'), 'rb'):
58 58 > line = line.strip()
59 59 > try:
60 60 > verb, args = line.split(None, 1)
61 61 > except ValueError:
62 62 > verb, args = line, b''
63 63 > if verb == b'remote-changegroup':
64 64 > url, file = args.split()
65 65 > bundledata = open(file, 'rb').read()
66 66 > digest = util.digester.preferred(b2caps[b'digests'])
67 67 > d = util.digester([digest], bundledata)
68 68 > part = newpart(b'remote-changegroup')
69 69 > part.addparam(b'url', url)
70 70 > part.addparam(b'size', b'%d' % len(bundledata))
71 71 > part.addparam(b'digests', digest)
72 72 > part.addparam(b'digest:%s' % digest, d[digest])
73 73 > elif verb == b'raw-remote-changegroup':
74 74 > part = newpart(b'remote-changegroup')
75 75 > for k, v in eval(args).items():
76 76 > part.addparam(pycompat.sysbytes(k), pycompat.bytestr(v))
77 77 > elif verb == b'changegroup':
78 78 > _common, heads = args.split()
79 79 > common.extend(repo[r].node() for r in repo.revs(_common))
80 80 > heads = [repo[r].node() for r in repo.revs(heads)]
81 81 > outgoing = discovery.outgoing(repo, common, heads)
82 82 > cg = changegroup.makechangegroup(repo, outgoing, b'01',
83 83 > b'changegroup')
84 84 > newpart(b'changegroup', cg.getchunks())
85 85 > else:
86 86 > raise Exception('unknown verb')
87 87 >
88 88 > exchange.getbundle2partsmapping[b'changegroup'] = _getbundlechangegrouppart
89 89 > EOF
90 90
91 91 Start a simple HTTP server to serve bundles
92 92
93 93 $ "$PYTHON" "$TESTDIR/dumbhttp.py" -p $HGPORT --pid dumb.pid
94 94 $ cat dumb.pid >> $DAEMON_PIDS
95 95
96 96 $ cat >> $HGRCPATH << EOF
97 97 > [ui]
98 98 > ssh="$PYTHON" "$TESTDIR/dummyssh"
99 99 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
100 100 > EOF
101 101
102 102 $ hg init repo
103 103
104 104 $ hg -R repo unbundle $TESTDIR/bundles/rebase.hg
105 105 adding changesets
106 106 adding manifests
107 107 adding file changes
108 108 added 8 changesets with 7 changes to 7 files (+2 heads)
109 109 new changesets cd010b8cd998:02de42196ebe (8 drafts)
110 110 (run 'hg heads' to see heads, 'hg merge' to merge)
111 111
112 112 $ hg -R repo log -G
113 113 o 7:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> H
114 114 |
115 115 | o 6:eea13746799a draft Nicolas Dumazet <nicdumz.commits@gmail.com> G
116 116 |/|
117 117 o | 5:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
118 118 | |
119 119 | o 4:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
120 120 |/
121 121 | o 3:32af7686d403 draft Nicolas Dumazet <nicdumz.commits@gmail.com> D
122 122 | |
123 123 | o 2:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> C
124 124 | |
125 125 | o 1:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> B
126 126 |/
127 127 o 0:cd010b8cd998 draft Nicolas Dumazet <nicdumz.commits@gmail.com> A
128 128
129 129 $ hg clone repo orig
130 130 updating to branch default
131 131 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
132 132
133 133 $ cat > repo/.hg/hgrc << EOF
134 134 > [extensions]
135 135 > bundle2=$TESTTMP/bundle2.py
136 136 > EOF
137 137
138 138 Test a pull with an remote-changegroup
139 139
140 140 $ hg bundle -R repo --type v1 --base '0:4' -r '5:7' bundle.hg
141 141 3 changesets found
142 142 $ cat > repo/.hg/bundle2maker << EOF
143 143 > remote-changegroup http://localhost:$HGPORT/bundle.hg bundle.hg
144 144 > EOF
145 145 $ hg clone orig clone -r 3 -r 4
146 146 adding changesets
147 147 adding manifests
148 148 adding file changes
149 149 added 5 changesets with 5 changes to 5 files (+1 heads)
150 150 new changesets cd010b8cd998:9520eea781bc
151 151 updating to branch default
152 152 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
153 153 $ hg pull -R clone ssh://user@dummy/repo
154 154 pulling from ssh://user@dummy/repo
155 155 searching for changes
156 156 remote: remote-changegroup
157 157 adding changesets
158 158 adding manifests
159 159 adding file changes
160 160 added 3 changesets with 2 changes to 2 files (+1 heads)
161 161 new changesets 24b6387c8c8c:02de42196ebe
162 162 (run 'hg heads .' to see heads, 'hg merge' to merge)
163 163 $ hg -R clone log -G
164 164 o 7:02de42196ebe public Nicolas Dumazet <nicdumz.commits@gmail.com> H
165 165 |
166 166 | o 6:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> G
167 167 |/|
168 168 o | 5:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
169 169 | |
170 170 | o 4:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
171 171 |/
172 172 | @ 3:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> D
173 173 | |
174 174 | o 2:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> C
175 175 | |
176 176 | o 1:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> B
177 177 |/
178 178 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
179 179
180 180 $ rm -rf clone
181 181
182 182 Test a pull with an remote-changegroup and a following changegroup
183 183
184 184 $ hg bundle -R repo --type v1 --base 2 -r '3:4' bundle2.hg
185 185 2 changesets found
186 186 $ cat > repo/.hg/bundle2maker << EOF
187 187 > remote-changegroup http://localhost:$HGPORT/bundle2.hg bundle2.hg
188 188 > changegroup 0:4 5:7
189 189 > EOF
190 190 $ hg clone orig clone -r 2
191 191 adding changesets
192 192 adding manifests
193 193 adding file changes
194 194 added 3 changesets with 3 changes to 3 files
195 195 new changesets cd010b8cd998:5fddd98957c8
196 196 updating to branch default
197 197 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
198 198 $ hg pull -R clone ssh://user@dummy/repo
199 199 pulling from ssh://user@dummy/repo
200 200 searching for changes
201 201 remote: remote-changegroup
202 202 adding changesets
203 203 adding manifests
204 204 adding file changes
205 added 2 changesets with 2 changes to 2 files (+1 heads)
206 205 remote: changegroup
207 206 adding changesets
208 207 adding manifests
209 208 adding file changes
210 added 3 changesets with 2 changes to 2 files (+1 heads)
209 added 5 changesets with 4 changes to 4 files (+2 heads)
211 210 new changesets 32af7686d403:02de42196ebe
212 211 (run 'hg heads' to see heads, 'hg merge' to merge)
213 212 $ hg -R clone log -G
214 213 o 7:02de42196ebe public Nicolas Dumazet <nicdumz.commits@gmail.com> H
215 214 |
216 215 | o 6:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> G
217 216 |/|
218 217 o | 5:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
219 218 | |
220 219 | o 4:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
221 220 |/
222 221 | o 3:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> D
223 222 | |
224 223 | @ 2:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> C
225 224 | |
226 225 | o 1:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> B
227 226 |/
228 227 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
229 228
230 229 $ rm -rf clone
231 230
232 231 Test a pull with a changegroup followed by an remote-changegroup
233 232
234 233 $ hg bundle -R repo --type v1 --base '0:4' -r '5:7' bundle3.hg
235 234 3 changesets found
236 235 $ cat > repo/.hg/bundle2maker << EOF
237 236 > changegroup 000000000000 :4
238 237 > remote-changegroup http://localhost:$HGPORT/bundle3.hg bundle3.hg
239 238 > EOF
240 239 $ hg clone orig clone -r 2
241 240 adding changesets
242 241 adding manifests
243 242 adding file changes
244 243 added 3 changesets with 3 changes to 3 files
245 244 new changesets cd010b8cd998:5fddd98957c8
246 245 updating to branch default
247 246 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 247 $ hg pull -R clone ssh://user@dummy/repo
249 248 pulling from ssh://user@dummy/repo
250 249 searching for changes
251 250 remote: changegroup
252 251 adding changesets
253 252 adding manifests
254 253 adding file changes
255 added 2 changesets with 2 changes to 2 files (+1 heads)
256 254 remote: remote-changegroup
257 255 adding changesets
258 256 adding manifests
259 257 adding file changes
260 added 3 changesets with 2 changes to 2 files (+1 heads)
258 added 5 changesets with 4 changes to 4 files (+2 heads)
261 259 new changesets 32af7686d403:02de42196ebe
262 260 (run 'hg heads' to see heads, 'hg merge' to merge)
263 261 $ hg -R clone log -G
264 262 o 7:02de42196ebe public Nicolas Dumazet <nicdumz.commits@gmail.com> H
265 263 |
266 264 | o 6:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> G
267 265 |/|
268 266 o | 5:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
269 267 | |
270 268 | o 4:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
271 269 |/
272 270 | o 3:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> D
273 271 | |
274 272 | @ 2:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> C
275 273 | |
276 274 | o 1:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> B
277 275 |/
278 276 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
279 277
280 278 $ rm -rf clone
281 279
282 280 Test a pull with two remote-changegroups and a changegroup
283 281
284 282 $ hg bundle -R repo --type v1 --base 2 -r '3:4' bundle4.hg
285 283 2 changesets found
286 284 $ hg bundle -R repo --type v1 --base '3:4' -r '5:6' bundle5.hg
287 285 2 changesets found
288 286 $ cat > repo/.hg/bundle2maker << EOF
289 287 > remote-changegroup http://localhost:$HGPORT/bundle4.hg bundle4.hg
290 288 > remote-changegroup http://localhost:$HGPORT/bundle5.hg bundle5.hg
291 289 > changegroup 0:6 7
292 290 > EOF
293 291 $ hg clone orig clone -r 2
294 292 adding changesets
295 293 adding manifests
296 294 adding file changes
297 295 added 3 changesets with 3 changes to 3 files
298 296 new changesets cd010b8cd998:5fddd98957c8
299 297 updating to branch default
300 298 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
301 299 $ hg pull -R clone ssh://user@dummy/repo
302 300 pulling from ssh://user@dummy/repo
303 301 searching for changes
304 302 remote: remote-changegroup
305 303 adding changesets
306 304 adding manifests
307 305 adding file changes
308 added 2 changesets with 2 changes to 2 files (+1 heads)
309 306 remote: remote-changegroup
310 307 adding changesets
311 308 adding manifests
312 309 adding file changes
313 added 2 changesets with 1 changes to 1 files
314 310 remote: changegroup
315 311 adding changesets
316 312 adding manifests
317 313 adding file changes
318 added 1 changesets with 1 changes to 1 files (+1 heads)
314 added 5 changesets with 4 changes to 4 files (+2 heads)
319 315 new changesets 32af7686d403:02de42196ebe
320 316 (run 'hg heads' to see heads, 'hg merge' to merge)
321 317 $ hg -R clone log -G
322 318 o 7:02de42196ebe public Nicolas Dumazet <nicdumz.commits@gmail.com> H
323 319 |
324 320 | o 6:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> G
325 321 |/|
326 322 o | 5:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
327 323 | |
328 324 | o 4:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
329 325 |/
330 326 | o 3:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> D
331 327 | |
332 328 | @ 2:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> C
333 329 | |
334 330 | o 1:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> B
335 331 |/
336 332 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
337 333
338 334 $ rm -rf clone
339 335
340 336 Hash digest tests
341 337
342 338 $ hg bundle -R repo --type v1 -a bundle6.hg
343 339 8 changesets found
344 340
345 341 $ cat > repo/.hg/bundle2maker << EOF
346 342 > raw-remote-changegroup {'url': 'http://localhost:$HGPORT/bundle6.hg', 'size': 1663, 'digests': 'sha1', 'digest:sha1': '2c880cfec23cff7d8f80c2f12958d1563cbdaba6'}
347 343 > EOF
348 344 $ hg clone ssh://user@dummy/repo clone
349 345 requesting all changes
350 346 remote: remote-changegroup
351 347 adding changesets
352 348 adding manifests
353 349 adding file changes
354 350 added 8 changesets with 7 changes to 7 files (+2 heads)
355 351 new changesets cd010b8cd998:02de42196ebe
356 352 updating to branch default
357 353 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
358 354 $ rm -rf clone
359 355
360 356 $ cat > repo/.hg/bundle2maker << EOF
361 357 > raw-remote-changegroup {'url': 'http://localhost:$HGPORT/bundle6.hg', 'size': 1663, 'digests': 'md5', 'digest:md5': 'e22172c2907ef88794b7bea6642c2394'}
362 358 > EOF
363 359 $ hg clone ssh://user@dummy/repo clone
364 360 requesting all changes
365 361 remote: remote-changegroup
366 362 adding changesets
367 363 adding manifests
368 364 adding file changes
369 365 added 8 changesets with 7 changes to 7 files (+2 heads)
370 366 new changesets cd010b8cd998:02de42196ebe
371 367 updating to branch default
372 368 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
373 369 $ rm -rf clone
374 370
375 371 Hash digest mismatch throws an error
376 372
377 373 $ cat > repo/.hg/bundle2maker << EOF
378 374 > raw-remote-changegroup {'url': 'http://localhost:$HGPORT/bundle6.hg', 'size': 1663, 'digests': 'sha1', 'digest:sha1': '0' * 40}
379 375 > EOF
380 376 $ hg clone ssh://user@dummy/repo clone
381 377 requesting all changes
382 378 remote: remote-changegroup
383 379 adding changesets
384 380 adding manifests
385 381 adding file changes
386 added 8 changesets with 7 changes to 7 files (+2 heads)
387 382 transaction abort!
388 383 rollback completed
389 384 abort: bundle at http://localhost:$HGPORT/bundle6.hg is corrupted:
390 385 sha1 mismatch: expected 0000000000000000000000000000000000000000, got 2c880cfec23cff7d8f80c2f12958d1563cbdaba6
391 386 [255]
392 387
393 388 Multiple hash digests can be given
394 389
395 390 $ cat > repo/.hg/bundle2maker << EOF
396 391 > raw-remote-changegroup {'url': 'http://localhost:$HGPORT/bundle6.hg', 'size': 1663, 'digests': 'md5 sha1', 'digest:md5': 'e22172c2907ef88794b7bea6642c2394', 'digest:sha1': '2c880cfec23cff7d8f80c2f12958d1563cbdaba6'}
397 392 > EOF
398 393 $ hg clone ssh://user@dummy/repo clone
399 394 requesting all changes
400 395 remote: remote-changegroup
401 396 adding changesets
402 397 adding manifests
403 398 adding file changes
404 399 added 8 changesets with 7 changes to 7 files (+2 heads)
405 400 new changesets cd010b8cd998:02de42196ebe
406 401 updating to branch default
407 402 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
408 403 $ rm -rf clone
409 404
410 405 If either of the multiple hash digests mismatches, an error is thrown
411 406
412 407 $ cat > repo/.hg/bundle2maker << EOF
413 408 > raw-remote-changegroup {'url': 'http://localhost:$HGPORT/bundle6.hg', 'size': 1663, 'digests': 'md5 sha1', 'digest:md5': '0' * 32, 'digest:sha1': '2c880cfec23cff7d8f80c2f12958d1563cbdaba6'}
414 409 > EOF
415 410 $ hg clone ssh://user@dummy/repo clone
416 411 requesting all changes
417 412 remote: remote-changegroup
418 413 adding changesets
419 414 adding manifests
420 415 adding file changes
421 added 8 changesets with 7 changes to 7 files (+2 heads)
422 416 transaction abort!
423 417 rollback completed
424 418 abort: bundle at http://localhost:$HGPORT/bundle6.hg is corrupted:
425 419 md5 mismatch: expected 00000000000000000000000000000000, got e22172c2907ef88794b7bea6642c2394
426 420 [255]
427 421
428 422 $ cat > repo/.hg/bundle2maker << EOF
429 423 > raw-remote-changegroup {'url': 'http://localhost:$HGPORT/bundle6.hg', 'size': 1663, 'digests': 'md5 sha1', 'digest:md5': 'e22172c2907ef88794b7bea6642c2394', 'digest:sha1': '0' * 40}
430 424 > EOF
431 425 $ hg clone ssh://user@dummy/repo clone
432 426 requesting all changes
433 427 remote: remote-changegroup
434 428 adding changesets
435 429 adding manifests
436 430 adding file changes
437 added 8 changesets with 7 changes to 7 files (+2 heads)
438 431 transaction abort!
439 432 rollback completed
440 433 abort: bundle at http://localhost:$HGPORT/bundle6.hg is corrupted:
441 434 sha1 mismatch: expected 0000000000000000000000000000000000000000, got 2c880cfec23cff7d8f80c2f12958d1563cbdaba6
442 435 [255]
443 436
444 437 Corruption tests
445 438
446 439 $ hg clone orig clone -r 2
447 440 adding changesets
448 441 adding manifests
449 442 adding file changes
450 443 added 3 changesets with 3 changes to 3 files
451 444 new changesets cd010b8cd998:5fddd98957c8
452 445 updating to branch default
453 446 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
454 447
455 448 $ cat > repo/.hg/bundle2maker << EOF
456 449 > remote-changegroup http://localhost:$HGPORT/bundle4.hg bundle4.hg
457 450 > raw-remote-changegroup {'url': 'http://localhost:$HGPORT/bundle5.hg', 'size': 578, 'digests': 'sha1', 'digest:sha1': '0' * 40}
458 451 > changegroup 0:6 7
459 452 > EOF
460 453 $ hg pull -R clone ssh://user@dummy/repo
461 454 pulling from ssh://user@dummy/repo
462 455 searching for changes
463 456 remote: remote-changegroup
464 457 adding changesets
465 458 adding manifests
466 459 adding file changes
467 added 2 changesets with 2 changes to 2 files (+1 heads)
468 460 remote: remote-changegroup
469 461 adding changesets
470 462 adding manifests
471 463 adding file changes
472 added 2 changesets with 1 changes to 1 files
473 464 transaction abort!
474 465 rollback completed
475 466 abort: bundle at http://localhost:$HGPORT/bundle5.hg is corrupted:
476 467 sha1 mismatch: expected 0000000000000000000000000000000000000000, got f29485d6bfd37db99983cfc95ecb52f8ca396106
477 468 [255]
478 469
479 470 The entire transaction has been rolled back in the pull above
480 471
481 472 $ hg -R clone log -G
482 473 @ 2:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> C
483 474 |
484 475 o 1:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> B
485 476 |
486 477 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
487 478
488 479
489 480 No params
490 481
491 482 $ cat > repo/.hg/bundle2maker << EOF
492 483 > raw-remote-changegroup {}
493 484 > EOF
494 485 $ hg pull -R clone ssh://user@dummy/repo
495 486 pulling from ssh://user@dummy/repo
496 487 searching for changes
497 488 remote: remote-changegroup
498 489 abort: remote-changegroup: missing "url" param
499 490 [255]
500 491
501 492 Missing size
502 493
503 494 $ cat > repo/.hg/bundle2maker << EOF
504 495 > raw-remote-changegroup {'url': 'http://localhost:$HGPORT/bundle4.hg'}
505 496 > EOF
506 497 $ hg pull -R clone ssh://user@dummy/repo
507 498 pulling from ssh://user@dummy/repo
508 499 searching for changes
509 500 remote: remote-changegroup
510 501 abort: remote-changegroup: missing "size" param
511 502 [255]
512 503
513 504 Invalid size
514 505
515 506 $ cat > repo/.hg/bundle2maker << EOF
516 507 > raw-remote-changegroup {'url': 'http://localhost:$HGPORT/bundle4.hg', 'size': 'foo'}
517 508 > EOF
518 509 $ hg pull -R clone ssh://user@dummy/repo
519 510 pulling from ssh://user@dummy/repo
520 511 searching for changes
521 512 remote: remote-changegroup
522 513 abort: remote-changegroup: invalid value for param "size"
523 514 [255]
524 515
525 516 Size mismatch
526 517
527 518 $ cat > repo/.hg/bundle2maker << EOF
528 519 > raw-remote-changegroup {'url': 'http://localhost:$HGPORT/bundle4.hg', 'size': 42}
529 520 > EOF
530 521 $ hg pull -R clone ssh://user@dummy/repo
531 522 pulling from ssh://user@dummy/repo
532 523 searching for changes
533 524 remote: remote-changegroup
534 525 adding changesets
535 526 adding manifests
536 527 adding file changes
537 added 2 changesets with 2 changes to 2 files (+1 heads)
538 528 transaction abort!
539 529 rollback completed
540 530 abort: bundle at http://localhost:$HGPORT/bundle4.hg is corrupted:
541 531 size mismatch: expected 42, got 581
542 532 [255]
543 533
544 534 Unknown digest
545 535
546 536 $ cat > repo/.hg/bundle2maker << EOF
547 537 > raw-remote-changegroup {'url': 'http://localhost:$HGPORT/bundle4.hg', 'size': 581, 'digests': 'foo', 'digest:foo': 'bar'}
548 538 > EOF
549 539 $ hg pull -R clone ssh://user@dummy/repo
550 540 pulling from ssh://user@dummy/repo
551 541 searching for changes
552 542 remote: remote-changegroup
553 543 abort: missing support for remote-changegroup - digest:foo
554 544 [255]
555 545
556 546 Missing digest
557 547
558 548 $ cat > repo/.hg/bundle2maker << EOF
559 549 > raw-remote-changegroup {'url': 'http://localhost:$HGPORT/bundle4.hg', 'size': 581, 'digests': 'sha1'}
560 550 > EOF
561 551 $ hg pull -R clone ssh://user@dummy/repo
562 552 pulling from ssh://user@dummy/repo
563 553 searching for changes
564 554 remote: remote-changegroup
565 555 abort: remote-changegroup: missing "digest:sha1" param
566 556 [255]
567 557
568 558 Not an HTTP url
569 559
570 560 $ cat > repo/.hg/bundle2maker << EOF
571 561 > raw-remote-changegroup {'url': 'ssh://localhost:$HGPORT/bundle4.hg', 'size': 581}
572 562 > EOF
573 563 $ hg pull -R clone ssh://user@dummy/repo
574 564 pulling from ssh://user@dummy/repo
575 565 searching for changes
576 566 remote: remote-changegroup
577 567 abort: remote-changegroup does not support ssh urls
578 568 [255]
579 569
580 570 Not a bundle
581 571
582 572 $ cat > notbundle.hg << EOF
583 573 > foo
584 574 > EOF
585 575 $ cat > repo/.hg/bundle2maker << EOF
586 576 > remote-changegroup http://localhost:$HGPORT/notbundle.hg notbundle.hg
587 577 > EOF
588 578 $ hg pull -R clone ssh://user@dummy/repo
589 579 pulling from ssh://user@dummy/repo
590 580 searching for changes
591 581 remote: remote-changegroup
592 582 abort: http://localhost:$HGPORT/notbundle.hg: not a Mercurial bundle
593 583 [255]
594 584
595 585 Not a bundle 1.0
596 586
597 587 $ cat > notbundle10.hg << EOF
598 588 > HG20
599 589 > EOF
600 590 $ cat > repo/.hg/bundle2maker << EOF
601 591 > remote-changegroup http://localhost:$HGPORT/notbundle10.hg notbundle10.hg
602 592 > EOF
603 593 $ hg pull -R clone ssh://user@dummy/repo
604 594 pulling from ssh://user@dummy/repo
605 595 searching for changes
606 596 remote: remote-changegroup
607 597 abort: http://localhost:$HGPORT/notbundle10.hg: not a bundle version 1.0
608 598 [255]
609 599
610 600 $ hg -R clone log -G
611 601 @ 2:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> C
612 602 |
613 603 o 1:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> B
614 604 |
615 605 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
616 606
617 607 $ rm -rf clone
618 608
619 609 $ killdaemons.py
@@ -1,1301 +1,1301 b''
1 1 #testcases sshv1 sshv2
2 2
3 3 #if sshv2
4 4 $ cat >> $HGRCPATH << EOF
5 5 > [experimental]
6 6 > sshpeer.advertise-v2 = true
7 7 > sshserver.support-v2 = true
8 8 > EOF
9 9 #endif
10 10
11 11 Prepare repo a:
12 12
13 13 $ hg init a
14 14 $ cd a
15 15 $ echo a > a
16 16 $ hg add a
17 17 $ hg commit -m test
18 18 $ echo first line > b
19 19 $ hg add b
20 20
21 21 Create a non-inlined filelog:
22 22
23 23 $ "$PYTHON" -c 'open("data1", "wb").write(b"".join(b"%d\n" % x for x in range(10000)))'
24 24 $ for j in 0 1 2 3 4 5 6 7 8 9; do
25 25 > cat data1 >> b
26 26 > hg commit -m test
27 27 > done
28 28
29 29 List files in store/data (should show a 'b.d'):
30 30
31 31 #if reporevlogstore
32 32 $ for i in .hg/store/data/*; do
33 33 > echo $i
34 34 > done
35 35 .hg/store/data/a.i
36 36 .hg/store/data/b.d
37 37 .hg/store/data/b.i
38 38 #endif
39 39
40 40 Trigger branchcache creation:
41 41
42 42 $ hg branches
43 43 default 10:a7949464abda
44 44 $ ls .hg/cache
45 45 branch2-served
46 46 rbc-names-v1
47 47 rbc-revs-v1
48 48
49 49 Default operation:
50 50
51 51 $ hg clone . ../b
52 52 updating to branch default
53 53 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
54 54 $ cd ../b
55 55
56 56 Ensure branchcache got copied over:
57 57
58 58 $ ls .hg/cache
59 59 branch2-served
60 60 rbc-names-v1
61 61 rbc-revs-v1
62 62
63 63 $ cat a
64 64 a
65 65 $ hg verify
66 66 checking changesets
67 67 checking manifests
68 68 crosschecking files in changesets and manifests
69 69 checking files
70 70 checked 11 changesets with 11 changes to 2 files
71 71
72 72 Invalid dest '' must abort:
73 73
74 74 $ hg clone . ''
75 75 abort: empty destination path is not valid
76 76 [255]
77 77
78 78 No update, with debug option:
79 79
80 80 #if hardlink
81 81 $ hg --debug clone -U . ../c --config progress.debug=true
82 82 linking: 1 files
83 83 linking: 2 files
84 84 linking: 3 files
85 85 linking: 4 files
86 86 linking: 5 files
87 87 linking: 6 files
88 88 linking: 7 files
89 89 linking: 8 files
90 90 linked 8 files (reporevlogstore !)
91 91 linking: 9 files (reposimplestore !)
92 92 linking: 10 files (reposimplestore !)
93 93 linking: 11 files (reposimplestore !)
94 94 linking: 12 files (reposimplestore !)
95 95 linking: 13 files (reposimplestore !)
96 96 linking: 14 files (reposimplestore !)
97 97 linking: 15 files (reposimplestore !)
98 98 linking: 16 files (reposimplestore !)
99 99 linking: 17 files (reposimplestore !)
100 100 linking: 18 files (reposimplestore !)
101 101 linked 18 files (reposimplestore !)
102 102 #else
103 103 $ hg --debug clone -U . ../c --config progress.debug=true
104 104 linking: 1 files
105 105 copying: 2 files
106 106 copying: 3 files
107 107 copying: 4 files
108 108 copying: 5 files
109 109 copying: 6 files
110 110 copying: 7 files
111 111 copying: 8 files
112 112 copied 8 files (reporevlogstore !)
113 113 copying: 9 files (reposimplestore !)
114 114 copying: 10 files (reposimplestore !)
115 115 copying: 11 files (reposimplestore !)
116 116 copying: 12 files (reposimplestore !)
117 117 copying: 13 files (reposimplestore !)
118 118 copying: 14 files (reposimplestore !)
119 119 copying: 15 files (reposimplestore !)
120 120 copying: 16 files (reposimplestore !)
121 121 copying: 17 files (reposimplestore !)
122 122 copying: 18 files (reposimplestore !)
123 123 copied 18 files (reposimplestore !)
124 124 #endif
125 125 $ cd ../c
126 126
127 127 Ensure branchcache got copied over:
128 128
129 129 $ ls .hg/cache
130 130 branch2-served
131 131 rbc-names-v1
132 132 rbc-revs-v1
133 133
134 134 $ cat a 2>/dev/null || echo "a not present"
135 135 a not present
136 136 $ hg verify
137 137 checking changesets
138 138 checking manifests
139 139 crosschecking files in changesets and manifests
140 140 checking files
141 141 checked 11 changesets with 11 changes to 2 files
142 142
143 143 Default destination:
144 144
145 145 $ mkdir ../d
146 146 $ cd ../d
147 147 $ hg clone ../a
148 148 destination directory: a
149 149 updating to branch default
150 150 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
151 151 $ cd a
152 152 $ hg cat a
153 153 a
154 154 $ cd ../..
155 155
156 156 Check that we drop the 'file:' from the path before writing the .hgrc:
157 157
158 158 $ hg clone file:a e
159 159 updating to branch default
160 160 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
161 161 $ grep 'file:' e/.hg/hgrc
162 162 [1]
163 163
164 164 Check that path aliases are expanded:
165 165
166 166 $ hg clone -q -U --config 'paths.foobar=a#0' foobar f
167 167 $ hg -R f showconfig paths.default
168 168 $TESTTMP/a#0
169 169
170 170 Use --pull:
171 171
172 172 $ hg clone --pull a g
173 173 requesting all changes
174 174 adding changesets
175 175 adding manifests
176 176 adding file changes
177 177 added 11 changesets with 11 changes to 2 files
178 178 new changesets acb14030fe0a:a7949464abda
179 179 updating to branch default
180 180 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
181 181 $ hg -R g verify
182 182 checking changesets
183 183 checking manifests
184 184 crosschecking files in changesets and manifests
185 185 checking files
186 186 checked 11 changesets with 11 changes to 2 files
187 187
188 188 Invalid dest '' with --pull must abort (issue2528):
189 189
190 190 $ hg clone --pull a ''
191 191 abort: empty destination path is not valid
192 192 [255]
193 193
194 194 Clone to '.':
195 195
196 196 $ mkdir h
197 197 $ cd h
198 198 $ hg clone ../a .
199 199 updating to branch default
200 200 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 201 $ cd ..
202 202
203 203
204 204 *** Tests for option -u ***
205 205
206 206 Adding some more history to repo a:
207 207
208 208 $ cd a
209 209 $ hg tag ref1
210 210 $ echo the quick brown fox >a
211 211 $ hg ci -m "hacked default"
212 212 $ hg up ref1
213 213 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
214 214 $ hg branch stable
215 215 marked working directory as branch stable
216 216 (branches are permanent and global, did you want a bookmark?)
217 217 $ echo some text >a
218 218 $ hg ci -m "starting branch stable"
219 219 $ hg tag ref2
220 220 $ echo some more text >a
221 221 $ hg ci -m "another change for branch stable"
222 222 $ hg up ref2
223 223 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
224 224 $ hg parents
225 225 changeset: 13:e8ece76546a6
226 226 branch: stable
227 227 tag: ref2
228 228 parent: 10:a7949464abda
229 229 user: test
230 230 date: Thu Jan 01 00:00:00 1970 +0000
231 231 summary: starting branch stable
232 232
233 233
234 234 Repo a has two heads:
235 235
236 236 $ hg heads
237 237 changeset: 15:0aae7cf88f0d
238 238 branch: stable
239 239 tag: tip
240 240 user: test
241 241 date: Thu Jan 01 00:00:00 1970 +0000
242 242 summary: another change for branch stable
243 243
244 244 changeset: 12:f21241060d6a
245 245 user: test
246 246 date: Thu Jan 01 00:00:00 1970 +0000
247 247 summary: hacked default
248 248
249 249
250 250 $ cd ..
251 251
252 252
253 253 Testing --noupdate with --updaterev (must abort):
254 254
255 255 $ hg clone --noupdate --updaterev 1 a ua
256 256 abort: cannot specify both --noupdate and --updaterev
257 257 [255]
258 258
259 259
260 260 Testing clone -u:
261 261
262 262 $ hg clone -u . a ua
263 263 updating to branch stable
264 264 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
265 265
266 266 Repo ua has both heads:
267 267
268 268 $ hg -R ua heads
269 269 changeset: 15:0aae7cf88f0d
270 270 branch: stable
271 271 tag: tip
272 272 user: test
273 273 date: Thu Jan 01 00:00:00 1970 +0000
274 274 summary: another change for branch stable
275 275
276 276 changeset: 12:f21241060d6a
277 277 user: test
278 278 date: Thu Jan 01 00:00:00 1970 +0000
279 279 summary: hacked default
280 280
281 281
282 282 Same revision checked out in repo a and ua:
283 283
284 284 $ hg -R a parents --template "{node|short}\n"
285 285 e8ece76546a6
286 286 $ hg -R ua parents --template "{node|short}\n"
287 287 e8ece76546a6
288 288
289 289 $ rm -r ua
290 290
291 291
292 292 Testing clone --pull -u:
293 293
294 294 $ hg clone --pull -u . a ua
295 295 requesting all changes
296 296 adding changesets
297 297 adding manifests
298 298 adding file changes
299 299 added 16 changesets with 16 changes to 3 files (+1 heads)
300 300 new changesets acb14030fe0a:0aae7cf88f0d
301 301 updating to branch stable
302 302 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
303 303
304 304 Repo ua has both heads:
305 305
306 306 $ hg -R ua heads
307 307 changeset: 15:0aae7cf88f0d
308 308 branch: stable
309 309 tag: tip
310 310 user: test
311 311 date: Thu Jan 01 00:00:00 1970 +0000
312 312 summary: another change for branch stable
313 313
314 314 changeset: 12:f21241060d6a
315 315 user: test
316 316 date: Thu Jan 01 00:00:00 1970 +0000
317 317 summary: hacked default
318 318
319 319
320 320 Same revision checked out in repo a and ua:
321 321
322 322 $ hg -R a parents --template "{node|short}\n"
323 323 e8ece76546a6
324 324 $ hg -R ua parents --template "{node|short}\n"
325 325 e8ece76546a6
326 326
327 327 $ rm -r ua
328 328
329 329
330 330 Testing clone -u <branch>:
331 331
332 332 $ hg clone -u stable a ua
333 333 updating to branch stable
334 334 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
335 335
336 336 Repo ua has both heads:
337 337
338 338 $ hg -R ua heads
339 339 changeset: 15:0aae7cf88f0d
340 340 branch: stable
341 341 tag: tip
342 342 user: test
343 343 date: Thu Jan 01 00:00:00 1970 +0000
344 344 summary: another change for branch stable
345 345
346 346 changeset: 12:f21241060d6a
347 347 user: test
348 348 date: Thu Jan 01 00:00:00 1970 +0000
349 349 summary: hacked default
350 350
351 351
352 352 Branch 'stable' is checked out:
353 353
354 354 $ hg -R ua parents
355 355 changeset: 15:0aae7cf88f0d
356 356 branch: stable
357 357 tag: tip
358 358 user: test
359 359 date: Thu Jan 01 00:00:00 1970 +0000
360 360 summary: another change for branch stable
361 361
362 362
363 363 $ rm -r ua
364 364
365 365
366 366 Testing default checkout:
367 367
368 368 $ hg clone a ua
369 369 updating to branch default
370 370 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
371 371
372 372 Repo ua has both heads:
373 373
374 374 $ hg -R ua heads
375 375 changeset: 15:0aae7cf88f0d
376 376 branch: stable
377 377 tag: tip
378 378 user: test
379 379 date: Thu Jan 01 00:00:00 1970 +0000
380 380 summary: another change for branch stable
381 381
382 382 changeset: 12:f21241060d6a
383 383 user: test
384 384 date: Thu Jan 01 00:00:00 1970 +0000
385 385 summary: hacked default
386 386
387 387
388 388 Branch 'default' is checked out:
389 389
390 390 $ hg -R ua parents
391 391 changeset: 12:f21241060d6a
392 392 user: test
393 393 date: Thu Jan 01 00:00:00 1970 +0000
394 394 summary: hacked default
395 395
396 396 Test clone with a branch named "@" (issue3677)
397 397
398 398 $ hg -R ua branch @
399 399 marked working directory as branch @
400 400 $ hg -R ua commit -m 'created branch @'
401 401 $ hg clone ua atbranch
402 402 updating to branch default
403 403 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
404 404 $ hg -R atbranch heads
405 405 changeset: 16:798b6d97153e
406 406 branch: @
407 407 tag: tip
408 408 parent: 12:f21241060d6a
409 409 user: test
410 410 date: Thu Jan 01 00:00:00 1970 +0000
411 411 summary: created branch @
412 412
413 413 changeset: 15:0aae7cf88f0d
414 414 branch: stable
415 415 user: test
416 416 date: Thu Jan 01 00:00:00 1970 +0000
417 417 summary: another change for branch stable
418 418
419 419 changeset: 12:f21241060d6a
420 420 user: test
421 421 date: Thu Jan 01 00:00:00 1970 +0000
422 422 summary: hacked default
423 423
424 424 $ hg -R atbranch parents
425 425 changeset: 12:f21241060d6a
426 426 user: test
427 427 date: Thu Jan 01 00:00:00 1970 +0000
428 428 summary: hacked default
429 429
430 430
431 431 $ rm -r ua atbranch
432 432
433 433
434 434 Testing #<branch>:
435 435
436 436 $ hg clone -u . a#stable ua
437 437 adding changesets
438 438 adding manifests
439 439 adding file changes
440 440 added 14 changesets with 14 changes to 3 files
441 441 new changesets acb14030fe0a:0aae7cf88f0d
442 442 updating to branch stable
443 443 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
444 444
445 445 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
446 446
447 447 $ hg -R ua heads
448 448 changeset: 13:0aae7cf88f0d
449 449 branch: stable
450 450 tag: tip
451 451 user: test
452 452 date: Thu Jan 01 00:00:00 1970 +0000
453 453 summary: another change for branch stable
454 454
455 455 changeset: 10:a7949464abda
456 456 user: test
457 457 date: Thu Jan 01 00:00:00 1970 +0000
458 458 summary: test
459 459
460 460
461 461 Same revision checked out in repo a and ua:
462 462
463 463 $ hg -R a parents --template "{node|short}\n"
464 464 e8ece76546a6
465 465 $ hg -R ua parents --template "{node|short}\n"
466 466 e8ece76546a6
467 467
468 468 $ rm -r ua
469 469
470 470
471 471 Testing -u -r <branch>:
472 472
473 473 $ hg clone -u . -r stable a ua
474 474 adding changesets
475 475 adding manifests
476 476 adding file changes
477 477 added 14 changesets with 14 changes to 3 files
478 478 new changesets acb14030fe0a:0aae7cf88f0d
479 479 updating to branch stable
480 480 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
481 481
482 482 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
483 483
484 484 $ hg -R ua heads
485 485 changeset: 13:0aae7cf88f0d
486 486 branch: stable
487 487 tag: tip
488 488 user: test
489 489 date: Thu Jan 01 00:00:00 1970 +0000
490 490 summary: another change for branch stable
491 491
492 492 changeset: 10:a7949464abda
493 493 user: test
494 494 date: Thu Jan 01 00:00:00 1970 +0000
495 495 summary: test
496 496
497 497
498 498 Same revision checked out in repo a and ua:
499 499
500 500 $ hg -R a parents --template "{node|short}\n"
501 501 e8ece76546a6
502 502 $ hg -R ua parents --template "{node|short}\n"
503 503 e8ece76546a6
504 504
505 505 $ rm -r ua
506 506
507 507
508 508 Testing -r <branch>:
509 509
510 510 $ hg clone -r stable a ua
511 511 adding changesets
512 512 adding manifests
513 513 adding file changes
514 514 added 14 changesets with 14 changes to 3 files
515 515 new changesets acb14030fe0a:0aae7cf88f0d
516 516 updating to branch stable
517 517 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
518 518
519 519 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
520 520
521 521 $ hg -R ua heads
522 522 changeset: 13:0aae7cf88f0d
523 523 branch: stable
524 524 tag: tip
525 525 user: test
526 526 date: Thu Jan 01 00:00:00 1970 +0000
527 527 summary: another change for branch stable
528 528
529 529 changeset: 10:a7949464abda
530 530 user: test
531 531 date: Thu Jan 01 00:00:00 1970 +0000
532 532 summary: test
533 533
534 534
535 535 Branch 'stable' is checked out:
536 536
537 537 $ hg -R ua parents
538 538 changeset: 13:0aae7cf88f0d
539 539 branch: stable
540 540 tag: tip
541 541 user: test
542 542 date: Thu Jan 01 00:00:00 1970 +0000
543 543 summary: another change for branch stable
544 544
545 545
546 546 $ rm -r ua
547 547
548 548
549 549 Issue2267: Error in 1.6 hg.py: TypeError: 'NoneType' object is not
550 550 iterable in addbranchrevs()
551 551
552 552 $ cat <<EOF > simpleclone.py
553 553 > from mercurial import hg, ui as uimod
554 554 > myui = uimod.ui.load()
555 555 > repo = hg.repository(myui, b'a')
556 556 > hg.clone(myui, {}, repo, dest=b"ua")
557 557 > EOF
558 558
559 559 $ "$PYTHON" simpleclone.py
560 560 updating to branch default
561 561 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
562 562
563 563 $ rm -r ua
564 564
565 565 $ cat <<EOF > branchclone.py
566 566 > from mercurial import extensions, hg, ui as uimod
567 567 > myui = uimod.ui.load()
568 568 > extensions.loadall(myui)
569 569 > extensions.populateui(myui)
570 570 > repo = hg.repository(myui, b'a')
571 571 > hg.clone(myui, {}, repo, dest=b"ua", branch=[b"stable"])
572 572 > EOF
573 573
574 574 $ "$PYTHON" branchclone.py
575 575 adding changesets
576 576 adding manifests
577 577 adding file changes
578 578 added 14 changesets with 14 changes to 3 files
579 579 new changesets acb14030fe0a:0aae7cf88f0d
580 580 updating to branch stable
581 581 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
582 582 $ rm -r ua
583 583
584 584
585 585 Test clone with special '@' bookmark:
586 586 $ cd a
587 587 $ hg bookmark -r a7949464abda @ # branch point of stable from default
588 588 $ hg clone . ../i
589 589 updating to bookmark @
590 590 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
591 591 $ hg id -i ../i
592 592 a7949464abda
593 593 $ rm -r ../i
594 594
595 595 $ hg bookmark -f -r stable @
596 596 $ hg bookmarks
597 597 @ 15:0aae7cf88f0d
598 598 $ hg clone . ../i
599 599 updating to bookmark @ on branch stable
600 600 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
601 601 $ hg id -i ../i
602 602 0aae7cf88f0d
603 603 $ cd "$TESTTMP"
604 604
605 605
606 606 Testing failures:
607 607
608 608 $ mkdir fail
609 609 $ cd fail
610 610
611 611 No local source
612 612
613 613 $ hg clone a b
614 614 abort: repository a not found!
615 615 [255]
616 616
617 617 No remote source
618 618
619 619 #if windows
620 620 $ hg clone http://$LOCALIP:3121/a b
621 621 abort: error: * (glob)
622 622 [255]
623 623 #else
624 624 $ hg clone http://$LOCALIP:3121/a b
625 625 abort: error: *refused* (glob)
626 626 [255]
627 627 #endif
628 628 $ rm -rf b # work around bug with http clone
629 629
630 630
631 631 #if unix-permissions no-root
632 632
633 633 Inaccessible source
634 634
635 635 $ mkdir a
636 636 $ chmod 000 a
637 637 $ hg clone a b
638 638 abort: Permission denied: *$TESTTMP/fail/a/.hg* (glob)
639 639 [255]
640 640
641 641 Inaccessible destination
642 642
643 643 $ hg init b
644 644 $ cd b
645 645 $ hg clone . ../a
646 646 abort: Permission denied: *../a* (glob)
647 647 [255]
648 648 $ cd ..
649 649 $ chmod 700 a
650 650 $ rm -r a b
651 651
652 652 #endif
653 653
654 654
655 655 #if fifo
656 656
657 657 Source of wrong type
658 658
659 659 $ mkfifo a
660 660 $ hg clone a b
661 661 abort: $ENOTDIR$: *$TESTTMP/fail/a/.hg* (glob)
662 662 [255]
663 663 $ rm a
664 664
665 665 #endif
666 666
667 667 Default destination, same directory
668 668
669 669 $ hg init q
670 670 $ hg clone q
671 671 destination directory: q
672 672 abort: destination 'q' is not empty
673 673 [255]
674 674
675 675 destination directory not empty
676 676
677 677 $ mkdir a
678 678 $ echo stuff > a/a
679 679 $ hg clone q a
680 680 abort: destination 'a' is not empty
681 681 [255]
682 682
683 683
684 684 #if unix-permissions no-root
685 685
686 686 leave existing directory in place after clone failure
687 687
688 688 $ hg init c
689 689 $ cd c
690 690 $ echo c > c
691 691 $ hg commit -A -m test
692 692 adding c
693 693 $ chmod -rx .hg/store/data
694 694 $ cd ..
695 695 $ mkdir d
696 696 $ hg clone c d 2> err
697 697 [255]
698 698 $ test -d d
699 699 $ test -d d/.hg
700 700 [1]
701 701
702 702 re-enable perm to allow deletion
703 703
704 704 $ chmod +rx c/.hg/store/data
705 705
706 706 #endif
707 707
708 708 $ cd ..
709 709
710 710 Test clone from the repository in (emulated) revlog format 0 (issue4203):
711 711
712 712 $ mkdir issue4203
713 713 $ mkdir -p src/.hg
714 714 $ echo foo > src/foo
715 715 $ hg -R src add src/foo
716 716 $ hg -R src commit -m '#0'
717 717 $ hg -R src log -q
718 718 0:e1bab28bca43
719 719 $ hg -R src debugrevlog -c | egrep 'format|flags'
720 720 format : 0
721 721 flags : (none)
722 722 $ hg root -R src -T json | sed 's|\\\\|\\|g'
723 723 [
724 724 {
725 725 "hgpath": "$TESTTMP/src/.hg",
726 726 "reporoot": "$TESTTMP/src",
727 727 "storepath": "$TESTTMP/src/.hg"
728 728 }
729 729 ]
730 730 $ hg clone -U -q src dst
731 731 $ hg -R dst log -q
732 732 0:e1bab28bca43
733 733
734 734 Create repositories to test auto sharing functionality
735 735
736 736 $ cat >> $HGRCPATH << EOF
737 737 > [extensions]
738 738 > share=
739 739 > EOF
740 740
741 741 $ hg init empty
742 742 $ hg init source1a
743 743 $ cd source1a
744 744 $ echo initial1 > foo
745 745 $ hg -q commit -A -m initial
746 746 $ echo second > foo
747 747 $ hg commit -m second
748 748 $ cd ..
749 749
750 750 $ hg init filteredrev0
751 751 $ cd filteredrev0
752 752 $ cat >> .hg/hgrc << EOF
753 753 > [experimental]
754 754 > evolution.createmarkers=True
755 755 > EOF
756 756 $ echo initial1 > foo
757 757 $ hg -q commit -A -m initial0
758 758 $ hg -q up -r null
759 759 $ echo initial2 > foo
760 760 $ hg -q commit -A -m initial1
761 761 $ hg debugobsolete c05d5c47a5cf81401869999f3d05f7d699d2b29a e082c1832e09a7d1e78b7fd49a592d372de854c8
762 762 1 new obsolescence markers
763 763 obsoleted 1 changesets
764 764 $ cd ..
765 765
766 766 $ hg -q clone --pull source1a source1b
767 767 $ cd source1a
768 768 $ hg bookmark bookA
769 769 $ echo 1a > foo
770 770 $ hg commit -m 1a
771 771 $ cd ../source1b
772 772 $ hg -q up -r 0
773 773 $ echo head1 > foo
774 774 $ hg commit -m head1
775 775 created new head
776 776 $ hg bookmark head1
777 777 $ hg -q up -r 0
778 778 $ echo head2 > foo
779 779 $ hg commit -m head2
780 780 created new head
781 781 $ hg bookmark head2
782 782 $ hg -q up -r 0
783 783 $ hg branch branch1
784 784 marked working directory as branch branch1
785 785 (branches are permanent and global, did you want a bookmark?)
786 786 $ echo branch1 > foo
787 787 $ hg commit -m branch1
788 788 $ hg -q up -r 0
789 789 $ hg branch branch2
790 790 marked working directory as branch branch2
791 791 $ echo branch2 > foo
792 792 $ hg commit -m branch2
793 793 $ cd ..
794 794 $ hg init source2
795 795 $ cd source2
796 796 $ echo initial2 > foo
797 797 $ hg -q commit -A -m initial2
798 798 $ echo second > foo
799 799 $ hg commit -m second
800 800 $ cd ..
801 801
802 802 Clone with auto share from an empty repo should not result in share
803 803
804 804 $ mkdir share
805 805 $ hg --config share.pool=share clone empty share-empty
806 806 (not using pooled storage: remote appears to be empty)
807 807 updating to branch default
808 808 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
809 809 $ ls share
810 810 $ test -d share-empty/.hg/store
811 811 $ test -f share-empty/.hg/sharedpath
812 812 [1]
813 813
814 814 Clone with auto share from a repo with filtered revision 0 should not result in share
815 815
816 816 $ hg --config share.pool=share clone filteredrev0 share-filtered
817 817 (not using pooled storage: unable to resolve identity of remote)
818 818 requesting all changes
819 819 adding changesets
820 820 adding manifests
821 821 adding file changes
822 822 added 1 changesets with 1 changes to 1 files
823 823 new changesets e082c1832e09
824 824 updating to branch default
825 825 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
826 826
827 827 Clone from repo with content should result in shared store being created
828 828
829 829 $ hg --config share.pool=share clone source1a share-dest1a
830 830 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
831 831 requesting all changes
832 832 adding changesets
833 833 adding manifests
834 834 adding file changes
835 835 added 3 changesets with 3 changes to 1 files
836 836 new changesets b5f04eac9d8f:e5bfe23c0b47
837 837 searching for changes
838 838 no changes found
839 839 adding remote bookmark bookA
840 840 updating working directory
841 841 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
842 842
843 843 The shared repo should have been created
844 844
845 845 $ ls share
846 846 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
847 847
848 848 The destination should point to it
849 849
850 850 $ cat share-dest1a/.hg/sharedpath; echo
851 851 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg
852 852
853 853 The destination should have bookmarks
854 854
855 855 $ hg -R share-dest1a bookmarks
856 856 bookA 2:e5bfe23c0b47
857 857
858 858 The default path should be the remote, not the share
859 859
860 860 $ hg -R share-dest1a config paths.default
861 861 $TESTTMP/source1a
862 862
863 863 Clone with existing share dir should result in pull + share
864 864
865 865 $ hg --config share.pool=share clone source1b share-dest1b
866 866 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
867 867 searching for changes
868 868 adding changesets
869 869 adding manifests
870 870 adding file changes
871 added 4 changesets with 4 changes to 1 files (+4 heads)
872 871 adding remote bookmark head1
873 872 adding remote bookmark head2
873 added 4 changesets with 4 changes to 1 files (+4 heads)
874 874 new changesets 4a8dc1ab4c13:6bacf4683960
875 875 updating working directory
876 876 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
877 877
878 878 $ ls share
879 879 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
880 880
881 881 $ cat share-dest1b/.hg/sharedpath; echo
882 882 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg
883 883
884 884 We only get bookmarks from the remote, not everything in the share
885 885
886 886 $ hg -R share-dest1b bookmarks
887 887 head1 3:4a8dc1ab4c13
888 888 head2 4:99f71071f117
889 889
890 890 Default path should be source, not share.
891 891
892 892 $ hg -R share-dest1b config paths.default
893 893 $TESTTMP/source1b
894 894
895 895 Checked out revision should be head of default branch
896 896
897 897 $ hg -R share-dest1b log -r .
898 898 changeset: 4:99f71071f117
899 899 bookmark: head2
900 900 parent: 0:b5f04eac9d8f
901 901 user: test
902 902 date: Thu Jan 01 00:00:00 1970 +0000
903 903 summary: head2
904 904
905 905
906 906 Clone from unrelated repo should result in new share
907 907
908 908 $ hg --config share.pool=share clone source2 share-dest2
909 909 (sharing from new pooled repository 22aeff664783fd44c6d9b435618173c118c3448e)
910 910 requesting all changes
911 911 adding changesets
912 912 adding manifests
913 913 adding file changes
914 914 added 2 changesets with 2 changes to 1 files
915 915 new changesets 22aeff664783:63cf6c3dba4a
916 916 searching for changes
917 917 no changes found
918 918 updating working directory
919 919 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
920 920
921 921 $ ls share
922 922 22aeff664783fd44c6d9b435618173c118c3448e
923 923 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
924 924
925 925 remote naming mode works as advertised
926 926
927 927 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1a share-remote1a
928 928 (sharing from new pooled repository 195bb1fcdb595c14a6c13e0269129ed78f6debde)
929 929 requesting all changes
930 930 adding changesets
931 931 adding manifests
932 932 adding file changes
933 933 added 3 changesets with 3 changes to 1 files
934 934 new changesets b5f04eac9d8f:e5bfe23c0b47
935 935 searching for changes
936 936 no changes found
937 937 adding remote bookmark bookA
938 938 updating working directory
939 939 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
940 940
941 941 $ ls shareremote
942 942 195bb1fcdb595c14a6c13e0269129ed78f6debde
943 943
944 944 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1b share-remote1b
945 945 (sharing from new pooled repository c0d4f83847ca2a873741feb7048a45085fd47c46)
946 946 requesting all changes
947 947 adding changesets
948 948 adding manifests
949 949 adding file changes
950 950 added 6 changesets with 6 changes to 1 files (+4 heads)
951 951 new changesets b5f04eac9d8f:6bacf4683960
952 952 searching for changes
953 953 no changes found
954 954 adding remote bookmark head1
955 955 adding remote bookmark head2
956 956 updating working directory
957 957 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
958 958
959 959 $ ls shareremote
960 960 195bb1fcdb595c14a6c13e0269129ed78f6debde
961 961 c0d4f83847ca2a873741feb7048a45085fd47c46
962 962
963 963 request to clone a single revision is respected in sharing mode
964 964
965 965 $ hg --config share.pool=sharerevs clone -r 4a8dc1ab4c13 source1b share-1arev
966 966 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
967 967 adding changesets
968 968 adding manifests
969 969 adding file changes
970 970 added 2 changesets with 2 changes to 1 files
971 971 new changesets b5f04eac9d8f:4a8dc1ab4c13
972 972 no changes found
973 973 adding remote bookmark head1
974 974 updating working directory
975 975 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
976 976
977 977 $ hg -R share-1arev log -G
978 978 @ changeset: 1:4a8dc1ab4c13
979 979 | bookmark: head1
980 980 | tag: tip
981 981 | user: test
982 982 | date: Thu Jan 01 00:00:00 1970 +0000
983 983 | summary: head1
984 984 |
985 985 o changeset: 0:b5f04eac9d8f
986 986 user: test
987 987 date: Thu Jan 01 00:00:00 1970 +0000
988 988 summary: initial
989 989
990 990
991 991 making another clone should only pull down requested rev
992 992
993 993 $ hg --config share.pool=sharerevs clone -r 99f71071f117 source1b share-1brev
994 994 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
995 995 searching for changes
996 996 adding changesets
997 997 adding manifests
998 998 adding file changes
999 added 1 changesets with 1 changes to 1 files (+1 heads)
1000 999 adding remote bookmark head1
1001 1000 adding remote bookmark head2
1001 added 1 changesets with 1 changes to 1 files (+1 heads)
1002 1002 new changesets 99f71071f117
1003 1003 updating working directory
1004 1004 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1005 1005
1006 1006 $ hg -R share-1brev log -G
1007 1007 @ changeset: 2:99f71071f117
1008 1008 | bookmark: head2
1009 1009 | tag: tip
1010 1010 | parent: 0:b5f04eac9d8f
1011 1011 | user: test
1012 1012 | date: Thu Jan 01 00:00:00 1970 +0000
1013 1013 | summary: head2
1014 1014 |
1015 1015 | o changeset: 1:4a8dc1ab4c13
1016 1016 |/ bookmark: head1
1017 1017 | user: test
1018 1018 | date: Thu Jan 01 00:00:00 1970 +0000
1019 1019 | summary: head1
1020 1020 |
1021 1021 o changeset: 0:b5f04eac9d8f
1022 1022 user: test
1023 1023 date: Thu Jan 01 00:00:00 1970 +0000
1024 1024 summary: initial
1025 1025
1026 1026
1027 1027 Request to clone a single branch is respected in sharing mode
1028 1028
1029 1029 $ hg --config share.pool=sharebranch clone -b branch1 source1b share-1bbranch1
1030 1030 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1031 1031 adding changesets
1032 1032 adding manifests
1033 1033 adding file changes
1034 1034 added 2 changesets with 2 changes to 1 files
1035 1035 new changesets b5f04eac9d8f:5f92a6c1a1b1
1036 1036 no changes found
1037 1037 updating working directory
1038 1038 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1039 1039
1040 1040 $ hg -R share-1bbranch1 log -G
1041 1041 o changeset: 1:5f92a6c1a1b1
1042 1042 | branch: branch1
1043 1043 | tag: tip
1044 1044 | user: test
1045 1045 | date: Thu Jan 01 00:00:00 1970 +0000
1046 1046 | summary: branch1
1047 1047 |
1048 1048 @ changeset: 0:b5f04eac9d8f
1049 1049 user: test
1050 1050 date: Thu Jan 01 00:00:00 1970 +0000
1051 1051 summary: initial
1052 1052
1053 1053
1054 1054 $ hg --config share.pool=sharebranch clone -b branch2 source1b share-1bbranch2
1055 1055 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1056 1056 searching for changes
1057 1057 adding changesets
1058 1058 adding manifests
1059 1059 adding file changes
1060 1060 added 1 changesets with 1 changes to 1 files (+1 heads)
1061 1061 new changesets 6bacf4683960
1062 1062 updating working directory
1063 1063 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1064 1064
1065 1065 $ hg -R share-1bbranch2 log -G
1066 1066 o changeset: 2:6bacf4683960
1067 1067 | branch: branch2
1068 1068 | tag: tip
1069 1069 | parent: 0:b5f04eac9d8f
1070 1070 | user: test
1071 1071 | date: Thu Jan 01 00:00:00 1970 +0000
1072 1072 | summary: branch2
1073 1073 |
1074 1074 | o changeset: 1:5f92a6c1a1b1
1075 1075 |/ branch: branch1
1076 1076 | user: test
1077 1077 | date: Thu Jan 01 00:00:00 1970 +0000
1078 1078 | summary: branch1
1079 1079 |
1080 1080 @ changeset: 0:b5f04eac9d8f
1081 1081 user: test
1082 1082 date: Thu Jan 01 00:00:00 1970 +0000
1083 1083 summary: initial
1084 1084
1085 1085
1086 1086 -U is respected in share clone mode
1087 1087
1088 1088 $ hg --config share.pool=share clone -U source1a share-1anowc
1089 1089 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1090 1090 searching for changes
1091 1091 no changes found
1092 1092 adding remote bookmark bookA
1093 1093
1094 1094 $ ls share-1anowc
1095 1095
1096 1096 Test that auto sharing doesn't cause failure of "hg clone local remote"
1097 1097
1098 1098 $ cd $TESTTMP
1099 1099 $ hg -R a id -r 0
1100 1100 acb14030fe0a
1101 1101 $ hg id -R remote -r 0
1102 1102 abort: repository remote not found!
1103 1103 [255]
1104 1104 $ hg --config share.pool=share -q clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" a ssh://user@dummy/remote
1105 1105 $ hg -R remote id -r 0
1106 1106 acb14030fe0a
1107 1107
1108 1108 Cloning into pooled storage doesn't race (issue5104)
1109 1109
1110 1110 $ HGPOSTLOCKDELAY=2.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace1 > race1.log 2>&1 &
1111 1111 $ HGPRELOCKDELAY=1.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace2 > race2.log 2>&1
1112 1112 $ wait
1113 1113
1114 1114 $ hg -R share-destrace1 log -r tip
1115 1115 changeset: 2:e5bfe23c0b47
1116 1116 bookmark: bookA
1117 1117 tag: tip
1118 1118 user: test
1119 1119 date: Thu Jan 01 00:00:00 1970 +0000
1120 1120 summary: 1a
1121 1121
1122 1122
1123 1123 $ hg -R share-destrace2 log -r tip
1124 1124 changeset: 2:e5bfe23c0b47
1125 1125 bookmark: bookA
1126 1126 tag: tip
1127 1127 user: test
1128 1128 date: Thu Jan 01 00:00:00 1970 +0000
1129 1129 summary: 1a
1130 1130
1131 1131 One repo should be new, the other should be shared from the pool. We
1132 1132 don't care which is which, so we just make sure we always print the
1133 1133 one containing "new pooled" first, then one one containing "existing
1134 1134 pooled".
1135 1135
1136 1136 $ (grep 'new pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1137 1137 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1138 1138 requesting all changes
1139 1139 adding changesets
1140 1140 adding manifests
1141 1141 adding file changes
1142 1142 added 3 changesets with 3 changes to 1 files
1143 1143 new changesets b5f04eac9d8f:e5bfe23c0b47
1144 1144 searching for changes
1145 1145 no changes found
1146 1146 adding remote bookmark bookA
1147 1147 updating working directory
1148 1148 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1149 1149
1150 1150 $ (grep 'existing pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1151 1151 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1152 1152 searching for changes
1153 1153 no changes found
1154 1154 adding remote bookmark bookA
1155 1155 updating working directory
1156 1156 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1157 1157
1158 1158 SEC: check for unsafe ssh url
1159 1159
1160 1160 $ cat >> $HGRCPATH << EOF
1161 1161 > [ui]
1162 1162 > ssh = sh -c "read l; read l; read l"
1163 1163 > EOF
1164 1164
1165 1165 $ hg clone 'ssh://-oProxyCommand=touch${IFS}owned/path'
1166 1166 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1167 1167 [255]
1168 1168 $ hg clone 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
1169 1169 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1170 1170 [255]
1171 1171 $ hg clone 'ssh://fakehost|touch%20owned/path'
1172 1172 abort: no suitable response from remote hg!
1173 1173 [255]
1174 1174 $ hg clone 'ssh://fakehost%7Ctouch%20owned/path'
1175 1175 abort: no suitable response from remote hg!
1176 1176 [255]
1177 1177
1178 1178 $ hg clone 'ssh://-oProxyCommand=touch owned%20foo@example.com/nonexistent/path'
1179 1179 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned foo@example.com/nonexistent/path'
1180 1180 [255]
1181 1181
1182 1182 #if windows
1183 1183 $ hg clone "ssh://%26touch%20owned%20/" --debug
1184 1184 running sh -c "read l; read l; read l" "&touch owned " "hg -R . serve --stdio"
1185 1185 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1186 1186 sending hello command
1187 1187 sending between command
1188 1188 abort: no suitable response from remote hg!
1189 1189 [255]
1190 1190 $ hg clone "ssh://example.com:%26touch%20owned%20/" --debug
1191 1191 running sh -c "read l; read l; read l" -p "&touch owned " example.com "hg -R . serve --stdio"
1192 1192 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1193 1193 sending hello command
1194 1194 sending between command
1195 1195 abort: no suitable response from remote hg!
1196 1196 [255]
1197 1197 #else
1198 1198 $ hg clone "ssh://%3btouch%20owned%20/" --debug
1199 1199 running sh -c "read l; read l; read l" ';touch owned ' 'hg -R . serve --stdio'
1200 1200 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1201 1201 sending hello command
1202 1202 sending between command
1203 1203 abort: no suitable response from remote hg!
1204 1204 [255]
1205 1205 $ hg clone "ssh://example.com:%3btouch%20owned%20/" --debug
1206 1206 running sh -c "read l; read l; read l" -p ';touch owned ' example.com 'hg -R . serve --stdio'
1207 1207 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1208 1208 sending hello command
1209 1209 sending between command
1210 1210 abort: no suitable response from remote hg!
1211 1211 [255]
1212 1212 #endif
1213 1213
1214 1214 $ hg clone "ssh://v-alid.example.com/" --debug
1215 1215 running sh -c "read l; read l; read l" v-alid\.example\.com ['"]hg -R \. serve --stdio['"] (re)
1216 1216 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1217 1217 sending hello command
1218 1218 sending between command
1219 1219 abort: no suitable response from remote hg!
1220 1220 [255]
1221 1221
1222 1222 We should not have created a file named owned - if it exists, the
1223 1223 attack succeeded.
1224 1224 $ if test -f owned; then echo 'you got owned'; fi
1225 1225
1226 1226 Cloning without fsmonitor enabled does not print a warning for small repos
1227 1227
1228 1228 $ hg clone a fsmonitor-default
1229 1229 updating to bookmark @ on branch stable
1230 1230 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1231 1231
1232 1232 Lower the warning threshold to simulate a large repo
1233 1233
1234 1234 $ cat >> $HGRCPATH << EOF
1235 1235 > [fsmonitor]
1236 1236 > warn_update_file_count = 2
1237 1237 > EOF
1238 1238
1239 1239 We should see a warning about no fsmonitor on supported platforms
1240 1240
1241 1241 #if linuxormacos no-fsmonitor
1242 1242 $ hg clone a nofsmonitor
1243 1243 updating to bookmark @ on branch stable
1244 1244 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor")
1245 1245 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1246 1246 #else
1247 1247 $ hg clone a nofsmonitor
1248 1248 updating to bookmark @ on branch stable
1249 1249 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1250 1250 #endif
1251 1251
1252 1252 We should not see warning about fsmonitor when it is enabled
1253 1253
1254 1254 #if fsmonitor
1255 1255 $ hg clone a fsmonitor-enabled
1256 1256 updating to bookmark @ on branch stable
1257 1257 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1258 1258 #endif
1259 1259
1260 1260 We can disable the fsmonitor warning
1261 1261
1262 1262 $ hg --config fsmonitor.warn_when_unused=false clone a fsmonitor-disable-warning
1263 1263 updating to bookmark @ on branch stable
1264 1264 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1265 1265
1266 1266 Loaded fsmonitor but disabled in config should still print warning
1267 1267
1268 1268 #if linuxormacos fsmonitor
1269 1269 $ hg --config fsmonitor.mode=off clone a fsmonitor-mode-off
1270 1270 updating to bookmark @ on branch stable
1271 1271 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor") (fsmonitor !)
1272 1272 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1273 1273 #endif
1274 1274
1275 1275 Warning not printed if working directory isn't empty
1276 1276
1277 1277 $ hg -q clone a fsmonitor-update
1278 1278 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor") (?)
1279 1279 $ cd fsmonitor-update
1280 1280 $ hg up acb14030fe0a
1281 1281 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1282 1282 (leaving bookmark @)
1283 1283 $ hg up cf0fe1914066
1284 1284 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1285 1285
1286 1286 `hg update` from null revision also prints
1287 1287
1288 1288 $ hg up null
1289 1289 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1290 1290
1291 1291 #if linuxormacos no-fsmonitor
1292 1292 $ hg up cf0fe1914066
1293 1293 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor")
1294 1294 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1295 1295 #else
1296 1296 $ hg up cf0fe1914066
1297 1297 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1298 1298 #endif
1299 1299
1300 1300 $ cd ..
1301 1301
@@ -1,218 +1,211 b''
1 1 Test the EOL hook
2 2
3 3 $ hg init main
4 4 $ cat > main/.hg/hgrc <<EOF
5 5 > [hooks]
6 6 > pretxnchangegroup = python:hgext.eol.hook
7 7 > EOF
8 8 $ hg clone main fork
9 9 updating to branch default
10 10 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
11 11 $ cd fork
12 12
13 13 Create repo
14 14 $ cat > .hgeol <<EOF
15 15 > [patterns]
16 16 > mixed.txt = BIN
17 17 > crlf.txt = CRLF
18 18 > **.txt = native
19 19 > EOF
20 20 $ hg add .hgeol
21 21 $ hg commit -m 'Commit .hgeol'
22 22
23 23 $ printf "first\nsecond\nthird\n" > a.txt
24 24 $ hg add a.txt
25 25 $ hg commit -m 'LF a.txt'
26 26 $ hg push ../main
27 27 pushing to ../main
28 28 searching for changes
29 29 adding changesets
30 30 adding manifests
31 31 adding file changes
32 32 added 2 changesets with 2 changes to 2 files
33 33
34 34 $ printf "first\r\nsecond\r\nthird\n" > a.txt
35 35 $ hg commit -m 'CRLF a.txt'
36 36 $ hg push ../main
37 37 pushing to ../main
38 38 searching for changes
39 39 adding changesets
40 40 adding manifests
41 41 adding file changes
42 added 1 changesets with 1 changes to 1 files
43 42 error: pretxnchangegroup hook failed: end-of-line check failed:
44 43 a.txt in a8ee6548cd86 should not have CRLF line endings
45 44 transaction abort!
46 45 rollback completed
47 46 abort: end-of-line check failed:
48 47 a.txt in a8ee6548cd86 should not have CRLF line endings
49 48 [255]
50 49
51 50 $ printf "first\nsecond\nthird\n" > a.txt
52 51 $ hg commit -m 'LF a.txt (fixed)'
53 52 $ hg push ../main
54 53 pushing to ../main
55 54 searching for changes
56 55 adding changesets
57 56 adding manifests
58 57 adding file changes
59 58 added 2 changesets with 2 changes to 1 files
60 59
61 60 $ printf "first\nsecond\nthird\n" > crlf.txt
62 61 $ hg add crlf.txt
63 62 $ hg commit -m 'LF crlf.txt'
64 63 $ hg push ../main
65 64 pushing to ../main
66 65 searching for changes
67 66 adding changesets
68 67 adding manifests
69 68 adding file changes
70 added 1 changesets with 1 changes to 1 files
71 69 error: pretxnchangegroup hook failed: end-of-line check failed:
72 70 crlf.txt in 004ba2132725 should not have LF line endings
73 71 transaction abort!
74 72 rollback completed
75 73 abort: end-of-line check failed:
76 74 crlf.txt in 004ba2132725 should not have LF line endings
77 75 [255]
78 76
79 77 $ printf "first\r\nsecond\r\nthird\r\n" > crlf.txt
80 78 $ hg commit -m 'CRLF crlf.txt (fixed)'
81 79 $ hg push ../main
82 80 pushing to ../main
83 81 searching for changes
84 82 adding changesets
85 83 adding manifests
86 84 adding file changes
87 85 added 2 changesets with 2 changes to 1 files
88 86
89 87 $ printf "first\r\nsecond" > b.txt
90 88 $ hg add b.txt
91 89 $ hg commit -m 'CRLF b.txt'
92 90 $ hg push ../main
93 91 pushing to ../main
94 92 searching for changes
95 93 adding changesets
96 94 adding manifests
97 95 adding file changes
98 added 1 changesets with 1 changes to 1 files
99 96 error: pretxnchangegroup hook failed: end-of-line check failed:
100 97 b.txt in fbcf9b1025f5 should not have CRLF line endings
101 98 transaction abort!
102 99 rollback completed
103 100 abort: end-of-line check failed:
104 101 b.txt in fbcf9b1025f5 should not have CRLF line endings
105 102 [255]
106 103
107 104 $ hg up -r -2
108 105 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
109 106 $ printf "some\nother\nfile" > c.txt
110 107 $ hg add c.txt
111 108 $ hg commit -m "LF c.txt, b.txt doesn't exist here"
112 109 created new head
113 110 $ hg push -f ../main
114 111 pushing to ../main
115 112 searching for changes
116 113 adding changesets
117 114 adding manifests
118 115 adding file changes
119 added 2 changesets with 2 changes to 2 files (+1 heads)
120 116 error: pretxnchangegroup hook failed: end-of-line check failed:
121 117 b.txt in fbcf9b1025f5 should not have CRLF line endings
122 118 transaction abort!
123 119 rollback completed
124 120 abort: end-of-line check failed:
125 121 b.txt in fbcf9b1025f5 should not have CRLF line endings
126 122 [255]
127 123
128 124 Test checkheadshook alias
129 125
130 126 $ cat > ../main/.hg/hgrc <<EOF
131 127 > [hooks]
132 128 > pretxnchangegroup = python:hgext.eol.checkheadshook
133 129 > EOF
134 130 $ hg push -f ../main
135 131 pushing to ../main
136 132 searching for changes
137 133 adding changesets
138 134 adding manifests
139 135 adding file changes
140 added 2 changesets with 2 changes to 2 files (+1 heads)
141 136 error: pretxnchangegroup hook failed: end-of-line check failed:
142 137 b.txt in fbcf9b1025f5 should not have CRLF line endings
143 138 transaction abort!
144 139 rollback completed
145 140 abort: end-of-line check failed:
146 141 b.txt in fbcf9b1025f5 should not have CRLF line endings
147 142 [255]
148 143
149 144 We can fix the head and push again
150 145
151 146 $ hg up 6
152 147 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
153 148 $ printf "first\nsecond" > b.txt
154 149 $ hg ci -m "remove CRLF from b.txt"
155 150 $ hg push -f ../main
156 151 pushing to ../main
157 152 searching for changes
158 153 adding changesets
159 154 adding manifests
160 155 adding file changes
161 156 added 3 changesets with 3 changes to 2 files (+1 heads)
162 157 $ hg -R ../main rollback
163 158 repository tip rolled back to revision 5 (undo push)
164 159
165 160 Test it still fails with checkallhook
166 161
167 162 $ cat > ../main/.hg/hgrc <<EOF
168 163 > [hooks]
169 164 > pretxnchangegroup = python:hgext.eol.checkallhook
170 165 > EOF
171 166 $ hg push -f ../main
172 167 pushing to ../main
173 168 searching for changes
174 169 adding changesets
175 170 adding manifests
176 171 adding file changes
177 added 3 changesets with 3 changes to 2 files (+1 heads)
178 172 error: pretxnchangegroup hook failed: end-of-line check failed:
179 173 b.txt in fbcf9b1025f5 should not have CRLF line endings
180 174 transaction abort!
181 175 rollback completed
182 176 abort: end-of-line check failed:
183 177 b.txt in fbcf9b1025f5 should not have CRLF line endings
184 178 [255]
185 179
186 180 But we can push the clean head
187 181
188 182 $ hg push -r7 -f ../main
189 183 pushing to ../main
190 184 searching for changes
191 185 adding changesets
192 186 adding manifests
193 187 adding file changes
194 188 added 1 changesets with 1 changes to 1 files
195 189
196 190 Test multiple files/revisions output
197 191
198 192 $ printf "another\r\nbad\r\none" > d.txt
199 193 $ hg add d.txt
200 194 $ hg ci -m "add d.txt"
201 195 $ hg push -f ../main
202 196 pushing to ../main
203 197 searching for changes
204 198 adding changesets
205 199 adding manifests
206 200 adding file changes
207 added 3 changesets with 3 changes to 2 files (+1 heads)
208 201 error: pretxnchangegroup hook failed: end-of-line check failed:
209 202 b.txt in fbcf9b1025f5 should not have CRLF line endings
210 203 d.txt in a7040e68714f should not have CRLF line endings
211 204 transaction abort!
212 205 rollback completed
213 206 abort: end-of-line check failed:
214 207 b.txt in fbcf9b1025f5 should not have CRLF line endings
215 208 d.txt in a7040e68714f should not have CRLF line endings
216 209 [255]
217 210
218 211 $ cd ..
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now