##// END OF EJS Templates
fileset: restrict getfileset() to not return a computed set (API)...
Yuya Nishihara -
r38631:760cc5dc default
parent child Browse files
Show More
@@ -1,2545 +1,2545 b''
1 1 # context.py - changeset and file context objects for mercurial
2 2 #
3 3 # Copyright 2006, 2007 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 filecmp
12 12 import os
13 13 import stat
14 14
15 15 from .i18n import _
16 16 from .node import (
17 17 addednodeid,
18 18 bin,
19 19 hex,
20 20 modifiednodeid,
21 21 nullid,
22 22 nullrev,
23 23 short,
24 24 wdirfilenodeids,
25 25 wdirid,
26 26 )
27 27 from . import (
28 28 dagop,
29 29 encoding,
30 30 error,
31 31 fileset,
32 32 match as matchmod,
33 33 obsolete as obsmod,
34 34 patch,
35 35 pathutil,
36 36 phases,
37 37 pycompat,
38 38 repoview,
39 39 revlog,
40 40 scmutil,
41 41 sparse,
42 42 subrepo,
43 43 subrepoutil,
44 44 util,
45 45 )
46 46 from .utils import (
47 47 dateutil,
48 48 stringutil,
49 49 )
50 50
51 51 propertycache = util.propertycache
52 52
53 53 class basectx(object):
54 54 """A basectx object represents the common logic for its children:
55 55 changectx: read-only context that is already present in the repo,
56 56 workingctx: a context that represents the working directory and can
57 57 be committed,
58 58 memctx: a context that represents changes in-memory and can also
59 59 be committed."""
60 60
61 61 def __init__(self, repo):
62 62 self._repo = repo
63 63
64 64 def __bytes__(self):
65 65 return short(self.node())
66 66
67 67 __str__ = encoding.strmethod(__bytes__)
68 68
69 69 def __repr__(self):
70 70 return r"<%s %s>" % (type(self).__name__, str(self))
71 71
72 72 def __eq__(self, other):
73 73 try:
74 74 return type(self) == type(other) and self._rev == other._rev
75 75 except AttributeError:
76 76 return False
77 77
78 78 def __ne__(self, other):
79 79 return not (self == other)
80 80
81 81 def __contains__(self, key):
82 82 return key in self._manifest
83 83
84 84 def __getitem__(self, key):
85 85 return self.filectx(key)
86 86
87 87 def __iter__(self):
88 88 return iter(self._manifest)
89 89
90 90 def _buildstatusmanifest(self, status):
91 91 """Builds a manifest that includes the given status results, if this is
92 92 a working copy context. For non-working copy contexts, it just returns
93 93 the normal manifest."""
94 94 return self.manifest()
95 95
96 96 def _matchstatus(self, other, match):
97 97 """This internal method provides a way for child objects to override the
98 98 match operator.
99 99 """
100 100 return match
101 101
102 102 def _buildstatus(self, other, s, match, listignored, listclean,
103 103 listunknown):
104 104 """build a status with respect to another context"""
105 105 # Load earliest manifest first for caching reasons. More specifically,
106 106 # if you have revisions 1000 and 1001, 1001 is probably stored as a
107 107 # delta against 1000. Thus, if you read 1000 first, we'll reconstruct
108 108 # 1000 and cache it so that when you read 1001, we just need to apply a
109 109 # delta to what's in the cache. So that's one full reconstruction + one
110 110 # delta application.
111 111 mf2 = None
112 112 if self.rev() is not None and self.rev() < other.rev():
113 113 mf2 = self._buildstatusmanifest(s)
114 114 mf1 = other._buildstatusmanifest(s)
115 115 if mf2 is None:
116 116 mf2 = self._buildstatusmanifest(s)
117 117
118 118 modified, added = [], []
119 119 removed = []
120 120 clean = []
121 121 deleted, unknown, ignored = s.deleted, s.unknown, s.ignored
122 122 deletedset = set(deleted)
123 123 d = mf1.diff(mf2, match=match, clean=listclean)
124 124 for fn, value in d.iteritems():
125 125 if fn in deletedset:
126 126 continue
127 127 if value is None:
128 128 clean.append(fn)
129 129 continue
130 130 (node1, flag1), (node2, flag2) = value
131 131 if node1 is None:
132 132 added.append(fn)
133 133 elif node2 is None:
134 134 removed.append(fn)
135 135 elif flag1 != flag2:
136 136 modified.append(fn)
137 137 elif node2 not in wdirfilenodeids:
138 138 # When comparing files between two commits, we save time by
139 139 # not comparing the file contents when the nodeids differ.
140 140 # Note that this means we incorrectly report a reverted change
141 141 # to a file as a modification.
142 142 modified.append(fn)
143 143 elif self[fn].cmp(other[fn]):
144 144 modified.append(fn)
145 145 else:
146 146 clean.append(fn)
147 147
148 148 if removed:
149 149 # need to filter files if they are already reported as removed
150 150 unknown = [fn for fn in unknown if fn not in mf1 and
151 151 (not match or match(fn))]
152 152 ignored = [fn for fn in ignored if fn not in mf1 and
153 153 (not match or match(fn))]
154 154 # if they're deleted, don't report them as removed
155 155 removed = [fn for fn in removed if fn not in deletedset]
156 156
157 157 return scmutil.status(modified, added, removed, deleted, unknown,
158 158 ignored, clean)
159 159
160 160 @propertycache
161 161 def substate(self):
162 162 return subrepoutil.state(self, self._repo.ui)
163 163
164 164 def subrev(self, subpath):
165 165 return self.substate[subpath][1]
166 166
167 167 def rev(self):
168 168 return self._rev
169 169 def node(self):
170 170 return self._node
171 171 def hex(self):
172 172 return hex(self.node())
173 173 def manifest(self):
174 174 return self._manifest
175 175 def manifestctx(self):
176 176 return self._manifestctx
177 177 def repo(self):
178 178 return self._repo
179 179 def phasestr(self):
180 180 return phases.phasenames[self.phase()]
181 181 def mutable(self):
182 182 return self.phase() > phases.public
183 183
184 def getfileset(self, expr):
185 return fileset.getfileset(self, expr)
184 def matchfileset(self, expr, badfn=None):
185 return fileset.match(self, expr, badfn=badfn)
186 186
187 187 def obsolete(self):
188 188 """True if the changeset is obsolete"""
189 189 return self.rev() in obsmod.getrevs(self._repo, 'obsolete')
190 190
191 191 def extinct(self):
192 192 """True if the changeset is extinct"""
193 193 return self.rev() in obsmod.getrevs(self._repo, 'extinct')
194 194
195 195 def orphan(self):
196 196 """True if the changeset is not obsolete but it's ancestor are"""
197 197 return self.rev() in obsmod.getrevs(self._repo, 'orphan')
198 198
199 199 def phasedivergent(self):
200 200 """True if the changeset try to be a successor of a public changeset
201 201
202 202 Only non-public and non-obsolete changesets may be bumped.
203 203 """
204 204 return self.rev() in obsmod.getrevs(self._repo, 'phasedivergent')
205 205
206 206 def contentdivergent(self):
207 207 """Is a successors of a changeset with multiple possible successors set
208 208
209 209 Only non-public and non-obsolete changesets may be divergent.
210 210 """
211 211 return self.rev() in obsmod.getrevs(self._repo, 'contentdivergent')
212 212
213 213 def isunstable(self):
214 214 """True if the changeset is either unstable, bumped or divergent"""
215 215 return self.orphan() or self.phasedivergent() or self.contentdivergent()
216 216
217 217 def instabilities(self):
218 218 """return the list of instabilities affecting this changeset.
219 219
220 220 Instabilities are returned as strings. possible values are:
221 221 - orphan,
222 222 - phase-divergent,
223 223 - content-divergent.
224 224 """
225 225 instabilities = []
226 226 if self.orphan():
227 227 instabilities.append('orphan')
228 228 if self.phasedivergent():
229 229 instabilities.append('phase-divergent')
230 230 if self.contentdivergent():
231 231 instabilities.append('content-divergent')
232 232 return instabilities
233 233
234 234 def parents(self):
235 235 """return contexts for each parent changeset"""
236 236 return self._parents
237 237
238 238 def p1(self):
239 239 return self._parents[0]
240 240
241 241 def p2(self):
242 242 parents = self._parents
243 243 if len(parents) == 2:
244 244 return parents[1]
245 245 return changectx(self._repo, nullrev)
246 246
247 247 def _fileinfo(self, path):
248 248 if r'_manifest' in self.__dict__:
249 249 try:
250 250 return self._manifest[path], self._manifest.flags(path)
251 251 except KeyError:
252 252 raise error.ManifestLookupError(self._node, path,
253 253 _('not found in manifest'))
254 254 if r'_manifestdelta' in self.__dict__ or path in self.files():
255 255 if path in self._manifestdelta:
256 256 return (self._manifestdelta[path],
257 257 self._manifestdelta.flags(path))
258 258 mfl = self._repo.manifestlog
259 259 try:
260 260 node, flag = mfl[self._changeset.manifest].find(path)
261 261 except KeyError:
262 262 raise error.ManifestLookupError(self._node, path,
263 263 _('not found in manifest'))
264 264
265 265 return node, flag
266 266
267 267 def filenode(self, path):
268 268 return self._fileinfo(path)[0]
269 269
270 270 def flags(self, path):
271 271 try:
272 272 return self._fileinfo(path)[1]
273 273 except error.LookupError:
274 274 return ''
275 275
276 276 def sub(self, path, allowcreate=True):
277 277 '''return a subrepo for the stored revision of path, never wdir()'''
278 278 return subrepo.subrepo(self, path, allowcreate=allowcreate)
279 279
280 280 def nullsub(self, path, pctx):
281 281 return subrepo.nullsubrepo(self, path, pctx)
282 282
283 283 def workingsub(self, path):
284 284 '''return a subrepo for the stored revision, or wdir if this is a wdir
285 285 context.
286 286 '''
287 287 return subrepo.subrepo(self, path, allowwdir=True)
288 288
289 289 def match(self, pats=None, include=None, exclude=None, default='glob',
290 290 listsubrepos=False, badfn=None):
291 291 r = self._repo
292 292 return matchmod.match(r.root, r.getcwd(), pats,
293 293 include, exclude, default,
294 294 auditor=r.nofsauditor, ctx=self,
295 295 listsubrepos=listsubrepos, badfn=badfn)
296 296
297 297 def diff(self, ctx2=None, match=None, changes=None, opts=None,
298 298 losedatafn=None, prefix='', relroot='', copy=None,
299 299 hunksfilterfn=None):
300 300 """Returns a diff generator for the given contexts and matcher"""
301 301 if ctx2 is None:
302 302 ctx2 = self.p1()
303 303 if ctx2 is not None:
304 304 ctx2 = self._repo[ctx2]
305 305 return patch.diff(self._repo, ctx2, self, match=match, changes=changes,
306 306 opts=opts, losedatafn=losedatafn, prefix=prefix,
307 307 relroot=relroot, copy=copy,
308 308 hunksfilterfn=hunksfilterfn)
309 309
310 310 def dirs(self):
311 311 return self._manifest.dirs()
312 312
313 313 def hasdir(self, dir):
314 314 return self._manifest.hasdir(dir)
315 315
316 316 def status(self, other=None, match=None, listignored=False,
317 317 listclean=False, listunknown=False, listsubrepos=False):
318 318 """return status of files between two nodes or node and working
319 319 directory.
320 320
321 321 If other is None, compare this node with working directory.
322 322
323 323 returns (modified, added, removed, deleted, unknown, ignored, clean)
324 324 """
325 325
326 326 ctx1 = self
327 327 ctx2 = self._repo[other]
328 328
329 329 # This next code block is, admittedly, fragile logic that tests for
330 330 # reversing the contexts and wouldn't need to exist if it weren't for
331 331 # the fast (and common) code path of comparing the working directory
332 332 # with its first parent.
333 333 #
334 334 # What we're aiming for here is the ability to call:
335 335 #
336 336 # workingctx.status(parentctx)
337 337 #
338 338 # If we always built the manifest for each context and compared those,
339 339 # then we'd be done. But the special case of the above call means we
340 340 # just copy the manifest of the parent.
341 341 reversed = False
342 342 if (not isinstance(ctx1, changectx)
343 343 and isinstance(ctx2, changectx)):
344 344 reversed = True
345 345 ctx1, ctx2 = ctx2, ctx1
346 346
347 347 match = match or matchmod.always(self._repo.root, self._repo.getcwd())
348 348 match = ctx2._matchstatus(ctx1, match)
349 349 r = scmutil.status([], [], [], [], [], [], [])
350 350 r = ctx2._buildstatus(ctx1, r, match, listignored, listclean,
351 351 listunknown)
352 352
353 353 if reversed:
354 354 # Reverse added and removed. Clear deleted, unknown and ignored as
355 355 # these make no sense to reverse.
356 356 r = scmutil.status(r.modified, r.removed, r.added, [], [], [],
357 357 r.clean)
358 358
359 359 if listsubrepos:
360 360 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
361 361 try:
362 362 rev2 = ctx2.subrev(subpath)
363 363 except KeyError:
364 364 # A subrepo that existed in node1 was deleted between
365 365 # node1 and node2 (inclusive). Thus, ctx2's substate
366 366 # won't contain that subpath. The best we can do ignore it.
367 367 rev2 = None
368 368 submatch = matchmod.subdirmatcher(subpath, match)
369 369 s = sub.status(rev2, match=submatch, ignored=listignored,
370 370 clean=listclean, unknown=listunknown,
371 371 listsubrepos=True)
372 372 for rfiles, sfiles in zip(r, s):
373 373 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
374 374
375 375 for l in r:
376 376 l.sort()
377 377
378 378 return r
379 379
380 380 class changectx(basectx):
381 381 """A changecontext object makes access to data related to a particular
382 382 changeset convenient. It represents a read-only context already present in
383 383 the repo."""
384 384 def __init__(self, repo, changeid='.'):
385 385 """changeid is a revision number, node, or tag"""
386 386 super(changectx, self).__init__(repo)
387 387
388 388 try:
389 389 if isinstance(changeid, int):
390 390 self._node = repo.changelog.node(changeid)
391 391 self._rev = changeid
392 392 return
393 393 elif changeid == 'null':
394 394 self._node = nullid
395 395 self._rev = nullrev
396 396 return
397 397 elif changeid == 'tip':
398 398 self._node = repo.changelog.tip()
399 399 self._rev = repo.changelog.rev(self._node)
400 400 return
401 401 elif (changeid == '.'
402 402 or repo.local() and changeid == repo.dirstate.p1()):
403 403 # this is a hack to delay/avoid loading obsmarkers
404 404 # when we know that '.' won't be hidden
405 405 self._node = repo.dirstate.p1()
406 406 self._rev = repo.unfiltered().changelog.rev(self._node)
407 407 return
408 408 elif len(changeid) == 20:
409 409 try:
410 410 self._node = changeid
411 411 self._rev = repo.changelog.rev(changeid)
412 412 return
413 413 except error.FilteredLookupError:
414 414 raise
415 415 except LookupError:
416 416 # check if it might have come from damaged dirstate
417 417 #
418 418 # XXX we could avoid the unfiltered if we had a recognizable
419 419 # exception for filtered changeset access
420 420 if (repo.local()
421 421 and changeid in repo.unfiltered().dirstate.parents()):
422 422 msg = _("working directory has unknown parent '%s'!")
423 423 raise error.Abort(msg % short(changeid))
424 424 changeid = hex(changeid) # for the error message
425 425
426 426 elif len(changeid) == 40:
427 427 try:
428 428 self._node = bin(changeid)
429 429 self._rev = repo.changelog.rev(self._node)
430 430 return
431 431 except error.FilteredLookupError:
432 432 raise
433 433 except (TypeError, LookupError):
434 434 pass
435 435 else:
436 436 raise error.ProgrammingError(
437 437 "unsupported changeid '%s' of type %s" %
438 438 (changeid, type(changeid)))
439 439
440 440 # lookup failed
441 441 except (error.FilteredIndexError, error.FilteredLookupError):
442 442 raise error.FilteredRepoLookupError(_("filtered revision '%s'")
443 443 % pycompat.bytestr(changeid))
444 444 except error.FilteredRepoLookupError:
445 445 raise
446 446 except IndexError:
447 447 pass
448 448 raise error.RepoLookupError(
449 449 _("unknown revision '%s'") % changeid)
450 450
451 451 def __hash__(self):
452 452 try:
453 453 return hash(self._rev)
454 454 except AttributeError:
455 455 return id(self)
456 456
457 457 def __nonzero__(self):
458 458 return self._rev != nullrev
459 459
460 460 __bool__ = __nonzero__
461 461
462 462 @propertycache
463 463 def _changeset(self):
464 464 return self._repo.changelog.changelogrevision(self.rev())
465 465
466 466 @propertycache
467 467 def _manifest(self):
468 468 return self._manifestctx.read()
469 469
470 470 @property
471 471 def _manifestctx(self):
472 472 return self._repo.manifestlog[self._changeset.manifest]
473 473
474 474 @propertycache
475 475 def _manifestdelta(self):
476 476 return self._manifestctx.readdelta()
477 477
478 478 @propertycache
479 479 def _parents(self):
480 480 repo = self._repo
481 481 p1, p2 = repo.changelog.parentrevs(self._rev)
482 482 if p2 == nullrev:
483 483 return [changectx(repo, p1)]
484 484 return [changectx(repo, p1), changectx(repo, p2)]
485 485
486 486 def changeset(self):
487 487 c = self._changeset
488 488 return (
489 489 c.manifest,
490 490 c.user,
491 491 c.date,
492 492 c.files,
493 493 c.description,
494 494 c.extra,
495 495 )
496 496 def manifestnode(self):
497 497 return self._changeset.manifest
498 498
499 499 def user(self):
500 500 return self._changeset.user
501 501 def date(self):
502 502 return self._changeset.date
503 503 def files(self):
504 504 return self._changeset.files
505 505 def description(self):
506 506 return self._changeset.description
507 507 def branch(self):
508 508 return encoding.tolocal(self._changeset.extra.get("branch"))
509 509 def closesbranch(self):
510 510 return 'close' in self._changeset.extra
511 511 def extra(self):
512 512 """Return a dict of extra information."""
513 513 return self._changeset.extra
514 514 def tags(self):
515 515 """Return a list of byte tag names"""
516 516 return self._repo.nodetags(self._node)
517 517 def bookmarks(self):
518 518 """Return a list of byte bookmark names."""
519 519 return self._repo.nodebookmarks(self._node)
520 520 def phase(self):
521 521 return self._repo._phasecache.phase(self._repo, self._rev)
522 522 def hidden(self):
523 523 return self._rev in repoview.filterrevs(self._repo, 'visible')
524 524
525 525 def isinmemory(self):
526 526 return False
527 527
528 528 def children(self):
529 529 """return list of changectx contexts for each child changeset.
530 530
531 531 This returns only the immediate child changesets. Use descendants() to
532 532 recursively walk children.
533 533 """
534 534 c = self._repo.changelog.children(self._node)
535 535 return [changectx(self._repo, x) for x in c]
536 536
537 537 def ancestors(self):
538 538 for a in self._repo.changelog.ancestors([self._rev]):
539 539 yield changectx(self._repo, a)
540 540
541 541 def descendants(self):
542 542 """Recursively yield all children of the changeset.
543 543
544 544 For just the immediate children, use children()
545 545 """
546 546 for d in self._repo.changelog.descendants([self._rev]):
547 547 yield changectx(self._repo, d)
548 548
549 549 def filectx(self, path, fileid=None, filelog=None):
550 550 """get a file context from this changeset"""
551 551 if fileid is None:
552 552 fileid = self.filenode(path)
553 553 return filectx(self._repo, path, fileid=fileid,
554 554 changectx=self, filelog=filelog)
555 555
556 556 def ancestor(self, c2, warn=False):
557 557 """return the "best" ancestor context of self and c2
558 558
559 559 If there are multiple candidates, it will show a message and check
560 560 merge.preferancestor configuration before falling back to the
561 561 revlog ancestor."""
562 562 # deal with workingctxs
563 563 n2 = c2._node
564 564 if n2 is None:
565 565 n2 = c2._parents[0]._node
566 566 cahs = self._repo.changelog.commonancestorsheads(self._node, n2)
567 567 if not cahs:
568 568 anc = nullid
569 569 elif len(cahs) == 1:
570 570 anc = cahs[0]
571 571 else:
572 572 # experimental config: merge.preferancestor
573 573 for r in self._repo.ui.configlist('merge', 'preferancestor'):
574 574 try:
575 575 ctx = scmutil.revsymbol(self._repo, r)
576 576 except error.RepoLookupError:
577 577 continue
578 578 anc = ctx.node()
579 579 if anc in cahs:
580 580 break
581 581 else:
582 582 anc = self._repo.changelog.ancestor(self._node, n2)
583 583 if warn:
584 584 self._repo.ui.status(
585 585 (_("note: using %s as ancestor of %s and %s\n") %
586 586 (short(anc), short(self._node), short(n2))) +
587 587 ''.join(_(" alternatively, use --config "
588 588 "merge.preferancestor=%s\n") %
589 589 short(n) for n in sorted(cahs) if n != anc))
590 590 return changectx(self._repo, anc)
591 591
592 592 def descendant(self, other):
593 593 """True if other is descendant of this changeset"""
594 594 return self._repo.changelog.descendant(self._rev, other._rev)
595 595
596 596 def walk(self, match):
597 597 '''Generates matching file names.'''
598 598
599 599 # Wrap match.bad method to have message with nodeid
600 600 def bad(fn, msg):
601 601 # The manifest doesn't know about subrepos, so don't complain about
602 602 # paths into valid subrepos.
603 603 if any(fn == s or fn.startswith(s + '/')
604 604 for s in self.substate):
605 605 return
606 606 match.bad(fn, _('no such file in rev %s') % self)
607 607
608 608 m = matchmod.badmatch(match, bad)
609 609 return self._manifest.walk(m)
610 610
611 611 def matches(self, match):
612 612 return self.walk(match)
613 613
614 614 class basefilectx(object):
615 615 """A filecontext object represents the common logic for its children:
616 616 filectx: read-only access to a filerevision that is already present
617 617 in the repo,
618 618 workingfilectx: a filecontext that represents files from the working
619 619 directory,
620 620 memfilectx: a filecontext that represents files in-memory,
621 621 overlayfilectx: duplicate another filecontext with some fields overridden.
622 622 """
623 623 @propertycache
624 624 def _filelog(self):
625 625 return self._repo.file(self._path)
626 626
627 627 @propertycache
628 628 def _changeid(self):
629 629 if r'_changeid' in self.__dict__:
630 630 return self._changeid
631 631 elif r'_changectx' in self.__dict__:
632 632 return self._changectx.rev()
633 633 elif r'_descendantrev' in self.__dict__:
634 634 # this file context was created from a revision with a known
635 635 # descendant, we can (lazily) correct for linkrev aliases
636 636 return self._adjustlinkrev(self._descendantrev)
637 637 else:
638 638 return self._filelog.linkrev(self._filerev)
639 639
640 640 @propertycache
641 641 def _filenode(self):
642 642 if r'_fileid' in self.__dict__:
643 643 return self._filelog.lookup(self._fileid)
644 644 else:
645 645 return self._changectx.filenode(self._path)
646 646
647 647 @propertycache
648 648 def _filerev(self):
649 649 return self._filelog.rev(self._filenode)
650 650
651 651 @propertycache
652 652 def _repopath(self):
653 653 return self._path
654 654
655 655 def __nonzero__(self):
656 656 try:
657 657 self._filenode
658 658 return True
659 659 except error.LookupError:
660 660 # file is missing
661 661 return False
662 662
663 663 __bool__ = __nonzero__
664 664
665 665 def __bytes__(self):
666 666 try:
667 667 return "%s@%s" % (self.path(), self._changectx)
668 668 except error.LookupError:
669 669 return "%s@???" % self.path()
670 670
671 671 __str__ = encoding.strmethod(__bytes__)
672 672
673 673 def __repr__(self):
674 674 return r"<%s %s>" % (type(self).__name__, str(self))
675 675
676 676 def __hash__(self):
677 677 try:
678 678 return hash((self._path, self._filenode))
679 679 except AttributeError:
680 680 return id(self)
681 681
682 682 def __eq__(self, other):
683 683 try:
684 684 return (type(self) == type(other) and self._path == other._path
685 685 and self._filenode == other._filenode)
686 686 except AttributeError:
687 687 return False
688 688
689 689 def __ne__(self, other):
690 690 return not (self == other)
691 691
692 692 def filerev(self):
693 693 return self._filerev
694 694 def filenode(self):
695 695 return self._filenode
696 696 @propertycache
697 697 def _flags(self):
698 698 return self._changectx.flags(self._path)
699 699 def flags(self):
700 700 return self._flags
701 701 def filelog(self):
702 702 return self._filelog
703 703 def rev(self):
704 704 return self._changeid
705 705 def linkrev(self):
706 706 return self._filelog.linkrev(self._filerev)
707 707 def node(self):
708 708 return self._changectx.node()
709 709 def hex(self):
710 710 return self._changectx.hex()
711 711 def user(self):
712 712 return self._changectx.user()
713 713 def date(self):
714 714 return self._changectx.date()
715 715 def files(self):
716 716 return self._changectx.files()
717 717 def description(self):
718 718 return self._changectx.description()
719 719 def branch(self):
720 720 return self._changectx.branch()
721 721 def extra(self):
722 722 return self._changectx.extra()
723 723 def phase(self):
724 724 return self._changectx.phase()
725 725 def phasestr(self):
726 726 return self._changectx.phasestr()
727 727 def obsolete(self):
728 728 return self._changectx.obsolete()
729 729 def instabilities(self):
730 730 return self._changectx.instabilities()
731 731 def manifest(self):
732 732 return self._changectx.manifest()
733 733 def changectx(self):
734 734 return self._changectx
735 735 def renamed(self):
736 736 return self._copied
737 737 def repo(self):
738 738 return self._repo
739 739 def size(self):
740 740 return len(self.data())
741 741
742 742 def path(self):
743 743 return self._path
744 744
745 745 def isbinary(self):
746 746 try:
747 747 return stringutil.binary(self.data())
748 748 except IOError:
749 749 return False
750 750 def isexec(self):
751 751 return 'x' in self.flags()
752 752 def islink(self):
753 753 return 'l' in self.flags()
754 754
755 755 def isabsent(self):
756 756 """whether this filectx represents a file not in self._changectx
757 757
758 758 This is mainly for merge code to detect change/delete conflicts. This is
759 759 expected to be True for all subclasses of basectx."""
760 760 return False
761 761
762 762 _customcmp = False
763 763 def cmp(self, fctx):
764 764 """compare with other file context
765 765
766 766 returns True if different than fctx.
767 767 """
768 768 if fctx._customcmp:
769 769 return fctx.cmp(self)
770 770
771 771 if (fctx._filenode is None
772 772 and (self._repo._encodefilterpats
773 773 # if file data starts with '\1\n', empty metadata block is
774 774 # prepended, which adds 4 bytes to filelog.size().
775 775 or self.size() - 4 == fctx.size())
776 776 or self.size() == fctx.size()):
777 777 return self._filelog.cmp(self._filenode, fctx.data())
778 778
779 779 return True
780 780
781 781 def _adjustlinkrev(self, srcrev, inclusive=False):
782 782 """return the first ancestor of <srcrev> introducing <fnode>
783 783
784 784 If the linkrev of the file revision does not point to an ancestor of
785 785 srcrev, we'll walk down the ancestors until we find one introducing
786 786 this file revision.
787 787
788 788 :srcrev: the changeset revision we search ancestors from
789 789 :inclusive: if true, the src revision will also be checked
790 790 """
791 791 repo = self._repo
792 792 cl = repo.unfiltered().changelog
793 793 mfl = repo.manifestlog
794 794 # fetch the linkrev
795 795 lkr = self.linkrev()
796 796 # hack to reuse ancestor computation when searching for renames
797 797 memberanc = getattr(self, '_ancestrycontext', None)
798 798 iteranc = None
799 799 if srcrev is None:
800 800 # wctx case, used by workingfilectx during mergecopy
801 801 revs = [p.rev() for p in self._repo[None].parents()]
802 802 inclusive = True # we skipped the real (revless) source
803 803 else:
804 804 revs = [srcrev]
805 805 if memberanc is None:
806 806 memberanc = iteranc = cl.ancestors(revs, lkr,
807 807 inclusive=inclusive)
808 808 # check if this linkrev is an ancestor of srcrev
809 809 if lkr not in memberanc:
810 810 if iteranc is None:
811 811 iteranc = cl.ancestors(revs, lkr, inclusive=inclusive)
812 812 fnode = self._filenode
813 813 path = self._path
814 814 for a in iteranc:
815 815 ac = cl.read(a) # get changeset data (we avoid object creation)
816 816 if path in ac[3]: # checking the 'files' field.
817 817 # The file has been touched, check if the content is
818 818 # similar to the one we search for.
819 819 if fnode == mfl[ac[0]].readfast().get(path):
820 820 return a
821 821 # In theory, we should never get out of that loop without a result.
822 822 # But if manifest uses a buggy file revision (not children of the
823 823 # one it replaces) we could. Such a buggy situation will likely
824 824 # result is crash somewhere else at to some point.
825 825 return lkr
826 826
827 827 def introrev(self):
828 828 """return the rev of the changeset which introduced this file revision
829 829
830 830 This method is different from linkrev because it take into account the
831 831 changeset the filectx was created from. It ensures the returned
832 832 revision is one of its ancestors. This prevents bugs from
833 833 'linkrev-shadowing' when a file revision is used by multiple
834 834 changesets.
835 835 """
836 836 lkr = self.linkrev()
837 837 attrs = vars(self)
838 838 noctx = not (r'_changeid' in attrs or r'_changectx' in attrs)
839 839 if noctx or self.rev() == lkr:
840 840 return self.linkrev()
841 841 return self._adjustlinkrev(self.rev(), inclusive=True)
842 842
843 843 def introfilectx(self):
844 844 """Return filectx having identical contents, but pointing to the
845 845 changeset revision where this filectx was introduced"""
846 846 introrev = self.introrev()
847 847 if self.rev() == introrev:
848 848 return self
849 849 return self.filectx(self.filenode(), changeid=introrev)
850 850
851 851 def _parentfilectx(self, path, fileid, filelog):
852 852 """create parent filectx keeping ancestry info for _adjustlinkrev()"""
853 853 fctx = filectx(self._repo, path, fileid=fileid, filelog=filelog)
854 854 if r'_changeid' in vars(self) or r'_changectx' in vars(self):
855 855 # If self is associated with a changeset (probably explicitly
856 856 # fed), ensure the created filectx is associated with a
857 857 # changeset that is an ancestor of self.changectx.
858 858 # This lets us later use _adjustlinkrev to get a correct link.
859 859 fctx._descendantrev = self.rev()
860 860 fctx._ancestrycontext = getattr(self, '_ancestrycontext', None)
861 861 elif r'_descendantrev' in vars(self):
862 862 # Otherwise propagate _descendantrev if we have one associated.
863 863 fctx._descendantrev = self._descendantrev
864 864 fctx._ancestrycontext = getattr(self, '_ancestrycontext', None)
865 865 return fctx
866 866
867 867 def parents(self):
868 868 _path = self._path
869 869 fl = self._filelog
870 870 parents = self._filelog.parents(self._filenode)
871 871 pl = [(_path, node, fl) for node in parents if node != nullid]
872 872
873 873 r = fl.renamed(self._filenode)
874 874 if r:
875 875 # - In the simple rename case, both parent are nullid, pl is empty.
876 876 # - In case of merge, only one of the parent is null id and should
877 877 # be replaced with the rename information. This parent is -always-
878 878 # the first one.
879 879 #
880 880 # As null id have always been filtered out in the previous list
881 881 # comprehension, inserting to 0 will always result in "replacing
882 882 # first nullid parent with rename information.
883 883 pl.insert(0, (r[0], r[1], self._repo.file(r[0])))
884 884
885 885 return [self._parentfilectx(path, fnode, l) for path, fnode, l in pl]
886 886
887 887 def p1(self):
888 888 return self.parents()[0]
889 889
890 890 def p2(self):
891 891 p = self.parents()
892 892 if len(p) == 2:
893 893 return p[1]
894 894 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
895 895
896 896 def annotate(self, follow=False, skiprevs=None, diffopts=None):
897 897 """Returns a list of annotateline objects for each line in the file
898 898
899 899 - line.fctx is the filectx of the node where that line was last changed
900 900 - line.lineno is the line number at the first appearance in the managed
901 901 file
902 902 - line.text is the data on that line (including newline character)
903 903 """
904 904 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
905 905
906 906 def parents(f):
907 907 # Cut _descendantrev here to mitigate the penalty of lazy linkrev
908 908 # adjustment. Otherwise, p._adjustlinkrev() would walk changelog
909 909 # from the topmost introrev (= srcrev) down to p.linkrev() if it
910 910 # isn't an ancestor of the srcrev.
911 911 f._changeid
912 912 pl = f.parents()
913 913
914 914 # Don't return renamed parents if we aren't following.
915 915 if not follow:
916 916 pl = [p for p in pl if p.path() == f.path()]
917 917
918 918 # renamed filectx won't have a filelog yet, so set it
919 919 # from the cache to save time
920 920 for p in pl:
921 921 if not r'_filelog' in p.__dict__:
922 922 p._filelog = getlog(p.path())
923 923
924 924 return pl
925 925
926 926 # use linkrev to find the first changeset where self appeared
927 927 base = self.introfilectx()
928 928 if getattr(base, '_ancestrycontext', None) is None:
929 929 cl = self._repo.changelog
930 930 if base.rev() is None:
931 931 # wctx is not inclusive, but works because _ancestrycontext
932 932 # is used to test filelog revisions
933 933 ac = cl.ancestors([p.rev() for p in base.parents()],
934 934 inclusive=True)
935 935 else:
936 936 ac = cl.ancestors([base.rev()], inclusive=True)
937 937 base._ancestrycontext = ac
938 938
939 939 return dagop.annotate(base, parents, skiprevs=skiprevs,
940 940 diffopts=diffopts)
941 941
942 942 def ancestors(self, followfirst=False):
943 943 visit = {}
944 944 c = self
945 945 if followfirst:
946 946 cut = 1
947 947 else:
948 948 cut = None
949 949
950 950 while True:
951 951 for parent in c.parents()[:cut]:
952 952 visit[(parent.linkrev(), parent.filenode())] = parent
953 953 if not visit:
954 954 break
955 955 c = visit.pop(max(visit))
956 956 yield c
957 957
958 958 def decodeddata(self):
959 959 """Returns `data()` after running repository decoding filters.
960 960
961 961 This is often equivalent to how the data would be expressed on disk.
962 962 """
963 963 return self._repo.wwritedata(self.path(), self.data())
964 964
965 965 class filectx(basefilectx):
966 966 """A filecontext object makes access to data related to a particular
967 967 filerevision convenient."""
968 968 def __init__(self, repo, path, changeid=None, fileid=None,
969 969 filelog=None, changectx=None):
970 970 """changeid can be a changeset revision, node, or tag.
971 971 fileid can be a file revision or node."""
972 972 self._repo = repo
973 973 self._path = path
974 974
975 975 assert (changeid is not None
976 976 or fileid is not None
977 977 or changectx is not None), \
978 978 ("bad args: changeid=%r, fileid=%r, changectx=%r"
979 979 % (changeid, fileid, changectx))
980 980
981 981 if filelog is not None:
982 982 self._filelog = filelog
983 983
984 984 if changeid is not None:
985 985 self._changeid = changeid
986 986 if changectx is not None:
987 987 self._changectx = changectx
988 988 if fileid is not None:
989 989 self._fileid = fileid
990 990
991 991 @propertycache
992 992 def _changectx(self):
993 993 try:
994 994 return changectx(self._repo, self._changeid)
995 995 except error.FilteredRepoLookupError:
996 996 # Linkrev may point to any revision in the repository. When the
997 997 # repository is filtered this may lead to `filectx` trying to build
998 998 # `changectx` for filtered revision. In such case we fallback to
999 999 # creating `changectx` on the unfiltered version of the reposition.
1000 1000 # This fallback should not be an issue because `changectx` from
1001 1001 # `filectx` are not used in complex operations that care about
1002 1002 # filtering.
1003 1003 #
1004 1004 # This fallback is a cheap and dirty fix that prevent several
1005 1005 # crashes. It does not ensure the behavior is correct. However the
1006 1006 # behavior was not correct before filtering either and "incorrect
1007 1007 # behavior" is seen as better as "crash"
1008 1008 #
1009 1009 # Linkrevs have several serious troubles with filtering that are
1010 1010 # complicated to solve. Proper handling of the issue here should be
1011 1011 # considered when solving linkrev issue are on the table.
1012 1012 return changectx(self._repo.unfiltered(), self._changeid)
1013 1013
1014 1014 def filectx(self, fileid, changeid=None):
1015 1015 '''opens an arbitrary revision of the file without
1016 1016 opening a new filelog'''
1017 1017 return filectx(self._repo, self._path, fileid=fileid,
1018 1018 filelog=self._filelog, changeid=changeid)
1019 1019
1020 1020 def rawdata(self):
1021 1021 return self._filelog.revision(self._filenode, raw=True)
1022 1022
1023 1023 def rawflags(self):
1024 1024 """low-level revlog flags"""
1025 1025 return self._filelog.flags(self._filerev)
1026 1026
1027 1027 def data(self):
1028 1028 try:
1029 1029 return self._filelog.read(self._filenode)
1030 1030 except error.CensoredNodeError:
1031 1031 if self._repo.ui.config("censor", "policy") == "ignore":
1032 1032 return ""
1033 1033 raise error.Abort(_("censored node: %s") % short(self._filenode),
1034 1034 hint=_("set censor.policy to ignore errors"))
1035 1035
1036 1036 def size(self):
1037 1037 return self._filelog.size(self._filerev)
1038 1038
1039 1039 @propertycache
1040 1040 def _copied(self):
1041 1041 """check if file was actually renamed in this changeset revision
1042 1042
1043 1043 If rename logged in file revision, we report copy for changeset only
1044 1044 if file revisions linkrev points back to the changeset in question
1045 1045 or both changeset parents contain different file revisions.
1046 1046 """
1047 1047
1048 1048 renamed = self._filelog.renamed(self._filenode)
1049 1049 if not renamed:
1050 1050 return renamed
1051 1051
1052 1052 if self.rev() == self.linkrev():
1053 1053 return renamed
1054 1054
1055 1055 name = self.path()
1056 1056 fnode = self._filenode
1057 1057 for p in self._changectx.parents():
1058 1058 try:
1059 1059 if fnode == p.filenode(name):
1060 1060 return None
1061 1061 except error.LookupError:
1062 1062 pass
1063 1063 return renamed
1064 1064
1065 1065 def children(self):
1066 1066 # hard for renames
1067 1067 c = self._filelog.children(self._filenode)
1068 1068 return [filectx(self._repo, self._path, fileid=x,
1069 1069 filelog=self._filelog) for x in c]
1070 1070
1071 1071 class committablectx(basectx):
1072 1072 """A committablectx object provides common functionality for a context that
1073 1073 wants the ability to commit, e.g. workingctx or memctx."""
1074 1074 def __init__(self, repo, text="", user=None, date=None, extra=None,
1075 1075 changes=None):
1076 1076 super(committablectx, self).__init__(repo)
1077 1077 self._rev = None
1078 1078 self._node = None
1079 1079 self._text = text
1080 1080 if date:
1081 1081 self._date = dateutil.parsedate(date)
1082 1082 if user:
1083 1083 self._user = user
1084 1084 if changes:
1085 1085 self._status = changes
1086 1086
1087 1087 self._extra = {}
1088 1088 if extra:
1089 1089 self._extra = extra.copy()
1090 1090 if 'branch' not in self._extra:
1091 1091 try:
1092 1092 branch = encoding.fromlocal(self._repo.dirstate.branch())
1093 1093 except UnicodeDecodeError:
1094 1094 raise error.Abort(_('branch name not in UTF-8!'))
1095 1095 self._extra['branch'] = branch
1096 1096 if self._extra['branch'] == '':
1097 1097 self._extra['branch'] = 'default'
1098 1098
1099 1099 def __bytes__(self):
1100 1100 return bytes(self._parents[0]) + "+"
1101 1101
1102 1102 __str__ = encoding.strmethod(__bytes__)
1103 1103
1104 1104 def __nonzero__(self):
1105 1105 return True
1106 1106
1107 1107 __bool__ = __nonzero__
1108 1108
1109 1109 def _buildflagfunc(self):
1110 1110 # Create a fallback function for getting file flags when the
1111 1111 # filesystem doesn't support them
1112 1112
1113 1113 copiesget = self._repo.dirstate.copies().get
1114 1114 parents = self.parents()
1115 1115 if len(parents) < 2:
1116 1116 # when we have one parent, it's easy: copy from parent
1117 1117 man = parents[0].manifest()
1118 1118 def func(f):
1119 1119 f = copiesget(f, f)
1120 1120 return man.flags(f)
1121 1121 else:
1122 1122 # merges are tricky: we try to reconstruct the unstored
1123 1123 # result from the merge (issue1802)
1124 1124 p1, p2 = parents
1125 1125 pa = p1.ancestor(p2)
1126 1126 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
1127 1127
1128 1128 def func(f):
1129 1129 f = copiesget(f, f) # may be wrong for merges with copies
1130 1130 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
1131 1131 if fl1 == fl2:
1132 1132 return fl1
1133 1133 if fl1 == fla:
1134 1134 return fl2
1135 1135 if fl2 == fla:
1136 1136 return fl1
1137 1137 return '' # punt for conflicts
1138 1138
1139 1139 return func
1140 1140
1141 1141 @propertycache
1142 1142 def _flagfunc(self):
1143 1143 return self._repo.dirstate.flagfunc(self._buildflagfunc)
1144 1144
1145 1145 @propertycache
1146 1146 def _status(self):
1147 1147 return self._repo.status()
1148 1148
1149 1149 @propertycache
1150 1150 def _user(self):
1151 1151 return self._repo.ui.username()
1152 1152
1153 1153 @propertycache
1154 1154 def _date(self):
1155 1155 ui = self._repo.ui
1156 1156 date = ui.configdate('devel', 'default-date')
1157 1157 if date is None:
1158 1158 date = dateutil.makedate()
1159 1159 return date
1160 1160
1161 1161 def subrev(self, subpath):
1162 1162 return None
1163 1163
1164 1164 def manifestnode(self):
1165 1165 return None
1166 1166 def user(self):
1167 1167 return self._user or self._repo.ui.username()
1168 1168 def date(self):
1169 1169 return self._date
1170 1170 def description(self):
1171 1171 return self._text
1172 1172 def files(self):
1173 1173 return sorted(self._status.modified + self._status.added +
1174 1174 self._status.removed)
1175 1175
1176 1176 def modified(self):
1177 1177 return self._status.modified
1178 1178 def added(self):
1179 1179 return self._status.added
1180 1180 def removed(self):
1181 1181 return self._status.removed
1182 1182 def deleted(self):
1183 1183 return self._status.deleted
1184 1184 def branch(self):
1185 1185 return encoding.tolocal(self._extra['branch'])
1186 1186 def closesbranch(self):
1187 1187 return 'close' in self._extra
1188 1188 def extra(self):
1189 1189 return self._extra
1190 1190
1191 1191 def isinmemory(self):
1192 1192 return False
1193 1193
1194 1194 def tags(self):
1195 1195 return []
1196 1196
1197 1197 def bookmarks(self):
1198 1198 b = []
1199 1199 for p in self.parents():
1200 1200 b.extend(p.bookmarks())
1201 1201 return b
1202 1202
1203 1203 def phase(self):
1204 1204 phase = phases.draft # default phase to draft
1205 1205 for p in self.parents():
1206 1206 phase = max(phase, p.phase())
1207 1207 return phase
1208 1208
1209 1209 def hidden(self):
1210 1210 return False
1211 1211
1212 1212 def children(self):
1213 1213 return []
1214 1214
1215 1215 def flags(self, path):
1216 1216 if r'_manifest' in self.__dict__:
1217 1217 try:
1218 1218 return self._manifest.flags(path)
1219 1219 except KeyError:
1220 1220 return ''
1221 1221
1222 1222 try:
1223 1223 return self._flagfunc(path)
1224 1224 except OSError:
1225 1225 return ''
1226 1226
1227 1227 def ancestor(self, c2):
1228 1228 """return the "best" ancestor context of self and c2"""
1229 1229 return self._parents[0].ancestor(c2) # punt on two parents for now
1230 1230
1231 1231 def walk(self, match):
1232 1232 '''Generates matching file names.'''
1233 1233 return sorted(self._repo.dirstate.walk(match,
1234 1234 subrepos=sorted(self.substate),
1235 1235 unknown=True, ignored=False))
1236 1236
1237 1237 def matches(self, match):
1238 1238 ds = self._repo.dirstate
1239 1239 return sorted(f for f in ds.matches(match) if ds[f] != 'r')
1240 1240
1241 1241 def ancestors(self):
1242 1242 for p in self._parents:
1243 1243 yield p
1244 1244 for a in self._repo.changelog.ancestors(
1245 1245 [p.rev() for p in self._parents]):
1246 1246 yield changectx(self._repo, a)
1247 1247
1248 1248 def markcommitted(self, node):
1249 1249 """Perform post-commit cleanup necessary after committing this ctx
1250 1250
1251 1251 Specifically, this updates backing stores this working context
1252 1252 wraps to reflect the fact that the changes reflected by this
1253 1253 workingctx have been committed. For example, it marks
1254 1254 modified and added files as normal in the dirstate.
1255 1255
1256 1256 """
1257 1257
1258 1258 with self._repo.dirstate.parentchange():
1259 1259 for f in self.modified() + self.added():
1260 1260 self._repo.dirstate.normal(f)
1261 1261 for f in self.removed():
1262 1262 self._repo.dirstate.drop(f)
1263 1263 self._repo.dirstate.setparents(node)
1264 1264
1265 1265 # write changes out explicitly, because nesting wlock at
1266 1266 # runtime may prevent 'wlock.release()' in 'repo.commit()'
1267 1267 # from immediately doing so for subsequent changing files
1268 1268 self._repo.dirstate.write(self._repo.currenttransaction())
1269 1269
1270 1270 def dirty(self, missing=False, merge=True, branch=True):
1271 1271 return False
1272 1272
1273 1273 class workingctx(committablectx):
1274 1274 """A workingctx object makes access to data related to
1275 1275 the current working directory convenient.
1276 1276 date - any valid date string or (unixtime, offset), or None.
1277 1277 user - username string, or None.
1278 1278 extra - a dictionary of extra values, or None.
1279 1279 changes - a list of file lists as returned by localrepo.status()
1280 1280 or None to use the repository status.
1281 1281 """
1282 1282 def __init__(self, repo, text="", user=None, date=None, extra=None,
1283 1283 changes=None):
1284 1284 super(workingctx, self).__init__(repo, text, user, date, extra, changes)
1285 1285
1286 1286 def __iter__(self):
1287 1287 d = self._repo.dirstate
1288 1288 for f in d:
1289 1289 if d[f] != 'r':
1290 1290 yield f
1291 1291
1292 1292 def __contains__(self, key):
1293 1293 return self._repo.dirstate[key] not in "?r"
1294 1294
1295 1295 def hex(self):
1296 1296 return hex(wdirid)
1297 1297
1298 1298 @propertycache
1299 1299 def _parents(self):
1300 1300 p = self._repo.dirstate.parents()
1301 1301 if p[1] == nullid:
1302 1302 p = p[:-1]
1303 1303 return [changectx(self._repo, x) for x in p]
1304 1304
1305 1305 def _fileinfo(self, path):
1306 1306 # populate __dict__['_manifest'] as workingctx has no _manifestdelta
1307 1307 self._manifest
1308 1308 return super(workingctx, self)._fileinfo(path)
1309 1309
1310 1310 def filectx(self, path, filelog=None):
1311 1311 """get a file context from the working directory"""
1312 1312 return workingfilectx(self._repo, path, workingctx=self,
1313 1313 filelog=filelog)
1314 1314
1315 1315 def dirty(self, missing=False, merge=True, branch=True):
1316 1316 "check whether a working directory is modified"
1317 1317 # check subrepos first
1318 1318 for s in sorted(self.substate):
1319 1319 if self.sub(s).dirty(missing=missing):
1320 1320 return True
1321 1321 # check current working dir
1322 1322 return ((merge and self.p2()) or
1323 1323 (branch and self.branch() != self.p1().branch()) or
1324 1324 self.modified() or self.added() or self.removed() or
1325 1325 (missing and self.deleted()))
1326 1326
1327 1327 def add(self, list, prefix=""):
1328 1328 with self._repo.wlock():
1329 1329 ui, ds = self._repo.ui, self._repo.dirstate
1330 1330 uipath = lambda f: ds.pathto(pathutil.join(prefix, f))
1331 1331 rejected = []
1332 1332 lstat = self._repo.wvfs.lstat
1333 1333 for f in list:
1334 1334 # ds.pathto() returns an absolute file when this is invoked from
1335 1335 # the keyword extension. That gets flagged as non-portable on
1336 1336 # Windows, since it contains the drive letter and colon.
1337 1337 scmutil.checkportable(ui, os.path.join(prefix, f))
1338 1338 try:
1339 1339 st = lstat(f)
1340 1340 except OSError:
1341 1341 ui.warn(_("%s does not exist!\n") % uipath(f))
1342 1342 rejected.append(f)
1343 1343 continue
1344 1344 limit = ui.configbytes('ui', 'large-file-limit')
1345 1345 if limit != 0 and st.st_size > limit:
1346 1346 ui.warn(_("%s: up to %d MB of RAM may be required "
1347 1347 "to manage this file\n"
1348 1348 "(use 'hg revert %s' to cancel the "
1349 1349 "pending addition)\n")
1350 1350 % (f, 3 * st.st_size // 1000000, uipath(f)))
1351 1351 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1352 1352 ui.warn(_("%s not added: only files and symlinks "
1353 1353 "supported currently\n") % uipath(f))
1354 1354 rejected.append(f)
1355 1355 elif ds[f] in 'amn':
1356 1356 ui.warn(_("%s already tracked!\n") % uipath(f))
1357 1357 elif ds[f] == 'r':
1358 1358 ds.normallookup(f)
1359 1359 else:
1360 1360 ds.add(f)
1361 1361 return rejected
1362 1362
1363 1363 def forget(self, files, prefix=""):
1364 1364 with self._repo.wlock():
1365 1365 ds = self._repo.dirstate
1366 1366 uipath = lambda f: ds.pathto(pathutil.join(prefix, f))
1367 1367 rejected = []
1368 1368 for f in files:
1369 1369 if f not in self._repo.dirstate:
1370 1370 self._repo.ui.warn(_("%s not tracked!\n") % uipath(f))
1371 1371 rejected.append(f)
1372 1372 elif self._repo.dirstate[f] != 'a':
1373 1373 self._repo.dirstate.remove(f)
1374 1374 else:
1375 1375 self._repo.dirstate.drop(f)
1376 1376 return rejected
1377 1377
1378 1378 def undelete(self, list):
1379 1379 pctxs = self.parents()
1380 1380 with self._repo.wlock():
1381 1381 ds = self._repo.dirstate
1382 1382 for f in list:
1383 1383 if self._repo.dirstate[f] != 'r':
1384 1384 self._repo.ui.warn(_("%s not removed!\n") % ds.pathto(f))
1385 1385 else:
1386 1386 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
1387 1387 t = fctx.data()
1388 1388 self._repo.wwrite(f, t, fctx.flags())
1389 1389 self._repo.dirstate.normal(f)
1390 1390
1391 1391 def copy(self, source, dest):
1392 1392 try:
1393 1393 st = self._repo.wvfs.lstat(dest)
1394 1394 except OSError as err:
1395 1395 if err.errno != errno.ENOENT:
1396 1396 raise
1397 1397 self._repo.ui.warn(_("%s does not exist!\n")
1398 1398 % self._repo.dirstate.pathto(dest))
1399 1399 return
1400 1400 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1401 1401 self._repo.ui.warn(_("copy failed: %s is not a file or a "
1402 1402 "symbolic link\n")
1403 1403 % self._repo.dirstate.pathto(dest))
1404 1404 else:
1405 1405 with self._repo.wlock():
1406 1406 if self._repo.dirstate[dest] in '?':
1407 1407 self._repo.dirstate.add(dest)
1408 1408 elif self._repo.dirstate[dest] in 'r':
1409 1409 self._repo.dirstate.normallookup(dest)
1410 1410 self._repo.dirstate.copy(source, dest)
1411 1411
1412 1412 def match(self, pats=None, include=None, exclude=None, default='glob',
1413 1413 listsubrepos=False, badfn=None):
1414 1414 r = self._repo
1415 1415
1416 1416 # Only a case insensitive filesystem needs magic to translate user input
1417 1417 # to actual case in the filesystem.
1418 1418 icasefs = not util.fscasesensitive(r.root)
1419 1419 return matchmod.match(r.root, r.getcwd(), pats, include, exclude,
1420 1420 default, auditor=r.auditor, ctx=self,
1421 1421 listsubrepos=listsubrepos, badfn=badfn,
1422 1422 icasefs=icasefs)
1423 1423
1424 1424 def _filtersuspectsymlink(self, files):
1425 1425 if not files or self._repo.dirstate._checklink:
1426 1426 return files
1427 1427
1428 1428 # Symlink placeholders may get non-symlink-like contents
1429 1429 # via user error or dereferencing by NFS or Samba servers,
1430 1430 # so we filter out any placeholders that don't look like a
1431 1431 # symlink
1432 1432 sane = []
1433 1433 for f in files:
1434 1434 if self.flags(f) == 'l':
1435 1435 d = self[f].data()
1436 1436 if (d == '' or len(d) >= 1024 or '\n' in d
1437 1437 or stringutil.binary(d)):
1438 1438 self._repo.ui.debug('ignoring suspect symlink placeholder'
1439 1439 ' "%s"\n' % f)
1440 1440 continue
1441 1441 sane.append(f)
1442 1442 return sane
1443 1443
1444 1444 def _checklookup(self, files):
1445 1445 # check for any possibly clean files
1446 1446 if not files:
1447 1447 return [], [], []
1448 1448
1449 1449 modified = []
1450 1450 deleted = []
1451 1451 fixup = []
1452 1452 pctx = self._parents[0]
1453 1453 # do a full compare of any files that might have changed
1454 1454 for f in sorted(files):
1455 1455 try:
1456 1456 # This will return True for a file that got replaced by a
1457 1457 # directory in the interim, but fixing that is pretty hard.
1458 1458 if (f not in pctx or self.flags(f) != pctx.flags(f)
1459 1459 or pctx[f].cmp(self[f])):
1460 1460 modified.append(f)
1461 1461 else:
1462 1462 fixup.append(f)
1463 1463 except (IOError, OSError):
1464 1464 # A file become inaccessible in between? Mark it as deleted,
1465 1465 # matching dirstate behavior (issue5584).
1466 1466 # The dirstate has more complex behavior around whether a
1467 1467 # missing file matches a directory, etc, but we don't need to
1468 1468 # bother with that: if f has made it to this point, we're sure
1469 1469 # it's in the dirstate.
1470 1470 deleted.append(f)
1471 1471
1472 1472 return modified, deleted, fixup
1473 1473
1474 1474 def _poststatusfixup(self, status, fixup):
1475 1475 """update dirstate for files that are actually clean"""
1476 1476 poststatus = self._repo.postdsstatus()
1477 1477 if fixup or poststatus:
1478 1478 try:
1479 1479 oldid = self._repo.dirstate.identity()
1480 1480
1481 1481 # updating the dirstate is optional
1482 1482 # so we don't wait on the lock
1483 1483 # wlock can invalidate the dirstate, so cache normal _after_
1484 1484 # taking the lock
1485 1485 with self._repo.wlock(False):
1486 1486 if self._repo.dirstate.identity() == oldid:
1487 1487 if fixup:
1488 1488 normal = self._repo.dirstate.normal
1489 1489 for f in fixup:
1490 1490 normal(f)
1491 1491 # write changes out explicitly, because nesting
1492 1492 # wlock at runtime may prevent 'wlock.release()'
1493 1493 # after this block from doing so for subsequent
1494 1494 # changing files
1495 1495 tr = self._repo.currenttransaction()
1496 1496 self._repo.dirstate.write(tr)
1497 1497
1498 1498 if poststatus:
1499 1499 for ps in poststatus:
1500 1500 ps(self, status)
1501 1501 else:
1502 1502 # in this case, writing changes out breaks
1503 1503 # consistency, because .hg/dirstate was
1504 1504 # already changed simultaneously after last
1505 1505 # caching (see also issue5584 for detail)
1506 1506 self._repo.ui.debug('skip updating dirstate: '
1507 1507 'identity mismatch\n')
1508 1508 except error.LockError:
1509 1509 pass
1510 1510 finally:
1511 1511 # Even if the wlock couldn't be grabbed, clear out the list.
1512 1512 self._repo.clearpostdsstatus()
1513 1513
1514 1514 def _dirstatestatus(self, match, ignored=False, clean=False, unknown=False):
1515 1515 '''Gets the status from the dirstate -- internal use only.'''
1516 1516 subrepos = []
1517 1517 if '.hgsub' in self:
1518 1518 subrepos = sorted(self.substate)
1519 1519 cmp, s = self._repo.dirstate.status(match, subrepos, ignored=ignored,
1520 1520 clean=clean, unknown=unknown)
1521 1521
1522 1522 # check for any possibly clean files
1523 1523 fixup = []
1524 1524 if cmp:
1525 1525 modified2, deleted2, fixup = self._checklookup(cmp)
1526 1526 s.modified.extend(modified2)
1527 1527 s.deleted.extend(deleted2)
1528 1528
1529 1529 if fixup and clean:
1530 1530 s.clean.extend(fixup)
1531 1531
1532 1532 self._poststatusfixup(s, fixup)
1533 1533
1534 1534 if match.always():
1535 1535 # cache for performance
1536 1536 if s.unknown or s.ignored or s.clean:
1537 1537 # "_status" is cached with list*=False in the normal route
1538 1538 self._status = scmutil.status(s.modified, s.added, s.removed,
1539 1539 s.deleted, [], [], [])
1540 1540 else:
1541 1541 self._status = s
1542 1542
1543 1543 return s
1544 1544
1545 1545 @propertycache
1546 1546 def _manifest(self):
1547 1547 """generate a manifest corresponding to the values in self._status
1548 1548
1549 1549 This reuse the file nodeid from parent, but we use special node
1550 1550 identifiers for added and modified files. This is used by manifests
1551 1551 merge to see that files are different and by update logic to avoid
1552 1552 deleting newly added files.
1553 1553 """
1554 1554 return self._buildstatusmanifest(self._status)
1555 1555
1556 1556 def _buildstatusmanifest(self, status):
1557 1557 """Builds a manifest that includes the given status results."""
1558 1558 parents = self.parents()
1559 1559
1560 1560 man = parents[0].manifest().copy()
1561 1561
1562 1562 ff = self._flagfunc
1563 1563 for i, l in ((addednodeid, status.added),
1564 1564 (modifiednodeid, status.modified)):
1565 1565 for f in l:
1566 1566 man[f] = i
1567 1567 try:
1568 1568 man.setflag(f, ff(f))
1569 1569 except OSError:
1570 1570 pass
1571 1571
1572 1572 for f in status.deleted + status.removed:
1573 1573 if f in man:
1574 1574 del man[f]
1575 1575
1576 1576 return man
1577 1577
1578 1578 def _buildstatus(self, other, s, match, listignored, listclean,
1579 1579 listunknown):
1580 1580 """build a status with respect to another context
1581 1581
1582 1582 This includes logic for maintaining the fast path of status when
1583 1583 comparing the working directory against its parent, which is to skip
1584 1584 building a new manifest if self (working directory) is not comparing
1585 1585 against its parent (repo['.']).
1586 1586 """
1587 1587 s = self._dirstatestatus(match, listignored, listclean, listunknown)
1588 1588 # Filter out symlinks that, in the case of FAT32 and NTFS filesystems,
1589 1589 # might have accidentally ended up with the entire contents of the file
1590 1590 # they are supposed to be linking to.
1591 1591 s.modified[:] = self._filtersuspectsymlink(s.modified)
1592 1592 if other != self._repo['.']:
1593 1593 s = super(workingctx, self)._buildstatus(other, s, match,
1594 1594 listignored, listclean,
1595 1595 listunknown)
1596 1596 return s
1597 1597
1598 1598 def _matchstatus(self, other, match):
1599 1599 """override the match method with a filter for directory patterns
1600 1600
1601 1601 We use inheritance to customize the match.bad method only in cases of
1602 1602 workingctx since it belongs only to the working directory when
1603 1603 comparing against the parent changeset.
1604 1604
1605 1605 If we aren't comparing against the working directory's parent, then we
1606 1606 just use the default match object sent to us.
1607 1607 """
1608 1608 if other != self._repo['.']:
1609 1609 def bad(f, msg):
1610 1610 # 'f' may be a directory pattern from 'match.files()',
1611 1611 # so 'f not in ctx1' is not enough
1612 1612 if f not in other and not other.hasdir(f):
1613 1613 self._repo.ui.warn('%s: %s\n' %
1614 1614 (self._repo.dirstate.pathto(f), msg))
1615 1615 match.bad = bad
1616 1616 return match
1617 1617
1618 1618 def markcommitted(self, node):
1619 1619 super(workingctx, self).markcommitted(node)
1620 1620
1621 1621 sparse.aftercommit(self._repo, node)
1622 1622
1623 1623 class committablefilectx(basefilectx):
1624 1624 """A committablefilectx provides common functionality for a file context
1625 1625 that wants the ability to commit, e.g. workingfilectx or memfilectx."""
1626 1626 def __init__(self, repo, path, filelog=None, ctx=None):
1627 1627 self._repo = repo
1628 1628 self._path = path
1629 1629 self._changeid = None
1630 1630 self._filerev = self._filenode = None
1631 1631
1632 1632 if filelog is not None:
1633 1633 self._filelog = filelog
1634 1634 if ctx:
1635 1635 self._changectx = ctx
1636 1636
1637 1637 def __nonzero__(self):
1638 1638 return True
1639 1639
1640 1640 __bool__ = __nonzero__
1641 1641
1642 1642 def linkrev(self):
1643 1643 # linked to self._changectx no matter if file is modified or not
1644 1644 return self.rev()
1645 1645
1646 1646 def parents(self):
1647 1647 '''return parent filectxs, following copies if necessary'''
1648 1648 def filenode(ctx, path):
1649 1649 return ctx._manifest.get(path, nullid)
1650 1650
1651 1651 path = self._path
1652 1652 fl = self._filelog
1653 1653 pcl = self._changectx._parents
1654 1654 renamed = self.renamed()
1655 1655
1656 1656 if renamed:
1657 1657 pl = [renamed + (None,)]
1658 1658 else:
1659 1659 pl = [(path, filenode(pcl[0], path), fl)]
1660 1660
1661 1661 for pc in pcl[1:]:
1662 1662 pl.append((path, filenode(pc, path), fl))
1663 1663
1664 1664 return [self._parentfilectx(p, fileid=n, filelog=l)
1665 1665 for p, n, l in pl if n != nullid]
1666 1666
1667 1667 def children(self):
1668 1668 return []
1669 1669
1670 1670 class workingfilectx(committablefilectx):
1671 1671 """A workingfilectx object makes access to data related to a particular
1672 1672 file in the working directory convenient."""
1673 1673 def __init__(self, repo, path, filelog=None, workingctx=None):
1674 1674 super(workingfilectx, self).__init__(repo, path, filelog, workingctx)
1675 1675
1676 1676 @propertycache
1677 1677 def _changectx(self):
1678 1678 return workingctx(self._repo)
1679 1679
1680 1680 def data(self):
1681 1681 return self._repo.wread(self._path)
1682 1682 def renamed(self):
1683 1683 rp = self._repo.dirstate.copied(self._path)
1684 1684 if not rp:
1685 1685 return None
1686 1686 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
1687 1687
1688 1688 def size(self):
1689 1689 return self._repo.wvfs.lstat(self._path).st_size
1690 1690 def date(self):
1691 1691 t, tz = self._changectx.date()
1692 1692 try:
1693 1693 return (self._repo.wvfs.lstat(self._path)[stat.ST_MTIME], tz)
1694 1694 except OSError as err:
1695 1695 if err.errno != errno.ENOENT:
1696 1696 raise
1697 1697 return (t, tz)
1698 1698
1699 1699 def exists(self):
1700 1700 return self._repo.wvfs.exists(self._path)
1701 1701
1702 1702 def lexists(self):
1703 1703 return self._repo.wvfs.lexists(self._path)
1704 1704
1705 1705 def audit(self):
1706 1706 return self._repo.wvfs.audit(self._path)
1707 1707
1708 1708 def cmp(self, fctx):
1709 1709 """compare with other file context
1710 1710
1711 1711 returns True if different than fctx.
1712 1712 """
1713 1713 # fctx should be a filectx (not a workingfilectx)
1714 1714 # invert comparison to reuse the same code path
1715 1715 return fctx.cmp(self)
1716 1716
1717 1717 def remove(self, ignoremissing=False):
1718 1718 """wraps unlink for a repo's working directory"""
1719 1719 rmdir = self._repo.ui.configbool('experimental', 'removeemptydirs')
1720 1720 self._repo.wvfs.unlinkpath(self._path, ignoremissing=ignoremissing,
1721 1721 rmdir=rmdir)
1722 1722
1723 1723 def write(self, data, flags, backgroundclose=False, **kwargs):
1724 1724 """wraps repo.wwrite"""
1725 1725 self._repo.wwrite(self._path, data, flags,
1726 1726 backgroundclose=backgroundclose,
1727 1727 **kwargs)
1728 1728
1729 1729 def markcopied(self, src):
1730 1730 """marks this file a copy of `src`"""
1731 1731 if self._repo.dirstate[self._path] in "nma":
1732 1732 self._repo.dirstate.copy(src, self._path)
1733 1733
1734 1734 def clearunknown(self):
1735 1735 """Removes conflicting items in the working directory so that
1736 1736 ``write()`` can be called successfully.
1737 1737 """
1738 1738 wvfs = self._repo.wvfs
1739 1739 f = self._path
1740 1740 wvfs.audit(f)
1741 1741 if wvfs.isdir(f) and not wvfs.islink(f):
1742 1742 wvfs.rmtree(f, forcibly=True)
1743 1743 if self._repo.ui.configbool('experimental', 'merge.checkpathconflicts'):
1744 1744 for p in reversed(list(util.finddirs(f))):
1745 1745 if wvfs.isfileorlink(p):
1746 1746 wvfs.unlink(p)
1747 1747 break
1748 1748
1749 1749 def setflags(self, l, x):
1750 1750 self._repo.wvfs.setflags(self._path, l, x)
1751 1751
1752 1752 class overlayworkingctx(committablectx):
1753 1753 """Wraps another mutable context with a write-back cache that can be
1754 1754 converted into a commit context.
1755 1755
1756 1756 self._cache[path] maps to a dict with keys: {
1757 1757 'exists': bool?
1758 1758 'date': date?
1759 1759 'data': str?
1760 1760 'flags': str?
1761 1761 'copied': str? (path or None)
1762 1762 }
1763 1763 If `exists` is True, `flags` must be non-None and 'date' is non-None. If it
1764 1764 is `False`, the file was deleted.
1765 1765 """
1766 1766
1767 1767 def __init__(self, repo):
1768 1768 super(overlayworkingctx, self).__init__(repo)
1769 1769 self.clean()
1770 1770
1771 1771 def setbase(self, wrappedctx):
1772 1772 self._wrappedctx = wrappedctx
1773 1773 self._parents = [wrappedctx]
1774 1774 # Drop old manifest cache as it is now out of date.
1775 1775 # This is necessary when, e.g., rebasing several nodes with one
1776 1776 # ``overlayworkingctx`` (e.g. with --collapse).
1777 1777 util.clearcachedproperty(self, '_manifest')
1778 1778
1779 1779 def data(self, path):
1780 1780 if self.isdirty(path):
1781 1781 if self._cache[path]['exists']:
1782 1782 if self._cache[path]['data']:
1783 1783 return self._cache[path]['data']
1784 1784 else:
1785 1785 # Must fallback here, too, because we only set flags.
1786 1786 return self._wrappedctx[path].data()
1787 1787 else:
1788 1788 raise error.ProgrammingError("No such file or directory: %s" %
1789 1789 path)
1790 1790 else:
1791 1791 return self._wrappedctx[path].data()
1792 1792
1793 1793 @propertycache
1794 1794 def _manifest(self):
1795 1795 parents = self.parents()
1796 1796 man = parents[0].manifest().copy()
1797 1797
1798 1798 flag = self._flagfunc
1799 1799 for path in self.added():
1800 1800 man[path] = addednodeid
1801 1801 man.setflag(path, flag(path))
1802 1802 for path in self.modified():
1803 1803 man[path] = modifiednodeid
1804 1804 man.setflag(path, flag(path))
1805 1805 for path in self.removed():
1806 1806 del man[path]
1807 1807 return man
1808 1808
1809 1809 @propertycache
1810 1810 def _flagfunc(self):
1811 1811 def f(path):
1812 1812 return self._cache[path]['flags']
1813 1813 return f
1814 1814
1815 1815 def files(self):
1816 1816 return sorted(self.added() + self.modified() + self.removed())
1817 1817
1818 1818 def modified(self):
1819 1819 return [f for f in self._cache.keys() if self._cache[f]['exists'] and
1820 1820 self._existsinparent(f)]
1821 1821
1822 1822 def added(self):
1823 1823 return [f for f in self._cache.keys() if self._cache[f]['exists'] and
1824 1824 not self._existsinparent(f)]
1825 1825
1826 1826 def removed(self):
1827 1827 return [f for f in self._cache.keys() if
1828 1828 not self._cache[f]['exists'] and self._existsinparent(f)]
1829 1829
1830 1830 def isinmemory(self):
1831 1831 return True
1832 1832
1833 1833 def filedate(self, path):
1834 1834 if self.isdirty(path):
1835 1835 return self._cache[path]['date']
1836 1836 else:
1837 1837 return self._wrappedctx[path].date()
1838 1838
1839 1839 def markcopied(self, path, origin):
1840 1840 if self.isdirty(path):
1841 1841 self._cache[path]['copied'] = origin
1842 1842 else:
1843 1843 raise error.ProgrammingError('markcopied() called on clean context')
1844 1844
1845 1845 def copydata(self, path):
1846 1846 if self.isdirty(path):
1847 1847 return self._cache[path]['copied']
1848 1848 else:
1849 1849 raise error.ProgrammingError('copydata() called on clean context')
1850 1850
1851 1851 def flags(self, path):
1852 1852 if self.isdirty(path):
1853 1853 if self._cache[path]['exists']:
1854 1854 return self._cache[path]['flags']
1855 1855 else:
1856 1856 raise error.ProgrammingError("No such file or directory: %s" %
1857 1857 self._path)
1858 1858 else:
1859 1859 return self._wrappedctx[path].flags()
1860 1860
1861 1861 def _existsinparent(self, path):
1862 1862 try:
1863 1863 # ``commitctx` raises a ``ManifestLookupError`` if a path does not
1864 1864 # exist, unlike ``workingctx``, which returns a ``workingfilectx``
1865 1865 # with an ``exists()`` function.
1866 1866 self._wrappedctx[path]
1867 1867 return True
1868 1868 except error.ManifestLookupError:
1869 1869 return False
1870 1870
1871 1871 def _auditconflicts(self, path):
1872 1872 """Replicates conflict checks done by wvfs.write().
1873 1873
1874 1874 Since we never write to the filesystem and never call `applyupdates` in
1875 1875 IMM, we'll never check that a path is actually writable -- e.g., because
1876 1876 it adds `a/foo`, but `a` is actually a file in the other commit.
1877 1877 """
1878 1878 def fail(path, component):
1879 1879 # p1() is the base and we're receiving "writes" for p2()'s
1880 1880 # files.
1881 1881 if 'l' in self.p1()[component].flags():
1882 1882 raise error.Abort("error: %s conflicts with symlink %s "
1883 1883 "in %s." % (path, component,
1884 1884 self.p1().rev()))
1885 1885 else:
1886 1886 raise error.Abort("error: '%s' conflicts with file '%s' in "
1887 1887 "%s." % (path, component,
1888 1888 self.p1().rev()))
1889 1889
1890 1890 # Test that each new directory to be created to write this path from p2
1891 1891 # is not a file in p1.
1892 1892 components = path.split('/')
1893 1893 for i in xrange(len(components)):
1894 1894 component = "/".join(components[0:i])
1895 1895 if component in self.p1():
1896 1896 fail(path, component)
1897 1897
1898 1898 # Test the other direction -- that this path from p2 isn't a directory
1899 1899 # in p1 (test that p1 doesn't any paths matching `path/*`).
1900 1900 match = matchmod.match('/', '', [path + '/'], default=b'relpath')
1901 1901 matches = self.p1().manifest().matches(match)
1902 1902 if len(matches) > 0:
1903 1903 if len(matches) == 1 and matches.keys()[0] == path:
1904 1904 return
1905 1905 raise error.Abort("error: file '%s' cannot be written because "
1906 1906 " '%s/' is a folder in %s (containing %d "
1907 1907 "entries: %s)"
1908 1908 % (path, path, self.p1(), len(matches),
1909 1909 ', '.join(matches.keys())))
1910 1910
1911 1911 def write(self, path, data, flags='', **kwargs):
1912 1912 if data is None:
1913 1913 raise error.ProgrammingError("data must be non-None")
1914 1914 self._auditconflicts(path)
1915 1915 self._markdirty(path, exists=True, data=data, date=dateutil.makedate(),
1916 1916 flags=flags)
1917 1917
1918 1918 def setflags(self, path, l, x):
1919 1919 self._markdirty(path, exists=True, date=dateutil.makedate(),
1920 1920 flags=(l and 'l' or '') + (x and 'x' or ''))
1921 1921
1922 1922 def remove(self, path):
1923 1923 self._markdirty(path, exists=False)
1924 1924
1925 1925 def exists(self, path):
1926 1926 """exists behaves like `lexists`, but needs to follow symlinks and
1927 1927 return False if they are broken.
1928 1928 """
1929 1929 if self.isdirty(path):
1930 1930 # If this path exists and is a symlink, "follow" it by calling
1931 1931 # exists on the destination path.
1932 1932 if (self._cache[path]['exists'] and
1933 1933 'l' in self._cache[path]['flags']):
1934 1934 return self.exists(self._cache[path]['data'].strip())
1935 1935 else:
1936 1936 return self._cache[path]['exists']
1937 1937
1938 1938 return self._existsinparent(path)
1939 1939
1940 1940 def lexists(self, path):
1941 1941 """lexists returns True if the path exists"""
1942 1942 if self.isdirty(path):
1943 1943 return self._cache[path]['exists']
1944 1944
1945 1945 return self._existsinparent(path)
1946 1946
1947 1947 def size(self, path):
1948 1948 if self.isdirty(path):
1949 1949 if self._cache[path]['exists']:
1950 1950 return len(self._cache[path]['data'])
1951 1951 else:
1952 1952 raise error.ProgrammingError("No such file or directory: %s" %
1953 1953 self._path)
1954 1954 return self._wrappedctx[path].size()
1955 1955
1956 1956 def tomemctx(self, text, branch=None, extra=None, date=None, parents=None,
1957 1957 user=None, editor=None):
1958 1958 """Converts this ``overlayworkingctx`` into a ``memctx`` ready to be
1959 1959 committed.
1960 1960
1961 1961 ``text`` is the commit message.
1962 1962 ``parents`` (optional) are rev numbers.
1963 1963 """
1964 1964 # Default parents to the wrapped contexts' if not passed.
1965 1965 if parents is None:
1966 1966 parents = self._wrappedctx.parents()
1967 1967 if len(parents) == 1:
1968 1968 parents = (parents[0], None)
1969 1969
1970 1970 # ``parents`` is passed as rev numbers; convert to ``commitctxs``.
1971 1971 if parents[1] is None:
1972 1972 parents = (self._repo[parents[0]], None)
1973 1973 else:
1974 1974 parents = (self._repo[parents[0]], self._repo[parents[1]])
1975 1975
1976 1976 files = self._cache.keys()
1977 1977 def getfile(repo, memctx, path):
1978 1978 if self._cache[path]['exists']:
1979 1979 return memfilectx(repo, memctx, path,
1980 1980 self._cache[path]['data'],
1981 1981 'l' in self._cache[path]['flags'],
1982 1982 'x' in self._cache[path]['flags'],
1983 1983 self._cache[path]['copied'])
1984 1984 else:
1985 1985 # Returning None, but including the path in `files`, is
1986 1986 # necessary for memctx to register a deletion.
1987 1987 return None
1988 1988 return memctx(self._repo, parents, text, files, getfile, date=date,
1989 1989 extra=extra, user=user, branch=branch, editor=editor)
1990 1990
1991 1991 def isdirty(self, path):
1992 1992 return path in self._cache
1993 1993
1994 1994 def isempty(self):
1995 1995 # We need to discard any keys that are actually clean before the empty
1996 1996 # commit check.
1997 1997 self._compact()
1998 1998 return len(self._cache) == 0
1999 1999
2000 2000 def clean(self):
2001 2001 self._cache = {}
2002 2002
2003 2003 def _compact(self):
2004 2004 """Removes keys from the cache that are actually clean, by comparing
2005 2005 them with the underlying context.
2006 2006
2007 2007 This can occur during the merge process, e.g. by passing --tool :local
2008 2008 to resolve a conflict.
2009 2009 """
2010 2010 keys = []
2011 2011 for path in self._cache.keys():
2012 2012 cache = self._cache[path]
2013 2013 try:
2014 2014 underlying = self._wrappedctx[path]
2015 2015 if (underlying.data() == cache['data'] and
2016 2016 underlying.flags() == cache['flags']):
2017 2017 keys.append(path)
2018 2018 except error.ManifestLookupError:
2019 2019 # Path not in the underlying manifest (created).
2020 2020 continue
2021 2021
2022 2022 for path in keys:
2023 2023 del self._cache[path]
2024 2024 return keys
2025 2025
2026 2026 def _markdirty(self, path, exists, data=None, date=None, flags=''):
2027 2027 self._cache[path] = {
2028 2028 'exists': exists,
2029 2029 'data': data,
2030 2030 'date': date,
2031 2031 'flags': flags,
2032 2032 'copied': None,
2033 2033 }
2034 2034
2035 2035 def filectx(self, path, filelog=None):
2036 2036 return overlayworkingfilectx(self._repo, path, parent=self,
2037 2037 filelog=filelog)
2038 2038
2039 2039 class overlayworkingfilectx(committablefilectx):
2040 2040 """Wrap a ``workingfilectx`` but intercepts all writes into an in-memory
2041 2041 cache, which can be flushed through later by calling ``flush()``."""
2042 2042
2043 2043 def __init__(self, repo, path, filelog=None, parent=None):
2044 2044 super(overlayworkingfilectx, self).__init__(repo, path, filelog,
2045 2045 parent)
2046 2046 self._repo = repo
2047 2047 self._parent = parent
2048 2048 self._path = path
2049 2049
2050 2050 def cmp(self, fctx):
2051 2051 return self.data() != fctx.data()
2052 2052
2053 2053 def changectx(self):
2054 2054 return self._parent
2055 2055
2056 2056 def data(self):
2057 2057 return self._parent.data(self._path)
2058 2058
2059 2059 def date(self):
2060 2060 return self._parent.filedate(self._path)
2061 2061
2062 2062 def exists(self):
2063 2063 return self.lexists()
2064 2064
2065 2065 def lexists(self):
2066 2066 return self._parent.exists(self._path)
2067 2067
2068 2068 def renamed(self):
2069 2069 path = self._parent.copydata(self._path)
2070 2070 if not path:
2071 2071 return None
2072 2072 return path, self._changectx._parents[0]._manifest.get(path, nullid)
2073 2073
2074 2074 def size(self):
2075 2075 return self._parent.size(self._path)
2076 2076
2077 2077 def markcopied(self, origin):
2078 2078 self._parent.markcopied(self._path, origin)
2079 2079
2080 2080 def audit(self):
2081 2081 pass
2082 2082
2083 2083 def flags(self):
2084 2084 return self._parent.flags(self._path)
2085 2085
2086 2086 def setflags(self, islink, isexec):
2087 2087 return self._parent.setflags(self._path, islink, isexec)
2088 2088
2089 2089 def write(self, data, flags, backgroundclose=False, **kwargs):
2090 2090 return self._parent.write(self._path, data, flags, **kwargs)
2091 2091
2092 2092 def remove(self, ignoremissing=False):
2093 2093 return self._parent.remove(self._path)
2094 2094
2095 2095 def clearunknown(self):
2096 2096 pass
2097 2097
2098 2098 class workingcommitctx(workingctx):
2099 2099 """A workingcommitctx object makes access to data related to
2100 2100 the revision being committed convenient.
2101 2101
2102 2102 This hides changes in the working directory, if they aren't
2103 2103 committed in this context.
2104 2104 """
2105 2105 def __init__(self, repo, changes,
2106 2106 text="", user=None, date=None, extra=None):
2107 2107 super(workingctx, self).__init__(repo, text, user, date, extra,
2108 2108 changes)
2109 2109
2110 2110 def _dirstatestatus(self, match, ignored=False, clean=False, unknown=False):
2111 2111 """Return matched files only in ``self._status``
2112 2112
2113 2113 Uncommitted files appear "clean" via this context, even if
2114 2114 they aren't actually so in the working directory.
2115 2115 """
2116 2116 if clean:
2117 2117 clean = [f for f in self._manifest if f not in self._changedset]
2118 2118 else:
2119 2119 clean = []
2120 2120 return scmutil.status([f for f in self._status.modified if match(f)],
2121 2121 [f for f in self._status.added if match(f)],
2122 2122 [f for f in self._status.removed if match(f)],
2123 2123 [], [], [], clean)
2124 2124
2125 2125 @propertycache
2126 2126 def _changedset(self):
2127 2127 """Return the set of files changed in this context
2128 2128 """
2129 2129 changed = set(self._status.modified)
2130 2130 changed.update(self._status.added)
2131 2131 changed.update(self._status.removed)
2132 2132 return changed
2133 2133
2134 2134 def makecachingfilectxfn(func):
2135 2135 """Create a filectxfn that caches based on the path.
2136 2136
2137 2137 We can't use util.cachefunc because it uses all arguments as the cache
2138 2138 key and this creates a cycle since the arguments include the repo and
2139 2139 memctx.
2140 2140 """
2141 2141 cache = {}
2142 2142
2143 2143 def getfilectx(repo, memctx, path):
2144 2144 if path not in cache:
2145 2145 cache[path] = func(repo, memctx, path)
2146 2146 return cache[path]
2147 2147
2148 2148 return getfilectx
2149 2149
2150 2150 def memfilefromctx(ctx):
2151 2151 """Given a context return a memfilectx for ctx[path]
2152 2152
2153 2153 This is a convenience method for building a memctx based on another
2154 2154 context.
2155 2155 """
2156 2156 def getfilectx(repo, memctx, path):
2157 2157 fctx = ctx[path]
2158 2158 # this is weird but apparently we only keep track of one parent
2159 2159 # (why not only store that instead of a tuple?)
2160 2160 copied = fctx.renamed()
2161 2161 if copied:
2162 2162 copied = copied[0]
2163 2163 return memfilectx(repo, memctx, path, fctx.data(),
2164 2164 islink=fctx.islink(), isexec=fctx.isexec(),
2165 2165 copied=copied)
2166 2166
2167 2167 return getfilectx
2168 2168
2169 2169 def memfilefrompatch(patchstore):
2170 2170 """Given a patch (e.g. patchstore object) return a memfilectx
2171 2171
2172 2172 This is a convenience method for building a memctx based on a patchstore.
2173 2173 """
2174 2174 def getfilectx(repo, memctx, path):
2175 2175 data, mode, copied = patchstore.getfile(path)
2176 2176 if data is None:
2177 2177 return None
2178 2178 islink, isexec = mode
2179 2179 return memfilectx(repo, memctx, path, data, islink=islink,
2180 2180 isexec=isexec, copied=copied)
2181 2181
2182 2182 return getfilectx
2183 2183
2184 2184 class memctx(committablectx):
2185 2185 """Use memctx to perform in-memory commits via localrepo.commitctx().
2186 2186
2187 2187 Revision information is supplied at initialization time while
2188 2188 related files data and is made available through a callback
2189 2189 mechanism. 'repo' is the current localrepo, 'parents' is a
2190 2190 sequence of two parent revisions identifiers (pass None for every
2191 2191 missing parent), 'text' is the commit message and 'files' lists
2192 2192 names of files touched by the revision (normalized and relative to
2193 2193 repository root).
2194 2194
2195 2195 filectxfn(repo, memctx, path) is a callable receiving the
2196 2196 repository, the current memctx object and the normalized path of
2197 2197 requested file, relative to repository root. It is fired by the
2198 2198 commit function for every file in 'files', but calls order is
2199 2199 undefined. If the file is available in the revision being
2200 2200 committed (updated or added), filectxfn returns a memfilectx
2201 2201 object. If the file was removed, filectxfn return None for recent
2202 2202 Mercurial. Moved files are represented by marking the source file
2203 2203 removed and the new file added with copy information (see
2204 2204 memfilectx).
2205 2205
2206 2206 user receives the committer name and defaults to current
2207 2207 repository username, date is the commit date in any format
2208 2208 supported by dateutil.parsedate() and defaults to current date, extra
2209 2209 is a dictionary of metadata or is left empty.
2210 2210 """
2211 2211
2212 2212 # Mercurial <= 3.1 expects the filectxfn to raise IOError for missing files.
2213 2213 # Extensions that need to retain compatibility across Mercurial 3.1 can use
2214 2214 # this field to determine what to do in filectxfn.
2215 2215 _returnnoneformissingfiles = True
2216 2216
2217 2217 def __init__(self, repo, parents, text, files, filectxfn, user=None,
2218 2218 date=None, extra=None, branch=None, editor=False):
2219 2219 super(memctx, self).__init__(repo, text, user, date, extra)
2220 2220 self._rev = None
2221 2221 self._node = None
2222 2222 parents = [(p or nullid) for p in parents]
2223 2223 p1, p2 = parents
2224 2224 self._parents = [self._repo[p] for p in (p1, p2)]
2225 2225 files = sorted(set(files))
2226 2226 self._files = files
2227 2227 if branch is not None:
2228 2228 self._extra['branch'] = encoding.fromlocal(branch)
2229 2229 self.substate = {}
2230 2230
2231 2231 if isinstance(filectxfn, patch.filestore):
2232 2232 filectxfn = memfilefrompatch(filectxfn)
2233 2233 elif not callable(filectxfn):
2234 2234 # if store is not callable, wrap it in a function
2235 2235 filectxfn = memfilefromctx(filectxfn)
2236 2236
2237 2237 # memoizing increases performance for e.g. vcs convert scenarios.
2238 2238 self._filectxfn = makecachingfilectxfn(filectxfn)
2239 2239
2240 2240 if editor:
2241 2241 self._text = editor(self._repo, self, [])
2242 2242 self._repo.savecommitmessage(self._text)
2243 2243
2244 2244 def filectx(self, path, filelog=None):
2245 2245 """get a file context from the working directory
2246 2246
2247 2247 Returns None if file doesn't exist and should be removed."""
2248 2248 return self._filectxfn(self._repo, self, path)
2249 2249
2250 2250 def commit(self):
2251 2251 """commit context to the repo"""
2252 2252 return self._repo.commitctx(self)
2253 2253
2254 2254 @propertycache
2255 2255 def _manifest(self):
2256 2256 """generate a manifest based on the return values of filectxfn"""
2257 2257
2258 2258 # keep this simple for now; just worry about p1
2259 2259 pctx = self._parents[0]
2260 2260 man = pctx.manifest().copy()
2261 2261
2262 2262 for f in self._status.modified:
2263 2263 p1node = nullid
2264 2264 p2node = nullid
2265 2265 p = pctx[f].parents() # if file isn't in pctx, check p2?
2266 2266 if len(p) > 0:
2267 2267 p1node = p[0].filenode()
2268 2268 if len(p) > 1:
2269 2269 p2node = p[1].filenode()
2270 2270 man[f] = revlog.hash(self[f].data(), p1node, p2node)
2271 2271
2272 2272 for f in self._status.added:
2273 2273 man[f] = revlog.hash(self[f].data(), nullid, nullid)
2274 2274
2275 2275 for f in self._status.removed:
2276 2276 if f in man:
2277 2277 del man[f]
2278 2278
2279 2279 return man
2280 2280
2281 2281 @propertycache
2282 2282 def _status(self):
2283 2283 """Calculate exact status from ``files`` specified at construction
2284 2284 """
2285 2285 man1 = self.p1().manifest()
2286 2286 p2 = self._parents[1]
2287 2287 # "1 < len(self._parents)" can't be used for checking
2288 2288 # existence of the 2nd parent, because "memctx._parents" is
2289 2289 # explicitly initialized by the list, of which length is 2.
2290 2290 if p2.node() != nullid:
2291 2291 man2 = p2.manifest()
2292 2292 managing = lambda f: f in man1 or f in man2
2293 2293 else:
2294 2294 managing = lambda f: f in man1
2295 2295
2296 2296 modified, added, removed = [], [], []
2297 2297 for f in self._files:
2298 2298 if not managing(f):
2299 2299 added.append(f)
2300 2300 elif self[f]:
2301 2301 modified.append(f)
2302 2302 else:
2303 2303 removed.append(f)
2304 2304
2305 2305 return scmutil.status(modified, added, removed, [], [], [], [])
2306 2306
2307 2307 class memfilectx(committablefilectx):
2308 2308 """memfilectx represents an in-memory file to commit.
2309 2309
2310 2310 See memctx and committablefilectx for more details.
2311 2311 """
2312 2312 def __init__(self, repo, changectx, path, data, islink=False,
2313 2313 isexec=False, copied=None):
2314 2314 """
2315 2315 path is the normalized file path relative to repository root.
2316 2316 data is the file content as a string.
2317 2317 islink is True if the file is a symbolic link.
2318 2318 isexec is True if the file is executable.
2319 2319 copied is the source file path if current file was copied in the
2320 2320 revision being committed, or None."""
2321 2321 super(memfilectx, self).__init__(repo, path, None, changectx)
2322 2322 self._data = data
2323 2323 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
2324 2324 self._copied = None
2325 2325 if copied:
2326 2326 self._copied = (copied, nullid)
2327 2327
2328 2328 def data(self):
2329 2329 return self._data
2330 2330
2331 2331 def remove(self, ignoremissing=False):
2332 2332 """wraps unlink for a repo's working directory"""
2333 2333 # need to figure out what to do here
2334 2334 del self._changectx[self._path]
2335 2335
2336 2336 def write(self, data, flags, **kwargs):
2337 2337 """wraps repo.wwrite"""
2338 2338 self._data = data
2339 2339
2340 2340 class overlayfilectx(committablefilectx):
2341 2341 """Like memfilectx but take an original filectx and optional parameters to
2342 2342 override parts of it. This is useful when fctx.data() is expensive (i.e.
2343 2343 flag processor is expensive) and raw data, flags, and filenode could be
2344 2344 reused (ex. rebase or mode-only amend a REVIDX_EXTSTORED file).
2345 2345 """
2346 2346
2347 2347 def __init__(self, originalfctx, datafunc=None, path=None, flags=None,
2348 2348 copied=None, ctx=None):
2349 2349 """originalfctx: filecontext to duplicate
2350 2350
2351 2351 datafunc: None or a function to override data (file content). It is a
2352 2352 function to be lazy. path, flags, copied, ctx: None or overridden value
2353 2353
2354 2354 copied could be (path, rev), or False. copied could also be just path,
2355 2355 and will be converted to (path, nullid). This simplifies some callers.
2356 2356 """
2357 2357
2358 2358 if path is None:
2359 2359 path = originalfctx.path()
2360 2360 if ctx is None:
2361 2361 ctx = originalfctx.changectx()
2362 2362 ctxmatch = lambda: True
2363 2363 else:
2364 2364 ctxmatch = lambda: ctx == originalfctx.changectx()
2365 2365
2366 2366 repo = originalfctx.repo()
2367 2367 flog = originalfctx.filelog()
2368 2368 super(overlayfilectx, self).__init__(repo, path, flog, ctx)
2369 2369
2370 2370 if copied is None:
2371 2371 copied = originalfctx.renamed()
2372 2372 copiedmatch = lambda: True
2373 2373 else:
2374 2374 if copied and not isinstance(copied, tuple):
2375 2375 # repo._filecommit will recalculate copyrev so nullid is okay
2376 2376 copied = (copied, nullid)
2377 2377 copiedmatch = lambda: copied == originalfctx.renamed()
2378 2378
2379 2379 # When data, copied (could affect data), ctx (could affect filelog
2380 2380 # parents) are not overridden, rawdata, rawflags, and filenode may be
2381 2381 # reused (repo._filecommit should double check filelog parents).
2382 2382 #
2383 2383 # path, flags are not hashed in filelog (but in manifestlog) so they do
2384 2384 # not affect reusable here.
2385 2385 #
2386 2386 # If ctx or copied is overridden to a same value with originalfctx,
2387 2387 # still consider it's reusable. originalfctx.renamed() may be a bit
2388 2388 # expensive so it's not called unless necessary. Assuming datafunc is
2389 2389 # always expensive, do not call it for this "reusable" test.
2390 2390 reusable = datafunc is None and ctxmatch() and copiedmatch()
2391 2391
2392 2392 if datafunc is None:
2393 2393 datafunc = originalfctx.data
2394 2394 if flags is None:
2395 2395 flags = originalfctx.flags()
2396 2396
2397 2397 self._datafunc = datafunc
2398 2398 self._flags = flags
2399 2399 self._copied = copied
2400 2400
2401 2401 if reusable:
2402 2402 # copy extra fields from originalfctx
2403 2403 attrs = ['rawdata', 'rawflags', '_filenode', '_filerev']
2404 2404 for attr_ in attrs:
2405 2405 if util.safehasattr(originalfctx, attr_):
2406 2406 setattr(self, attr_, getattr(originalfctx, attr_))
2407 2407
2408 2408 def data(self):
2409 2409 return self._datafunc()
2410 2410
2411 2411 class metadataonlyctx(committablectx):
2412 2412 """Like memctx but it's reusing the manifest of different commit.
2413 2413 Intended to be used by lightweight operations that are creating
2414 2414 metadata-only changes.
2415 2415
2416 2416 Revision information is supplied at initialization time. 'repo' is the
2417 2417 current localrepo, 'ctx' is original revision which manifest we're reuisng
2418 2418 'parents' is a sequence of two parent revisions identifiers (pass None for
2419 2419 every missing parent), 'text' is the commit.
2420 2420
2421 2421 user receives the committer name and defaults to current repository
2422 2422 username, date is the commit date in any format supported by
2423 2423 dateutil.parsedate() and defaults to current date, extra is a dictionary of
2424 2424 metadata or is left empty.
2425 2425 """
2426 2426 def __init__(self, repo, originalctx, parents=None, text=None, user=None,
2427 2427 date=None, extra=None, editor=False):
2428 2428 if text is None:
2429 2429 text = originalctx.description()
2430 2430 super(metadataonlyctx, self).__init__(repo, text, user, date, extra)
2431 2431 self._rev = None
2432 2432 self._node = None
2433 2433 self._originalctx = originalctx
2434 2434 self._manifestnode = originalctx.manifestnode()
2435 2435 if parents is None:
2436 2436 parents = originalctx.parents()
2437 2437 else:
2438 2438 parents = [repo[p] for p in parents if p is not None]
2439 2439 parents = parents[:]
2440 2440 while len(parents) < 2:
2441 2441 parents.append(repo[nullid])
2442 2442 p1, p2 = self._parents = parents
2443 2443
2444 2444 # sanity check to ensure that the reused manifest parents are
2445 2445 # manifests of our commit parents
2446 2446 mp1, mp2 = self.manifestctx().parents
2447 2447 if p1 != nullid and p1.manifestnode() != mp1:
2448 2448 raise RuntimeError('can\'t reuse the manifest: '
2449 2449 'its p1 doesn\'t match the new ctx p1')
2450 2450 if p2 != nullid and p2.manifestnode() != mp2:
2451 2451 raise RuntimeError('can\'t reuse the manifest: '
2452 2452 'its p2 doesn\'t match the new ctx p2')
2453 2453
2454 2454 self._files = originalctx.files()
2455 2455 self.substate = {}
2456 2456
2457 2457 if editor:
2458 2458 self._text = editor(self._repo, self, [])
2459 2459 self._repo.savecommitmessage(self._text)
2460 2460
2461 2461 def manifestnode(self):
2462 2462 return self._manifestnode
2463 2463
2464 2464 @property
2465 2465 def _manifestctx(self):
2466 2466 return self._repo.manifestlog[self._manifestnode]
2467 2467
2468 2468 def filectx(self, path, filelog=None):
2469 2469 return self._originalctx.filectx(path, filelog=filelog)
2470 2470
2471 2471 def commit(self):
2472 2472 """commit context to the repo"""
2473 2473 return self._repo.commitctx(self)
2474 2474
2475 2475 @property
2476 2476 def _manifest(self):
2477 2477 return self._originalctx.manifest()
2478 2478
2479 2479 @propertycache
2480 2480 def _status(self):
2481 2481 """Calculate exact status from ``files`` specified in the ``origctx``
2482 2482 and parents manifests.
2483 2483 """
2484 2484 man1 = self.p1().manifest()
2485 2485 p2 = self._parents[1]
2486 2486 # "1 < len(self._parents)" can't be used for checking
2487 2487 # existence of the 2nd parent, because "metadataonlyctx._parents" is
2488 2488 # explicitly initialized by the list, of which length is 2.
2489 2489 if p2.node() != nullid:
2490 2490 man2 = p2.manifest()
2491 2491 managing = lambda f: f in man1 or f in man2
2492 2492 else:
2493 2493 managing = lambda f: f in man1
2494 2494
2495 2495 modified, added, removed = [], [], []
2496 2496 for f in self._files:
2497 2497 if not managing(f):
2498 2498 added.append(f)
2499 2499 elif f in self:
2500 2500 modified.append(f)
2501 2501 else:
2502 2502 removed.append(f)
2503 2503
2504 2504 return scmutil.status(modified, added, removed, [], [], [], [])
2505 2505
2506 2506 class arbitraryfilectx(object):
2507 2507 """Allows you to use filectx-like functions on a file in an arbitrary
2508 2508 location on disk, possibly not in the working directory.
2509 2509 """
2510 2510 def __init__(self, path, repo=None):
2511 2511 # Repo is optional because contrib/simplemerge uses this class.
2512 2512 self._repo = repo
2513 2513 self._path = path
2514 2514
2515 2515 def cmp(self, fctx):
2516 2516 # filecmp follows symlinks whereas `cmp` should not, so skip the fast
2517 2517 # path if either side is a symlink.
2518 2518 symlinks = ('l' in self.flags() or 'l' in fctx.flags())
2519 2519 if not symlinks and isinstance(fctx, workingfilectx) and self._repo:
2520 2520 # Add a fast-path for merge if both sides are disk-backed.
2521 2521 # Note that filecmp uses the opposite return values (True if same)
2522 2522 # from our cmp functions (True if different).
2523 2523 return not filecmp.cmp(self.path(), self._repo.wjoin(fctx.path()))
2524 2524 return self.data() != fctx.data()
2525 2525
2526 2526 def path(self):
2527 2527 return self._path
2528 2528
2529 2529 def flags(self):
2530 2530 return ''
2531 2531
2532 2532 def data(self):
2533 2533 return util.readfile(self._path)
2534 2534
2535 2535 def decodeddata(self):
2536 2536 with open(self._path, "rb") as f:
2537 2537 return f.read()
2538 2538
2539 2539 def remove(self):
2540 2540 util.unlink(self._path)
2541 2541
2542 2542 def write(self, data, flags, **kwargs):
2543 2543 assert not flags
2544 2544 with open(self._path, "w") as f:
2545 2545 f.write(data)
@@ -1,3166 +1,3166 b''
1 1 # debugcommands.py - command processing for debug* commands
2 2 #
3 3 # Copyright 2005-2016 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 codecs
11 11 import collections
12 12 import difflib
13 13 import errno
14 14 import operator
15 15 import os
16 16 import random
17 17 import re
18 18 import socket
19 19 import ssl
20 20 import stat
21 21 import string
22 22 import subprocess
23 23 import sys
24 24 import time
25 25
26 26 from .i18n import _
27 27 from .node import (
28 28 bin,
29 29 hex,
30 30 nullhex,
31 31 nullid,
32 32 nullrev,
33 33 short,
34 34 )
35 35 from .thirdparty import (
36 36 cbor,
37 37 )
38 38 from . import (
39 39 bundle2,
40 40 changegroup,
41 41 cmdutil,
42 42 color,
43 43 context,
44 44 dagparser,
45 45 dagutil,
46 46 encoding,
47 47 error,
48 48 exchange,
49 49 extensions,
50 50 filemerge,
51 51 fileset,
52 52 formatter,
53 53 hg,
54 54 httppeer,
55 55 localrepo,
56 56 lock as lockmod,
57 57 logcmdutil,
58 58 merge as mergemod,
59 59 obsolete,
60 60 obsutil,
61 61 phases,
62 62 policy,
63 63 pvec,
64 64 pycompat,
65 65 registrar,
66 66 repair,
67 67 revlog,
68 68 revset,
69 69 revsetlang,
70 70 scmutil,
71 71 setdiscovery,
72 72 simplemerge,
73 73 sshpeer,
74 74 sslutil,
75 75 streamclone,
76 76 templater,
77 77 treediscovery,
78 78 upgrade,
79 79 url as urlmod,
80 80 util,
81 81 vfs as vfsmod,
82 82 wireprotoframing,
83 83 wireprotoserver,
84 84 wireprotov2peer,
85 85 )
86 86 from .utils import (
87 87 dateutil,
88 88 procutil,
89 89 stringutil,
90 90 )
91 91
92 92 release = lockmod.release
93 93
94 94 command = registrar.command()
95 95
96 96 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
97 97 def debugancestor(ui, repo, *args):
98 98 """find the ancestor revision of two revisions in a given index"""
99 99 if len(args) == 3:
100 100 index, rev1, rev2 = args
101 101 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False), index)
102 102 lookup = r.lookup
103 103 elif len(args) == 2:
104 104 if not repo:
105 105 raise error.Abort(_('there is no Mercurial repository here '
106 106 '(.hg not found)'))
107 107 rev1, rev2 = args
108 108 r = repo.changelog
109 109 lookup = repo.lookup
110 110 else:
111 111 raise error.Abort(_('either two or three arguments required'))
112 112 a = r.ancestor(lookup(rev1), lookup(rev2))
113 113 ui.write('%d:%s\n' % (r.rev(a), hex(a)))
114 114
115 115 @command('debugapplystreamclonebundle', [], 'FILE')
116 116 def debugapplystreamclonebundle(ui, repo, fname):
117 117 """apply a stream clone bundle file"""
118 118 f = hg.openpath(ui, fname)
119 119 gen = exchange.readbundle(ui, f, fname)
120 120 gen.apply(repo)
121 121
122 122 @command('debugbuilddag',
123 123 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
124 124 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
125 125 ('n', 'new-file', None, _('add new file at each rev'))],
126 126 _('[OPTION]... [TEXT]'))
127 127 def debugbuilddag(ui, repo, text=None,
128 128 mergeable_file=False,
129 129 overwritten_file=False,
130 130 new_file=False):
131 131 """builds a repo with a given DAG from scratch in the current empty repo
132 132
133 133 The description of the DAG is read from stdin if not given on the
134 134 command line.
135 135
136 136 Elements:
137 137
138 138 - "+n" is a linear run of n nodes based on the current default parent
139 139 - "." is a single node based on the current default parent
140 140 - "$" resets the default parent to null (implied at the start);
141 141 otherwise the default parent is always the last node created
142 142 - "<p" sets the default parent to the backref p
143 143 - "*p" is a fork at parent p, which is a backref
144 144 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
145 145 - "/p2" is a merge of the preceding node and p2
146 146 - ":tag" defines a local tag for the preceding node
147 147 - "@branch" sets the named branch for subsequent nodes
148 148 - "#...\\n" is a comment up to the end of the line
149 149
150 150 Whitespace between the above elements is ignored.
151 151
152 152 A backref is either
153 153
154 154 - a number n, which references the node curr-n, where curr is the current
155 155 node, or
156 156 - the name of a local tag you placed earlier using ":tag", or
157 157 - empty to denote the default parent.
158 158
159 159 All string valued-elements are either strictly alphanumeric, or must
160 160 be enclosed in double quotes ("..."), with "\\" as escape character.
161 161 """
162 162
163 163 if text is None:
164 164 ui.status(_("reading DAG from stdin\n"))
165 165 text = ui.fin.read()
166 166
167 167 cl = repo.changelog
168 168 if len(cl) > 0:
169 169 raise error.Abort(_('repository is not empty'))
170 170
171 171 # determine number of revs in DAG
172 172 total = 0
173 173 for type, data in dagparser.parsedag(text):
174 174 if type == 'n':
175 175 total += 1
176 176
177 177 if mergeable_file:
178 178 linesperrev = 2
179 179 # make a file with k lines per rev
180 180 initialmergedlines = ['%d' % i for i in xrange(0, total * linesperrev)]
181 181 initialmergedlines.append("")
182 182
183 183 tags = []
184 184 progress = ui.makeprogress(_('building'), unit=_('revisions'),
185 185 total=total)
186 186 with progress, repo.wlock(), repo.lock(), repo.transaction("builddag"):
187 187 at = -1
188 188 atbranch = 'default'
189 189 nodeids = []
190 190 id = 0
191 191 progress.update(id)
192 192 for type, data in dagparser.parsedag(text):
193 193 if type == 'n':
194 194 ui.note(('node %s\n' % pycompat.bytestr(data)))
195 195 id, ps = data
196 196
197 197 files = []
198 198 filecontent = {}
199 199
200 200 p2 = None
201 201 if mergeable_file:
202 202 fn = "mf"
203 203 p1 = repo[ps[0]]
204 204 if len(ps) > 1:
205 205 p2 = repo[ps[1]]
206 206 pa = p1.ancestor(p2)
207 207 base, local, other = [x[fn].data() for x in (pa, p1,
208 208 p2)]
209 209 m3 = simplemerge.Merge3Text(base, local, other)
210 210 ml = [l.strip() for l in m3.merge_lines()]
211 211 ml.append("")
212 212 elif at > 0:
213 213 ml = p1[fn].data().split("\n")
214 214 else:
215 215 ml = initialmergedlines
216 216 ml[id * linesperrev] += " r%i" % id
217 217 mergedtext = "\n".join(ml)
218 218 files.append(fn)
219 219 filecontent[fn] = mergedtext
220 220
221 221 if overwritten_file:
222 222 fn = "of"
223 223 files.append(fn)
224 224 filecontent[fn] = "r%i\n" % id
225 225
226 226 if new_file:
227 227 fn = "nf%i" % id
228 228 files.append(fn)
229 229 filecontent[fn] = "r%i\n" % id
230 230 if len(ps) > 1:
231 231 if not p2:
232 232 p2 = repo[ps[1]]
233 233 for fn in p2:
234 234 if fn.startswith("nf"):
235 235 files.append(fn)
236 236 filecontent[fn] = p2[fn].data()
237 237
238 238 def fctxfn(repo, cx, path):
239 239 if path in filecontent:
240 240 return context.memfilectx(repo, cx, path,
241 241 filecontent[path])
242 242 return None
243 243
244 244 if len(ps) == 0 or ps[0] < 0:
245 245 pars = [None, None]
246 246 elif len(ps) == 1:
247 247 pars = [nodeids[ps[0]], None]
248 248 else:
249 249 pars = [nodeids[p] for p in ps]
250 250 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
251 251 date=(id, 0),
252 252 user="debugbuilddag",
253 253 extra={'branch': atbranch})
254 254 nodeid = repo.commitctx(cx)
255 255 nodeids.append(nodeid)
256 256 at = id
257 257 elif type == 'l':
258 258 id, name = data
259 259 ui.note(('tag %s\n' % name))
260 260 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
261 261 elif type == 'a':
262 262 ui.note(('branch %s\n' % data))
263 263 atbranch = data
264 264 progress.update(id)
265 265
266 266 if tags:
267 267 repo.vfs.write("localtags", "".join(tags))
268 268
269 269 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
270 270 indent_string = ' ' * indent
271 271 if all:
272 272 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
273 273 % indent_string)
274 274
275 275 def showchunks(named):
276 276 ui.write("\n%s%s\n" % (indent_string, named))
277 277 for deltadata in gen.deltaiter():
278 278 node, p1, p2, cs, deltabase, delta, flags = deltadata
279 279 ui.write("%s%s %s %s %s %s %d\n" %
280 280 (indent_string, hex(node), hex(p1), hex(p2),
281 281 hex(cs), hex(deltabase), len(delta)))
282 282
283 283 chunkdata = gen.changelogheader()
284 284 showchunks("changelog")
285 285 chunkdata = gen.manifestheader()
286 286 showchunks("manifest")
287 287 for chunkdata in iter(gen.filelogheader, {}):
288 288 fname = chunkdata['filename']
289 289 showchunks(fname)
290 290 else:
291 291 if isinstance(gen, bundle2.unbundle20):
292 292 raise error.Abort(_('use debugbundle2 for this file'))
293 293 chunkdata = gen.changelogheader()
294 294 for deltadata in gen.deltaiter():
295 295 node, p1, p2, cs, deltabase, delta, flags = deltadata
296 296 ui.write("%s%s\n" % (indent_string, hex(node)))
297 297
298 298 def _debugobsmarkers(ui, part, indent=0, **opts):
299 299 """display version and markers contained in 'data'"""
300 300 opts = pycompat.byteskwargs(opts)
301 301 data = part.read()
302 302 indent_string = ' ' * indent
303 303 try:
304 304 version, markers = obsolete._readmarkers(data)
305 305 except error.UnknownVersion as exc:
306 306 msg = "%sunsupported version: %s (%d bytes)\n"
307 307 msg %= indent_string, exc.version, len(data)
308 308 ui.write(msg)
309 309 else:
310 310 msg = "%sversion: %d (%d bytes)\n"
311 311 msg %= indent_string, version, len(data)
312 312 ui.write(msg)
313 313 fm = ui.formatter('debugobsolete', opts)
314 314 for rawmarker in sorted(markers):
315 315 m = obsutil.marker(None, rawmarker)
316 316 fm.startitem()
317 317 fm.plain(indent_string)
318 318 cmdutil.showmarker(fm, m)
319 319 fm.end()
320 320
321 321 def _debugphaseheads(ui, data, indent=0):
322 322 """display version and markers contained in 'data'"""
323 323 indent_string = ' ' * indent
324 324 headsbyphase = phases.binarydecode(data)
325 325 for phase in phases.allphases:
326 326 for head in headsbyphase[phase]:
327 327 ui.write(indent_string)
328 328 ui.write('%s %s\n' % (hex(head), phases.phasenames[phase]))
329 329
330 330 def _quasirepr(thing):
331 331 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
332 332 return '{%s}' % (
333 333 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing)))
334 334 return pycompat.bytestr(repr(thing))
335 335
336 336 def _debugbundle2(ui, gen, all=None, **opts):
337 337 """lists the contents of a bundle2"""
338 338 if not isinstance(gen, bundle2.unbundle20):
339 339 raise error.Abort(_('not a bundle2 file'))
340 340 ui.write(('Stream params: %s\n' % _quasirepr(gen.params)))
341 341 parttypes = opts.get(r'part_type', [])
342 342 for part in gen.iterparts():
343 343 if parttypes and part.type not in parttypes:
344 344 continue
345 345 msg = '%s -- %s (mandatory: %r)\n'
346 346 ui.write((msg % (part.type, _quasirepr(part.params), part.mandatory)))
347 347 if part.type == 'changegroup':
348 348 version = part.params.get('version', '01')
349 349 cg = changegroup.getunbundler(version, part, 'UN')
350 350 if not ui.quiet:
351 351 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
352 352 if part.type == 'obsmarkers':
353 353 if not ui.quiet:
354 354 _debugobsmarkers(ui, part, indent=4, **opts)
355 355 if part.type == 'phase-heads':
356 356 if not ui.quiet:
357 357 _debugphaseheads(ui, part, indent=4)
358 358
359 359 @command('debugbundle',
360 360 [('a', 'all', None, _('show all details')),
361 361 ('', 'part-type', [], _('show only the named part type')),
362 362 ('', 'spec', None, _('print the bundlespec of the bundle'))],
363 363 _('FILE'),
364 364 norepo=True)
365 365 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
366 366 """lists the contents of a bundle"""
367 367 with hg.openpath(ui, bundlepath) as f:
368 368 if spec:
369 369 spec = exchange.getbundlespec(ui, f)
370 370 ui.write('%s\n' % spec)
371 371 return
372 372
373 373 gen = exchange.readbundle(ui, f, bundlepath)
374 374 if isinstance(gen, bundle2.unbundle20):
375 375 return _debugbundle2(ui, gen, all=all, **opts)
376 376 _debugchangegroup(ui, gen, all=all, **opts)
377 377
378 378 @command('debugcapabilities',
379 379 [], _('PATH'),
380 380 norepo=True)
381 381 def debugcapabilities(ui, path, **opts):
382 382 """lists the capabilities of a remote peer"""
383 383 opts = pycompat.byteskwargs(opts)
384 384 peer = hg.peer(ui, opts, path)
385 385 caps = peer.capabilities()
386 386 ui.write(('Main capabilities:\n'))
387 387 for c in sorted(caps):
388 388 ui.write((' %s\n') % c)
389 389 b2caps = bundle2.bundle2caps(peer)
390 390 if b2caps:
391 391 ui.write(('Bundle2 capabilities:\n'))
392 392 for key, values in sorted(b2caps.iteritems()):
393 393 ui.write((' %s\n') % key)
394 394 for v in values:
395 395 ui.write((' %s\n') % v)
396 396
397 397 @command('debugcheckstate', [], '')
398 398 def debugcheckstate(ui, repo):
399 399 """validate the correctness of the current dirstate"""
400 400 parent1, parent2 = repo.dirstate.parents()
401 401 m1 = repo[parent1].manifest()
402 402 m2 = repo[parent2].manifest()
403 403 errors = 0
404 404 for f in repo.dirstate:
405 405 state = repo.dirstate[f]
406 406 if state in "nr" and f not in m1:
407 407 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
408 408 errors += 1
409 409 if state in "a" and f in m1:
410 410 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
411 411 errors += 1
412 412 if state in "m" and f not in m1 and f not in m2:
413 413 ui.warn(_("%s in state %s, but not in either manifest\n") %
414 414 (f, state))
415 415 errors += 1
416 416 for f in m1:
417 417 state = repo.dirstate[f]
418 418 if state not in "nrm":
419 419 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
420 420 errors += 1
421 421 if errors:
422 422 error = _(".hg/dirstate inconsistent with current parent's manifest")
423 423 raise error.Abort(error)
424 424
425 425 @command('debugcolor',
426 426 [('', 'style', None, _('show all configured styles'))],
427 427 'hg debugcolor')
428 428 def debugcolor(ui, repo, **opts):
429 429 """show available color, effects or style"""
430 430 ui.write(('color mode: %s\n') % stringutil.pprint(ui._colormode))
431 431 if opts.get(r'style'):
432 432 return _debugdisplaystyle(ui)
433 433 else:
434 434 return _debugdisplaycolor(ui)
435 435
436 436 def _debugdisplaycolor(ui):
437 437 ui = ui.copy()
438 438 ui._styles.clear()
439 439 for effect in color._activeeffects(ui).keys():
440 440 ui._styles[effect] = effect
441 441 if ui._terminfoparams:
442 442 for k, v in ui.configitems('color'):
443 443 if k.startswith('color.'):
444 444 ui._styles[k] = k[6:]
445 445 elif k.startswith('terminfo.'):
446 446 ui._styles[k] = k[9:]
447 447 ui.write(_('available colors:\n'))
448 448 # sort label with a '_' after the other to group '_background' entry.
449 449 items = sorted(ui._styles.items(),
450 450 key=lambda i: ('_' in i[0], i[0], i[1]))
451 451 for colorname, label in items:
452 452 ui.write(('%s\n') % colorname, label=label)
453 453
454 454 def _debugdisplaystyle(ui):
455 455 ui.write(_('available style:\n'))
456 456 if not ui._styles:
457 457 return
458 458 width = max(len(s) for s in ui._styles)
459 459 for label, effects in sorted(ui._styles.items()):
460 460 ui.write('%s' % label, label=label)
461 461 if effects:
462 462 # 50
463 463 ui.write(': ')
464 464 ui.write(' ' * (max(0, width - len(label))))
465 465 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
466 466 ui.write('\n')
467 467
468 468 @command('debugcreatestreamclonebundle', [], 'FILE')
469 469 def debugcreatestreamclonebundle(ui, repo, fname):
470 470 """create a stream clone bundle file
471 471
472 472 Stream bundles are special bundles that are essentially archives of
473 473 revlog files. They are commonly used for cloning very quickly.
474 474 """
475 475 # TODO we may want to turn this into an abort when this functionality
476 476 # is moved into `hg bundle`.
477 477 if phases.hassecret(repo):
478 478 ui.warn(_('(warning: stream clone bundle will contain secret '
479 479 'revisions)\n'))
480 480
481 481 requirements, gen = streamclone.generatebundlev1(repo)
482 482 changegroup.writechunks(ui, gen, fname)
483 483
484 484 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
485 485
486 486 @command('debugdag',
487 487 [('t', 'tags', None, _('use tags as labels')),
488 488 ('b', 'branches', None, _('annotate with branch names')),
489 489 ('', 'dots', None, _('use dots for runs')),
490 490 ('s', 'spaces', None, _('separate elements by spaces'))],
491 491 _('[OPTION]... [FILE [REV]...]'),
492 492 optionalrepo=True)
493 493 def debugdag(ui, repo, file_=None, *revs, **opts):
494 494 """format the changelog or an index DAG as a concise textual description
495 495
496 496 If you pass a revlog index, the revlog's DAG is emitted. If you list
497 497 revision numbers, they get labeled in the output as rN.
498 498
499 499 Otherwise, the changelog DAG of the current repo is emitted.
500 500 """
501 501 spaces = opts.get(r'spaces')
502 502 dots = opts.get(r'dots')
503 503 if file_:
504 504 rlog = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
505 505 file_)
506 506 revs = set((int(r) for r in revs))
507 507 def events():
508 508 for r in rlog:
509 509 yield 'n', (r, list(p for p in rlog.parentrevs(r)
510 510 if p != -1))
511 511 if r in revs:
512 512 yield 'l', (r, "r%i" % r)
513 513 elif repo:
514 514 cl = repo.changelog
515 515 tags = opts.get(r'tags')
516 516 branches = opts.get(r'branches')
517 517 if tags:
518 518 labels = {}
519 519 for l, n in repo.tags().items():
520 520 labels.setdefault(cl.rev(n), []).append(l)
521 521 def events():
522 522 b = "default"
523 523 for r in cl:
524 524 if branches:
525 525 newb = cl.read(cl.node(r))[5]['branch']
526 526 if newb != b:
527 527 yield 'a', newb
528 528 b = newb
529 529 yield 'n', (r, list(p for p in cl.parentrevs(r)
530 530 if p != -1))
531 531 if tags:
532 532 ls = labels.get(r)
533 533 if ls:
534 534 for l in ls:
535 535 yield 'l', (r, l)
536 536 else:
537 537 raise error.Abort(_('need repo for changelog dag'))
538 538
539 539 for line in dagparser.dagtextlines(events(),
540 540 addspaces=spaces,
541 541 wraplabels=True,
542 542 wrapannotations=True,
543 543 wrapnonlinear=dots,
544 544 usedots=dots,
545 545 maxlinewidth=70):
546 546 ui.write(line)
547 547 ui.write("\n")
548 548
549 549 @command('debugdata', cmdutil.debugrevlogopts, _('-c|-m|FILE REV'))
550 550 def debugdata(ui, repo, file_, rev=None, **opts):
551 551 """dump the contents of a data file revision"""
552 552 opts = pycompat.byteskwargs(opts)
553 553 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
554 554 if rev is not None:
555 555 raise error.CommandError('debugdata', _('invalid arguments'))
556 556 file_, rev = None, file_
557 557 elif rev is None:
558 558 raise error.CommandError('debugdata', _('invalid arguments'))
559 559 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
560 560 try:
561 561 ui.write(r.revision(r.lookup(rev), raw=True))
562 562 except KeyError:
563 563 raise error.Abort(_('invalid revision identifier %s') % rev)
564 564
565 565 @command('debugdate',
566 566 [('e', 'extended', None, _('try extended date formats'))],
567 567 _('[-e] DATE [RANGE]'),
568 568 norepo=True, optionalrepo=True)
569 569 def debugdate(ui, date, range=None, **opts):
570 570 """parse and display a date"""
571 571 if opts[r"extended"]:
572 572 d = dateutil.parsedate(date, util.extendeddateformats)
573 573 else:
574 574 d = dateutil.parsedate(date)
575 575 ui.write(("internal: %d %d\n") % d)
576 576 ui.write(("standard: %s\n") % dateutil.datestr(d))
577 577 if range:
578 578 m = dateutil.matchdate(range)
579 579 ui.write(("match: %s\n") % m(d[0]))
580 580
581 581 @command('debugdeltachain',
582 582 cmdutil.debugrevlogopts + cmdutil.formatteropts,
583 583 _('-c|-m|FILE'),
584 584 optionalrepo=True)
585 585 def debugdeltachain(ui, repo, file_=None, **opts):
586 586 """dump information about delta chains in a revlog
587 587
588 588 Output can be templatized. Available template keywords are:
589 589
590 590 :``rev``: revision number
591 591 :``chainid``: delta chain identifier (numbered by unique base)
592 592 :``chainlen``: delta chain length to this revision
593 593 :``prevrev``: previous revision in delta chain
594 594 :``deltatype``: role of delta / how it was computed
595 595 :``compsize``: compressed size of revision
596 596 :``uncompsize``: uncompressed size of revision
597 597 :``chainsize``: total size of compressed revisions in chain
598 598 :``chainratio``: total chain size divided by uncompressed revision size
599 599 (new delta chains typically start at ratio 2.00)
600 600 :``lindist``: linear distance from base revision in delta chain to end
601 601 of this revision
602 602 :``extradist``: total size of revisions not part of this delta chain from
603 603 base of delta chain to end of this revision; a measurement
604 604 of how much extra data we need to read/seek across to read
605 605 the delta chain for this revision
606 606 :``extraratio``: extradist divided by chainsize; another representation of
607 607 how much unrelated data is needed to load this delta chain
608 608
609 609 If the repository is configured to use the sparse read, additional keywords
610 610 are available:
611 611
612 612 :``readsize``: total size of data read from the disk for a revision
613 613 (sum of the sizes of all the blocks)
614 614 :``largestblock``: size of the largest block of data read from the disk
615 615 :``readdensity``: density of useful bytes in the data read from the disk
616 616 :``srchunks``: in how many data hunks the whole revision would be read
617 617
618 618 The sparse read can be enabled with experimental.sparse-read = True
619 619 """
620 620 opts = pycompat.byteskwargs(opts)
621 621 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
622 622 index = r.index
623 623 start = r.start
624 624 length = r.length
625 625 generaldelta = r.version & revlog.FLAG_GENERALDELTA
626 626 withsparseread = getattr(r, '_withsparseread', False)
627 627
628 628 def revinfo(rev):
629 629 e = index[rev]
630 630 compsize = e[1]
631 631 uncompsize = e[2]
632 632 chainsize = 0
633 633
634 634 if generaldelta:
635 635 if e[3] == e[5]:
636 636 deltatype = 'p1'
637 637 elif e[3] == e[6]:
638 638 deltatype = 'p2'
639 639 elif e[3] == rev - 1:
640 640 deltatype = 'prev'
641 641 elif e[3] == rev:
642 642 deltatype = 'base'
643 643 else:
644 644 deltatype = 'other'
645 645 else:
646 646 if e[3] == rev:
647 647 deltatype = 'base'
648 648 else:
649 649 deltatype = 'prev'
650 650
651 651 chain = r._deltachain(rev)[0]
652 652 for iterrev in chain:
653 653 e = index[iterrev]
654 654 chainsize += e[1]
655 655
656 656 return compsize, uncompsize, deltatype, chain, chainsize
657 657
658 658 fm = ui.formatter('debugdeltachain', opts)
659 659
660 660 fm.plain(' rev chain# chainlen prev delta '
661 661 'size rawsize chainsize ratio lindist extradist '
662 662 'extraratio')
663 663 if withsparseread:
664 664 fm.plain(' readsize largestblk rddensity srchunks')
665 665 fm.plain('\n')
666 666
667 667 chainbases = {}
668 668 for rev in r:
669 669 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
670 670 chainbase = chain[0]
671 671 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
672 672 basestart = start(chainbase)
673 673 revstart = start(rev)
674 674 lineardist = revstart + comp - basestart
675 675 extradist = lineardist - chainsize
676 676 try:
677 677 prevrev = chain[-2]
678 678 except IndexError:
679 679 prevrev = -1
680 680
681 681 chainratio = float(chainsize) / float(uncomp)
682 682 extraratio = float(extradist) / float(chainsize)
683 683
684 684 fm.startitem()
685 685 fm.write('rev chainid chainlen prevrev deltatype compsize '
686 686 'uncompsize chainsize chainratio lindist extradist '
687 687 'extraratio',
688 688 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
689 689 rev, chainid, len(chain), prevrev, deltatype, comp,
690 690 uncomp, chainsize, chainratio, lineardist, extradist,
691 691 extraratio,
692 692 rev=rev, chainid=chainid, chainlen=len(chain),
693 693 prevrev=prevrev, deltatype=deltatype, compsize=comp,
694 694 uncompsize=uncomp, chainsize=chainsize,
695 695 chainratio=chainratio, lindist=lineardist,
696 696 extradist=extradist, extraratio=extraratio)
697 697 if withsparseread:
698 698 readsize = 0
699 699 largestblock = 0
700 700 srchunks = 0
701 701
702 702 for revschunk in revlog._slicechunk(r, chain):
703 703 srchunks += 1
704 704 blkend = start(revschunk[-1]) + length(revschunk[-1])
705 705 blksize = blkend - start(revschunk[0])
706 706
707 707 readsize += blksize
708 708 if largestblock < blksize:
709 709 largestblock = blksize
710 710
711 711 readdensity = float(chainsize) / float(readsize)
712 712
713 713 fm.write('readsize largestblock readdensity srchunks',
714 714 ' %10d %10d %9.5f %8d',
715 715 readsize, largestblock, readdensity, srchunks,
716 716 readsize=readsize, largestblock=largestblock,
717 717 readdensity=readdensity, srchunks=srchunks)
718 718
719 719 fm.plain('\n')
720 720
721 721 fm.end()
722 722
723 723 @command('debugdirstate|debugstate',
724 724 [('', 'nodates', None, _('do not display the saved mtime')),
725 725 ('', 'datesort', None, _('sort by saved mtime'))],
726 726 _('[OPTION]...'))
727 727 def debugstate(ui, repo, **opts):
728 728 """show the contents of the current dirstate"""
729 729
730 730 nodates = opts.get(r'nodates')
731 731 datesort = opts.get(r'datesort')
732 732
733 733 timestr = ""
734 734 if datesort:
735 735 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
736 736 else:
737 737 keyfunc = None # sort by filename
738 738 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
739 739 if ent[3] == -1:
740 740 timestr = 'unset '
741 741 elif nodates:
742 742 timestr = 'set '
743 743 else:
744 744 timestr = time.strftime(r"%Y-%m-%d %H:%M:%S ",
745 745 time.localtime(ent[3]))
746 746 timestr = encoding.strtolocal(timestr)
747 747 if ent[1] & 0o20000:
748 748 mode = 'lnk'
749 749 else:
750 750 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
751 751 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
752 752 for f in repo.dirstate.copies():
753 753 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
754 754
755 755 @command('debugdiscovery',
756 756 [('', 'old', None, _('use old-style discovery')),
757 757 ('', 'nonheads', None,
758 758 _('use old-style discovery with non-heads included')),
759 759 ('', 'rev', [], 'restrict discovery to this set of revs'),
760 760 ] + cmdutil.remoteopts,
761 761 _('[--rev REV] [OTHER]'))
762 762 def debugdiscovery(ui, repo, remoteurl="default", **opts):
763 763 """runs the changeset discovery protocol in isolation"""
764 764 opts = pycompat.byteskwargs(opts)
765 765 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
766 766 remote = hg.peer(repo, opts, remoteurl)
767 767 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
768 768
769 769 # make sure tests are repeatable
770 770 random.seed(12323)
771 771
772 772 def doit(pushedrevs, remoteheads, remote=remote):
773 773 if opts.get('old'):
774 774 if not util.safehasattr(remote, 'branches'):
775 775 # enable in-client legacy support
776 776 remote = localrepo.locallegacypeer(remote.local())
777 777 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
778 778 force=True)
779 779 common = set(common)
780 780 if not opts.get('nonheads'):
781 781 ui.write(("unpruned common: %s\n") %
782 782 " ".join(sorted(short(n) for n in common)))
783 783 dag = dagutil.revlogdag(repo.changelog)
784 784 all = dag.ancestorset(dag.internalizeall(common))
785 785 common = dag.externalizeall(dag.headsetofconnecteds(all))
786 786 else:
787 787 nodes = None
788 788 if pushedrevs:
789 789 revs = scmutil.revrange(repo, pushedrevs)
790 790 nodes = [repo[r].node() for r in revs]
791 791 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote,
792 792 ancestorsof=nodes)
793 793 common = set(common)
794 794 rheads = set(hds)
795 795 lheads = set(repo.heads())
796 796 ui.write(("common heads: %s\n") %
797 797 " ".join(sorted(short(n) for n in common)))
798 798 if lheads <= common:
799 799 ui.write(("local is subset\n"))
800 800 elif rheads <= common:
801 801 ui.write(("remote is subset\n"))
802 802
803 803 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
804 804 localrevs = opts['rev']
805 805 doit(localrevs, remoterevs)
806 806
807 807 _chunksize = 4 << 10
808 808
809 809 @command('debugdownload',
810 810 [
811 811 ('o', 'output', '', _('path')),
812 812 ],
813 813 optionalrepo=True)
814 814 def debugdownload(ui, repo, url, output=None, **opts):
815 815 """download a resource using Mercurial logic and config
816 816 """
817 817 fh = urlmod.open(ui, url, output)
818 818
819 819 dest = ui
820 820 if output:
821 821 dest = open(output, "wb", _chunksize)
822 822 try:
823 823 data = fh.read(_chunksize)
824 824 while data:
825 825 dest.write(data)
826 826 data = fh.read(_chunksize)
827 827 finally:
828 828 if output:
829 829 dest.close()
830 830
831 831 @command('debugextensions', cmdutil.formatteropts, [], optionalrepo=True)
832 832 def debugextensions(ui, repo, **opts):
833 833 '''show information about active extensions'''
834 834 opts = pycompat.byteskwargs(opts)
835 835 exts = extensions.extensions(ui)
836 836 hgver = util.version()
837 837 fm = ui.formatter('debugextensions', opts)
838 838 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
839 839 isinternal = extensions.ismoduleinternal(extmod)
840 840 extsource = pycompat.fsencode(extmod.__file__)
841 841 if isinternal:
842 842 exttestedwith = [] # never expose magic string to users
843 843 else:
844 844 exttestedwith = getattr(extmod, 'testedwith', '').split()
845 845 extbuglink = getattr(extmod, 'buglink', None)
846 846
847 847 fm.startitem()
848 848
849 849 if ui.quiet or ui.verbose:
850 850 fm.write('name', '%s\n', extname)
851 851 else:
852 852 fm.write('name', '%s', extname)
853 853 if isinternal or hgver in exttestedwith:
854 854 fm.plain('\n')
855 855 elif not exttestedwith:
856 856 fm.plain(_(' (untested!)\n'))
857 857 else:
858 858 lasttestedversion = exttestedwith[-1]
859 859 fm.plain(' (%s!)\n' % lasttestedversion)
860 860
861 861 fm.condwrite(ui.verbose and extsource, 'source',
862 862 _(' location: %s\n'), extsource or "")
863 863
864 864 if ui.verbose:
865 865 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
866 866 fm.data(bundled=isinternal)
867 867
868 868 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
869 869 _(' tested with: %s\n'),
870 870 fm.formatlist(exttestedwith, name='ver'))
871 871
872 872 fm.condwrite(ui.verbose and extbuglink, 'buglink',
873 873 _(' bug reporting: %s\n'), extbuglink or "")
874 874
875 875 fm.end()
876 876
877 877 @command('debugfileset',
878 878 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV')),
879 879 ('', 'all-files', False,
880 880 _('test files from all revisions and working directory'))],
881 881 _('[-r REV] [--all-files] FILESPEC'))
882 882 def debugfileset(ui, repo, expr, **opts):
883 883 '''parse and apply a fileset specification'''
884 884 opts = pycompat.byteskwargs(opts)
885 885 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
886 886 if ui.verbose:
887 887 tree = fileset.parse(expr)
888 888 ui.note(fileset.prettyformat(tree), "\n")
889 889
890 890 files = set()
891 891 if opts['all_files']:
892 892 for r in repo:
893 893 c = repo[r]
894 894 files.update(c.files())
895 895 files.update(c.substate)
896 896 if opts['all_files'] or ctx.rev() is None:
897 897 wctx = repo[None]
898 898 files.update(repo.dirstate.walk(scmutil.matchall(repo),
899 899 subrepos=list(wctx.substate),
900 900 unknown=True, ignored=True))
901 901 files.update(wctx.substate)
902 902 else:
903 903 files.update(ctx.files())
904 904 files.update(ctx.substate)
905 905
906 m = scmutil.matchfiles(repo, ctx.getfileset(expr))
906 m = ctx.matchfileset(expr)
907 907 for f in sorted(files):
908 908 if not m(f):
909 909 continue
910 910 ui.write("%s\n" % f)
911 911
912 912 @command('debugformat',
913 913 [] + cmdutil.formatteropts,
914 914 _(''))
915 915 def debugformat(ui, repo, **opts):
916 916 """display format information about the current repository
917 917
918 918 Use --verbose to get extra information about current config value and
919 919 Mercurial default."""
920 920 opts = pycompat.byteskwargs(opts)
921 921 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
922 922 maxvariantlength = max(len('format-variant'), maxvariantlength)
923 923
924 924 def makeformatname(name):
925 925 return '%s:' + (' ' * (maxvariantlength - len(name)))
926 926
927 927 fm = ui.formatter('debugformat', opts)
928 928 if fm.isplain():
929 929 def formatvalue(value):
930 930 if util.safehasattr(value, 'startswith'):
931 931 return value
932 932 if value:
933 933 return 'yes'
934 934 else:
935 935 return 'no'
936 936 else:
937 937 formatvalue = pycompat.identity
938 938
939 939 fm.plain('format-variant')
940 940 fm.plain(' ' * (maxvariantlength - len('format-variant')))
941 941 fm.plain(' repo')
942 942 if ui.verbose:
943 943 fm.plain(' config default')
944 944 fm.plain('\n')
945 945 for fv in upgrade.allformatvariant:
946 946 fm.startitem()
947 947 repovalue = fv.fromrepo(repo)
948 948 configvalue = fv.fromconfig(repo)
949 949
950 950 if repovalue != configvalue:
951 951 namelabel = 'formatvariant.name.mismatchconfig'
952 952 repolabel = 'formatvariant.repo.mismatchconfig'
953 953 elif repovalue != fv.default:
954 954 namelabel = 'formatvariant.name.mismatchdefault'
955 955 repolabel = 'formatvariant.repo.mismatchdefault'
956 956 else:
957 957 namelabel = 'formatvariant.name.uptodate'
958 958 repolabel = 'formatvariant.repo.uptodate'
959 959
960 960 fm.write('name', makeformatname(fv.name), fv.name,
961 961 label=namelabel)
962 962 fm.write('repo', ' %3s', formatvalue(repovalue),
963 963 label=repolabel)
964 964 if fv.default != configvalue:
965 965 configlabel = 'formatvariant.config.special'
966 966 else:
967 967 configlabel = 'formatvariant.config.default'
968 968 fm.condwrite(ui.verbose, 'config', ' %6s', formatvalue(configvalue),
969 969 label=configlabel)
970 970 fm.condwrite(ui.verbose, 'default', ' %7s', formatvalue(fv.default),
971 971 label='formatvariant.default')
972 972 fm.plain('\n')
973 973 fm.end()
974 974
975 975 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
976 976 def debugfsinfo(ui, path="."):
977 977 """show information detected about current filesystem"""
978 978 ui.write(('path: %s\n') % path)
979 979 ui.write(('mounted on: %s\n') % (util.getfsmountpoint(path) or '(unknown)'))
980 980 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
981 981 ui.write(('fstype: %s\n') % (util.getfstype(path) or '(unknown)'))
982 982 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
983 983 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
984 984 casesensitive = '(unknown)'
985 985 try:
986 986 with pycompat.namedtempfile(prefix='.debugfsinfo', dir=path) as f:
987 987 casesensitive = util.fscasesensitive(f.name) and 'yes' or 'no'
988 988 except OSError:
989 989 pass
990 990 ui.write(('case-sensitive: %s\n') % casesensitive)
991 991
992 992 @command('debuggetbundle',
993 993 [('H', 'head', [], _('id of head node'), _('ID')),
994 994 ('C', 'common', [], _('id of common node'), _('ID')),
995 995 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
996 996 _('REPO FILE [-H|-C ID]...'),
997 997 norepo=True)
998 998 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
999 999 """retrieves a bundle from a repo
1000 1000
1001 1001 Every ID must be a full-length hex node id string. Saves the bundle to the
1002 1002 given file.
1003 1003 """
1004 1004 opts = pycompat.byteskwargs(opts)
1005 1005 repo = hg.peer(ui, opts, repopath)
1006 1006 if not repo.capable('getbundle'):
1007 1007 raise error.Abort("getbundle() not supported by target repository")
1008 1008 args = {}
1009 1009 if common:
1010 1010 args[r'common'] = [bin(s) for s in common]
1011 1011 if head:
1012 1012 args[r'heads'] = [bin(s) for s in head]
1013 1013 # TODO: get desired bundlecaps from command line.
1014 1014 args[r'bundlecaps'] = None
1015 1015 bundle = repo.getbundle('debug', **args)
1016 1016
1017 1017 bundletype = opts.get('type', 'bzip2').lower()
1018 1018 btypes = {'none': 'HG10UN',
1019 1019 'bzip2': 'HG10BZ',
1020 1020 'gzip': 'HG10GZ',
1021 1021 'bundle2': 'HG20'}
1022 1022 bundletype = btypes.get(bundletype)
1023 1023 if bundletype not in bundle2.bundletypes:
1024 1024 raise error.Abort(_('unknown bundle type specified with --type'))
1025 1025 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
1026 1026
1027 1027 @command('debugignore', [], '[FILE]')
1028 1028 def debugignore(ui, repo, *files, **opts):
1029 1029 """display the combined ignore pattern and information about ignored files
1030 1030
1031 1031 With no argument display the combined ignore pattern.
1032 1032
1033 1033 Given space separated file names, shows if the given file is ignored and
1034 1034 if so, show the ignore rule (file and line number) that matched it.
1035 1035 """
1036 1036 ignore = repo.dirstate._ignore
1037 1037 if not files:
1038 1038 # Show all the patterns
1039 1039 ui.write("%s\n" % pycompat.byterepr(ignore))
1040 1040 else:
1041 1041 m = scmutil.match(repo[None], pats=files)
1042 1042 for f in m.files():
1043 1043 nf = util.normpath(f)
1044 1044 ignored = None
1045 1045 ignoredata = None
1046 1046 if nf != '.':
1047 1047 if ignore(nf):
1048 1048 ignored = nf
1049 1049 ignoredata = repo.dirstate._ignorefileandline(nf)
1050 1050 else:
1051 1051 for p in util.finddirs(nf):
1052 1052 if ignore(p):
1053 1053 ignored = p
1054 1054 ignoredata = repo.dirstate._ignorefileandline(p)
1055 1055 break
1056 1056 if ignored:
1057 1057 if ignored == nf:
1058 1058 ui.write(_("%s is ignored\n") % m.uipath(f))
1059 1059 else:
1060 1060 ui.write(_("%s is ignored because of "
1061 1061 "containing folder %s\n")
1062 1062 % (m.uipath(f), ignored))
1063 1063 ignorefile, lineno, line = ignoredata
1064 1064 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
1065 1065 % (ignorefile, lineno, line))
1066 1066 else:
1067 1067 ui.write(_("%s is not ignored\n") % m.uipath(f))
1068 1068
1069 1069 @command('debugindex', cmdutil.debugrevlogopts +
1070 1070 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1071 1071 _('[-f FORMAT] -c|-m|FILE'),
1072 1072 optionalrepo=True)
1073 1073 def debugindex(ui, repo, file_=None, **opts):
1074 1074 """dump the contents of an index file"""
1075 1075 opts = pycompat.byteskwargs(opts)
1076 1076 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1077 1077 format = opts.get('format', 0)
1078 1078 if format not in (0, 1):
1079 1079 raise error.Abort(_("unknown format %d") % format)
1080 1080
1081 1081 if ui.debugflag:
1082 1082 shortfn = hex
1083 1083 else:
1084 1084 shortfn = short
1085 1085
1086 1086 # There might not be anything in r, so have a sane default
1087 1087 idlen = 12
1088 1088 for i in r:
1089 1089 idlen = len(shortfn(r.node(i)))
1090 1090 break
1091 1091
1092 1092 if format == 0:
1093 1093 if ui.verbose:
1094 1094 ui.write((" rev offset length linkrev"
1095 1095 " %s %s p2\n") % ("nodeid".ljust(idlen),
1096 1096 "p1".ljust(idlen)))
1097 1097 else:
1098 1098 ui.write((" rev linkrev %s %s p2\n") % (
1099 1099 "nodeid".ljust(idlen), "p1".ljust(idlen)))
1100 1100 elif format == 1:
1101 1101 if ui.verbose:
1102 1102 ui.write((" rev flag offset length size link p1"
1103 1103 " p2 %s\n") % "nodeid".rjust(idlen))
1104 1104 else:
1105 1105 ui.write((" rev flag size link p1 p2 %s\n") %
1106 1106 "nodeid".rjust(idlen))
1107 1107
1108 1108 for i in r:
1109 1109 node = r.node(i)
1110 1110 if format == 0:
1111 1111 try:
1112 1112 pp = r.parents(node)
1113 1113 except Exception:
1114 1114 pp = [nullid, nullid]
1115 1115 if ui.verbose:
1116 1116 ui.write("% 6d % 9d % 7d % 7d %s %s %s\n" % (
1117 1117 i, r.start(i), r.length(i), r.linkrev(i),
1118 1118 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
1119 1119 else:
1120 1120 ui.write("% 6d % 7d %s %s %s\n" % (
1121 1121 i, r.linkrev(i), shortfn(node), shortfn(pp[0]),
1122 1122 shortfn(pp[1])))
1123 1123 elif format == 1:
1124 1124 pr = r.parentrevs(i)
1125 1125 if ui.verbose:
1126 1126 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n" % (
1127 1127 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1128 1128 r.linkrev(i), pr[0], pr[1], shortfn(node)))
1129 1129 else:
1130 1130 ui.write("% 6d %04x % 8d % 6d % 6d % 6d %s\n" % (
1131 1131 i, r.flags(i), r.rawsize(i), r.linkrev(i), pr[0], pr[1],
1132 1132 shortfn(node)))
1133 1133
1134 1134 @command('debugindexdot', cmdutil.debugrevlogopts,
1135 1135 _('-c|-m|FILE'), optionalrepo=True)
1136 1136 def debugindexdot(ui, repo, file_=None, **opts):
1137 1137 """dump an index DAG as a graphviz dot file"""
1138 1138 opts = pycompat.byteskwargs(opts)
1139 1139 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
1140 1140 ui.write(("digraph G {\n"))
1141 1141 for i in r:
1142 1142 node = r.node(i)
1143 1143 pp = r.parents(node)
1144 1144 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1145 1145 if pp[1] != nullid:
1146 1146 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1147 1147 ui.write("}\n")
1148 1148
1149 1149 @command('debuginstall', [] + cmdutil.formatteropts, '', norepo=True)
1150 1150 def debuginstall(ui, **opts):
1151 1151 '''test Mercurial installation
1152 1152
1153 1153 Returns 0 on success.
1154 1154 '''
1155 1155 opts = pycompat.byteskwargs(opts)
1156 1156
1157 1157 def writetemp(contents):
1158 1158 (fd, name) = pycompat.mkstemp(prefix="hg-debuginstall-")
1159 1159 f = os.fdopen(fd, r"wb")
1160 1160 f.write(contents)
1161 1161 f.close()
1162 1162 return name
1163 1163
1164 1164 problems = 0
1165 1165
1166 1166 fm = ui.formatter('debuginstall', opts)
1167 1167 fm.startitem()
1168 1168
1169 1169 # encoding
1170 1170 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
1171 1171 err = None
1172 1172 try:
1173 1173 codecs.lookup(pycompat.sysstr(encoding.encoding))
1174 1174 except LookupError as inst:
1175 1175 err = stringutil.forcebytestr(inst)
1176 1176 problems += 1
1177 1177 fm.condwrite(err, 'encodingerror', _(" %s\n"
1178 1178 " (check that your locale is properly set)\n"), err)
1179 1179
1180 1180 # Python
1181 1181 fm.write('pythonexe', _("checking Python executable (%s)\n"),
1182 1182 pycompat.sysexecutable)
1183 1183 fm.write('pythonver', _("checking Python version (%s)\n"),
1184 1184 ("%d.%d.%d" % sys.version_info[:3]))
1185 1185 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
1186 1186 os.path.dirname(pycompat.fsencode(os.__file__)))
1187 1187
1188 1188 security = set(sslutil.supportedprotocols)
1189 1189 if sslutil.hassni:
1190 1190 security.add('sni')
1191 1191
1192 1192 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
1193 1193 fm.formatlist(sorted(security), name='protocol',
1194 1194 fmt='%s', sep=','))
1195 1195
1196 1196 # These are warnings, not errors. So don't increment problem count. This
1197 1197 # may change in the future.
1198 1198 if 'tls1.2' not in security:
1199 1199 fm.plain(_(' TLS 1.2 not supported by Python install; '
1200 1200 'network connections lack modern security\n'))
1201 1201 if 'sni' not in security:
1202 1202 fm.plain(_(' SNI not supported by Python install; may have '
1203 1203 'connectivity issues with some servers\n'))
1204 1204
1205 1205 # TODO print CA cert info
1206 1206
1207 1207 # hg version
1208 1208 hgver = util.version()
1209 1209 fm.write('hgver', _("checking Mercurial version (%s)\n"),
1210 1210 hgver.split('+')[0])
1211 1211 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
1212 1212 '+'.join(hgver.split('+')[1:]))
1213 1213
1214 1214 # compiled modules
1215 1215 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
1216 1216 policy.policy)
1217 1217 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
1218 1218 os.path.dirname(pycompat.fsencode(__file__)))
1219 1219
1220 1220 if policy.policy in ('c', 'allow'):
1221 1221 err = None
1222 1222 try:
1223 1223 from .cext import (
1224 1224 base85,
1225 1225 bdiff,
1226 1226 mpatch,
1227 1227 osutil,
1228 1228 )
1229 1229 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1230 1230 except Exception as inst:
1231 1231 err = stringutil.forcebytestr(inst)
1232 1232 problems += 1
1233 1233 fm.condwrite(err, 'extensionserror', " %s\n", err)
1234 1234
1235 1235 compengines = util.compengines._engines.values()
1236 1236 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1237 1237 fm.formatlist(sorted(e.name() for e in compengines),
1238 1238 name='compengine', fmt='%s', sep=', '))
1239 1239 fm.write('compenginesavail', _('checking available compression engines '
1240 1240 '(%s)\n'),
1241 1241 fm.formatlist(sorted(e.name() for e in compengines
1242 1242 if e.available()),
1243 1243 name='compengine', fmt='%s', sep=', '))
1244 1244 wirecompengines = util.compengines.supportedwireengines(util.SERVERROLE)
1245 1245 fm.write('compenginesserver', _('checking available compression engines '
1246 1246 'for wire protocol (%s)\n'),
1247 1247 fm.formatlist([e.name() for e in wirecompengines
1248 1248 if e.wireprotosupport()],
1249 1249 name='compengine', fmt='%s', sep=', '))
1250 1250 re2 = 'missing'
1251 1251 if util._re2:
1252 1252 re2 = 'available'
1253 1253 fm.plain(_('checking "re2" regexp engine (%s)\n') % re2)
1254 1254 fm.data(re2=bool(util._re2))
1255 1255
1256 1256 # templates
1257 1257 p = templater.templatepaths()
1258 1258 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1259 1259 fm.condwrite(not p, '', _(" no template directories found\n"))
1260 1260 if p:
1261 1261 m = templater.templatepath("map-cmdline.default")
1262 1262 if m:
1263 1263 # template found, check if it is working
1264 1264 err = None
1265 1265 try:
1266 1266 templater.templater.frommapfile(m)
1267 1267 except Exception as inst:
1268 1268 err = stringutil.forcebytestr(inst)
1269 1269 p = None
1270 1270 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1271 1271 else:
1272 1272 p = None
1273 1273 fm.condwrite(p, 'defaulttemplate',
1274 1274 _("checking default template (%s)\n"), m)
1275 1275 fm.condwrite(not m, 'defaulttemplatenotfound',
1276 1276 _(" template '%s' not found\n"), "default")
1277 1277 if not p:
1278 1278 problems += 1
1279 1279 fm.condwrite(not p, '',
1280 1280 _(" (templates seem to have been installed incorrectly)\n"))
1281 1281
1282 1282 # editor
1283 1283 editor = ui.geteditor()
1284 1284 editor = util.expandpath(editor)
1285 1285 editorbin = procutil.shellsplit(editor)[0]
1286 1286 fm.write('editor', _("checking commit editor... (%s)\n"), editorbin)
1287 1287 cmdpath = procutil.findexe(editorbin)
1288 1288 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1289 1289 _(" No commit editor set and can't find %s in PATH\n"
1290 1290 " (specify a commit editor in your configuration"
1291 1291 " file)\n"), not cmdpath and editor == 'vi' and editorbin)
1292 1292 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1293 1293 _(" Can't find editor '%s' in PATH\n"
1294 1294 " (specify a commit editor in your configuration"
1295 1295 " file)\n"), not cmdpath and editorbin)
1296 1296 if not cmdpath and editor != 'vi':
1297 1297 problems += 1
1298 1298
1299 1299 # check username
1300 1300 username = None
1301 1301 err = None
1302 1302 try:
1303 1303 username = ui.username()
1304 1304 except error.Abort as e:
1305 1305 err = stringutil.forcebytestr(e)
1306 1306 problems += 1
1307 1307
1308 1308 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
1309 1309 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
1310 1310 " (specify a username in your configuration file)\n"), err)
1311 1311
1312 1312 fm.condwrite(not problems, '',
1313 1313 _("no problems detected\n"))
1314 1314 if not problems:
1315 1315 fm.data(problems=problems)
1316 1316 fm.condwrite(problems, 'problems',
1317 1317 _("%d problems detected,"
1318 1318 " please check your install!\n"), problems)
1319 1319 fm.end()
1320 1320
1321 1321 return problems
1322 1322
1323 1323 @command('debugknown', [], _('REPO ID...'), norepo=True)
1324 1324 def debugknown(ui, repopath, *ids, **opts):
1325 1325 """test whether node ids are known to a repo
1326 1326
1327 1327 Every ID must be a full-length hex node id string. Returns a list of 0s
1328 1328 and 1s indicating unknown/known.
1329 1329 """
1330 1330 opts = pycompat.byteskwargs(opts)
1331 1331 repo = hg.peer(ui, opts, repopath)
1332 1332 if not repo.capable('known'):
1333 1333 raise error.Abort("known() not supported by target repository")
1334 1334 flags = repo.known([bin(s) for s in ids])
1335 1335 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1336 1336
1337 1337 @command('debuglabelcomplete', [], _('LABEL...'))
1338 1338 def debuglabelcomplete(ui, repo, *args):
1339 1339 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1340 1340 debugnamecomplete(ui, repo, *args)
1341 1341
1342 1342 @command('debuglocks',
1343 1343 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
1344 1344 ('W', 'force-wlock', None,
1345 1345 _('free the working state lock (DANGEROUS)')),
1346 1346 ('s', 'set-lock', None, _('set the store lock until stopped')),
1347 1347 ('S', 'set-wlock', None,
1348 1348 _('set the working state lock until stopped'))],
1349 1349 _('[OPTION]...'))
1350 1350 def debuglocks(ui, repo, **opts):
1351 1351 """show or modify state of locks
1352 1352
1353 1353 By default, this command will show which locks are held. This
1354 1354 includes the user and process holding the lock, the amount of time
1355 1355 the lock has been held, and the machine name where the process is
1356 1356 running if it's not local.
1357 1357
1358 1358 Locks protect the integrity of Mercurial's data, so should be
1359 1359 treated with care. System crashes or other interruptions may cause
1360 1360 locks to not be properly released, though Mercurial will usually
1361 1361 detect and remove such stale locks automatically.
1362 1362
1363 1363 However, detecting stale locks may not always be possible (for
1364 1364 instance, on a shared filesystem). Removing locks may also be
1365 1365 blocked by filesystem permissions.
1366 1366
1367 1367 Setting a lock will prevent other commands from changing the data.
1368 1368 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
1369 1369 The set locks are removed when the command exits.
1370 1370
1371 1371 Returns 0 if no locks are held.
1372 1372
1373 1373 """
1374 1374
1375 1375 if opts.get(r'force_lock'):
1376 1376 repo.svfs.unlink('lock')
1377 1377 if opts.get(r'force_wlock'):
1378 1378 repo.vfs.unlink('wlock')
1379 1379 if opts.get(r'force_lock') or opts.get(r'force_wlock'):
1380 1380 return 0
1381 1381
1382 1382 locks = []
1383 1383 try:
1384 1384 if opts.get(r'set_wlock'):
1385 1385 try:
1386 1386 locks.append(repo.wlock(False))
1387 1387 except error.LockHeld:
1388 1388 raise error.Abort(_('wlock is already held'))
1389 1389 if opts.get(r'set_lock'):
1390 1390 try:
1391 1391 locks.append(repo.lock(False))
1392 1392 except error.LockHeld:
1393 1393 raise error.Abort(_('lock is already held'))
1394 1394 if len(locks):
1395 1395 ui.promptchoice(_("ready to release the lock (y)? $$ &Yes"))
1396 1396 return 0
1397 1397 finally:
1398 1398 release(*locks)
1399 1399
1400 1400 now = time.time()
1401 1401 held = 0
1402 1402
1403 1403 def report(vfs, name, method):
1404 1404 # this causes stale locks to get reaped for more accurate reporting
1405 1405 try:
1406 1406 l = method(False)
1407 1407 except error.LockHeld:
1408 1408 l = None
1409 1409
1410 1410 if l:
1411 1411 l.release()
1412 1412 else:
1413 1413 try:
1414 1414 st = vfs.lstat(name)
1415 1415 age = now - st[stat.ST_MTIME]
1416 1416 user = util.username(st.st_uid)
1417 1417 locker = vfs.readlock(name)
1418 1418 if ":" in locker:
1419 1419 host, pid = locker.split(':')
1420 1420 if host == socket.gethostname():
1421 1421 locker = 'user %s, process %s' % (user, pid)
1422 1422 else:
1423 1423 locker = 'user %s, process %s, host %s' \
1424 1424 % (user, pid, host)
1425 1425 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
1426 1426 return 1
1427 1427 except OSError as e:
1428 1428 if e.errno != errno.ENOENT:
1429 1429 raise
1430 1430
1431 1431 ui.write(("%-6s free\n") % (name + ":"))
1432 1432 return 0
1433 1433
1434 1434 held += report(repo.svfs, "lock", repo.lock)
1435 1435 held += report(repo.vfs, "wlock", repo.wlock)
1436 1436
1437 1437 return held
1438 1438
1439 1439 @command('debugmergestate', [], '')
1440 1440 def debugmergestate(ui, repo, *args):
1441 1441 """print merge state
1442 1442
1443 1443 Use --verbose to print out information about whether v1 or v2 merge state
1444 1444 was chosen."""
1445 1445 def _hashornull(h):
1446 1446 if h == nullhex:
1447 1447 return 'null'
1448 1448 else:
1449 1449 return h
1450 1450
1451 1451 def printrecords(version):
1452 1452 ui.write(('* version %d records\n') % version)
1453 1453 if version == 1:
1454 1454 records = v1records
1455 1455 else:
1456 1456 records = v2records
1457 1457
1458 1458 for rtype, record in records:
1459 1459 # pretty print some record types
1460 1460 if rtype == 'L':
1461 1461 ui.write(('local: %s\n') % record)
1462 1462 elif rtype == 'O':
1463 1463 ui.write(('other: %s\n') % record)
1464 1464 elif rtype == 'm':
1465 1465 driver, mdstate = record.split('\0', 1)
1466 1466 ui.write(('merge driver: %s (state "%s")\n')
1467 1467 % (driver, mdstate))
1468 1468 elif rtype in 'FDC':
1469 1469 r = record.split('\0')
1470 1470 f, state, hash, lfile, afile, anode, ofile = r[0:7]
1471 1471 if version == 1:
1472 1472 onode = 'not stored in v1 format'
1473 1473 flags = r[7]
1474 1474 else:
1475 1475 onode, flags = r[7:9]
1476 1476 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
1477 1477 % (f, rtype, state, _hashornull(hash)))
1478 1478 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
1479 1479 ui.write((' ancestor path: %s (node %s)\n')
1480 1480 % (afile, _hashornull(anode)))
1481 1481 ui.write((' other path: %s (node %s)\n')
1482 1482 % (ofile, _hashornull(onode)))
1483 1483 elif rtype == 'f':
1484 1484 filename, rawextras = record.split('\0', 1)
1485 1485 extras = rawextras.split('\0')
1486 1486 i = 0
1487 1487 extrastrings = []
1488 1488 while i < len(extras):
1489 1489 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
1490 1490 i += 2
1491 1491
1492 1492 ui.write(('file extras: %s (%s)\n')
1493 1493 % (filename, ', '.join(extrastrings)))
1494 1494 elif rtype == 'l':
1495 1495 labels = record.split('\0', 2)
1496 1496 labels = [l for l in labels if len(l) > 0]
1497 1497 ui.write(('labels:\n'))
1498 1498 ui.write((' local: %s\n' % labels[0]))
1499 1499 ui.write((' other: %s\n' % labels[1]))
1500 1500 if len(labels) > 2:
1501 1501 ui.write((' base: %s\n' % labels[2]))
1502 1502 else:
1503 1503 ui.write(('unrecognized entry: %s\t%s\n')
1504 1504 % (rtype, record.replace('\0', '\t')))
1505 1505
1506 1506 # Avoid mergestate.read() since it may raise an exception for unsupported
1507 1507 # merge state records. We shouldn't be doing this, but this is OK since this
1508 1508 # command is pretty low-level.
1509 1509 ms = mergemod.mergestate(repo)
1510 1510
1511 1511 # sort so that reasonable information is on top
1512 1512 v1records = ms._readrecordsv1()
1513 1513 v2records = ms._readrecordsv2()
1514 1514 order = 'LOml'
1515 1515 def key(r):
1516 1516 idx = order.find(r[0])
1517 1517 if idx == -1:
1518 1518 return (1, r[1])
1519 1519 else:
1520 1520 return (0, idx)
1521 1521 v1records.sort(key=key)
1522 1522 v2records.sort(key=key)
1523 1523
1524 1524 if not v1records and not v2records:
1525 1525 ui.write(('no merge state found\n'))
1526 1526 elif not v2records:
1527 1527 ui.note(('no version 2 merge state\n'))
1528 1528 printrecords(1)
1529 1529 elif ms._v1v2match(v1records, v2records):
1530 1530 ui.note(('v1 and v2 states match: using v2\n'))
1531 1531 printrecords(2)
1532 1532 else:
1533 1533 ui.note(('v1 and v2 states mismatch: using v1\n'))
1534 1534 printrecords(1)
1535 1535 if ui.verbose:
1536 1536 printrecords(2)
1537 1537
1538 1538 @command('debugnamecomplete', [], _('NAME...'))
1539 1539 def debugnamecomplete(ui, repo, *args):
1540 1540 '''complete "names" - tags, open branch names, bookmark names'''
1541 1541
1542 1542 names = set()
1543 1543 # since we previously only listed open branches, we will handle that
1544 1544 # specially (after this for loop)
1545 1545 for name, ns in repo.names.iteritems():
1546 1546 if name != 'branches':
1547 1547 names.update(ns.listnames(repo))
1548 1548 names.update(tag for (tag, heads, tip, closed)
1549 1549 in repo.branchmap().iterbranches() if not closed)
1550 1550 completions = set()
1551 1551 if not args:
1552 1552 args = ['']
1553 1553 for a in args:
1554 1554 completions.update(n for n in names if n.startswith(a))
1555 1555 ui.write('\n'.join(sorted(completions)))
1556 1556 ui.write('\n')
1557 1557
1558 1558 @command('debugobsolete',
1559 1559 [('', 'flags', 0, _('markers flag')),
1560 1560 ('', 'record-parents', False,
1561 1561 _('record parent information for the precursor')),
1562 1562 ('r', 'rev', [], _('display markers relevant to REV')),
1563 1563 ('', 'exclusive', False, _('restrict display to markers only '
1564 1564 'relevant to REV')),
1565 1565 ('', 'index', False, _('display index of the marker')),
1566 1566 ('', 'delete', [], _('delete markers specified by indices')),
1567 1567 ] + cmdutil.commitopts2 + cmdutil.formatteropts,
1568 1568 _('[OBSOLETED [REPLACEMENT ...]]'))
1569 1569 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
1570 1570 """create arbitrary obsolete marker
1571 1571
1572 1572 With no arguments, displays the list of obsolescence markers."""
1573 1573
1574 1574 opts = pycompat.byteskwargs(opts)
1575 1575
1576 1576 def parsenodeid(s):
1577 1577 try:
1578 1578 # We do not use revsingle/revrange functions here to accept
1579 1579 # arbitrary node identifiers, possibly not present in the
1580 1580 # local repository.
1581 1581 n = bin(s)
1582 1582 if len(n) != len(nullid):
1583 1583 raise TypeError()
1584 1584 return n
1585 1585 except TypeError:
1586 1586 raise error.Abort('changeset references must be full hexadecimal '
1587 1587 'node identifiers')
1588 1588
1589 1589 if opts.get('delete'):
1590 1590 indices = []
1591 1591 for v in opts.get('delete'):
1592 1592 try:
1593 1593 indices.append(int(v))
1594 1594 except ValueError:
1595 1595 raise error.Abort(_('invalid index value: %r') % v,
1596 1596 hint=_('use integers for indices'))
1597 1597
1598 1598 if repo.currenttransaction():
1599 1599 raise error.Abort(_('cannot delete obsmarkers in the middle '
1600 1600 'of transaction.'))
1601 1601
1602 1602 with repo.lock():
1603 1603 n = repair.deleteobsmarkers(repo.obsstore, indices)
1604 1604 ui.write(_('deleted %i obsolescence markers\n') % n)
1605 1605
1606 1606 return
1607 1607
1608 1608 if precursor is not None:
1609 1609 if opts['rev']:
1610 1610 raise error.Abort('cannot select revision when creating marker')
1611 1611 metadata = {}
1612 1612 metadata['user'] = opts['user'] or ui.username()
1613 1613 succs = tuple(parsenodeid(succ) for succ in successors)
1614 1614 l = repo.lock()
1615 1615 try:
1616 1616 tr = repo.transaction('debugobsolete')
1617 1617 try:
1618 1618 date = opts.get('date')
1619 1619 if date:
1620 1620 date = dateutil.parsedate(date)
1621 1621 else:
1622 1622 date = None
1623 1623 prec = parsenodeid(precursor)
1624 1624 parents = None
1625 1625 if opts['record_parents']:
1626 1626 if prec not in repo.unfiltered():
1627 1627 raise error.Abort('cannot used --record-parents on '
1628 1628 'unknown changesets')
1629 1629 parents = repo.unfiltered()[prec].parents()
1630 1630 parents = tuple(p.node() for p in parents)
1631 1631 repo.obsstore.create(tr, prec, succs, opts['flags'],
1632 1632 parents=parents, date=date,
1633 1633 metadata=metadata, ui=ui)
1634 1634 tr.close()
1635 1635 except ValueError as exc:
1636 1636 raise error.Abort(_('bad obsmarker input: %s') %
1637 1637 pycompat.bytestr(exc))
1638 1638 finally:
1639 1639 tr.release()
1640 1640 finally:
1641 1641 l.release()
1642 1642 else:
1643 1643 if opts['rev']:
1644 1644 revs = scmutil.revrange(repo, opts['rev'])
1645 1645 nodes = [repo[r].node() for r in revs]
1646 1646 markers = list(obsutil.getmarkers(repo, nodes=nodes,
1647 1647 exclusive=opts['exclusive']))
1648 1648 markers.sort(key=lambda x: x._data)
1649 1649 else:
1650 1650 markers = obsutil.getmarkers(repo)
1651 1651
1652 1652 markerstoiter = markers
1653 1653 isrelevant = lambda m: True
1654 1654 if opts.get('rev') and opts.get('index'):
1655 1655 markerstoiter = obsutil.getmarkers(repo)
1656 1656 markerset = set(markers)
1657 1657 isrelevant = lambda m: m in markerset
1658 1658
1659 1659 fm = ui.formatter('debugobsolete', opts)
1660 1660 for i, m in enumerate(markerstoiter):
1661 1661 if not isrelevant(m):
1662 1662 # marker can be irrelevant when we're iterating over a set
1663 1663 # of markers (markerstoiter) which is bigger than the set
1664 1664 # of markers we want to display (markers)
1665 1665 # this can happen if both --index and --rev options are
1666 1666 # provided and thus we need to iterate over all of the markers
1667 1667 # to get the correct indices, but only display the ones that
1668 1668 # are relevant to --rev value
1669 1669 continue
1670 1670 fm.startitem()
1671 1671 ind = i if opts.get('index') else None
1672 1672 cmdutil.showmarker(fm, m, index=ind)
1673 1673 fm.end()
1674 1674
1675 1675 @command('debugpathcomplete',
1676 1676 [('f', 'full', None, _('complete an entire path')),
1677 1677 ('n', 'normal', None, _('show only normal files')),
1678 1678 ('a', 'added', None, _('show only added files')),
1679 1679 ('r', 'removed', None, _('show only removed files'))],
1680 1680 _('FILESPEC...'))
1681 1681 def debugpathcomplete(ui, repo, *specs, **opts):
1682 1682 '''complete part or all of a tracked path
1683 1683
1684 1684 This command supports shells that offer path name completion. It
1685 1685 currently completes only files already known to the dirstate.
1686 1686
1687 1687 Completion extends only to the next path segment unless
1688 1688 --full is specified, in which case entire paths are used.'''
1689 1689
1690 1690 def complete(path, acceptable):
1691 1691 dirstate = repo.dirstate
1692 1692 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
1693 1693 rootdir = repo.root + pycompat.ossep
1694 1694 if spec != repo.root and not spec.startswith(rootdir):
1695 1695 return [], []
1696 1696 if os.path.isdir(spec):
1697 1697 spec += '/'
1698 1698 spec = spec[len(rootdir):]
1699 1699 fixpaths = pycompat.ossep != '/'
1700 1700 if fixpaths:
1701 1701 spec = spec.replace(pycompat.ossep, '/')
1702 1702 speclen = len(spec)
1703 1703 fullpaths = opts[r'full']
1704 1704 files, dirs = set(), set()
1705 1705 adddir, addfile = dirs.add, files.add
1706 1706 for f, st in dirstate.iteritems():
1707 1707 if f.startswith(spec) and st[0] in acceptable:
1708 1708 if fixpaths:
1709 1709 f = f.replace('/', pycompat.ossep)
1710 1710 if fullpaths:
1711 1711 addfile(f)
1712 1712 continue
1713 1713 s = f.find(pycompat.ossep, speclen)
1714 1714 if s >= 0:
1715 1715 adddir(f[:s])
1716 1716 else:
1717 1717 addfile(f)
1718 1718 return files, dirs
1719 1719
1720 1720 acceptable = ''
1721 1721 if opts[r'normal']:
1722 1722 acceptable += 'nm'
1723 1723 if opts[r'added']:
1724 1724 acceptable += 'a'
1725 1725 if opts[r'removed']:
1726 1726 acceptable += 'r'
1727 1727 cwd = repo.getcwd()
1728 1728 if not specs:
1729 1729 specs = ['.']
1730 1730
1731 1731 files, dirs = set(), set()
1732 1732 for spec in specs:
1733 1733 f, d = complete(spec, acceptable or 'nmar')
1734 1734 files.update(f)
1735 1735 dirs.update(d)
1736 1736 files.update(dirs)
1737 1737 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
1738 1738 ui.write('\n')
1739 1739
1740 1740 @command('debugpeer', [], _('PATH'), norepo=True)
1741 1741 def debugpeer(ui, path):
1742 1742 """establish a connection to a peer repository"""
1743 1743 # Always enable peer request logging. Requires --debug to display
1744 1744 # though.
1745 1745 overrides = {
1746 1746 ('devel', 'debug.peer-request'): True,
1747 1747 }
1748 1748
1749 1749 with ui.configoverride(overrides):
1750 1750 peer = hg.peer(ui, {}, path)
1751 1751
1752 1752 local = peer.local() is not None
1753 1753 canpush = peer.canpush()
1754 1754
1755 1755 ui.write(_('url: %s\n') % peer.url())
1756 1756 ui.write(_('local: %s\n') % (_('yes') if local else _('no')))
1757 1757 ui.write(_('pushable: %s\n') % (_('yes') if canpush else _('no')))
1758 1758
1759 1759 @command('debugpickmergetool',
1760 1760 [('r', 'rev', '', _('check for files in this revision'), _('REV')),
1761 1761 ('', 'changedelete', None, _('emulate merging change and delete')),
1762 1762 ] + cmdutil.walkopts + cmdutil.mergetoolopts,
1763 1763 _('[PATTERN]...'),
1764 1764 inferrepo=True)
1765 1765 def debugpickmergetool(ui, repo, *pats, **opts):
1766 1766 """examine which merge tool is chosen for specified file
1767 1767
1768 1768 As described in :hg:`help merge-tools`, Mercurial examines
1769 1769 configurations below in this order to decide which merge tool is
1770 1770 chosen for specified file.
1771 1771
1772 1772 1. ``--tool`` option
1773 1773 2. ``HGMERGE`` environment variable
1774 1774 3. configurations in ``merge-patterns`` section
1775 1775 4. configuration of ``ui.merge``
1776 1776 5. configurations in ``merge-tools`` section
1777 1777 6. ``hgmerge`` tool (for historical reason only)
1778 1778 7. default tool for fallback (``:merge`` or ``:prompt``)
1779 1779
1780 1780 This command writes out examination result in the style below::
1781 1781
1782 1782 FILE = MERGETOOL
1783 1783
1784 1784 By default, all files known in the first parent context of the
1785 1785 working directory are examined. Use file patterns and/or -I/-X
1786 1786 options to limit target files. -r/--rev is also useful to examine
1787 1787 files in another context without actual updating to it.
1788 1788
1789 1789 With --debug, this command shows warning messages while matching
1790 1790 against ``merge-patterns`` and so on, too. It is recommended to
1791 1791 use this option with explicit file patterns and/or -I/-X options,
1792 1792 because this option increases amount of output per file according
1793 1793 to configurations in hgrc.
1794 1794
1795 1795 With -v/--verbose, this command shows configurations below at
1796 1796 first (only if specified).
1797 1797
1798 1798 - ``--tool`` option
1799 1799 - ``HGMERGE`` environment variable
1800 1800 - configuration of ``ui.merge``
1801 1801
1802 1802 If merge tool is chosen before matching against
1803 1803 ``merge-patterns``, this command can't show any helpful
1804 1804 information, even with --debug. In such case, information above is
1805 1805 useful to know why a merge tool is chosen.
1806 1806 """
1807 1807 opts = pycompat.byteskwargs(opts)
1808 1808 overrides = {}
1809 1809 if opts['tool']:
1810 1810 overrides[('ui', 'forcemerge')] = opts['tool']
1811 1811 ui.note(('with --tool %r\n') % (pycompat.bytestr(opts['tool'])))
1812 1812
1813 1813 with ui.configoverride(overrides, 'debugmergepatterns'):
1814 1814 hgmerge = encoding.environ.get("HGMERGE")
1815 1815 if hgmerge is not None:
1816 1816 ui.note(('with HGMERGE=%r\n') % (pycompat.bytestr(hgmerge)))
1817 1817 uimerge = ui.config("ui", "merge")
1818 1818 if uimerge:
1819 1819 ui.note(('with ui.merge=%r\n') % (pycompat.bytestr(uimerge)))
1820 1820
1821 1821 ctx = scmutil.revsingle(repo, opts.get('rev'))
1822 1822 m = scmutil.match(ctx, pats, opts)
1823 1823 changedelete = opts['changedelete']
1824 1824 for path in ctx.walk(m):
1825 1825 fctx = ctx[path]
1826 1826 try:
1827 1827 if not ui.debugflag:
1828 1828 ui.pushbuffer(error=True)
1829 1829 tool, toolpath = filemerge._picktool(repo, ui, path,
1830 1830 fctx.isbinary(),
1831 1831 'l' in fctx.flags(),
1832 1832 changedelete)
1833 1833 finally:
1834 1834 if not ui.debugflag:
1835 1835 ui.popbuffer()
1836 1836 ui.write(('%s = %s\n') % (path, tool))
1837 1837
1838 1838 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
1839 1839 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1840 1840 '''access the pushkey key/value protocol
1841 1841
1842 1842 With two args, list the keys in the given namespace.
1843 1843
1844 1844 With five args, set a key to new if it currently is set to old.
1845 1845 Reports success or failure.
1846 1846 '''
1847 1847
1848 1848 target = hg.peer(ui, {}, repopath)
1849 1849 if keyinfo:
1850 1850 key, old, new = keyinfo
1851 1851 with target.commandexecutor() as e:
1852 1852 r = e.callcommand('pushkey', {
1853 1853 'namespace': namespace,
1854 1854 'key': key,
1855 1855 'old': old,
1856 1856 'new': new,
1857 1857 }).result()
1858 1858
1859 1859 ui.status(pycompat.bytestr(r) + '\n')
1860 1860 return not r
1861 1861 else:
1862 1862 for k, v in sorted(target.listkeys(namespace).iteritems()):
1863 1863 ui.write("%s\t%s\n" % (stringutil.escapestr(k),
1864 1864 stringutil.escapestr(v)))
1865 1865
1866 1866 @command('debugpvec', [], _('A B'))
1867 1867 def debugpvec(ui, repo, a, b=None):
1868 1868 ca = scmutil.revsingle(repo, a)
1869 1869 cb = scmutil.revsingle(repo, b)
1870 1870 pa = pvec.ctxpvec(ca)
1871 1871 pb = pvec.ctxpvec(cb)
1872 1872 if pa == pb:
1873 1873 rel = "="
1874 1874 elif pa > pb:
1875 1875 rel = ">"
1876 1876 elif pa < pb:
1877 1877 rel = "<"
1878 1878 elif pa | pb:
1879 1879 rel = "|"
1880 1880 ui.write(_("a: %s\n") % pa)
1881 1881 ui.write(_("b: %s\n") % pb)
1882 1882 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
1883 1883 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
1884 1884 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
1885 1885 pa.distance(pb), rel))
1886 1886
1887 1887 @command('debugrebuilddirstate|debugrebuildstate',
1888 1888 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
1889 1889 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
1890 1890 'the working copy parent')),
1891 1891 ],
1892 1892 _('[-r REV]'))
1893 1893 def debugrebuilddirstate(ui, repo, rev, **opts):
1894 1894 """rebuild the dirstate as it would look like for the given revision
1895 1895
1896 1896 If no revision is specified the first current parent will be used.
1897 1897
1898 1898 The dirstate will be set to the files of the given revision.
1899 1899 The actual working directory content or existing dirstate
1900 1900 information such as adds or removes is not considered.
1901 1901
1902 1902 ``minimal`` will only rebuild the dirstate status for files that claim to be
1903 1903 tracked but are not in the parent manifest, or that exist in the parent
1904 1904 manifest but are not in the dirstate. It will not change adds, removes, or
1905 1905 modified files that are in the working copy parent.
1906 1906
1907 1907 One use of this command is to make the next :hg:`status` invocation
1908 1908 check the actual file content.
1909 1909 """
1910 1910 ctx = scmutil.revsingle(repo, rev)
1911 1911 with repo.wlock():
1912 1912 dirstate = repo.dirstate
1913 1913 changedfiles = None
1914 1914 # See command doc for what minimal does.
1915 1915 if opts.get(r'minimal'):
1916 1916 manifestfiles = set(ctx.manifest().keys())
1917 1917 dirstatefiles = set(dirstate)
1918 1918 manifestonly = manifestfiles - dirstatefiles
1919 1919 dsonly = dirstatefiles - manifestfiles
1920 1920 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
1921 1921 changedfiles = manifestonly | dsnotadded
1922 1922
1923 1923 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
1924 1924
1925 1925 @command('debugrebuildfncache', [], '')
1926 1926 def debugrebuildfncache(ui, repo):
1927 1927 """rebuild the fncache file"""
1928 1928 repair.rebuildfncache(ui, repo)
1929 1929
1930 1930 @command('debugrename',
1931 1931 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1932 1932 _('[-r REV] FILE'))
1933 1933 def debugrename(ui, repo, file1, *pats, **opts):
1934 1934 """dump rename information"""
1935 1935
1936 1936 opts = pycompat.byteskwargs(opts)
1937 1937 ctx = scmutil.revsingle(repo, opts.get('rev'))
1938 1938 m = scmutil.match(ctx, (file1,) + pats, opts)
1939 1939 for abs in ctx.walk(m):
1940 1940 fctx = ctx[abs]
1941 1941 o = fctx.filelog().renamed(fctx.filenode())
1942 1942 rel = m.rel(abs)
1943 1943 if o:
1944 1944 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1945 1945 else:
1946 1946 ui.write(_("%s not renamed\n") % rel)
1947 1947
1948 1948 @command('debugrevlog', cmdutil.debugrevlogopts +
1949 1949 [('d', 'dump', False, _('dump index data'))],
1950 1950 _('-c|-m|FILE'),
1951 1951 optionalrepo=True)
1952 1952 def debugrevlog(ui, repo, file_=None, **opts):
1953 1953 """show data and statistics about a revlog"""
1954 1954 opts = pycompat.byteskwargs(opts)
1955 1955 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1956 1956
1957 1957 if opts.get("dump"):
1958 1958 numrevs = len(r)
1959 1959 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
1960 1960 " rawsize totalsize compression heads chainlen\n"))
1961 1961 ts = 0
1962 1962 heads = set()
1963 1963
1964 1964 for rev in xrange(numrevs):
1965 1965 dbase = r.deltaparent(rev)
1966 1966 if dbase == -1:
1967 1967 dbase = rev
1968 1968 cbase = r.chainbase(rev)
1969 1969 clen = r.chainlen(rev)
1970 1970 p1, p2 = r.parentrevs(rev)
1971 1971 rs = r.rawsize(rev)
1972 1972 ts = ts + rs
1973 1973 heads -= set(r.parentrevs(rev))
1974 1974 heads.add(rev)
1975 1975 try:
1976 1976 compression = ts / r.end(rev)
1977 1977 except ZeroDivisionError:
1978 1978 compression = 0
1979 1979 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
1980 1980 "%11d %5d %8d\n" %
1981 1981 (rev, p1, p2, r.start(rev), r.end(rev),
1982 1982 r.start(dbase), r.start(cbase),
1983 1983 r.start(p1), r.start(p2),
1984 1984 rs, ts, compression, len(heads), clen))
1985 1985 return 0
1986 1986
1987 1987 v = r.version
1988 1988 format = v & 0xFFFF
1989 1989 flags = []
1990 1990 gdelta = False
1991 1991 if v & revlog.FLAG_INLINE_DATA:
1992 1992 flags.append('inline')
1993 1993 if v & revlog.FLAG_GENERALDELTA:
1994 1994 gdelta = True
1995 1995 flags.append('generaldelta')
1996 1996 if not flags:
1997 1997 flags = ['(none)']
1998 1998
1999 1999 nummerges = 0
2000 2000 numfull = 0
2001 2001 numprev = 0
2002 2002 nump1 = 0
2003 2003 nump2 = 0
2004 2004 numother = 0
2005 2005 nump1prev = 0
2006 2006 nump2prev = 0
2007 2007 chainlengths = []
2008 2008 chainbases = []
2009 2009 chainspans = []
2010 2010
2011 2011 datasize = [None, 0, 0]
2012 2012 fullsize = [None, 0, 0]
2013 2013 deltasize = [None, 0, 0]
2014 2014 chunktypecounts = {}
2015 2015 chunktypesizes = {}
2016 2016
2017 2017 def addsize(size, l):
2018 2018 if l[0] is None or size < l[0]:
2019 2019 l[0] = size
2020 2020 if size > l[1]:
2021 2021 l[1] = size
2022 2022 l[2] += size
2023 2023
2024 2024 numrevs = len(r)
2025 2025 for rev in xrange(numrevs):
2026 2026 p1, p2 = r.parentrevs(rev)
2027 2027 delta = r.deltaparent(rev)
2028 2028 if format > 0:
2029 2029 addsize(r.rawsize(rev), datasize)
2030 2030 if p2 != nullrev:
2031 2031 nummerges += 1
2032 2032 size = r.length(rev)
2033 2033 if delta == nullrev:
2034 2034 chainlengths.append(0)
2035 2035 chainbases.append(r.start(rev))
2036 2036 chainspans.append(size)
2037 2037 numfull += 1
2038 2038 addsize(size, fullsize)
2039 2039 else:
2040 2040 chainlengths.append(chainlengths[delta] + 1)
2041 2041 baseaddr = chainbases[delta]
2042 2042 revaddr = r.start(rev)
2043 2043 chainbases.append(baseaddr)
2044 2044 chainspans.append((revaddr - baseaddr) + size)
2045 2045 addsize(size, deltasize)
2046 2046 if delta == rev - 1:
2047 2047 numprev += 1
2048 2048 if delta == p1:
2049 2049 nump1prev += 1
2050 2050 elif delta == p2:
2051 2051 nump2prev += 1
2052 2052 elif delta == p1:
2053 2053 nump1 += 1
2054 2054 elif delta == p2:
2055 2055 nump2 += 1
2056 2056 elif delta != nullrev:
2057 2057 numother += 1
2058 2058
2059 2059 # Obtain data on the raw chunks in the revlog.
2060 2060 segment = r._getsegmentforrevs(rev, rev)[1]
2061 2061 if segment:
2062 2062 chunktype = bytes(segment[0:1])
2063 2063 else:
2064 2064 chunktype = 'empty'
2065 2065
2066 2066 if chunktype not in chunktypecounts:
2067 2067 chunktypecounts[chunktype] = 0
2068 2068 chunktypesizes[chunktype] = 0
2069 2069
2070 2070 chunktypecounts[chunktype] += 1
2071 2071 chunktypesizes[chunktype] += size
2072 2072
2073 2073 # Adjust size min value for empty cases
2074 2074 for size in (datasize, fullsize, deltasize):
2075 2075 if size[0] is None:
2076 2076 size[0] = 0
2077 2077
2078 2078 numdeltas = numrevs - numfull
2079 2079 numoprev = numprev - nump1prev - nump2prev
2080 2080 totalrawsize = datasize[2]
2081 2081 datasize[2] /= numrevs
2082 2082 fulltotal = fullsize[2]
2083 2083 fullsize[2] /= numfull
2084 2084 deltatotal = deltasize[2]
2085 2085 if numrevs - numfull > 0:
2086 2086 deltasize[2] /= numrevs - numfull
2087 2087 totalsize = fulltotal + deltatotal
2088 2088 avgchainlen = sum(chainlengths) / numrevs
2089 2089 maxchainlen = max(chainlengths)
2090 2090 maxchainspan = max(chainspans)
2091 2091 compratio = 1
2092 2092 if totalsize:
2093 2093 compratio = totalrawsize / totalsize
2094 2094
2095 2095 basedfmtstr = '%%%dd\n'
2096 2096 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2097 2097
2098 2098 def dfmtstr(max):
2099 2099 return basedfmtstr % len(str(max))
2100 2100 def pcfmtstr(max, padding=0):
2101 2101 return basepcfmtstr % (len(str(max)), ' ' * padding)
2102 2102
2103 2103 def pcfmt(value, total):
2104 2104 if total:
2105 2105 return (value, 100 * float(value) / total)
2106 2106 else:
2107 2107 return value, 100.0
2108 2108
2109 2109 ui.write(('format : %d\n') % format)
2110 2110 ui.write(('flags : %s\n') % ', '.join(flags))
2111 2111
2112 2112 ui.write('\n')
2113 2113 fmt = pcfmtstr(totalsize)
2114 2114 fmt2 = dfmtstr(totalsize)
2115 2115 ui.write(('revisions : ') + fmt2 % numrevs)
2116 2116 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2117 2117 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2118 2118 ui.write(('revisions : ') + fmt2 % numrevs)
2119 2119 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2120 2120 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2121 2121 ui.write(('revision size : ') + fmt2 % totalsize)
2122 2122 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2123 2123 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2124 2124
2125 2125 def fmtchunktype(chunktype):
2126 2126 if chunktype == 'empty':
2127 2127 return ' %s : ' % chunktype
2128 2128 elif chunktype in pycompat.bytestr(string.ascii_letters):
2129 2129 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2130 2130 else:
2131 2131 return ' 0x%s : ' % hex(chunktype)
2132 2132
2133 2133 ui.write('\n')
2134 2134 ui.write(('chunks : ') + fmt2 % numrevs)
2135 2135 for chunktype in sorted(chunktypecounts):
2136 2136 ui.write(fmtchunktype(chunktype))
2137 2137 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2138 2138 ui.write(('chunks size : ') + fmt2 % totalsize)
2139 2139 for chunktype in sorted(chunktypecounts):
2140 2140 ui.write(fmtchunktype(chunktype))
2141 2141 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2142 2142
2143 2143 ui.write('\n')
2144 2144 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
2145 2145 ui.write(('avg chain length : ') + fmt % avgchainlen)
2146 2146 ui.write(('max chain length : ') + fmt % maxchainlen)
2147 2147 ui.write(('max chain reach : ') + fmt % maxchainspan)
2148 2148 ui.write(('compression ratio : ') + fmt % compratio)
2149 2149
2150 2150 if format > 0:
2151 2151 ui.write('\n')
2152 2152 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2153 2153 % tuple(datasize))
2154 2154 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2155 2155 % tuple(fullsize))
2156 2156 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2157 2157 % tuple(deltasize))
2158 2158
2159 2159 if numdeltas > 0:
2160 2160 ui.write('\n')
2161 2161 fmt = pcfmtstr(numdeltas)
2162 2162 fmt2 = pcfmtstr(numdeltas, 4)
2163 2163 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2164 2164 if numprev > 0:
2165 2165 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2166 2166 numprev))
2167 2167 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2168 2168 numprev))
2169 2169 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2170 2170 numprev))
2171 2171 if gdelta:
2172 2172 ui.write(('deltas against p1 : ')
2173 2173 + fmt % pcfmt(nump1, numdeltas))
2174 2174 ui.write(('deltas against p2 : ')
2175 2175 + fmt % pcfmt(nump2, numdeltas))
2176 2176 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2177 2177 numdeltas))
2178 2178
2179 2179 @command('debugrevspec',
2180 2180 [('', 'optimize', None,
2181 2181 _('print parsed tree after optimizing (DEPRECATED)')),
2182 2182 ('', 'show-revs', True, _('print list of result revisions (default)')),
2183 2183 ('s', 'show-set', None, _('print internal representation of result set')),
2184 2184 ('p', 'show-stage', [],
2185 2185 _('print parsed tree at the given stage'), _('NAME')),
2186 2186 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2187 2187 ('', 'verify-optimized', False, _('verify optimized result')),
2188 2188 ],
2189 2189 ('REVSPEC'))
2190 2190 def debugrevspec(ui, repo, expr, **opts):
2191 2191 """parse and apply a revision specification
2192 2192
2193 2193 Use -p/--show-stage option to print the parsed tree at the given stages.
2194 2194 Use -p all to print tree at every stage.
2195 2195
2196 2196 Use --no-show-revs option with -s or -p to print only the set
2197 2197 representation or the parsed tree respectively.
2198 2198
2199 2199 Use --verify-optimized to compare the optimized result with the unoptimized
2200 2200 one. Returns 1 if the optimized result differs.
2201 2201 """
2202 2202 opts = pycompat.byteskwargs(opts)
2203 2203 aliases = ui.configitems('revsetalias')
2204 2204 stages = [
2205 2205 ('parsed', lambda tree: tree),
2206 2206 ('expanded', lambda tree: revsetlang.expandaliases(tree, aliases,
2207 2207 ui.warn)),
2208 2208 ('concatenated', revsetlang.foldconcat),
2209 2209 ('analyzed', revsetlang.analyze),
2210 2210 ('optimized', revsetlang.optimize),
2211 2211 ]
2212 2212 if opts['no_optimized']:
2213 2213 stages = stages[:-1]
2214 2214 if opts['verify_optimized'] and opts['no_optimized']:
2215 2215 raise error.Abort(_('cannot use --verify-optimized with '
2216 2216 '--no-optimized'))
2217 2217 stagenames = set(n for n, f in stages)
2218 2218
2219 2219 showalways = set()
2220 2220 showchanged = set()
2221 2221 if ui.verbose and not opts['show_stage']:
2222 2222 # show parsed tree by --verbose (deprecated)
2223 2223 showalways.add('parsed')
2224 2224 showchanged.update(['expanded', 'concatenated'])
2225 2225 if opts['optimize']:
2226 2226 showalways.add('optimized')
2227 2227 if opts['show_stage'] and opts['optimize']:
2228 2228 raise error.Abort(_('cannot use --optimize with --show-stage'))
2229 2229 if opts['show_stage'] == ['all']:
2230 2230 showalways.update(stagenames)
2231 2231 else:
2232 2232 for n in opts['show_stage']:
2233 2233 if n not in stagenames:
2234 2234 raise error.Abort(_('invalid stage name: %s') % n)
2235 2235 showalways.update(opts['show_stage'])
2236 2236
2237 2237 treebystage = {}
2238 2238 printedtree = None
2239 2239 tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
2240 2240 for n, f in stages:
2241 2241 treebystage[n] = tree = f(tree)
2242 2242 if n in showalways or (n in showchanged and tree != printedtree):
2243 2243 if opts['show_stage'] or n != 'parsed':
2244 2244 ui.write(("* %s:\n") % n)
2245 2245 ui.write(revsetlang.prettyformat(tree), "\n")
2246 2246 printedtree = tree
2247 2247
2248 2248 if opts['verify_optimized']:
2249 2249 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2250 2250 brevs = revset.makematcher(treebystage['optimized'])(repo)
2251 2251 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2252 2252 ui.write(("* analyzed set:\n"), stringutil.prettyrepr(arevs), "\n")
2253 2253 ui.write(("* optimized set:\n"), stringutil.prettyrepr(brevs), "\n")
2254 2254 arevs = list(arevs)
2255 2255 brevs = list(brevs)
2256 2256 if arevs == brevs:
2257 2257 return 0
2258 2258 ui.write(('--- analyzed\n'), label='diff.file_a')
2259 2259 ui.write(('+++ optimized\n'), label='diff.file_b')
2260 2260 sm = difflib.SequenceMatcher(None, arevs, brevs)
2261 2261 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2262 2262 if tag in ('delete', 'replace'):
2263 2263 for c in arevs[alo:ahi]:
2264 2264 ui.write('-%s\n' % c, label='diff.deleted')
2265 2265 if tag in ('insert', 'replace'):
2266 2266 for c in brevs[blo:bhi]:
2267 2267 ui.write('+%s\n' % c, label='diff.inserted')
2268 2268 if tag == 'equal':
2269 2269 for c in arevs[alo:ahi]:
2270 2270 ui.write(' %s\n' % c)
2271 2271 return 1
2272 2272
2273 2273 func = revset.makematcher(tree)
2274 2274 revs = func(repo)
2275 2275 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2276 2276 ui.write(("* set:\n"), stringutil.prettyrepr(revs), "\n")
2277 2277 if not opts['show_revs']:
2278 2278 return
2279 2279 for c in revs:
2280 2280 ui.write("%d\n" % c)
2281 2281
2282 2282 @command('debugserve', [
2283 2283 ('', 'sshstdio', False, _('run an SSH server bound to process handles')),
2284 2284 ('', 'logiofd', '', _('file descriptor to log server I/O to')),
2285 2285 ('', 'logiofile', '', _('file to log server I/O to')),
2286 2286 ], '')
2287 2287 def debugserve(ui, repo, **opts):
2288 2288 """run a server with advanced settings
2289 2289
2290 2290 This command is similar to :hg:`serve`. It exists partially as a
2291 2291 workaround to the fact that ``hg serve --stdio`` must have specific
2292 2292 arguments for security reasons.
2293 2293 """
2294 2294 opts = pycompat.byteskwargs(opts)
2295 2295
2296 2296 if not opts['sshstdio']:
2297 2297 raise error.Abort(_('only --sshstdio is currently supported'))
2298 2298
2299 2299 logfh = None
2300 2300
2301 2301 if opts['logiofd'] and opts['logiofile']:
2302 2302 raise error.Abort(_('cannot use both --logiofd and --logiofile'))
2303 2303
2304 2304 if opts['logiofd']:
2305 2305 # Line buffered because output is line based.
2306 2306 try:
2307 2307 logfh = os.fdopen(int(opts['logiofd']), r'ab', 1)
2308 2308 except OSError as e:
2309 2309 if e.errno != errno.ESPIPE:
2310 2310 raise
2311 2311 # can't seek a pipe, so `ab` mode fails on py3
2312 2312 logfh = os.fdopen(int(opts['logiofd']), r'wb', 1)
2313 2313 elif opts['logiofile']:
2314 2314 logfh = open(opts['logiofile'], 'ab', 1)
2315 2315
2316 2316 s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
2317 2317 s.serve_forever()
2318 2318
2319 2319 @command('debugsetparents', [], _('REV1 [REV2]'))
2320 2320 def debugsetparents(ui, repo, rev1, rev2=None):
2321 2321 """manually set the parents of the current working directory
2322 2322
2323 2323 This is useful for writing repository conversion tools, but should
2324 2324 be used with care. For example, neither the working directory nor the
2325 2325 dirstate is updated, so file status may be incorrect after running this
2326 2326 command.
2327 2327
2328 2328 Returns 0 on success.
2329 2329 """
2330 2330
2331 2331 node1 = scmutil.revsingle(repo, rev1).node()
2332 2332 node2 = scmutil.revsingle(repo, rev2, 'null').node()
2333 2333
2334 2334 with repo.wlock():
2335 2335 repo.setparents(node1, node2)
2336 2336
2337 2337 @command('debugssl', [], '[SOURCE]', optionalrepo=True)
2338 2338 def debugssl(ui, repo, source=None, **opts):
2339 2339 '''test a secure connection to a server
2340 2340
2341 2341 This builds the certificate chain for the server on Windows, installing the
2342 2342 missing intermediates and trusted root via Windows Update if necessary. It
2343 2343 does nothing on other platforms.
2344 2344
2345 2345 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
2346 2346 that server is used. See :hg:`help urls` for more information.
2347 2347
2348 2348 If the update succeeds, retry the original operation. Otherwise, the cause
2349 2349 of the SSL error is likely another issue.
2350 2350 '''
2351 2351 if not pycompat.iswindows:
2352 2352 raise error.Abort(_('certificate chain building is only possible on '
2353 2353 'Windows'))
2354 2354
2355 2355 if not source:
2356 2356 if not repo:
2357 2357 raise error.Abort(_("there is no Mercurial repository here, and no "
2358 2358 "server specified"))
2359 2359 source = "default"
2360 2360
2361 2361 source, branches = hg.parseurl(ui.expandpath(source))
2362 2362 url = util.url(source)
2363 2363 addr = None
2364 2364
2365 2365 defaultport = {'https': 443, 'ssh': 22}
2366 2366 if url.scheme in defaultport:
2367 2367 try:
2368 2368 addr = (url.host, int(url.port or defaultport[url.scheme]))
2369 2369 except ValueError:
2370 2370 raise error.Abort(_("malformed port number in URL"))
2371 2371 else:
2372 2372 raise error.Abort(_("only https and ssh connections are supported"))
2373 2373
2374 2374 from . import win32
2375 2375
2376 2376 s = ssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_TLS,
2377 2377 cert_reqs=ssl.CERT_NONE, ca_certs=None)
2378 2378
2379 2379 try:
2380 2380 s.connect(addr)
2381 2381 cert = s.getpeercert(True)
2382 2382
2383 2383 ui.status(_('checking the certificate chain for %s\n') % url.host)
2384 2384
2385 2385 complete = win32.checkcertificatechain(cert, build=False)
2386 2386
2387 2387 if not complete:
2388 2388 ui.status(_('certificate chain is incomplete, updating... '))
2389 2389
2390 2390 if not win32.checkcertificatechain(cert):
2391 2391 ui.status(_('failed.\n'))
2392 2392 else:
2393 2393 ui.status(_('done.\n'))
2394 2394 else:
2395 2395 ui.status(_('full certificate chain is available\n'))
2396 2396 finally:
2397 2397 s.close()
2398 2398
2399 2399 @command('debugsub',
2400 2400 [('r', 'rev', '',
2401 2401 _('revision to check'), _('REV'))],
2402 2402 _('[-r REV] [REV]'))
2403 2403 def debugsub(ui, repo, rev=None):
2404 2404 ctx = scmutil.revsingle(repo, rev, None)
2405 2405 for k, v in sorted(ctx.substate.items()):
2406 2406 ui.write(('path %s\n') % k)
2407 2407 ui.write((' source %s\n') % v[0])
2408 2408 ui.write((' revision %s\n') % v[1])
2409 2409
2410 2410 @command('debugsuccessorssets',
2411 2411 [('', 'closest', False, _('return closest successors sets only'))],
2412 2412 _('[REV]'))
2413 2413 def debugsuccessorssets(ui, repo, *revs, **opts):
2414 2414 """show set of successors for revision
2415 2415
2416 2416 A successors set of changeset A is a consistent group of revisions that
2417 2417 succeed A. It contains non-obsolete changesets only unless closests
2418 2418 successors set is set.
2419 2419
2420 2420 In most cases a changeset A has a single successors set containing a single
2421 2421 successor (changeset A replaced by A').
2422 2422
2423 2423 A changeset that is made obsolete with no successors are called "pruned".
2424 2424 Such changesets have no successors sets at all.
2425 2425
2426 2426 A changeset that has been "split" will have a successors set containing
2427 2427 more than one successor.
2428 2428
2429 2429 A changeset that has been rewritten in multiple different ways is called
2430 2430 "divergent". Such changesets have multiple successor sets (each of which
2431 2431 may also be split, i.e. have multiple successors).
2432 2432
2433 2433 Results are displayed as follows::
2434 2434
2435 2435 <rev1>
2436 2436 <successors-1A>
2437 2437 <rev2>
2438 2438 <successors-2A>
2439 2439 <successors-2B1> <successors-2B2> <successors-2B3>
2440 2440
2441 2441 Here rev2 has two possible (i.e. divergent) successors sets. The first
2442 2442 holds one element, whereas the second holds three (i.e. the changeset has
2443 2443 been split).
2444 2444 """
2445 2445 # passed to successorssets caching computation from one call to another
2446 2446 cache = {}
2447 2447 ctx2str = bytes
2448 2448 node2str = short
2449 2449 for rev in scmutil.revrange(repo, revs):
2450 2450 ctx = repo[rev]
2451 2451 ui.write('%s\n'% ctx2str(ctx))
2452 2452 for succsset in obsutil.successorssets(repo, ctx.node(),
2453 2453 closest=opts[r'closest'],
2454 2454 cache=cache):
2455 2455 if succsset:
2456 2456 ui.write(' ')
2457 2457 ui.write(node2str(succsset[0]))
2458 2458 for node in succsset[1:]:
2459 2459 ui.write(' ')
2460 2460 ui.write(node2str(node))
2461 2461 ui.write('\n')
2462 2462
2463 2463 @command('debugtemplate',
2464 2464 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2465 2465 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2466 2466 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2467 2467 optionalrepo=True)
2468 2468 def debugtemplate(ui, repo, tmpl, **opts):
2469 2469 """parse and apply a template
2470 2470
2471 2471 If -r/--rev is given, the template is processed as a log template and
2472 2472 applied to the given changesets. Otherwise, it is processed as a generic
2473 2473 template.
2474 2474
2475 2475 Use --verbose to print the parsed tree.
2476 2476 """
2477 2477 revs = None
2478 2478 if opts[r'rev']:
2479 2479 if repo is None:
2480 2480 raise error.RepoError(_('there is no Mercurial repository here '
2481 2481 '(.hg not found)'))
2482 2482 revs = scmutil.revrange(repo, opts[r'rev'])
2483 2483
2484 2484 props = {}
2485 2485 for d in opts[r'define']:
2486 2486 try:
2487 2487 k, v = (e.strip() for e in d.split('=', 1))
2488 2488 if not k or k == 'ui':
2489 2489 raise ValueError
2490 2490 props[k] = v
2491 2491 except ValueError:
2492 2492 raise error.Abort(_('malformed keyword definition: %s') % d)
2493 2493
2494 2494 if ui.verbose:
2495 2495 aliases = ui.configitems('templatealias')
2496 2496 tree = templater.parse(tmpl)
2497 2497 ui.note(templater.prettyformat(tree), '\n')
2498 2498 newtree = templater.expandaliases(tree, aliases)
2499 2499 if newtree != tree:
2500 2500 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2501 2501
2502 2502 if revs is None:
2503 2503 tres = formatter.templateresources(ui, repo)
2504 2504 t = formatter.maketemplater(ui, tmpl, resources=tres)
2505 2505 if ui.verbose:
2506 2506 kwds, funcs = t.symbolsuseddefault()
2507 2507 ui.write(("* keywords: %s\n") % ', '.join(sorted(kwds)))
2508 2508 ui.write(("* functions: %s\n") % ', '.join(sorted(funcs)))
2509 2509 ui.write(t.renderdefault(props))
2510 2510 else:
2511 2511 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
2512 2512 if ui.verbose:
2513 2513 kwds, funcs = displayer.t.symbolsuseddefault()
2514 2514 ui.write(("* keywords: %s\n") % ', '.join(sorted(kwds)))
2515 2515 ui.write(("* functions: %s\n") % ', '.join(sorted(funcs)))
2516 2516 for r in revs:
2517 2517 displayer.show(repo[r], **pycompat.strkwargs(props))
2518 2518 displayer.close()
2519 2519
2520 2520 @command('debuguigetpass', [
2521 2521 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2522 2522 ], _('[-p TEXT]'), norepo=True)
2523 2523 def debuguigetpass(ui, prompt=''):
2524 2524 """show prompt to type password"""
2525 2525 r = ui.getpass(prompt)
2526 2526 ui.write(('respose: %s\n') % r)
2527 2527
2528 2528 @command('debuguiprompt', [
2529 2529 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2530 2530 ], _('[-p TEXT]'), norepo=True)
2531 2531 def debuguiprompt(ui, prompt=''):
2532 2532 """show plain prompt"""
2533 2533 r = ui.prompt(prompt)
2534 2534 ui.write(('response: %s\n') % r)
2535 2535
2536 2536 @command('debugupdatecaches', [])
2537 2537 def debugupdatecaches(ui, repo, *pats, **opts):
2538 2538 """warm all known caches in the repository"""
2539 2539 with repo.wlock(), repo.lock():
2540 2540 repo.updatecaches(full=True)
2541 2541
2542 2542 @command('debugupgraderepo', [
2543 2543 ('o', 'optimize', [], _('extra optimization to perform'), _('NAME')),
2544 2544 ('', 'run', False, _('performs an upgrade')),
2545 2545 ])
2546 2546 def debugupgraderepo(ui, repo, run=False, optimize=None):
2547 2547 """upgrade a repository to use different features
2548 2548
2549 2549 If no arguments are specified, the repository is evaluated for upgrade
2550 2550 and a list of problems and potential optimizations is printed.
2551 2551
2552 2552 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
2553 2553 can be influenced via additional arguments. More details will be provided
2554 2554 by the command output when run without ``--run``.
2555 2555
2556 2556 During the upgrade, the repository will be locked and no writes will be
2557 2557 allowed.
2558 2558
2559 2559 At the end of the upgrade, the repository may not be readable while new
2560 2560 repository data is swapped in. This window will be as long as it takes to
2561 2561 rename some directories inside the ``.hg`` directory. On most machines, this
2562 2562 should complete almost instantaneously and the chances of a consumer being
2563 2563 unable to access the repository should be low.
2564 2564 """
2565 2565 return upgrade.upgraderepo(ui, repo, run=run, optimize=optimize)
2566 2566
2567 2567 @command('debugwalk', cmdutil.walkopts, _('[OPTION]... [FILE]...'),
2568 2568 inferrepo=True)
2569 2569 def debugwalk(ui, repo, *pats, **opts):
2570 2570 """show how files match on given patterns"""
2571 2571 opts = pycompat.byteskwargs(opts)
2572 2572 m = scmutil.match(repo[None], pats, opts)
2573 2573 if ui.verbose:
2574 2574 ui.write(('* matcher:\n'), stringutil.prettyrepr(m), '\n')
2575 2575 items = list(repo[None].walk(m))
2576 2576 if not items:
2577 2577 return
2578 2578 f = lambda fn: fn
2579 2579 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2580 2580 f = lambda fn: util.normpath(fn)
2581 2581 fmt = 'f %%-%ds %%-%ds %%s' % (
2582 2582 max([len(abs) for abs in items]),
2583 2583 max([len(m.rel(abs)) for abs in items]))
2584 2584 for abs in items:
2585 2585 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2586 2586 ui.write("%s\n" % line.rstrip())
2587 2587
2588 2588 @command('debugwhyunstable', [], _('REV'))
2589 2589 def debugwhyunstable(ui, repo, rev):
2590 2590 """explain instabilities of a changeset"""
2591 2591 for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)):
2592 2592 dnodes = ''
2593 2593 if entry.get('divergentnodes'):
2594 2594 dnodes = ' '.join('%s (%s)' % (ctx.hex(), ctx.phasestr())
2595 2595 for ctx in entry['divergentnodes']) + ' '
2596 2596 ui.write('%s: %s%s %s\n' % (entry['instability'], dnodes,
2597 2597 entry['reason'], entry['node']))
2598 2598
2599 2599 @command('debugwireargs',
2600 2600 [('', 'three', '', 'three'),
2601 2601 ('', 'four', '', 'four'),
2602 2602 ('', 'five', '', 'five'),
2603 2603 ] + cmdutil.remoteopts,
2604 2604 _('REPO [OPTIONS]... [ONE [TWO]]'),
2605 2605 norepo=True)
2606 2606 def debugwireargs(ui, repopath, *vals, **opts):
2607 2607 opts = pycompat.byteskwargs(opts)
2608 2608 repo = hg.peer(ui, opts, repopath)
2609 2609 for opt in cmdutil.remoteopts:
2610 2610 del opts[opt[1]]
2611 2611 args = {}
2612 2612 for k, v in opts.iteritems():
2613 2613 if v:
2614 2614 args[k] = v
2615 2615 args = pycompat.strkwargs(args)
2616 2616 # run twice to check that we don't mess up the stream for the next command
2617 2617 res1 = repo.debugwireargs(*vals, **args)
2618 2618 res2 = repo.debugwireargs(*vals, **args)
2619 2619 ui.write("%s\n" % res1)
2620 2620 if res1 != res2:
2621 2621 ui.warn("%s\n" % res2)
2622 2622
2623 2623 def _parsewirelangblocks(fh):
2624 2624 activeaction = None
2625 2625 blocklines = []
2626 2626
2627 2627 for line in fh:
2628 2628 line = line.rstrip()
2629 2629 if not line:
2630 2630 continue
2631 2631
2632 2632 if line.startswith(b'#'):
2633 2633 continue
2634 2634
2635 2635 if not line.startswith(' '):
2636 2636 # New block. Flush previous one.
2637 2637 if activeaction:
2638 2638 yield activeaction, blocklines
2639 2639
2640 2640 activeaction = line
2641 2641 blocklines = []
2642 2642 continue
2643 2643
2644 2644 # Else we start with an indent.
2645 2645
2646 2646 if not activeaction:
2647 2647 raise error.Abort(_('indented line outside of block'))
2648 2648
2649 2649 blocklines.append(line)
2650 2650
2651 2651 # Flush last block.
2652 2652 if activeaction:
2653 2653 yield activeaction, blocklines
2654 2654
2655 2655 @command('debugwireproto',
2656 2656 [
2657 2657 ('', 'localssh', False, _('start an SSH server for this repo')),
2658 2658 ('', 'peer', '', _('construct a specific version of the peer')),
2659 2659 ('', 'noreadstderr', False, _('do not read from stderr of the remote')),
2660 2660 ('', 'nologhandshake', False,
2661 2661 _('do not log I/O related to the peer handshake')),
2662 2662 ] + cmdutil.remoteopts,
2663 2663 _('[PATH]'),
2664 2664 optionalrepo=True)
2665 2665 def debugwireproto(ui, repo, path=None, **opts):
2666 2666 """send wire protocol commands to a server
2667 2667
2668 2668 This command can be used to issue wire protocol commands to remote
2669 2669 peers and to debug the raw data being exchanged.
2670 2670
2671 2671 ``--localssh`` will start an SSH server against the current repository
2672 2672 and connect to that. By default, the connection will perform a handshake
2673 2673 and establish an appropriate peer instance.
2674 2674
2675 2675 ``--peer`` can be used to bypass the handshake protocol and construct a
2676 2676 peer instance using the specified class type. Valid values are ``raw``,
2677 2677 ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending
2678 2678 raw data payloads and don't support higher-level command actions.
2679 2679
2680 2680 ``--noreadstderr`` can be used to disable automatic reading from stderr
2681 2681 of the peer (for SSH connections only). Disabling automatic reading of
2682 2682 stderr is useful for making output more deterministic.
2683 2683
2684 2684 Commands are issued via a mini language which is specified via stdin.
2685 2685 The language consists of individual actions to perform. An action is
2686 2686 defined by a block. A block is defined as a line with no leading
2687 2687 space followed by 0 or more lines with leading space. Blocks are
2688 2688 effectively a high-level command with additional metadata.
2689 2689
2690 2690 Lines beginning with ``#`` are ignored.
2691 2691
2692 2692 The following sections denote available actions.
2693 2693
2694 2694 raw
2695 2695 ---
2696 2696
2697 2697 Send raw data to the server.
2698 2698
2699 2699 The block payload contains the raw data to send as one atomic send
2700 2700 operation. The data may not actually be delivered in a single system
2701 2701 call: it depends on the abilities of the transport being used.
2702 2702
2703 2703 Each line in the block is de-indented and concatenated. Then, that
2704 2704 value is evaluated as a Python b'' literal. This allows the use of
2705 2705 backslash escaping, etc.
2706 2706
2707 2707 raw+
2708 2708 ----
2709 2709
2710 2710 Behaves like ``raw`` except flushes output afterwards.
2711 2711
2712 2712 command <X>
2713 2713 -----------
2714 2714
2715 2715 Send a request to run a named command, whose name follows the ``command``
2716 2716 string.
2717 2717
2718 2718 Arguments to the command are defined as lines in this block. The format of
2719 2719 each line is ``<key> <value>``. e.g.::
2720 2720
2721 2721 command listkeys
2722 2722 namespace bookmarks
2723 2723
2724 2724 If the value begins with ``eval:``, it will be interpreted as a Python
2725 2725 literal expression. Otherwise values are interpreted as Python b'' literals.
2726 2726 This allows sending complex types and encoding special byte sequences via
2727 2727 backslash escaping.
2728 2728
2729 2729 The following arguments have special meaning:
2730 2730
2731 2731 ``PUSHFILE``
2732 2732 When defined, the *push* mechanism of the peer will be used instead
2733 2733 of the static request-response mechanism and the content of the
2734 2734 file specified in the value of this argument will be sent as the
2735 2735 command payload.
2736 2736
2737 2737 This can be used to submit a local bundle file to the remote.
2738 2738
2739 2739 batchbegin
2740 2740 ----------
2741 2741
2742 2742 Instruct the peer to begin a batched send.
2743 2743
2744 2744 All ``command`` blocks are queued for execution until the next
2745 2745 ``batchsubmit`` block.
2746 2746
2747 2747 batchsubmit
2748 2748 -----------
2749 2749
2750 2750 Submit previously queued ``command`` blocks as a batch request.
2751 2751
2752 2752 This action MUST be paired with a ``batchbegin`` action.
2753 2753
2754 2754 httprequest <method> <path>
2755 2755 ---------------------------
2756 2756
2757 2757 (HTTP peer only)
2758 2758
2759 2759 Send an HTTP request to the peer.
2760 2760
2761 2761 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
2762 2762
2763 2763 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
2764 2764 headers to add to the request. e.g. ``Accept: foo``.
2765 2765
2766 2766 The following arguments are special:
2767 2767
2768 2768 ``BODYFILE``
2769 2769 The content of the file defined as the value to this argument will be
2770 2770 transferred verbatim as the HTTP request body.
2771 2771
2772 2772 ``frame <type> <flags> <payload>``
2773 2773 Send a unified protocol frame as part of the request body.
2774 2774
2775 2775 All frames will be collected and sent as the body to the HTTP
2776 2776 request.
2777 2777
2778 2778 close
2779 2779 -----
2780 2780
2781 2781 Close the connection to the server.
2782 2782
2783 2783 flush
2784 2784 -----
2785 2785
2786 2786 Flush data written to the server.
2787 2787
2788 2788 readavailable
2789 2789 -------------
2790 2790
2791 2791 Close the write end of the connection and read all available data from
2792 2792 the server.
2793 2793
2794 2794 If the connection to the server encompasses multiple pipes, we poll both
2795 2795 pipes and read available data.
2796 2796
2797 2797 readline
2798 2798 --------
2799 2799
2800 2800 Read a line of output from the server. If there are multiple output
2801 2801 pipes, reads only the main pipe.
2802 2802
2803 2803 ereadline
2804 2804 ---------
2805 2805
2806 2806 Like ``readline``, but read from the stderr pipe, if available.
2807 2807
2808 2808 read <X>
2809 2809 --------
2810 2810
2811 2811 ``read()`` N bytes from the server's main output pipe.
2812 2812
2813 2813 eread <X>
2814 2814 ---------
2815 2815
2816 2816 ``read()`` N bytes from the server's stderr pipe, if available.
2817 2817
2818 2818 Specifying Unified Frame-Based Protocol Frames
2819 2819 ----------------------------------------------
2820 2820
2821 2821 It is possible to emit a *Unified Frame-Based Protocol* by using special
2822 2822 syntax.
2823 2823
2824 2824 A frame is composed as a type, flags, and payload. These can be parsed
2825 2825 from a string of the form:
2826 2826
2827 2827 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
2828 2828
2829 2829 ``request-id`` and ``stream-id`` are integers defining the request and
2830 2830 stream identifiers.
2831 2831
2832 2832 ``type`` can be an integer value for the frame type or the string name
2833 2833 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
2834 2834 ``command-name``.
2835 2835
2836 2836 ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag
2837 2837 components. Each component (and there can be just one) can be an integer
2838 2838 or a flag name for stream flags or frame flags, respectively. Values are
2839 2839 resolved to integers and then bitwise OR'd together.
2840 2840
2841 2841 ``payload`` represents the raw frame payload. If it begins with
2842 2842 ``cbor:``, the following string is evaluated as Python code and the
2843 2843 resulting object is fed into a CBOR encoder. Otherwise it is interpreted
2844 2844 as a Python byte string literal.
2845 2845 """
2846 2846 opts = pycompat.byteskwargs(opts)
2847 2847
2848 2848 if opts['localssh'] and not repo:
2849 2849 raise error.Abort(_('--localssh requires a repository'))
2850 2850
2851 2851 if opts['peer'] and opts['peer'] not in ('raw', 'http2', 'ssh1', 'ssh2'):
2852 2852 raise error.Abort(_('invalid value for --peer'),
2853 2853 hint=_('valid values are "raw", "ssh1", and "ssh2"'))
2854 2854
2855 2855 if path and opts['localssh']:
2856 2856 raise error.Abort(_('cannot specify --localssh with an explicit '
2857 2857 'path'))
2858 2858
2859 2859 if ui.interactive():
2860 2860 ui.write(_('(waiting for commands on stdin)\n'))
2861 2861
2862 2862 blocks = list(_parsewirelangblocks(ui.fin))
2863 2863
2864 2864 proc = None
2865 2865 stdin = None
2866 2866 stdout = None
2867 2867 stderr = None
2868 2868 opener = None
2869 2869
2870 2870 if opts['localssh']:
2871 2871 # We start the SSH server in its own process so there is process
2872 2872 # separation. This prevents a whole class of potential bugs around
2873 2873 # shared state from interfering with server operation.
2874 2874 args = procutil.hgcmd() + [
2875 2875 '-R', repo.root,
2876 2876 'debugserve', '--sshstdio',
2877 2877 ]
2878 2878 proc = subprocess.Popen(args, stdin=subprocess.PIPE,
2879 2879 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
2880 2880 bufsize=0)
2881 2881
2882 2882 stdin = proc.stdin
2883 2883 stdout = proc.stdout
2884 2884 stderr = proc.stderr
2885 2885
2886 2886 # We turn the pipes into observers so we can log I/O.
2887 2887 if ui.verbose or opts['peer'] == 'raw':
2888 2888 stdin = util.makeloggingfileobject(ui, proc.stdin, b'i',
2889 2889 logdata=True)
2890 2890 stdout = util.makeloggingfileobject(ui, proc.stdout, b'o',
2891 2891 logdata=True)
2892 2892 stderr = util.makeloggingfileobject(ui, proc.stderr, b'e',
2893 2893 logdata=True)
2894 2894
2895 2895 # --localssh also implies the peer connection settings.
2896 2896
2897 2897 url = 'ssh://localserver'
2898 2898 autoreadstderr = not opts['noreadstderr']
2899 2899
2900 2900 if opts['peer'] == 'ssh1':
2901 2901 ui.write(_('creating ssh peer for wire protocol version 1\n'))
2902 2902 peer = sshpeer.sshv1peer(ui, url, proc, stdin, stdout, stderr,
2903 2903 None, autoreadstderr=autoreadstderr)
2904 2904 elif opts['peer'] == 'ssh2':
2905 2905 ui.write(_('creating ssh peer for wire protocol version 2\n'))
2906 2906 peer = sshpeer.sshv2peer(ui, url, proc, stdin, stdout, stderr,
2907 2907 None, autoreadstderr=autoreadstderr)
2908 2908 elif opts['peer'] == 'raw':
2909 2909 ui.write(_('using raw connection to peer\n'))
2910 2910 peer = None
2911 2911 else:
2912 2912 ui.write(_('creating ssh peer from handshake results\n'))
2913 2913 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr,
2914 2914 autoreadstderr=autoreadstderr)
2915 2915
2916 2916 elif path:
2917 2917 # We bypass hg.peer() so we can proxy the sockets.
2918 2918 # TODO consider not doing this because we skip
2919 2919 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
2920 2920 u = util.url(path)
2921 2921 if u.scheme != 'http':
2922 2922 raise error.Abort(_('only http:// paths are currently supported'))
2923 2923
2924 2924 url, authinfo = u.authinfo()
2925 2925 openerargs = {
2926 2926 r'useragent': b'Mercurial debugwireproto',
2927 2927 }
2928 2928
2929 2929 # Turn pipes/sockets into observers so we can log I/O.
2930 2930 if ui.verbose:
2931 2931 openerargs.update({
2932 2932 r'loggingfh': ui,
2933 2933 r'loggingname': b's',
2934 2934 r'loggingopts': {
2935 2935 r'logdata': True,
2936 2936 r'logdataapis': False,
2937 2937 },
2938 2938 })
2939 2939
2940 2940 if ui.debugflag:
2941 2941 openerargs[r'loggingopts'][r'logdataapis'] = True
2942 2942
2943 2943 # Don't send default headers when in raw mode. This allows us to
2944 2944 # bypass most of the behavior of our URL handling code so we can
2945 2945 # have near complete control over what's sent on the wire.
2946 2946 if opts['peer'] == 'raw':
2947 2947 openerargs[r'sendaccept'] = False
2948 2948
2949 2949 opener = urlmod.opener(ui, authinfo, **openerargs)
2950 2950
2951 2951 if opts['peer'] == 'http2':
2952 2952 ui.write(_('creating http peer for wire protocol version 2\n'))
2953 2953 # We go through makepeer() because we need an API descriptor for
2954 2954 # the peer instance to be useful.
2955 2955 with ui.configoverride({
2956 2956 ('experimental', 'httppeer.advertise-v2'): True}):
2957 2957 if opts['nologhandshake']:
2958 2958 ui.pushbuffer()
2959 2959
2960 2960 peer = httppeer.makepeer(ui, path, opener=opener)
2961 2961
2962 2962 if opts['nologhandshake']:
2963 2963 ui.popbuffer()
2964 2964
2965 2965 if not isinstance(peer, httppeer.httpv2peer):
2966 2966 raise error.Abort(_('could not instantiate HTTP peer for '
2967 2967 'wire protocol version 2'),
2968 2968 hint=_('the server may not have the feature '
2969 2969 'enabled or is not allowing this '
2970 2970 'client version'))
2971 2971
2972 2972 elif opts['peer'] == 'raw':
2973 2973 ui.write(_('using raw connection to peer\n'))
2974 2974 peer = None
2975 2975 elif opts['peer']:
2976 2976 raise error.Abort(_('--peer %s not supported with HTTP peers') %
2977 2977 opts['peer'])
2978 2978 else:
2979 2979 peer = httppeer.makepeer(ui, path, opener=opener)
2980 2980
2981 2981 # We /could/ populate stdin/stdout with sock.makefile()...
2982 2982 else:
2983 2983 raise error.Abort(_('unsupported connection configuration'))
2984 2984
2985 2985 batchedcommands = None
2986 2986
2987 2987 # Now perform actions based on the parsed wire language instructions.
2988 2988 for action, lines in blocks:
2989 2989 if action in ('raw', 'raw+'):
2990 2990 if not stdin:
2991 2991 raise error.Abort(_('cannot call raw/raw+ on this peer'))
2992 2992
2993 2993 # Concatenate the data together.
2994 2994 data = ''.join(l.lstrip() for l in lines)
2995 2995 data = stringutil.unescapestr(data)
2996 2996 stdin.write(data)
2997 2997
2998 2998 if action == 'raw+':
2999 2999 stdin.flush()
3000 3000 elif action == 'flush':
3001 3001 if not stdin:
3002 3002 raise error.Abort(_('cannot call flush on this peer'))
3003 3003 stdin.flush()
3004 3004 elif action.startswith('command'):
3005 3005 if not peer:
3006 3006 raise error.Abort(_('cannot send commands unless peer instance '
3007 3007 'is available'))
3008 3008
3009 3009 command = action.split(' ', 1)[1]
3010 3010
3011 3011 args = {}
3012 3012 for line in lines:
3013 3013 # We need to allow empty values.
3014 3014 fields = line.lstrip().split(' ', 1)
3015 3015 if len(fields) == 1:
3016 3016 key = fields[0]
3017 3017 value = ''
3018 3018 else:
3019 3019 key, value = fields
3020 3020
3021 3021 if value.startswith('eval:'):
3022 3022 value = stringutil.evalpythonliteral(value[5:])
3023 3023 else:
3024 3024 value = stringutil.unescapestr(value)
3025 3025
3026 3026 args[key] = value
3027 3027
3028 3028 if batchedcommands is not None:
3029 3029 batchedcommands.append((command, args))
3030 3030 continue
3031 3031
3032 3032 ui.status(_('sending %s command\n') % command)
3033 3033
3034 3034 if 'PUSHFILE' in args:
3035 3035 with open(args['PUSHFILE'], r'rb') as fh:
3036 3036 del args['PUSHFILE']
3037 3037 res, output = peer._callpush(command, fh,
3038 3038 **pycompat.strkwargs(args))
3039 3039 ui.status(_('result: %s\n') % stringutil.escapestr(res))
3040 3040 ui.status(_('remote output: %s\n') %
3041 3041 stringutil.escapestr(output))
3042 3042 else:
3043 3043 with peer.commandexecutor() as e:
3044 3044 res = e.callcommand(command, args).result()
3045 3045
3046 3046 if isinstance(res, wireprotov2peer.commandresponse):
3047 3047 val = list(res.cborobjects())
3048 3048 ui.status(_('response: %s\n') %
3049 3049 stringutil.pprint(val, bprefix=True))
3050 3050
3051 3051 else:
3052 3052 ui.status(_('response: %s\n') %
3053 3053 stringutil.pprint(res, bprefix=True))
3054 3054
3055 3055 elif action == 'batchbegin':
3056 3056 if batchedcommands is not None:
3057 3057 raise error.Abort(_('nested batchbegin not allowed'))
3058 3058
3059 3059 batchedcommands = []
3060 3060 elif action == 'batchsubmit':
3061 3061 # There is a batching API we could go through. But it would be
3062 3062 # difficult to normalize requests into function calls. It is easier
3063 3063 # to bypass this layer and normalize to commands + args.
3064 3064 ui.status(_('sending batch with %d sub-commands\n') %
3065 3065 len(batchedcommands))
3066 3066 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
3067 3067 ui.status(_('response #%d: %s\n') %
3068 3068 (i, stringutil.escapestr(chunk)))
3069 3069
3070 3070 batchedcommands = None
3071 3071
3072 3072 elif action.startswith('httprequest '):
3073 3073 if not opener:
3074 3074 raise error.Abort(_('cannot use httprequest without an HTTP '
3075 3075 'peer'))
3076 3076
3077 3077 request = action.split(' ', 2)
3078 3078 if len(request) != 3:
3079 3079 raise error.Abort(_('invalid httprequest: expected format is '
3080 3080 '"httprequest <method> <path>'))
3081 3081
3082 3082 method, httppath = request[1:]
3083 3083 headers = {}
3084 3084 body = None
3085 3085 frames = []
3086 3086 for line in lines:
3087 3087 line = line.lstrip()
3088 3088 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
3089 3089 if m:
3090 3090 headers[m.group(1)] = m.group(2)
3091 3091 continue
3092 3092
3093 3093 if line.startswith(b'BODYFILE '):
3094 3094 with open(line.split(b' ', 1), 'rb') as fh:
3095 3095 body = fh.read()
3096 3096 elif line.startswith(b'frame '):
3097 3097 frame = wireprotoframing.makeframefromhumanstring(
3098 3098 line[len(b'frame '):])
3099 3099
3100 3100 frames.append(frame)
3101 3101 else:
3102 3102 raise error.Abort(_('unknown argument to httprequest: %s') %
3103 3103 line)
3104 3104
3105 3105 url = path + httppath
3106 3106
3107 3107 if frames:
3108 3108 body = b''.join(bytes(f) for f in frames)
3109 3109
3110 3110 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
3111 3111
3112 3112 # urllib.Request insists on using has_data() as a proxy for
3113 3113 # determining the request method. Override that to use our
3114 3114 # explicitly requested method.
3115 3115 req.get_method = lambda: method
3116 3116
3117 3117 try:
3118 3118 res = opener.open(req)
3119 3119 body = res.read()
3120 3120 except util.urlerr.urlerror as e:
3121 3121 e.read()
3122 3122 continue
3123 3123
3124 3124 if res.headers.get('Content-Type') == 'application/mercurial-cbor':
3125 3125 ui.write(_('cbor> %s\n') %
3126 3126 stringutil.pprint(cbor.loads(body), bprefix=True))
3127 3127
3128 3128 elif action == 'close':
3129 3129 peer.close()
3130 3130 elif action == 'readavailable':
3131 3131 if not stdout or not stderr:
3132 3132 raise error.Abort(_('readavailable not available on this peer'))
3133 3133
3134 3134 stdin.close()
3135 3135 stdout.read()
3136 3136 stderr.read()
3137 3137
3138 3138 elif action == 'readline':
3139 3139 if not stdout:
3140 3140 raise error.Abort(_('readline not available on this peer'))
3141 3141 stdout.readline()
3142 3142 elif action == 'ereadline':
3143 3143 if not stderr:
3144 3144 raise error.Abort(_('ereadline not available on this peer'))
3145 3145 stderr.readline()
3146 3146 elif action.startswith('read '):
3147 3147 count = int(action.split(' ', 1)[1])
3148 3148 if not stdout:
3149 3149 raise error.Abort(_('read not available on this peer'))
3150 3150 stdout.read(count)
3151 3151 elif action.startswith('eread '):
3152 3152 count = int(action.split(' ', 1)[1])
3153 3153 if not stderr:
3154 3154 raise error.Abort(_('eread not available on this peer'))
3155 3155 stderr.read(count)
3156 3156 else:
3157 3157 raise error.Abort(_('unknown action: %s') % action)
3158 3158
3159 3159 if batchedcommands is not None:
3160 3160 raise error.Abort(_('unclosed "batchbegin" request'))
3161 3161
3162 3162 if peer:
3163 3163 peer.close()
3164 3164
3165 3165 if proc:
3166 3166 proc.kill()
@@ -1,665 +1,670 b''
1 1 # fileset.py - file set queries for mercurial
2 2 #
3 3 # Copyright 2010 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 re
11 11
12 12 from .i18n import _
13 13 from . import (
14 14 error,
15 15 match as matchmod,
16 16 merge,
17 17 parser,
18 18 pycompat,
19 19 registrar,
20 20 scmutil,
21 21 util,
22 22 )
23 23 from .utils import (
24 24 stringutil,
25 25 )
26 26
27 27 elements = {
28 28 # token-type: binding-strength, primary, prefix, infix, suffix
29 29 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
30 30 ":": (15, None, None, ("kindpat", 15), None),
31 31 "-": (5, None, ("negate", 19), ("minus", 5), None),
32 32 "not": (10, None, ("not", 10), None, None),
33 33 "!": (10, None, ("not", 10), None, None),
34 34 "and": (5, None, None, ("and", 5), None),
35 35 "&": (5, None, None, ("and", 5), None),
36 36 "or": (4, None, None, ("or", 4), None),
37 37 "|": (4, None, None, ("or", 4), None),
38 38 "+": (4, None, None, ("or", 4), None),
39 39 ",": (2, None, None, ("list", 2), None),
40 40 ")": (0, None, None, None, None),
41 41 "symbol": (0, "symbol", None, None, None),
42 42 "string": (0, "string", None, None, None),
43 43 "end": (0, None, None, None, None),
44 44 }
45 45
46 46 keywords = {'and', 'or', 'not'}
47 47
48 48 globchars = ".*{}[]?/\\_"
49 49
50 50 def tokenize(program):
51 51 pos, l = 0, len(program)
52 52 program = pycompat.bytestr(program)
53 53 while pos < l:
54 54 c = program[pos]
55 55 if c.isspace(): # skip inter-token whitespace
56 56 pass
57 57 elif c in "(),-:|&+!": # handle simple operators
58 58 yield (c, None, pos)
59 59 elif (c in '"\'' or c == 'r' and
60 60 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
61 61 if c == 'r':
62 62 pos += 1
63 63 c = program[pos]
64 64 decode = lambda x: x
65 65 else:
66 66 decode = parser.unescapestr
67 67 pos += 1
68 68 s = pos
69 69 while pos < l: # find closing quote
70 70 d = program[pos]
71 71 if d == '\\': # skip over escaped characters
72 72 pos += 2
73 73 continue
74 74 if d == c:
75 75 yield ('string', decode(program[s:pos]), s)
76 76 break
77 77 pos += 1
78 78 else:
79 79 raise error.ParseError(_("unterminated string"), s)
80 80 elif c.isalnum() or c in globchars or ord(c) > 127:
81 81 # gather up a symbol/keyword
82 82 s = pos
83 83 pos += 1
84 84 while pos < l: # find end of symbol
85 85 d = program[pos]
86 86 if not (d.isalnum() or d in globchars or ord(d) > 127):
87 87 break
88 88 pos += 1
89 89 sym = program[s:pos]
90 90 if sym in keywords: # operator keywords
91 91 yield (sym, None, s)
92 92 else:
93 93 yield ('symbol', sym, s)
94 94 pos -= 1
95 95 else:
96 96 raise error.ParseError(_("syntax error"), pos)
97 97 pos += 1
98 98 yield ('end', None, pos)
99 99
100 100 def parse(expr):
101 101 p = parser.parser(elements)
102 102 tree, pos = p.parse(tokenize(expr))
103 103 if pos != len(expr):
104 104 raise error.ParseError(_("invalid token"), pos)
105 105 return tree
106 106
107 107 def getsymbol(x):
108 108 if x and x[0] == 'symbol':
109 109 return x[1]
110 110 raise error.ParseError(_('not a symbol'))
111 111
112 112 def getstring(x, err):
113 113 if x and (x[0] == 'string' or x[0] == 'symbol'):
114 114 return x[1]
115 115 raise error.ParseError(err)
116 116
117 117 def _getkindpat(x, y, allkinds, err):
118 118 kind = getsymbol(x)
119 119 pat = getstring(y, err)
120 120 if kind not in allkinds:
121 121 raise error.ParseError(_("invalid pattern kind: %s") % kind)
122 122 return '%s:%s' % (kind, pat)
123 123
124 124 def getpattern(x, allkinds, err):
125 125 if x and x[0] == 'kindpat':
126 126 return _getkindpat(x[1], x[2], allkinds, err)
127 127 return getstring(x, err)
128 128
129 129 def getlist(x):
130 130 if not x:
131 131 return []
132 132 if x[0] == 'list':
133 133 return getlist(x[1]) + [x[2]]
134 134 return [x]
135 135
136 136 def getargs(x, min, max, err):
137 137 l = getlist(x)
138 138 if len(l) < min or len(l) > max:
139 139 raise error.ParseError(err)
140 140 return l
141 141
142 142 def getset(mctx, x):
143 143 if not x:
144 144 raise error.ParseError(_("missing argument"))
145 145 return methods[x[0]](mctx, *x[1:])
146 146
147 147 def stringset(mctx, x):
148 148 m = mctx.matcher([x])
149 149 return [f for f in mctx.subset if m(f)]
150 150
151 151 def kindpatset(mctx, x, y):
152 152 return stringset(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
153 153 _("pattern must be a string")))
154 154
155 155 def andset(mctx, x, y):
156 156 return getset(mctx.narrow(getset(mctx, x)), y)
157 157
158 158 def orset(mctx, x, y):
159 159 # needs optimizing
160 160 xl = getset(mctx, x)
161 161 yl = getset(mctx, y)
162 162 return xl + [f for f in yl if f not in xl]
163 163
164 164 def notset(mctx, x):
165 165 s = set(getset(mctx, x))
166 166 return [r for r in mctx.subset if r not in s]
167 167
168 168 def minusset(mctx, x, y):
169 169 xl = getset(mctx, x)
170 170 yl = set(getset(mctx, y))
171 171 return [f for f in xl if f not in yl]
172 172
173 173 def negateset(mctx, x):
174 174 raise error.ParseError(_("can't use negate operator in this context"))
175 175
176 176 def listset(mctx, a, b):
177 177 raise error.ParseError(_("can't use a list in this context"),
178 178 hint=_('see hg help "filesets.x or y"'))
179 179
180 180 def func(mctx, a, b):
181 181 funcname = getsymbol(a)
182 182 if funcname in symbols:
183 183 enabled = mctx._existingenabled
184 184 mctx._existingenabled = funcname in _existingcallers
185 185 try:
186 186 return symbols[funcname](mctx, b)
187 187 finally:
188 188 mctx._existingenabled = enabled
189 189
190 190 keep = lambda fn: getattr(fn, '__doc__', None) is not None
191 191
192 192 syms = [s for (s, fn) in symbols.items() if keep(fn)]
193 193 raise error.UnknownIdentifier(funcname, syms)
194 194
195 195 # symbols are callable like:
196 196 # fun(mctx, x)
197 197 # with:
198 198 # mctx - current matchctx instance
199 199 # x - argument in tree form
200 200 symbols = {}
201 201
202 202 # filesets using matchctx.status()
203 203 _statuscallers = set()
204 204
205 205 # filesets using matchctx.existing()
206 206 _existingcallers = set()
207 207
208 208 predicate = registrar.filesetpredicate()
209 209
210 210 @predicate('modified()', callstatus=True)
211 211 def modified(mctx, x):
212 212 """File that is modified according to :hg:`status`.
213 213 """
214 214 # i18n: "modified" is a keyword
215 215 getargs(x, 0, 0, _("modified takes no arguments"))
216 216 s = set(mctx.status().modified)
217 217 return [f for f in mctx.subset if f in s]
218 218
219 219 @predicate('added()', callstatus=True)
220 220 def added(mctx, x):
221 221 """File that is added according to :hg:`status`.
222 222 """
223 223 # i18n: "added" is a keyword
224 224 getargs(x, 0, 0, _("added takes no arguments"))
225 225 s = set(mctx.status().added)
226 226 return [f for f in mctx.subset if f in s]
227 227
228 228 @predicate('removed()', callstatus=True)
229 229 def removed(mctx, x):
230 230 """File that is removed according to :hg:`status`.
231 231 """
232 232 # i18n: "removed" is a keyword
233 233 getargs(x, 0, 0, _("removed takes no arguments"))
234 234 s = set(mctx.status().removed)
235 235 return [f for f in mctx.subset if f in s]
236 236
237 237 @predicate('deleted()', callstatus=True)
238 238 def deleted(mctx, x):
239 239 """Alias for ``missing()``.
240 240 """
241 241 # i18n: "deleted" is a keyword
242 242 getargs(x, 0, 0, _("deleted takes no arguments"))
243 243 s = set(mctx.status().deleted)
244 244 return [f for f in mctx.subset if f in s]
245 245
246 246 @predicate('missing()', callstatus=True)
247 247 def missing(mctx, x):
248 248 """File that is missing according to :hg:`status`.
249 249 """
250 250 # i18n: "missing" is a keyword
251 251 getargs(x, 0, 0, _("missing takes no arguments"))
252 252 s = set(mctx.status().deleted)
253 253 return [f for f in mctx.subset if f in s]
254 254
255 255 @predicate('unknown()', callstatus=True)
256 256 def unknown(mctx, x):
257 257 """File that is unknown according to :hg:`status`. These files will only be
258 258 considered if this predicate is used.
259 259 """
260 260 # i18n: "unknown" is a keyword
261 261 getargs(x, 0, 0, _("unknown takes no arguments"))
262 262 s = set(mctx.status().unknown)
263 263 return [f for f in mctx.subset if f in s]
264 264
265 265 @predicate('ignored()', callstatus=True)
266 266 def ignored(mctx, x):
267 267 """File that is ignored according to :hg:`status`. These files will only be
268 268 considered if this predicate is used.
269 269 """
270 270 # i18n: "ignored" is a keyword
271 271 getargs(x, 0, 0, _("ignored takes no arguments"))
272 272 s = set(mctx.status().ignored)
273 273 return [f for f in mctx.subset if f in s]
274 274
275 275 @predicate('clean()', callstatus=True)
276 276 def clean(mctx, x):
277 277 """File that is clean according to :hg:`status`.
278 278 """
279 279 # i18n: "clean" is a keyword
280 280 getargs(x, 0, 0, _("clean takes no arguments"))
281 281 s = set(mctx.status().clean)
282 282 return [f for f in mctx.subset if f in s]
283 283
284 284 @predicate('binary()', callexisting=True)
285 285 def binary(mctx, x):
286 286 """File that appears to be binary (contains NUL bytes).
287 287 """
288 288 # i18n: "binary" is a keyword
289 289 getargs(x, 0, 0, _("binary takes no arguments"))
290 290 return [f for f in mctx.existing() if mctx.ctx[f].isbinary()]
291 291
292 292 @predicate('exec()', callexisting=True)
293 293 def exec_(mctx, x):
294 294 """File that is marked as executable.
295 295 """
296 296 # i18n: "exec" is a keyword
297 297 getargs(x, 0, 0, _("exec takes no arguments"))
298 298 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
299 299
300 300 @predicate('symlink()', callexisting=True)
301 301 def symlink(mctx, x):
302 302 """File that is marked as a symlink.
303 303 """
304 304 # i18n: "symlink" is a keyword
305 305 getargs(x, 0, 0, _("symlink takes no arguments"))
306 306 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
307 307
308 308 @predicate('resolved()')
309 309 def resolved(mctx, x):
310 310 """File that is marked resolved according to :hg:`resolve -l`.
311 311 """
312 312 # i18n: "resolved" is a keyword
313 313 getargs(x, 0, 0, _("resolved takes no arguments"))
314 314 if mctx.ctx.rev() is not None:
315 315 return []
316 316 ms = merge.mergestate.read(mctx.ctx.repo())
317 317 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
318 318
319 319 @predicate('unresolved()')
320 320 def unresolved(mctx, x):
321 321 """File that is marked unresolved according to :hg:`resolve -l`.
322 322 """
323 323 # i18n: "unresolved" is a keyword
324 324 getargs(x, 0, 0, _("unresolved takes no arguments"))
325 325 if mctx.ctx.rev() is not None:
326 326 return []
327 327 ms = merge.mergestate.read(mctx.ctx.repo())
328 328 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
329 329
330 330 @predicate('hgignore()')
331 331 def hgignore(mctx, x):
332 332 """File that matches the active .hgignore pattern.
333 333 """
334 334 # i18n: "hgignore" is a keyword
335 335 getargs(x, 0, 0, _("hgignore takes no arguments"))
336 336 ignore = mctx.ctx.repo().dirstate._ignore
337 337 return [f for f in mctx.subset if ignore(f)]
338 338
339 339 @predicate('portable()')
340 340 def portable(mctx, x):
341 341 """File that has a portable name. (This doesn't include filenames with case
342 342 collisions.)
343 343 """
344 344 # i18n: "portable" is a keyword
345 345 getargs(x, 0, 0, _("portable takes no arguments"))
346 346 checkwinfilename = util.checkwinfilename
347 347 return [f for f in mctx.subset if checkwinfilename(f) is None]
348 348
349 349 @predicate('grep(regex)', callexisting=True)
350 350 def grep(mctx, x):
351 351 """File contains the given regular expression.
352 352 """
353 353 try:
354 354 # i18n: "grep" is a keyword
355 355 r = re.compile(getstring(x, _("grep requires a pattern")))
356 356 except re.error as e:
357 357 raise error.ParseError(_('invalid match pattern: %s') %
358 358 stringutil.forcebytestr(e))
359 359 return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
360 360
361 361 def _sizetomax(s):
362 362 try:
363 363 s = s.strip().lower()
364 364 for k, v in util._sizeunits:
365 365 if s.endswith(k):
366 366 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
367 367 n = s[:-len(k)]
368 368 inc = 1.0
369 369 if "." in n:
370 370 inc /= 10 ** len(n.split(".")[1])
371 371 return int((float(n) + inc) * v) - 1
372 372 # no extension, this is a precise value
373 373 return int(s)
374 374 except ValueError:
375 375 raise error.ParseError(_("couldn't parse size: %s") % s)
376 376
377 377 def sizematcher(x):
378 378 """Return a function(size) -> bool from the ``size()`` expression"""
379 379
380 380 # i18n: "size" is a keyword
381 381 expr = getstring(x, _("size requires an expression")).strip()
382 382 if '-' in expr: # do we have a range?
383 383 a, b = expr.split('-', 1)
384 384 a = util.sizetoint(a)
385 385 b = util.sizetoint(b)
386 386 return lambda x: x >= a and x <= b
387 387 elif expr.startswith("<="):
388 388 a = util.sizetoint(expr[2:])
389 389 return lambda x: x <= a
390 390 elif expr.startswith("<"):
391 391 a = util.sizetoint(expr[1:])
392 392 return lambda x: x < a
393 393 elif expr.startswith(">="):
394 394 a = util.sizetoint(expr[2:])
395 395 return lambda x: x >= a
396 396 elif expr.startswith(">"):
397 397 a = util.sizetoint(expr[1:])
398 398 return lambda x: x > a
399 399 else:
400 400 a = util.sizetoint(expr)
401 401 b = _sizetomax(expr)
402 402 return lambda x: x >= a and x <= b
403 403
404 404 @predicate('size(expression)', callexisting=True)
405 405 def size(mctx, x):
406 406 """File size matches the given expression. Examples:
407 407
408 408 - size('1k') - files from 1024 to 2047 bytes
409 409 - size('< 20k') - files less than 20480 bytes
410 410 - size('>= .5MB') - files at least 524288 bytes
411 411 - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
412 412 """
413 413 m = sizematcher(x)
414 414 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
415 415
416 416 @predicate('encoding(name)', callexisting=True)
417 417 def encoding(mctx, x):
418 418 """File can be successfully decoded with the given character
419 419 encoding. May not be useful for encodings other than ASCII and
420 420 UTF-8.
421 421 """
422 422
423 423 # i18n: "encoding" is a keyword
424 424 enc = getstring(x, _("encoding requires an encoding name"))
425 425
426 426 s = []
427 427 for f in mctx.existing():
428 428 d = mctx.ctx[f].data()
429 429 try:
430 430 d.decode(pycompat.sysstr(enc))
431 431 except LookupError:
432 432 raise error.Abort(_("unknown encoding '%s'") % enc)
433 433 except UnicodeDecodeError:
434 434 continue
435 435 s.append(f)
436 436
437 437 return s
438 438
439 439 @predicate('eol(style)', callexisting=True)
440 440 def eol(mctx, x):
441 441 """File contains newlines of the given style (dos, unix, mac). Binary
442 442 files are excluded, files with mixed line endings match multiple
443 443 styles.
444 444 """
445 445
446 446 # i18n: "eol" is a keyword
447 447 enc = getstring(x, _("eol requires a style name"))
448 448
449 449 s = []
450 450 for f in mctx.existing():
451 451 fctx = mctx.ctx[f]
452 452 if fctx.isbinary():
453 453 continue
454 454 d = fctx.data()
455 455 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
456 456 s.append(f)
457 457 elif enc == 'unix' and re.search('(?<!\r)\n', d):
458 458 s.append(f)
459 459 elif enc == 'mac' and re.search('\r(?!\n)', d):
460 460 s.append(f)
461 461 return s
462 462
463 463 @predicate('copied()')
464 464 def copied(mctx, x):
465 465 """File that is recorded as being copied.
466 466 """
467 467 # i18n: "copied" is a keyword
468 468 getargs(x, 0, 0, _("copied takes no arguments"))
469 469 s = []
470 470 for f in mctx.subset:
471 471 if f in mctx.ctx:
472 472 p = mctx.ctx[f].parents()
473 473 if p and p[0].path() != f:
474 474 s.append(f)
475 475 return s
476 476
477 477 @predicate('revs(revs, pattern)')
478 478 def revs(mctx, x):
479 479 """Evaluate set in the specified revisions. If the revset match multiple
480 480 revs, this will return file matching pattern in any of the revision.
481 481 """
482 482 # i18n: "revs" is a keyword
483 483 r, x = getargs(x, 2, 2, _("revs takes two arguments"))
484 484 # i18n: "revs" is a keyword
485 485 revspec = getstring(r, _("first argument to revs must be a revision"))
486 486 repo = mctx.ctx.repo()
487 487 revs = scmutil.revrange(repo, [revspec])
488 488
489 489 found = set()
490 490 result = []
491 491 for r in revs:
492 492 ctx = repo[r]
493 493 for f in getset(mctx.switch(ctx, _buildstatus(ctx, x)), x):
494 494 if f not in found:
495 495 found.add(f)
496 496 result.append(f)
497 497 return result
498 498
499 499 @predicate('status(base, rev, pattern)')
500 500 def status(mctx, x):
501 501 """Evaluate predicate using status change between ``base`` and
502 502 ``rev``. Examples:
503 503
504 504 - ``status(3, 7, added())`` - matches files added from "3" to "7"
505 505 """
506 506 repo = mctx.ctx.repo()
507 507 # i18n: "status" is a keyword
508 508 b, r, x = getargs(x, 3, 3, _("status takes three arguments"))
509 509 # i18n: "status" is a keyword
510 510 baseerr = _("first argument to status must be a revision")
511 511 baserevspec = getstring(b, baseerr)
512 512 if not baserevspec:
513 513 raise error.ParseError(baseerr)
514 514 reverr = _("second argument to status must be a revision")
515 515 revspec = getstring(r, reverr)
516 516 if not revspec:
517 517 raise error.ParseError(reverr)
518 518 basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
519 519 return getset(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
520 520
521 521 @predicate('subrepo([pattern])')
522 522 def subrepo(mctx, x):
523 523 """Subrepositories whose paths match the given pattern.
524 524 """
525 525 # i18n: "subrepo" is a keyword
526 526 getargs(x, 0, 1, _("subrepo takes at most one argument"))
527 527 ctx = mctx.ctx
528 528 sstate = sorted(ctx.substate)
529 529 if x:
530 530 pat = getpattern(x, matchmod.allpatternkinds,
531 531 # i18n: "subrepo" is a keyword
532 532 _("subrepo requires a pattern or no arguments"))
533 533 fast = not matchmod.patkind(pat)
534 534 if fast:
535 535 def m(s):
536 536 return (s == pat)
537 537 else:
538 538 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
539 539 return [sub for sub in sstate if m(sub)]
540 540 else:
541 541 return [sub for sub in sstate]
542 542
543 543 methods = {
544 544 'string': stringset,
545 545 'symbol': stringset,
546 546 'kindpat': kindpatset,
547 547 'and': andset,
548 548 'or': orset,
549 549 'minus': minusset,
550 550 'negate': negateset,
551 551 'list': listset,
552 552 'group': getset,
553 553 'not': notset,
554 554 'func': func,
555 555 }
556 556
557 557 class matchctx(object):
558 558 def __init__(self, ctx, subset, status=None):
559 559 self.ctx = ctx
560 560 self.subset = subset
561 561 self._status = status
562 562 self._existingenabled = False
563 563 def status(self):
564 564 return self._status
565 565 def matcher(self, patterns):
566 566 return self.ctx.match(patterns)
567 567 def filter(self, files):
568 568 return [f for f in files if f in self.subset]
569 569 def existing(self):
570 570 if not self._existingenabled:
571 571 raise error.ProgrammingError('unexpected existing() invocation')
572 572 if self._status is not None:
573 573 removed = set(self._status[3])
574 574 unknown = set(self._status[4] + self._status[5])
575 575 else:
576 576 removed = set()
577 577 unknown = set()
578 578 return (f for f in self.subset
579 579 if (f in self.ctx and f not in removed) or f in unknown)
580 580 def narrow(self, files):
581 581 return matchctx(self.ctx, self.filter(files), self._status)
582 582 def switch(self, ctx, status=None):
583 583 subset = self.filter(_buildsubset(ctx, status))
584 584 return matchctx(ctx, subset, status)
585 585
586 586 class fullmatchctx(matchctx):
587 587 """A match context where any files in any revisions should be valid"""
588 588
589 589 def __init__(self, ctx, status=None):
590 590 subset = _buildsubset(ctx, status)
591 591 super(fullmatchctx, self).__init__(ctx, subset, status)
592 592 def switch(self, ctx, status=None):
593 593 return fullmatchctx(ctx, status)
594 594
595 595 # filesets using matchctx.switch()
596 596 _switchcallers = [
597 597 'revs',
598 598 'status',
599 599 ]
600 600
601 601 def _intree(funcs, tree):
602 602 if isinstance(tree, tuple):
603 603 if tree[0] == 'func' and tree[1][0] == 'symbol':
604 604 if tree[1][1] in funcs:
605 605 return True
606 606 if tree[1][1] in _switchcallers:
607 607 # arguments won't be evaluated in the current context
608 608 return False
609 609 for s in tree[1:]:
610 610 if _intree(funcs, s):
611 611 return True
612 612 return False
613 613
614 614 def _buildsubset(ctx, status):
615 615 if status:
616 616 subset = []
617 617 for c in status:
618 618 subset.extend(c)
619 619 return subset
620 620 else:
621 621 return list(ctx.walk(ctx.match([])))
622 622
623 def getfileset(ctx, expr):
623 def match(ctx, expr, badfn=None):
624 """Create a matcher for a single fileset expression"""
625 repo = ctx.repo()
624 626 tree = parse(expr)
625 return getset(fullmatchctx(ctx, _buildstatus(ctx, tree)), tree)
627 fset = getset(fullmatchctx(ctx, _buildstatus(ctx, tree)), tree)
628 return matchmod.predicatematcher(repo.root, repo.getcwd(),
629 fset.__contains__,
630 predrepr='fileset', badfn=badfn)
626 631
627 632 def _buildstatus(ctx, tree, basectx=None):
628 633 # do we need status info?
629 634
630 635 # temporaty boolean to simplify the next conditional
631 636 purewdir = ctx.rev() is None and basectx is None
632 637
633 638 if (_intree(_statuscallers, tree) or
634 639 # Using matchctx.existing() on a workingctx requires us to check
635 640 # for deleted files.
636 641 (purewdir and _intree(_existingcallers, tree))):
637 642 unknown = _intree(['unknown'], tree)
638 643 ignored = _intree(['ignored'], tree)
639 644
640 645 r = ctx.repo()
641 646 if basectx is None:
642 647 basectx = ctx.p1()
643 648 return r.status(basectx, ctx,
644 649 unknown=unknown, ignored=ignored, clean=True)
645 650 else:
646 651 return None
647 652
648 653 def prettyformat(tree):
649 654 return parser.prettyformat(tree, ('string', 'symbol'))
650 655
651 656 def loadpredicate(ui, extname, registrarobj):
652 657 """Load fileset predicates from specified registrarobj
653 658 """
654 659 for name, func in registrarobj._table.iteritems():
655 660 symbols[name] = func
656 661 if func._callstatus:
657 662 _statuscallers.add(name)
658 663 if func._callexisting:
659 664 _existingcallers.add(name)
660 665
661 666 # load built-in predicates explicitly to setup _statuscallers/_existingcallers
662 667 loadpredicate(None, None, predicate)
663 668
664 669 # tell hggettext to extract docstrings from these functions:
665 670 i18nfunctions = symbols.values()
@@ -1,1133 +1,1132 b''
1 1 # match.py - filename matching
2 2 #
3 3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
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, print_function
9 9
10 10 import copy
11 11 import os
12 12 import re
13 13
14 14 from .i18n import _
15 15 from . import (
16 16 encoding,
17 17 error,
18 18 pathutil,
19 19 pycompat,
20 20 util,
21 21 )
22 22 from .utils import (
23 23 stringutil,
24 24 )
25 25
26 26 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
27 27 'listfile', 'listfile0', 'set', 'include', 'subinclude',
28 28 'rootfilesin')
29 29 cwdrelativepatternkinds = ('relpath', 'glob')
30 30
31 31 propertycache = util.propertycache
32 32
33 33 def _rematcher(regex):
34 34 '''compile the regexp with the best available regexp engine and return a
35 35 matcher function'''
36 36 m = util.re.compile(regex)
37 37 try:
38 38 # slightly faster, provided by facebook's re2 bindings
39 39 return m.test_match
40 40 except AttributeError:
41 41 return m.match
42 42
43 def _expandsets(kindpats, ctx, listsubrepos):
44 '''Returns the kindpats list with the 'set' patterns expanded.'''
45 fset = set()
43 def _expandsets(root, cwd, kindpats, ctx, listsubrepos, badfn):
44 '''Returns the kindpats list with the 'set' patterns expanded to matchers'''
45 matchers = []
46 46 other = []
47 47
48 48 for kind, pat, source in kindpats:
49 49 if kind == 'set':
50 50 if not ctx:
51 51 raise error.ProgrammingError("fileset expression with no "
52 52 "context")
53 s = ctx.getfileset(pat)
54 fset.update(s)
53 matchers.append(ctx.matchfileset(pat, badfn=badfn))
55 54
56 55 if listsubrepos:
57 56 for subpath in ctx.substate:
58 s = ctx.sub(subpath).getfileset(pat)
59 fset.update(subpath + '/' + f for f in s)
57 sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn)
58 pm = prefixdirmatcher(root, cwd, subpath, sm, badfn=badfn)
59 matchers.append(pm)
60 60
61 61 continue
62 62 other.append((kind, pat, source))
63 return fset, other
63 return matchers, other
64 64
65 65 def _expandsubinclude(kindpats, root):
66 66 '''Returns the list of subinclude matcher args and the kindpats without the
67 67 subincludes in it.'''
68 68 relmatchers = []
69 69 other = []
70 70
71 71 for kind, pat, source in kindpats:
72 72 if kind == 'subinclude':
73 73 sourceroot = pathutil.dirname(util.normpath(source))
74 74 pat = util.pconvert(pat)
75 75 path = pathutil.join(sourceroot, pat)
76 76
77 77 newroot = pathutil.dirname(path)
78 78 matcherargs = (newroot, '', [], ['include:%s' % path])
79 79
80 80 prefix = pathutil.canonpath(root, root, newroot)
81 81 if prefix:
82 82 prefix += '/'
83 83 relmatchers.append((prefix, matcherargs))
84 84 else:
85 85 other.append((kind, pat, source))
86 86
87 87 return relmatchers, other
88 88
89 89 def _kindpatsalwaysmatch(kindpats):
90 90 """"Checks whether the kindspats match everything, as e.g.
91 91 'relpath:.' does.
92 92 """
93 93 for kind, pat, source in kindpats:
94 94 if pat != '' or kind not in ['relpath', 'glob']:
95 95 return False
96 96 return True
97 97
98 98 def _buildkindpatsmatcher(matchercls, root, cwd, kindpats, ctx=None,
99 99 listsubrepos=False, badfn=None):
100 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
101 100 matchers = []
101 fms, kindpats = _expandsets(root, cwd, kindpats, ctx=ctx,
102 listsubrepos=listsubrepos, badfn=badfn)
102 103 if kindpats:
103 104 m = matchercls(root, cwd, kindpats, listsubrepos=listsubrepos,
104 105 badfn=badfn)
105 106 matchers.append(m)
106 if fset:
107 m = predicatematcher(root, cwd, fset.__contains__,
108 predrepr='fileset', badfn=badfn)
109 matchers.append(m)
107 if fms:
108 matchers.extend(fms)
110 109 if not matchers:
111 110 return nevermatcher(root, cwd, badfn=badfn)
112 111 if len(matchers) == 1:
113 112 return matchers[0]
114 113 return unionmatcher(matchers)
115 114
116 115 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
117 116 exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
118 117 badfn=None, icasefs=False):
119 118 """build an object to match a set of file patterns
120 119
121 120 arguments:
122 121 root - the canonical root of the tree you're matching against
123 122 cwd - the current working directory, if relevant
124 123 patterns - patterns to find
125 124 include - patterns to include (unless they are excluded)
126 125 exclude - patterns to exclude (even if they are included)
127 126 default - if a pattern in patterns has no explicit type, assume this one
128 127 exact - patterns are actually filenames (include/exclude still apply)
129 128 warn - optional function used for printing warnings
130 129 badfn - optional bad() callback for this matcher instead of the default
131 130 icasefs - make a matcher for wdir on case insensitive filesystems, which
132 131 normalizes the given patterns to the case in the filesystem
133 132
134 133 a pattern is one of:
135 134 'glob:<glob>' - a glob relative to cwd
136 135 're:<regexp>' - a regular expression
137 136 'path:<path>' - a path relative to repository root, which is matched
138 137 recursively
139 138 'rootfilesin:<path>' - a path relative to repository root, which is
140 139 matched non-recursively (will not match subdirectories)
141 140 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
142 141 'relpath:<path>' - a path relative to cwd
143 142 'relre:<regexp>' - a regexp that needn't match the start of a name
144 143 'set:<fileset>' - a fileset expression
145 144 'include:<path>' - a file of patterns to read and include
146 145 'subinclude:<path>' - a file of patterns to match against files under
147 146 the same directory
148 147 '<something>' - a pattern of the specified default type
149 148 """
150 149 normalize = _donormalize
151 150 if icasefs:
152 151 if exact:
153 152 raise error.ProgrammingError("a case-insensitive exact matcher "
154 153 "doesn't make sense")
155 154 dirstate = ctx.repo().dirstate
156 155 dsnormalize = dirstate.normalize
157 156
158 157 def normalize(patterns, default, root, cwd, auditor, warn):
159 158 kp = _donormalize(patterns, default, root, cwd, auditor, warn)
160 159 kindpats = []
161 160 for kind, pats, source in kp:
162 161 if kind not in ('re', 'relre'): # regex can't be normalized
163 162 p = pats
164 163 pats = dsnormalize(pats)
165 164
166 165 # Preserve the original to handle a case only rename.
167 166 if p != pats and p in dirstate:
168 167 kindpats.append((kind, p, source))
169 168
170 169 kindpats.append((kind, pats, source))
171 170 return kindpats
172 171
173 172 if exact:
174 173 m = exactmatcher(root, cwd, patterns, badfn)
175 174 elif patterns:
176 175 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
177 176 if _kindpatsalwaysmatch(kindpats):
178 177 m = alwaysmatcher(root, cwd, badfn, relativeuipath=True)
179 178 else:
180 179 m = _buildkindpatsmatcher(patternmatcher, root, cwd, kindpats,
181 180 ctx=ctx, listsubrepos=listsubrepos,
182 181 badfn=badfn)
183 182 else:
184 183 # It's a little strange that no patterns means to match everything.
185 184 # Consider changing this to match nothing (probably using nevermatcher).
186 185 m = alwaysmatcher(root, cwd, badfn)
187 186
188 187 if include:
189 188 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
190 189 im = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
191 190 listsubrepos=listsubrepos, badfn=None)
192 191 m = intersectmatchers(m, im)
193 192 if exclude:
194 193 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
195 194 em = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
196 195 listsubrepos=listsubrepos, badfn=None)
197 196 m = differencematcher(m, em)
198 197 return m
199 198
200 199 def exact(root, cwd, files, badfn=None):
201 200 return exactmatcher(root, cwd, files, badfn=badfn)
202 201
203 202 def always(root, cwd):
204 203 return alwaysmatcher(root, cwd)
205 204
206 205 def never(root, cwd):
207 206 return nevermatcher(root, cwd)
208 207
209 208 def badmatch(match, badfn):
210 209 """Make a copy of the given matcher, replacing its bad method with the given
211 210 one.
212 211 """
213 212 m = copy.copy(match)
214 213 m.bad = badfn
215 214 return m
216 215
217 216 def _donormalize(patterns, default, root, cwd, auditor, warn):
218 217 '''Convert 'kind:pat' from the patterns list to tuples with kind and
219 218 normalized and rooted patterns and with listfiles expanded.'''
220 219 kindpats = []
221 220 for kind, pat in [_patsplit(p, default) for p in patterns]:
222 221 if kind in cwdrelativepatternkinds:
223 222 pat = pathutil.canonpath(root, cwd, pat, auditor)
224 223 elif kind in ('relglob', 'path', 'rootfilesin'):
225 224 pat = util.normpath(pat)
226 225 elif kind in ('listfile', 'listfile0'):
227 226 try:
228 227 files = util.readfile(pat)
229 228 if kind == 'listfile0':
230 229 files = files.split('\0')
231 230 else:
232 231 files = files.splitlines()
233 232 files = [f for f in files if f]
234 233 except EnvironmentError:
235 234 raise error.Abort(_("unable to read file list (%s)") % pat)
236 235 for k, p, source in _donormalize(files, default, root, cwd,
237 236 auditor, warn):
238 237 kindpats.append((k, p, pat))
239 238 continue
240 239 elif kind == 'include':
241 240 try:
242 241 fullpath = os.path.join(root, util.localpath(pat))
243 242 includepats = readpatternfile(fullpath, warn)
244 243 for k, p, source in _donormalize(includepats, default,
245 244 root, cwd, auditor, warn):
246 245 kindpats.append((k, p, source or pat))
247 246 except error.Abort as inst:
248 247 raise error.Abort('%s: %s' % (pat, inst[0]))
249 248 except IOError as inst:
250 249 if warn:
251 250 warn(_("skipping unreadable pattern file '%s': %s\n") %
252 251 (pat, stringutil.forcebytestr(inst.strerror)))
253 252 continue
254 253 # else: re or relre - which cannot be normalized
255 254 kindpats.append((kind, pat, ''))
256 255 return kindpats
257 256
258 257 class basematcher(object):
259 258
260 259 def __init__(self, root, cwd, badfn=None, relativeuipath=True):
261 260 self._root = root
262 261 self._cwd = cwd
263 262 if badfn is not None:
264 263 self.bad = badfn
265 264 self._relativeuipath = relativeuipath
266 265
267 266 def __call__(self, fn):
268 267 return self.matchfn(fn)
269 268 def __iter__(self):
270 269 for f in self._files:
271 270 yield f
272 271 # Callbacks related to how the matcher is used by dirstate.walk.
273 272 # Subscribers to these events must monkeypatch the matcher object.
274 273 def bad(self, f, msg):
275 274 '''Callback from dirstate.walk for each explicit file that can't be
276 275 found/accessed, with an error message.'''
277 276
278 277 # If an explicitdir is set, it will be called when an explicitly listed
279 278 # directory is visited.
280 279 explicitdir = None
281 280
282 281 # If an traversedir is set, it will be called when a directory discovered
283 282 # by recursive traversal is visited.
284 283 traversedir = None
285 284
286 285 def abs(self, f):
287 286 '''Convert a repo path back to path that is relative to the root of the
288 287 matcher.'''
289 288 return f
290 289
291 290 def rel(self, f):
292 291 '''Convert repo path back to path that is relative to cwd of matcher.'''
293 292 return util.pathto(self._root, self._cwd, f)
294 293
295 294 def uipath(self, f):
296 295 '''Convert repo path to a display path. If patterns or -I/-X were used
297 296 to create this matcher, the display path will be relative to cwd.
298 297 Otherwise it is relative to the root of the repo.'''
299 298 return (self._relativeuipath and self.rel(f)) or self.abs(f)
300 299
301 300 @propertycache
302 301 def _files(self):
303 302 return []
304 303
305 304 def files(self):
306 305 '''Explicitly listed files or patterns or roots:
307 306 if no patterns or .always(): empty list,
308 307 if exact: list exact files,
309 308 if not .anypats(): list all files and dirs,
310 309 else: optimal roots'''
311 310 return self._files
312 311
313 312 @propertycache
314 313 def _fileset(self):
315 314 return set(self._files)
316 315
317 316 def exact(self, f):
318 317 '''Returns True if f is in .files().'''
319 318 return f in self._fileset
320 319
321 320 def matchfn(self, f):
322 321 return False
323 322
324 323 def visitdir(self, dir):
325 324 '''Decides whether a directory should be visited based on whether it
326 325 has potential matches in it or one of its subdirectories. This is
327 326 based on the match's primary, included, and excluded patterns.
328 327
329 328 Returns the string 'all' if the given directory and all subdirectories
330 329 should be visited. Otherwise returns True or False indicating whether
331 330 the given directory should be visited.
332 331 '''
333 332 return True
334 333
335 334 def always(self):
336 335 '''Matcher will match everything and .files() will be empty --
337 336 optimization might be possible.'''
338 337 return False
339 338
340 339 def isexact(self):
341 340 '''Matcher will match exactly the list of files in .files() --
342 341 optimization might be possible.'''
343 342 return False
344 343
345 344 def prefix(self):
346 345 '''Matcher will match the paths in .files() recursively --
347 346 optimization might be possible.'''
348 347 return False
349 348
350 349 def anypats(self):
351 350 '''None of .always(), .isexact(), and .prefix() is true --
352 351 optimizations will be difficult.'''
353 352 return not self.always() and not self.isexact() and not self.prefix()
354 353
355 354 class alwaysmatcher(basematcher):
356 355 '''Matches everything.'''
357 356
358 357 def __init__(self, root, cwd, badfn=None, relativeuipath=False):
359 358 super(alwaysmatcher, self).__init__(root, cwd, badfn,
360 359 relativeuipath=relativeuipath)
361 360
362 361 def always(self):
363 362 return True
364 363
365 364 def matchfn(self, f):
366 365 return True
367 366
368 367 def visitdir(self, dir):
369 368 return 'all'
370 369
371 370 def __repr__(self):
372 371 return r'<alwaysmatcher>'
373 372
374 373 class nevermatcher(basematcher):
375 374 '''Matches nothing.'''
376 375
377 376 def __init__(self, root, cwd, badfn=None):
378 377 super(nevermatcher, self).__init__(root, cwd, badfn)
379 378
380 379 # It's a little weird to say that the nevermatcher is an exact matcher
381 380 # or a prefix matcher, but it seems to make sense to let callers take
382 381 # fast paths based on either. There will be no exact matches, nor any
383 382 # prefixes (files() returns []), so fast paths iterating over them should
384 383 # be efficient (and correct).
385 384 def isexact(self):
386 385 return True
387 386
388 387 def prefix(self):
389 388 return True
390 389
391 390 def visitdir(self, dir):
392 391 return False
393 392
394 393 def __repr__(self):
395 394 return r'<nevermatcher>'
396 395
397 396 class predicatematcher(basematcher):
398 397 """A matcher adapter for a simple boolean function"""
399 398
400 399 def __init__(self, root, cwd, predfn, predrepr=None, badfn=None):
401 400 super(predicatematcher, self).__init__(root, cwd, badfn)
402 401 self.matchfn = predfn
403 402 self._predrepr = predrepr
404 403
405 404 @encoding.strmethod
406 405 def __repr__(self):
407 406 s = (stringutil.buildrepr(self._predrepr)
408 407 or pycompat.byterepr(self.matchfn))
409 408 return '<predicatenmatcher pred=%s>' % s
410 409
411 410 class patternmatcher(basematcher):
412 411
413 412 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
414 413 super(patternmatcher, self).__init__(root, cwd, badfn)
415 414
416 415 self._files = _explicitfiles(kindpats)
417 416 self._prefix = _prefix(kindpats)
418 417 self._pats, self.matchfn = _buildmatch(kindpats, '$', listsubrepos,
419 418 root)
420 419
421 420 @propertycache
422 421 def _dirs(self):
423 422 return set(util.dirs(self._fileset)) | {'.'}
424 423
425 424 def visitdir(self, dir):
426 425 if self._prefix and dir in self._fileset:
427 426 return 'all'
428 427 return ('.' in self._fileset or
429 428 dir in self._fileset or
430 429 dir in self._dirs or
431 430 any(parentdir in self._fileset
432 431 for parentdir in util.finddirs(dir)))
433 432
434 433 def prefix(self):
435 434 return self._prefix
436 435
437 436 @encoding.strmethod
438 437 def __repr__(self):
439 438 return ('<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats))
440 439
441 440 class includematcher(basematcher):
442 441
443 442 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
444 443 super(includematcher, self).__init__(root, cwd, badfn)
445 444
446 445 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)',
447 446 listsubrepos, root)
448 447 self._prefix = _prefix(kindpats)
449 448 roots, dirs = _rootsanddirs(kindpats)
450 449 # roots are directories which are recursively included.
451 450 self._roots = set(roots)
452 451 # dirs are directories which are non-recursively included.
453 452 self._dirs = set(dirs)
454 453
455 454 def visitdir(self, dir):
456 455 if self._prefix and dir in self._roots:
457 456 return 'all'
458 457 return ('.' in self._roots or
459 458 dir in self._roots or
460 459 dir in self._dirs or
461 460 any(parentdir in self._roots
462 461 for parentdir in util.finddirs(dir)))
463 462
464 463 @encoding.strmethod
465 464 def __repr__(self):
466 465 return ('<includematcher includes=%r>' % pycompat.bytestr(self._pats))
467 466
468 467 class exactmatcher(basematcher):
469 468 '''Matches the input files exactly. They are interpreted as paths, not
470 469 patterns (so no kind-prefixes).
471 470 '''
472 471
473 472 def __init__(self, root, cwd, files, badfn=None):
474 473 super(exactmatcher, self).__init__(root, cwd, badfn)
475 474
476 475 if isinstance(files, list):
477 476 self._files = files
478 477 else:
479 478 self._files = list(files)
480 479
481 480 matchfn = basematcher.exact
482 481
483 482 @propertycache
484 483 def _dirs(self):
485 484 return set(util.dirs(self._fileset)) | {'.'}
486 485
487 486 def visitdir(self, dir):
488 487 return dir in self._dirs
489 488
490 489 def isexact(self):
491 490 return True
492 491
493 492 @encoding.strmethod
494 493 def __repr__(self):
495 494 return ('<exactmatcher files=%r>' % self._files)
496 495
497 496 class differencematcher(basematcher):
498 497 '''Composes two matchers by matching if the first matches and the second
499 498 does not.
500 499
501 500 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
502 501 traversedir) are ignored.
503 502 '''
504 503 def __init__(self, m1, m2):
505 504 super(differencematcher, self).__init__(m1._root, m1._cwd)
506 505 self._m1 = m1
507 506 self._m2 = m2
508 507 self.bad = m1.bad
509 508 self.explicitdir = m1.explicitdir
510 509 self.traversedir = m1.traversedir
511 510
512 511 def matchfn(self, f):
513 512 return self._m1(f) and not self._m2(f)
514 513
515 514 @propertycache
516 515 def _files(self):
517 516 if self.isexact():
518 517 return [f for f in self._m1.files() if self(f)]
519 518 # If m1 is not an exact matcher, we can't easily figure out the set of
520 519 # files, because its files() are not always files. For example, if
521 520 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
522 521 # want to remove "dir" from the set even though it would match m2,
523 522 # because the "dir" in m1 may not be a file.
524 523 return self._m1.files()
525 524
526 525 def visitdir(self, dir):
527 526 if self._m2.visitdir(dir) == 'all':
528 527 return False
529 528 return bool(self._m1.visitdir(dir))
530 529
531 530 def isexact(self):
532 531 return self._m1.isexact()
533 532
534 533 @encoding.strmethod
535 534 def __repr__(self):
536 535 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
537 536
538 537 def intersectmatchers(m1, m2):
539 538 '''Composes two matchers by matching if both of them match.
540 539
541 540 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
542 541 traversedir) are ignored.
543 542 '''
544 543 if m1 is None or m2 is None:
545 544 return m1 or m2
546 545 if m1.always():
547 546 m = copy.copy(m2)
548 547 # TODO: Consider encapsulating these things in a class so there's only
549 548 # one thing to copy from m1.
550 549 m.bad = m1.bad
551 550 m.explicitdir = m1.explicitdir
552 551 m.traversedir = m1.traversedir
553 552 m.abs = m1.abs
554 553 m.rel = m1.rel
555 554 m._relativeuipath |= m1._relativeuipath
556 555 return m
557 556 if m2.always():
558 557 m = copy.copy(m1)
559 558 m._relativeuipath |= m2._relativeuipath
560 559 return m
561 560 return intersectionmatcher(m1, m2)
562 561
563 562 class intersectionmatcher(basematcher):
564 563 def __init__(self, m1, m2):
565 564 super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
566 565 self._m1 = m1
567 566 self._m2 = m2
568 567 self.bad = m1.bad
569 568 self.explicitdir = m1.explicitdir
570 569 self.traversedir = m1.traversedir
571 570
572 571 @propertycache
573 572 def _files(self):
574 573 if self.isexact():
575 574 m1, m2 = self._m1, self._m2
576 575 if not m1.isexact():
577 576 m1, m2 = m2, m1
578 577 return [f for f in m1.files() if m2(f)]
579 578 # It neither m1 nor m2 is an exact matcher, we can't easily intersect
580 579 # the set of files, because their files() are not always files. For
581 580 # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
582 581 # "path:dir2", we don't want to remove "dir2" from the set.
583 582 return self._m1.files() + self._m2.files()
584 583
585 584 def matchfn(self, f):
586 585 return self._m1(f) and self._m2(f)
587 586
588 587 def visitdir(self, dir):
589 588 visit1 = self._m1.visitdir(dir)
590 589 if visit1 == 'all':
591 590 return self._m2.visitdir(dir)
592 591 # bool() because visit1=True + visit2='all' should not be 'all'
593 592 return bool(visit1 and self._m2.visitdir(dir))
594 593
595 594 def always(self):
596 595 return self._m1.always() and self._m2.always()
597 596
598 597 def isexact(self):
599 598 return self._m1.isexact() or self._m2.isexact()
600 599
601 600 @encoding.strmethod
602 601 def __repr__(self):
603 602 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
604 603
605 604 class subdirmatcher(basematcher):
606 605 """Adapt a matcher to work on a subdirectory only.
607 606
608 607 The paths are remapped to remove/insert the path as needed:
609 608
610 609 >>> from . import pycompat
611 610 >>> m1 = match(b'root', b'', [b'a.txt', b'sub/b.txt'])
612 611 >>> m2 = subdirmatcher(b'sub', m1)
613 612 >>> bool(m2(b'a.txt'))
614 613 False
615 614 >>> bool(m2(b'b.txt'))
616 615 True
617 616 >>> bool(m2.matchfn(b'a.txt'))
618 617 False
619 618 >>> bool(m2.matchfn(b'b.txt'))
620 619 True
621 620 >>> m2.files()
622 621 ['b.txt']
623 622 >>> m2.exact(b'b.txt')
624 623 True
625 624 >>> util.pconvert(m2.rel(b'b.txt'))
626 625 'sub/b.txt'
627 626 >>> def bad(f, msg):
628 627 ... print(pycompat.sysstr(b"%s: %s" % (f, msg)))
629 628 >>> m1.bad = bad
630 629 >>> m2.bad(b'x.txt', b'No such file')
631 630 sub/x.txt: No such file
632 631 >>> m2.abs(b'c.txt')
633 632 'sub/c.txt'
634 633 """
635 634
636 635 def __init__(self, path, matcher):
637 636 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
638 637 self._path = path
639 638 self._matcher = matcher
640 639 self._always = matcher.always()
641 640
642 641 self._files = [f[len(path) + 1:] for f in matcher._files
643 642 if f.startswith(path + "/")]
644 643
645 644 # If the parent repo had a path to this subrepo and the matcher is
646 645 # a prefix matcher, this submatcher always matches.
647 646 if matcher.prefix():
648 647 self._always = any(f == path for f in matcher._files)
649 648
650 649 def bad(self, f, msg):
651 650 self._matcher.bad(self._path + "/" + f, msg)
652 651
653 652 def abs(self, f):
654 653 return self._matcher.abs(self._path + "/" + f)
655 654
656 655 def rel(self, f):
657 656 return self._matcher.rel(self._path + "/" + f)
658 657
659 658 def uipath(self, f):
660 659 return self._matcher.uipath(self._path + "/" + f)
661 660
662 661 def matchfn(self, f):
663 662 # Some information is lost in the superclass's constructor, so we
664 663 # can not accurately create the matching function for the subdirectory
665 664 # from the inputs. Instead, we override matchfn() and visitdir() to
666 665 # call the original matcher with the subdirectory path prepended.
667 666 return self._matcher.matchfn(self._path + "/" + f)
668 667
669 668 def visitdir(self, dir):
670 669 if dir == '.':
671 670 dir = self._path
672 671 else:
673 672 dir = self._path + "/" + dir
674 673 return self._matcher.visitdir(dir)
675 674
676 675 def always(self):
677 676 return self._always
678 677
679 678 def prefix(self):
680 679 return self._matcher.prefix() and not self._always
681 680
682 681 @encoding.strmethod
683 682 def __repr__(self):
684 683 return ('<subdirmatcher path=%r, matcher=%r>' %
685 684 (self._path, self._matcher))
686 685
687 686 class prefixdirmatcher(basematcher):
688 687 """Adapt a matcher to work on a parent directory.
689 688
690 689 The matcher's non-matching-attributes (root, cwd, bad, explicitdir,
691 690 traversedir) are ignored.
692 691
693 692 The prefix path should usually be the relative path from the root of
694 693 this matcher to the root of the wrapped matcher.
695 694
696 695 >>> m1 = match(b'root/d/e', b'f', [b'../a.txt', b'b.txt'])
697 696 >>> m2 = prefixdirmatcher(b'root', b'd/e/f', b'd/e', m1)
698 697 >>> bool(m2(b'a.txt'),)
699 698 False
700 699 >>> bool(m2(b'd/e/a.txt'))
701 700 True
702 701 >>> bool(m2(b'd/e/b.txt'))
703 702 False
704 703 >>> m2.files()
705 704 ['d/e/a.txt', 'd/e/f/b.txt']
706 705 >>> m2.exact(b'd/e/a.txt')
707 706 True
708 707 >>> m2.visitdir(b'd')
709 708 True
710 709 >>> m2.visitdir(b'd/e')
711 710 True
712 711 >>> m2.visitdir(b'd/e/f')
713 712 True
714 713 >>> m2.visitdir(b'd/e/g')
715 714 False
716 715 >>> m2.visitdir(b'd/ef')
717 716 False
718 717 """
719 718
720 719 def __init__(self, root, cwd, path, matcher, badfn=None):
721 720 super(prefixdirmatcher, self).__init__(root, cwd, badfn)
722 721 if not path:
723 722 raise error.ProgrammingError('prefix path must not be empty')
724 723 self._path = path
725 724 self._pathprefix = path + '/'
726 725 self._matcher = matcher
727 726
728 727 @propertycache
729 728 def _files(self):
730 729 return [self._pathprefix + f for f in self._matcher._files]
731 730
732 731 def matchfn(self, f):
733 732 if not f.startswith(self._pathprefix):
734 733 return False
735 734 return self._matcher.matchfn(f[len(self._pathprefix):])
736 735
737 736 @propertycache
738 737 def _pathdirs(self):
739 738 return set(util.finddirs(self._path)) | {'.'}
740 739
741 740 def visitdir(self, dir):
742 741 if dir == self._path:
743 742 return self._matcher.visitdir('.')
744 743 if dir.startswith(self._pathprefix):
745 744 return self._matcher.visitdir(dir[len(self._pathprefix):])
746 745 return dir in self._pathdirs
747 746
748 747 def isexact(self):
749 748 return self._matcher.isexact()
750 749
751 750 def prefix(self):
752 751 return self._matcher.prefix()
753 752
754 753 @encoding.strmethod
755 754 def __repr__(self):
756 755 return ('<prefixdirmatcher path=%r, matcher=%r>'
757 756 % (pycompat.bytestr(self._path), self._matcher))
758 757
759 758 class unionmatcher(basematcher):
760 759 """A matcher that is the union of several matchers.
761 760
762 761 The non-matching-attributes (root, cwd, bad, explicitdir, traversedir) are
763 762 taken from the first matcher.
764 763 """
765 764
766 765 def __init__(self, matchers):
767 766 m1 = matchers[0]
768 767 super(unionmatcher, self).__init__(m1._root, m1._cwd)
769 768 self.explicitdir = m1.explicitdir
770 769 self.traversedir = m1.traversedir
771 770 self._matchers = matchers
772 771
773 772 def matchfn(self, f):
774 773 for match in self._matchers:
775 774 if match(f):
776 775 return True
777 776 return False
778 777
779 778 def visitdir(self, dir):
780 779 r = False
781 780 for m in self._matchers:
782 781 v = m.visitdir(dir)
783 782 if v == 'all':
784 783 return v
785 784 r |= v
786 785 return r
787 786
788 787 @encoding.strmethod
789 788 def __repr__(self):
790 789 return ('<unionmatcher matchers=%r>' % self._matchers)
791 790
792 791 def patkind(pattern, default=None):
793 792 '''If pattern is 'kind:pat' with a known kind, return kind.'''
794 793 return _patsplit(pattern, default)[0]
795 794
796 795 def _patsplit(pattern, default):
797 796 """Split a string into the optional pattern kind prefix and the actual
798 797 pattern."""
799 798 if ':' in pattern:
800 799 kind, pat = pattern.split(':', 1)
801 800 if kind in allpatternkinds:
802 801 return kind, pat
803 802 return default, pattern
804 803
805 804 def _globre(pat):
806 805 r'''Convert an extended glob string to a regexp string.
807 806
808 807 >>> from . import pycompat
809 808 >>> def bprint(s):
810 809 ... print(pycompat.sysstr(s))
811 810 >>> bprint(_globre(br'?'))
812 811 .
813 812 >>> bprint(_globre(br'*'))
814 813 [^/]*
815 814 >>> bprint(_globre(br'**'))
816 815 .*
817 816 >>> bprint(_globre(br'**/a'))
818 817 (?:.*/)?a
819 818 >>> bprint(_globre(br'a/**/b'))
820 819 a/(?:.*/)?b
821 820 >>> bprint(_globre(br'[a*?!^][^b][!c]'))
822 821 [a*?!^][\^b][^c]
823 822 >>> bprint(_globre(br'{a,b}'))
824 823 (?:a|b)
825 824 >>> bprint(_globre(br'.\*\?'))
826 825 \.\*\?
827 826 '''
828 827 i, n = 0, len(pat)
829 828 res = ''
830 829 group = 0
831 830 escape = util.stringutil.reescape
832 831 def peek():
833 832 return i < n and pat[i:i + 1]
834 833 while i < n:
835 834 c = pat[i:i + 1]
836 835 i += 1
837 836 if c not in '*?[{},\\':
838 837 res += escape(c)
839 838 elif c == '*':
840 839 if peek() == '*':
841 840 i += 1
842 841 if peek() == '/':
843 842 i += 1
844 843 res += '(?:.*/)?'
845 844 else:
846 845 res += '.*'
847 846 else:
848 847 res += '[^/]*'
849 848 elif c == '?':
850 849 res += '.'
851 850 elif c == '[':
852 851 j = i
853 852 if j < n and pat[j:j + 1] in '!]':
854 853 j += 1
855 854 while j < n and pat[j:j + 1] != ']':
856 855 j += 1
857 856 if j >= n:
858 857 res += '\\['
859 858 else:
860 859 stuff = pat[i:j].replace('\\','\\\\')
861 860 i = j + 1
862 861 if stuff[0:1] == '!':
863 862 stuff = '^' + stuff[1:]
864 863 elif stuff[0:1] == '^':
865 864 stuff = '\\' + stuff
866 865 res = '%s[%s]' % (res, stuff)
867 866 elif c == '{':
868 867 group += 1
869 868 res += '(?:'
870 869 elif c == '}' and group:
871 870 res += ')'
872 871 group -= 1
873 872 elif c == ',' and group:
874 873 res += '|'
875 874 elif c == '\\':
876 875 p = peek()
877 876 if p:
878 877 i += 1
879 878 res += escape(p)
880 879 else:
881 880 res += escape(c)
882 881 else:
883 882 res += escape(c)
884 883 return res
885 884
886 885 def _regex(kind, pat, globsuffix):
887 886 '''Convert a (normalized) pattern of any kind into a regular expression.
888 887 globsuffix is appended to the regexp of globs.'''
889 888 if not pat:
890 889 return ''
891 890 if kind == 're':
892 891 return pat
893 892 if kind in ('path', 'relpath'):
894 893 if pat == '.':
895 894 return ''
896 895 return util.stringutil.reescape(pat) + '(?:/|$)'
897 896 if kind == 'rootfilesin':
898 897 if pat == '.':
899 898 escaped = ''
900 899 else:
901 900 # Pattern is a directory name.
902 901 escaped = util.stringutil.reescape(pat) + '/'
903 902 # Anything after the pattern must be a non-directory.
904 903 return escaped + '[^/]+$'
905 904 if kind == 'relglob':
906 905 return '(?:|.*/)' + _globre(pat) + globsuffix
907 906 if kind == 'relre':
908 907 if pat.startswith('^'):
909 908 return pat
910 909 return '.*' + pat
911 910 if kind == 'glob':
912 911 return _globre(pat) + globsuffix
913 912 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat))
914 913
915 914 def _buildmatch(kindpats, globsuffix, listsubrepos, root):
916 915 '''Return regexp string and a matcher function for kindpats.
917 916 globsuffix is appended to the regexp of globs.'''
918 917 matchfuncs = []
919 918
920 919 subincludes, kindpats = _expandsubinclude(kindpats, root)
921 920 if subincludes:
922 921 submatchers = {}
923 922 def matchsubinclude(f):
924 923 for prefix, matcherargs in subincludes:
925 924 if f.startswith(prefix):
926 925 mf = submatchers.get(prefix)
927 926 if mf is None:
928 927 mf = match(*matcherargs)
929 928 submatchers[prefix] = mf
930 929
931 930 if mf(f[len(prefix):]):
932 931 return True
933 932 return False
934 933 matchfuncs.append(matchsubinclude)
935 934
936 935 regex = ''
937 936 if kindpats:
938 937 regex, mf = _buildregexmatch(kindpats, globsuffix)
939 938 matchfuncs.append(mf)
940 939
941 940 if len(matchfuncs) == 1:
942 941 return regex, matchfuncs[0]
943 942 else:
944 943 return regex, lambda f: any(mf(f) for mf in matchfuncs)
945 944
946 945 def _buildregexmatch(kindpats, globsuffix):
947 946 """Build a match function from a list of kinds and kindpats,
948 947 return regexp string and a matcher function."""
949 948 try:
950 949 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
951 950 for (k, p, s) in kindpats])
952 951 if len(regex) > 20000:
953 952 raise OverflowError
954 953 return regex, _rematcher(regex)
955 954 except OverflowError:
956 955 # We're using a Python with a tiny regex engine and we
957 956 # made it explode, so we'll divide the pattern list in two
958 957 # until it works
959 958 l = len(kindpats)
960 959 if l < 2:
961 960 raise
962 961 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
963 962 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
964 963 return regex, lambda s: a(s) or b(s)
965 964 except re.error:
966 965 for k, p, s in kindpats:
967 966 try:
968 967 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
969 968 except re.error:
970 969 if s:
971 970 raise error.Abort(_("%s: invalid pattern (%s): %s") %
972 971 (s, k, p))
973 972 else:
974 973 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
975 974 raise error.Abort(_("invalid pattern"))
976 975
977 976 def _patternrootsanddirs(kindpats):
978 977 '''Returns roots and directories corresponding to each pattern.
979 978
980 979 This calculates the roots and directories exactly matching the patterns and
981 980 returns a tuple of (roots, dirs) for each. It does not return other
982 981 directories which may also need to be considered, like the parent
983 982 directories.
984 983 '''
985 984 r = []
986 985 d = []
987 986 for kind, pat, source in kindpats:
988 987 if kind == 'glob': # find the non-glob prefix
989 988 root = []
990 989 for p in pat.split('/'):
991 990 if '[' in p or '{' in p or '*' in p or '?' in p:
992 991 break
993 992 root.append(p)
994 993 r.append('/'.join(root) or '.')
995 994 elif kind in ('relpath', 'path'):
996 995 r.append(pat or '.')
997 996 elif kind in ('rootfilesin',):
998 997 d.append(pat or '.')
999 998 else: # relglob, re, relre
1000 999 r.append('.')
1001 1000 return r, d
1002 1001
1003 1002 def _roots(kindpats):
1004 1003 '''Returns root directories to match recursively from the given patterns.'''
1005 1004 roots, dirs = _patternrootsanddirs(kindpats)
1006 1005 return roots
1007 1006
1008 1007 def _rootsanddirs(kindpats):
1009 1008 '''Returns roots and exact directories from patterns.
1010 1009
1011 1010 roots are directories to match recursively, whereas exact directories should
1012 1011 be matched non-recursively. The returned (roots, dirs) tuple will also
1013 1012 include directories that need to be implicitly considered as either, such as
1014 1013 parent directories.
1015 1014
1016 1015 >>> _rootsanddirs(
1017 1016 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''),
1018 1017 ... (b'glob', b'g*', b'')])
1019 1018 (['g/h', 'g/h', '.'], ['g', '.'])
1020 1019 >>> _rootsanddirs(
1021 1020 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')])
1022 1021 ([], ['g/h', '.', 'g', '.'])
1023 1022 >>> _rootsanddirs(
1024 1023 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''),
1025 1024 ... (b'path', b'', b'')])
1026 1025 (['r', 'p/p', '.'], ['p', '.'])
1027 1026 >>> _rootsanddirs(
1028 1027 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''),
1029 1028 ... (b'relre', b'rr', b'')])
1030 1029 (['.', '.', '.'], ['.'])
1031 1030 '''
1032 1031 r, d = _patternrootsanddirs(kindpats)
1033 1032
1034 1033 # Append the parents as non-recursive/exact directories, since they must be
1035 1034 # scanned to get to either the roots or the other exact directories.
1036 1035 d.extend(util.dirs(d))
1037 1036 d.extend(util.dirs(r))
1038 1037 # util.dirs() does not include the root directory, so add it manually
1039 1038 d.append('.')
1040 1039
1041 1040 return r, d
1042 1041
1043 1042 def _explicitfiles(kindpats):
1044 1043 '''Returns the potential explicit filenames from the patterns.
1045 1044
1046 1045 >>> _explicitfiles([(b'path', b'foo/bar', b'')])
1047 1046 ['foo/bar']
1048 1047 >>> _explicitfiles([(b'rootfilesin', b'foo/bar', b'')])
1049 1048 []
1050 1049 '''
1051 1050 # Keep only the pattern kinds where one can specify filenames (vs only
1052 1051 # directory names).
1053 1052 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
1054 1053 return _roots(filable)
1055 1054
1056 1055 def _prefix(kindpats):
1057 1056 '''Whether all the patterns match a prefix (i.e. recursively)'''
1058 1057 for kind, pat, source in kindpats:
1059 1058 if kind not in ('path', 'relpath'):
1060 1059 return False
1061 1060 return True
1062 1061
1063 1062 _commentre = None
1064 1063
1065 1064 def readpatternfile(filepath, warn, sourceinfo=False):
1066 1065 '''parse a pattern file, returning a list of
1067 1066 patterns. These patterns should be given to compile()
1068 1067 to be validated and converted into a match function.
1069 1068
1070 1069 trailing white space is dropped.
1071 1070 the escape character is backslash.
1072 1071 comments start with #.
1073 1072 empty lines are skipped.
1074 1073
1075 1074 lines can be of the following formats:
1076 1075
1077 1076 syntax: regexp # defaults following lines to non-rooted regexps
1078 1077 syntax: glob # defaults following lines to non-rooted globs
1079 1078 re:pattern # non-rooted regular expression
1080 1079 glob:pattern # non-rooted glob
1081 1080 pattern # pattern of the current default type
1082 1081
1083 1082 if sourceinfo is set, returns a list of tuples:
1084 1083 (pattern, lineno, originalline). This is useful to debug ignore patterns.
1085 1084 '''
1086 1085
1087 1086 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
1088 1087 'include': 'include', 'subinclude': 'subinclude'}
1089 1088 syntax = 'relre:'
1090 1089 patterns = []
1091 1090
1092 1091 fp = open(filepath, 'rb')
1093 1092 for lineno, line in enumerate(util.iterfile(fp), start=1):
1094 1093 if "#" in line:
1095 1094 global _commentre
1096 1095 if not _commentre:
1097 1096 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
1098 1097 # remove comments prefixed by an even number of escapes
1099 1098 m = _commentre.search(line)
1100 1099 if m:
1101 1100 line = line[:m.end(1)]
1102 1101 # fixup properly escaped comments that survived the above
1103 1102 line = line.replace("\\#", "#")
1104 1103 line = line.rstrip()
1105 1104 if not line:
1106 1105 continue
1107 1106
1108 1107 if line.startswith('syntax:'):
1109 1108 s = line[7:].strip()
1110 1109 try:
1111 1110 syntax = syntaxes[s]
1112 1111 except KeyError:
1113 1112 if warn:
1114 1113 warn(_("%s: ignoring invalid syntax '%s'\n") %
1115 1114 (filepath, s))
1116 1115 continue
1117 1116
1118 1117 linesyntax = syntax
1119 1118 for s, rels in syntaxes.iteritems():
1120 1119 if line.startswith(rels):
1121 1120 linesyntax = rels
1122 1121 line = line[len(rels):]
1123 1122 break
1124 1123 elif line.startswith(s+':'):
1125 1124 linesyntax = rels
1126 1125 line = line[len(s) + 1:]
1127 1126 break
1128 1127 if sourceinfo:
1129 1128 patterns.append((linesyntax + line, lineno, line))
1130 1129 else:
1131 1130 patterns.append(linesyntax + line)
1132 1131 fp.close()
1133 1132 return patterns
@@ -1,1816 +1,1822 b''
1 1 # subrepo.py - sub-repository classes and factory
2 2 #
3 3 # Copyright 2009-2010 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 copy
11 11 import errno
12 12 import hashlib
13 13 import os
14 14 import posixpath
15 15 import re
16 16 import stat
17 17 import subprocess
18 18 import sys
19 19 import tarfile
20 20 import xml.dom.minidom
21 21
22 22 from .i18n import _
23 23 from . import (
24 24 cmdutil,
25 25 encoding,
26 26 error,
27 27 exchange,
28 28 logcmdutil,
29 29 match as matchmod,
30 30 node,
31 31 pathutil,
32 32 phases,
33 33 pycompat,
34 34 scmutil,
35 35 subrepoutil,
36 36 util,
37 37 vfs as vfsmod,
38 38 )
39 39 from .utils import (
40 40 dateutil,
41 41 procutil,
42 42 stringutil,
43 43 )
44 44
45 45 hg = None
46 46 reporelpath = subrepoutil.reporelpath
47 47 subrelpath = subrepoutil.subrelpath
48 48 _abssource = subrepoutil._abssource
49 49 propertycache = util.propertycache
50 50
51 51 def _expandedabspath(path):
52 52 '''
53 53 get a path or url and if it is a path expand it and return an absolute path
54 54 '''
55 55 expandedpath = util.urllocalpath(util.expandpath(path))
56 56 u = util.url(expandedpath)
57 57 if not u.scheme:
58 58 path = util.normpath(os.path.abspath(u.path))
59 59 return path
60 60
61 61 def _getstorehashcachename(remotepath):
62 62 '''get a unique filename for the store hash cache of a remote repository'''
63 63 return node.hex(hashlib.sha1(_expandedabspath(remotepath)).digest())[0:12]
64 64
65 65 class SubrepoAbort(error.Abort):
66 66 """Exception class used to avoid handling a subrepo error more than once"""
67 67 def __init__(self, *args, **kw):
68 68 self.subrepo = kw.pop(r'subrepo', None)
69 69 self.cause = kw.pop(r'cause', None)
70 70 error.Abort.__init__(self, *args, **kw)
71 71
72 72 def annotatesubrepoerror(func):
73 73 def decoratedmethod(self, *args, **kargs):
74 74 try:
75 75 res = func(self, *args, **kargs)
76 76 except SubrepoAbort as ex:
77 77 # This exception has already been handled
78 78 raise ex
79 79 except error.Abort as ex:
80 80 subrepo = subrelpath(self)
81 81 errormsg = (stringutil.forcebytestr(ex) + ' '
82 82 + _('(in subrepository "%s")') % subrepo)
83 83 # avoid handling this exception by raising a SubrepoAbort exception
84 84 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
85 85 cause=sys.exc_info())
86 86 return res
87 87 return decoratedmethod
88 88
89 89 def _updateprompt(ui, sub, dirty, local, remote):
90 90 if dirty:
91 91 msg = (_(' subrepository sources for %s differ\n'
92 92 'use (l)ocal source (%s) or (r)emote source (%s)?'
93 93 '$$ &Local $$ &Remote')
94 94 % (subrelpath(sub), local, remote))
95 95 else:
96 96 msg = (_(' subrepository sources for %s differ (in checked out '
97 97 'version)\n'
98 98 'use (l)ocal source (%s) or (r)emote source (%s)?'
99 99 '$$ &Local $$ &Remote')
100 100 % (subrelpath(sub), local, remote))
101 101 return ui.promptchoice(msg, 0)
102 102
103 103 def _sanitize(ui, vfs, ignore):
104 104 for dirname, dirs, names in vfs.walk():
105 105 for i, d in enumerate(dirs):
106 106 if d.lower() == ignore:
107 107 del dirs[i]
108 108 break
109 109 if vfs.basename(dirname).lower() != '.hg':
110 110 continue
111 111 for f in names:
112 112 if f.lower() == 'hgrc':
113 113 ui.warn(_("warning: removing potentially hostile 'hgrc' "
114 114 "in '%s'\n") % vfs.join(dirname))
115 115 vfs.unlink(vfs.reljoin(dirname, f))
116 116
117 117 def _auditsubrepopath(repo, path):
118 118 # auditor doesn't check if the path itself is a symlink
119 119 pathutil.pathauditor(repo.root)(path)
120 120 if repo.wvfs.islink(path):
121 121 raise error.Abort(_("subrepo '%s' traverses symbolic link") % path)
122 122
123 123 SUBREPO_ALLOWED_DEFAULTS = {
124 124 'hg': True,
125 125 'git': False,
126 126 'svn': False,
127 127 }
128 128
129 129 def _checktype(ui, kind):
130 130 # subrepos.allowed is a master kill switch. If disabled, subrepos are
131 131 # disabled period.
132 132 if not ui.configbool('subrepos', 'allowed', True):
133 133 raise error.Abort(_('subrepos not enabled'),
134 134 hint=_("see 'hg help config.subrepos' for details"))
135 135
136 136 default = SUBREPO_ALLOWED_DEFAULTS.get(kind, False)
137 137 if not ui.configbool('subrepos', '%s:allowed' % kind, default):
138 138 raise error.Abort(_('%s subrepos not allowed') % kind,
139 139 hint=_("see 'hg help config.subrepos' for details"))
140 140
141 141 if kind not in types:
142 142 raise error.Abort(_('unknown subrepo type %s') % kind)
143 143
144 144 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
145 145 """return instance of the right subrepo class for subrepo in path"""
146 146 # subrepo inherently violates our import layering rules
147 147 # because it wants to make repo objects from deep inside the stack
148 148 # so we manually delay the circular imports to not break
149 149 # scripts that don't use our demand-loading
150 150 global hg
151 151 from . import hg as h
152 152 hg = h
153 153
154 154 repo = ctx.repo()
155 155 _auditsubrepopath(repo, path)
156 156 state = ctx.substate[path]
157 157 _checktype(repo.ui, state[2])
158 158 if allowwdir:
159 159 state = (state[0], ctx.subrev(path), state[2])
160 160 return types[state[2]](ctx, path, state[:2], allowcreate)
161 161
162 162 def nullsubrepo(ctx, path, pctx):
163 163 """return an empty subrepo in pctx for the extant subrepo in ctx"""
164 164 # subrepo inherently violates our import layering rules
165 165 # because it wants to make repo objects from deep inside the stack
166 166 # so we manually delay the circular imports to not break
167 167 # scripts that don't use our demand-loading
168 168 global hg
169 169 from . import hg as h
170 170 hg = h
171 171
172 172 repo = ctx.repo()
173 173 _auditsubrepopath(repo, path)
174 174 state = ctx.substate[path]
175 175 _checktype(repo.ui, state[2])
176 176 subrev = ''
177 177 if state[2] == 'hg':
178 178 subrev = "0" * 40
179 179 return types[state[2]](pctx, path, (state[0], subrev), True)
180 180
181 181 # subrepo classes need to implement the following abstract class:
182 182
183 183 class abstractsubrepo(object):
184 184
185 185 def __init__(self, ctx, path):
186 186 """Initialize abstractsubrepo part
187 187
188 188 ``ctx`` is the context referring this subrepository in the
189 189 parent repository.
190 190
191 191 ``path`` is the path to this subrepository as seen from
192 192 innermost repository.
193 193 """
194 194 self.ui = ctx.repo().ui
195 195 self._ctx = ctx
196 196 self._path = path
197 197
198 198 def addwebdirpath(self, serverpath, webconf):
199 199 """Add the hgwebdir entries for this subrepo, and any of its subrepos.
200 200
201 201 ``serverpath`` is the path component of the URL for this repo.
202 202
203 203 ``webconf`` is the dictionary of hgwebdir entries.
204 204 """
205 205 pass
206 206
207 207 def storeclean(self, path):
208 208 """
209 209 returns true if the repository has not changed since it was last
210 210 cloned from or pushed to a given repository.
211 211 """
212 212 return False
213 213
214 214 def dirty(self, ignoreupdate=False, missing=False):
215 215 """returns true if the dirstate of the subrepo is dirty or does not
216 216 match current stored state. If ignoreupdate is true, only check
217 217 whether the subrepo has uncommitted changes in its dirstate. If missing
218 218 is true, check for deleted files.
219 219 """
220 220 raise NotImplementedError
221 221
222 222 def dirtyreason(self, ignoreupdate=False, missing=False):
223 223 """return reason string if it is ``dirty()``
224 224
225 225 Returned string should have enough information for the message
226 226 of exception.
227 227
228 228 This returns None, otherwise.
229 229 """
230 230 if self.dirty(ignoreupdate=ignoreupdate, missing=missing):
231 231 return _('uncommitted changes in subrepository "%s"'
232 232 ) % subrelpath(self)
233 233
234 234 def bailifchanged(self, ignoreupdate=False, hint=None):
235 235 """raise Abort if subrepository is ``dirty()``
236 236 """
237 237 dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate,
238 238 missing=True)
239 239 if dirtyreason:
240 240 raise error.Abort(dirtyreason, hint=hint)
241 241
242 242 def basestate(self):
243 243 """current working directory base state, disregarding .hgsubstate
244 244 state and working directory modifications"""
245 245 raise NotImplementedError
246 246
247 247 def checknested(self, path):
248 248 """check if path is a subrepository within this repository"""
249 249 return False
250 250
251 251 def commit(self, text, user, date):
252 252 """commit the current changes to the subrepo with the given
253 253 log message. Use given user and date if possible. Return the
254 254 new state of the subrepo.
255 255 """
256 256 raise NotImplementedError
257 257
258 258 def phase(self, state):
259 259 """returns phase of specified state in the subrepository.
260 260 """
261 261 return phases.public
262 262
263 263 def remove(self):
264 264 """remove the subrepo
265 265
266 266 (should verify the dirstate is not dirty first)
267 267 """
268 268 raise NotImplementedError
269 269
270 270 def get(self, state, overwrite=False):
271 271 """run whatever commands are needed to put the subrepo into
272 272 this state
273 273 """
274 274 raise NotImplementedError
275 275
276 276 def merge(self, state):
277 277 """merge currently-saved state with the new state."""
278 278 raise NotImplementedError
279 279
280 280 def push(self, opts):
281 281 """perform whatever action is analogous to 'hg push'
282 282
283 283 This may be a no-op on some systems.
284 284 """
285 285 raise NotImplementedError
286 286
287 287 def add(self, ui, match, prefix, explicitonly, **opts):
288 288 return []
289 289
290 290 def addremove(self, matcher, prefix, opts):
291 291 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
292 292 return 1
293 293
294 294 def cat(self, match, fm, fntemplate, prefix, **opts):
295 295 return 1
296 296
297 297 def status(self, rev2, **opts):
298 298 return scmutil.status([], [], [], [], [], [], [])
299 299
300 300 def diff(self, ui, diffopts, node2, match, prefix, **opts):
301 301 pass
302 302
303 303 def outgoing(self, ui, dest, opts):
304 304 return 1
305 305
306 306 def incoming(self, ui, source, opts):
307 307 return 1
308 308
309 309 def files(self):
310 310 """return filename iterator"""
311 311 raise NotImplementedError
312 312
313 313 def filedata(self, name, decode):
314 314 """return file data, optionally passed through repo decoders"""
315 315 raise NotImplementedError
316 316
317 317 def fileflags(self, name):
318 318 """return file flags"""
319 319 return ''
320 320
321 def getfileset(self, expr):
321 def matchfileset(self, expr, badfn=None):
322 322 """Resolve the fileset expression for this repo"""
323 return set()
323 return matchmod.nevermatcher(self.wvfs.base, '', badfn=badfn)
324 324
325 325 def printfiles(self, ui, m, fm, fmt, subrepos):
326 326 """handle the files command for this subrepo"""
327 327 return 1
328 328
329 329 def archive(self, archiver, prefix, match=None, decode=True):
330 330 if match is not None:
331 331 files = [f for f in self.files() if match(f)]
332 332 else:
333 333 files = self.files()
334 334 total = len(files)
335 335 relpath = subrelpath(self)
336 336 progress = self.ui.makeprogress(_('archiving (%s)') % relpath,
337 337 unit=_('files'), total=total)
338 338 progress.update(0)
339 339 for name in files:
340 340 flags = self.fileflags(name)
341 341 mode = 'x' in flags and 0o755 or 0o644
342 342 symlink = 'l' in flags
343 343 archiver.addfile(prefix + self._path + '/' + name,
344 344 mode, symlink, self.filedata(name, decode))
345 345 progress.increment()
346 346 progress.complete()
347 347 return total
348 348
349 349 def walk(self, match):
350 350 '''
351 351 walk recursively through the directory tree, finding all files
352 352 matched by the match function
353 353 '''
354 354
355 355 def forget(self, match, prefix, dryrun, interactive):
356 356 return ([], [])
357 357
358 358 def removefiles(self, matcher, prefix, after, force, subrepos,
359 359 dryrun, warnings):
360 360 """remove the matched files from the subrepository and the filesystem,
361 361 possibly by force and/or after the file has been removed from the
362 362 filesystem. Return 0 on success, 1 on any warning.
363 363 """
364 364 warnings.append(_("warning: removefiles not implemented (%s)")
365 365 % self._path)
366 366 return 1
367 367
368 368 def revert(self, substate, *pats, **opts):
369 369 self.ui.warn(_('%s: reverting %s subrepos is unsupported\n') \
370 370 % (substate[0], substate[2]))
371 371 return []
372 372
373 373 def shortid(self, revid):
374 374 return revid
375 375
376 376 def unshare(self):
377 377 '''
378 378 convert this repository from shared to normal storage.
379 379 '''
380 380
381 381 def verify(self):
382 382 '''verify the integrity of the repository. Return 0 on success or
383 383 warning, 1 on any error.
384 384 '''
385 385 return 0
386 386
387 387 @propertycache
388 388 def wvfs(self):
389 389 """return vfs to access the working directory of this subrepository
390 390 """
391 391 return vfsmod.vfs(self._ctx.repo().wvfs.join(self._path))
392 392
393 393 @propertycache
394 394 def _relpath(self):
395 395 """return path to this subrepository as seen from outermost repository
396 396 """
397 397 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
398 398
399 399 class hgsubrepo(abstractsubrepo):
400 400 def __init__(self, ctx, path, state, allowcreate):
401 401 super(hgsubrepo, self).__init__(ctx, path)
402 402 self._state = state
403 403 r = ctx.repo()
404 404 root = r.wjoin(path)
405 405 create = allowcreate and not r.wvfs.exists('%s/.hg' % path)
406 406 self._repo = hg.repository(r.baseui, root, create=create)
407 407
408 408 # Propagate the parent's --hidden option
409 409 if r is r.unfiltered():
410 410 self._repo = self._repo.unfiltered()
411 411
412 412 self.ui = self._repo.ui
413 413 for s, k in [('ui', 'commitsubrepos')]:
414 414 v = r.ui.config(s, k)
415 415 if v:
416 416 self.ui.setconfig(s, k, v, 'subrepo')
417 417 # internal config: ui._usedassubrepo
418 418 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
419 419 self._initrepo(r, state[0], create)
420 420
421 421 @annotatesubrepoerror
422 422 def addwebdirpath(self, serverpath, webconf):
423 423 cmdutil.addwebdirpath(self._repo, subrelpath(self), webconf)
424 424
425 425 def storeclean(self, path):
426 426 with self._repo.lock():
427 427 return self._storeclean(path)
428 428
429 429 def _storeclean(self, path):
430 430 clean = True
431 431 itercache = self._calcstorehash(path)
432 432 for filehash in self._readstorehashcache(path):
433 433 if filehash != next(itercache, None):
434 434 clean = False
435 435 break
436 436 if clean:
437 437 # if not empty:
438 438 # the cached and current pull states have a different size
439 439 clean = next(itercache, None) is None
440 440 return clean
441 441
442 442 def _calcstorehash(self, remotepath):
443 443 '''calculate a unique "store hash"
444 444
445 445 This method is used to to detect when there are changes that may
446 446 require a push to a given remote path.'''
447 447 # sort the files that will be hashed in increasing (likely) file size
448 448 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
449 449 yield '# %s\n' % _expandedabspath(remotepath)
450 450 vfs = self._repo.vfs
451 451 for relname in filelist:
452 452 filehash = node.hex(hashlib.sha1(vfs.tryread(relname)).digest())
453 453 yield '%s = %s\n' % (relname, filehash)
454 454
455 455 @propertycache
456 456 def _cachestorehashvfs(self):
457 457 return vfsmod.vfs(self._repo.vfs.join('cache/storehash'))
458 458
459 459 def _readstorehashcache(self, remotepath):
460 460 '''read the store hash cache for a given remote repository'''
461 461 cachefile = _getstorehashcachename(remotepath)
462 462 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
463 463
464 464 def _cachestorehash(self, remotepath):
465 465 '''cache the current store hash
466 466
467 467 Each remote repo requires its own store hash cache, because a subrepo
468 468 store may be "clean" versus a given remote repo, but not versus another
469 469 '''
470 470 cachefile = _getstorehashcachename(remotepath)
471 471 with self._repo.lock():
472 472 storehash = list(self._calcstorehash(remotepath))
473 473 vfs = self._cachestorehashvfs
474 474 vfs.writelines(cachefile, storehash, mode='wb', notindexed=True)
475 475
476 476 def _getctx(self):
477 477 '''fetch the context for this subrepo revision, possibly a workingctx
478 478 '''
479 479 if self._ctx.rev() is None:
480 480 return self._repo[None] # workingctx if parent is workingctx
481 481 else:
482 482 rev = self._state[1]
483 483 return self._repo[rev]
484 484
485 485 @annotatesubrepoerror
486 486 def _initrepo(self, parentrepo, source, create):
487 487 self._repo._subparent = parentrepo
488 488 self._repo._subsource = source
489 489
490 490 if create:
491 491 lines = ['[paths]\n']
492 492
493 493 def addpathconfig(key, value):
494 494 if value:
495 495 lines.append('%s = %s\n' % (key, value))
496 496 self.ui.setconfig('paths', key, value, 'subrepo')
497 497
498 498 defpath = _abssource(self._repo, abort=False)
499 499 defpushpath = _abssource(self._repo, True, abort=False)
500 500 addpathconfig('default', defpath)
501 501 if defpath != defpushpath:
502 502 addpathconfig('default-push', defpushpath)
503 503
504 504 self._repo.vfs.write('hgrc', util.tonativeeol(''.join(lines)))
505 505
506 506 @annotatesubrepoerror
507 507 def add(self, ui, match, prefix, explicitonly, **opts):
508 508 return cmdutil.add(ui, self._repo, match,
509 509 self.wvfs.reljoin(prefix, self._path),
510 510 explicitonly, **opts)
511 511
512 512 @annotatesubrepoerror
513 513 def addremove(self, m, prefix, opts):
514 514 # In the same way as sub directories are processed, once in a subrepo,
515 515 # always entry any of its subrepos. Don't corrupt the options that will
516 516 # be used to process sibling subrepos however.
517 517 opts = copy.copy(opts)
518 518 opts['subrepos'] = True
519 519 return scmutil.addremove(self._repo, m,
520 520 self.wvfs.reljoin(prefix, self._path), opts)
521 521
522 522 @annotatesubrepoerror
523 523 def cat(self, match, fm, fntemplate, prefix, **opts):
524 524 rev = self._state[1]
525 525 ctx = self._repo[rev]
526 526 return cmdutil.cat(self.ui, self._repo, ctx, match, fm, fntemplate,
527 527 prefix, **opts)
528 528
529 529 @annotatesubrepoerror
530 530 def status(self, rev2, **opts):
531 531 try:
532 532 rev1 = self._state[1]
533 533 ctx1 = self._repo[rev1]
534 534 ctx2 = self._repo[rev2]
535 535 return self._repo.status(ctx1, ctx2, **opts)
536 536 except error.RepoLookupError as inst:
537 537 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
538 538 % (inst, subrelpath(self)))
539 539 return scmutil.status([], [], [], [], [], [], [])
540 540
541 541 @annotatesubrepoerror
542 542 def diff(self, ui, diffopts, node2, match, prefix, **opts):
543 543 try:
544 544 node1 = node.bin(self._state[1])
545 545 # We currently expect node2 to come from substate and be
546 546 # in hex format
547 547 if node2 is not None:
548 548 node2 = node.bin(node2)
549 549 logcmdutil.diffordiffstat(ui, self._repo, diffopts,
550 550 node1, node2, match,
551 551 prefix=posixpath.join(prefix, self._path),
552 552 listsubrepos=True, **opts)
553 553 except error.RepoLookupError as inst:
554 554 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
555 555 % (inst, subrelpath(self)))
556 556
557 557 @annotatesubrepoerror
558 558 def archive(self, archiver, prefix, match=None, decode=True):
559 559 self._get(self._state + ('hg',))
560 560 files = self.files()
561 561 if match:
562 562 files = [f for f in files if match(f)]
563 563 rev = self._state[1]
564 564 ctx = self._repo[rev]
565 565 scmutil.prefetchfiles(self._repo, [ctx.rev()],
566 566 scmutil.matchfiles(self._repo, files))
567 567 total = abstractsubrepo.archive(self, archiver, prefix, match)
568 568 for subpath in ctx.substate:
569 569 s = subrepo(ctx, subpath, True)
570 570 submatch = matchmod.subdirmatcher(subpath, match)
571 571 total += s.archive(archiver, prefix + self._path + '/', submatch,
572 572 decode)
573 573 return total
574 574
575 575 @annotatesubrepoerror
576 576 def dirty(self, ignoreupdate=False, missing=False):
577 577 r = self._state[1]
578 578 if r == '' and not ignoreupdate: # no state recorded
579 579 return True
580 580 w = self._repo[None]
581 581 if r != w.p1().hex() and not ignoreupdate:
582 582 # different version checked out
583 583 return True
584 584 return w.dirty(missing=missing) # working directory changed
585 585
586 586 def basestate(self):
587 587 return self._repo['.'].hex()
588 588
589 589 def checknested(self, path):
590 590 return self._repo._checknested(self._repo.wjoin(path))
591 591
592 592 @annotatesubrepoerror
593 593 def commit(self, text, user, date):
594 594 # don't bother committing in the subrepo if it's only been
595 595 # updated
596 596 if not self.dirty(True):
597 597 return self._repo['.'].hex()
598 598 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
599 599 n = self._repo.commit(text, user, date)
600 600 if not n:
601 601 return self._repo['.'].hex() # different version checked out
602 602 return node.hex(n)
603 603
604 604 @annotatesubrepoerror
605 605 def phase(self, state):
606 606 return self._repo[state or '.'].phase()
607 607
608 608 @annotatesubrepoerror
609 609 def remove(self):
610 610 # we can't fully delete the repository as it may contain
611 611 # local-only history
612 612 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
613 613 hg.clean(self._repo, node.nullid, False)
614 614
615 615 def _get(self, state):
616 616 source, revision, kind = state
617 617 parentrepo = self._repo._subparent
618 618
619 619 if revision in self._repo.unfiltered():
620 620 # Allow shared subrepos tracked at null to setup the sharedpath
621 621 if len(self._repo) != 0 or not parentrepo.shared():
622 622 return True
623 623 self._repo._subsource = source
624 624 srcurl = _abssource(self._repo)
625 625 other = hg.peer(self._repo, {}, srcurl)
626 626 if len(self._repo) == 0:
627 627 # use self._repo.vfs instead of self.wvfs to remove .hg only
628 628 self._repo.vfs.rmtree()
629 629
630 630 # A remote subrepo could be shared if there is a local copy
631 631 # relative to the parent's share source. But clone pooling doesn't
632 632 # assemble the repos in a tree, so that can't be consistently done.
633 633 # A simpler option is for the user to configure clone pooling, and
634 634 # work with that.
635 635 if parentrepo.shared() and hg.islocal(srcurl):
636 636 self.ui.status(_('sharing subrepo %s from %s\n')
637 637 % (subrelpath(self), srcurl))
638 638 shared = hg.share(self._repo._subparent.baseui,
639 639 other, self._repo.root,
640 640 update=False, bookmarks=False)
641 641 self._repo = shared.local()
642 642 else:
643 643 # TODO: find a common place for this and this code in the
644 644 # share.py wrap of the clone command.
645 645 if parentrepo.shared():
646 646 pool = self.ui.config('share', 'pool')
647 647 if pool:
648 648 pool = util.expandpath(pool)
649 649
650 650 shareopts = {
651 651 'pool': pool,
652 652 'mode': self.ui.config('share', 'poolnaming'),
653 653 }
654 654 else:
655 655 shareopts = {}
656 656
657 657 self.ui.status(_('cloning subrepo %s from %s\n')
658 658 % (subrelpath(self), srcurl))
659 659 other, cloned = hg.clone(self._repo._subparent.baseui, {},
660 660 other, self._repo.root,
661 661 update=False, shareopts=shareopts)
662 662 self._repo = cloned.local()
663 663 self._initrepo(parentrepo, source, create=True)
664 664 self._cachestorehash(srcurl)
665 665 else:
666 666 self.ui.status(_('pulling subrepo %s from %s\n')
667 667 % (subrelpath(self), srcurl))
668 668 cleansub = self.storeclean(srcurl)
669 669 exchange.pull(self._repo, other)
670 670 if cleansub:
671 671 # keep the repo clean after pull
672 672 self._cachestorehash(srcurl)
673 673 return False
674 674
675 675 @annotatesubrepoerror
676 676 def get(self, state, overwrite=False):
677 677 inrepo = self._get(state)
678 678 source, revision, kind = state
679 679 repo = self._repo
680 680 repo.ui.debug("getting subrepo %s\n" % self._path)
681 681 if inrepo:
682 682 urepo = repo.unfiltered()
683 683 ctx = urepo[revision]
684 684 if ctx.hidden():
685 685 urepo.ui.warn(
686 686 _('revision %s in subrepository "%s" is hidden\n') \
687 687 % (revision[0:12], self._path))
688 688 repo = urepo
689 689 hg.updaterepo(repo, revision, overwrite)
690 690
691 691 @annotatesubrepoerror
692 692 def merge(self, state):
693 693 self._get(state)
694 694 cur = self._repo['.']
695 695 dst = self._repo[state[1]]
696 696 anc = dst.ancestor(cur)
697 697
698 698 def mergefunc():
699 699 if anc == cur and dst.branch() == cur.branch():
700 700 self.ui.debug('updating subrepository "%s"\n'
701 701 % subrelpath(self))
702 702 hg.update(self._repo, state[1])
703 703 elif anc == dst:
704 704 self.ui.debug('skipping subrepository "%s"\n'
705 705 % subrelpath(self))
706 706 else:
707 707 self.ui.debug('merging subrepository "%s"\n' % subrelpath(self))
708 708 hg.merge(self._repo, state[1], remind=False)
709 709
710 710 wctx = self._repo[None]
711 711 if self.dirty():
712 712 if anc != dst:
713 713 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
714 714 mergefunc()
715 715 else:
716 716 mergefunc()
717 717 else:
718 718 mergefunc()
719 719
720 720 @annotatesubrepoerror
721 721 def push(self, opts):
722 722 force = opts.get('force')
723 723 newbranch = opts.get('new_branch')
724 724 ssh = opts.get('ssh')
725 725
726 726 # push subrepos depth-first for coherent ordering
727 727 c = self._repo['.']
728 728 subs = c.substate # only repos that are committed
729 729 for s in sorted(subs):
730 730 if c.sub(s).push(opts) == 0:
731 731 return False
732 732
733 733 dsturl = _abssource(self._repo, True)
734 734 if not force:
735 735 if self.storeclean(dsturl):
736 736 self.ui.status(
737 737 _('no changes made to subrepo %s since last push to %s\n')
738 738 % (subrelpath(self), dsturl))
739 739 return None
740 740 self.ui.status(_('pushing subrepo %s to %s\n') %
741 741 (subrelpath(self), dsturl))
742 742 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
743 743 res = exchange.push(self._repo, other, force, newbranch=newbranch)
744 744
745 745 # the repo is now clean
746 746 self._cachestorehash(dsturl)
747 747 return res.cgresult
748 748
749 749 @annotatesubrepoerror
750 750 def outgoing(self, ui, dest, opts):
751 751 if 'rev' in opts or 'branch' in opts:
752 752 opts = copy.copy(opts)
753 753 opts.pop('rev', None)
754 754 opts.pop('branch', None)
755 755 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
756 756
757 757 @annotatesubrepoerror
758 758 def incoming(self, ui, source, opts):
759 759 if 'rev' in opts or 'branch' in opts:
760 760 opts = copy.copy(opts)
761 761 opts.pop('rev', None)
762 762 opts.pop('branch', None)
763 763 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
764 764
765 765 @annotatesubrepoerror
766 766 def files(self):
767 767 rev = self._state[1]
768 768 ctx = self._repo[rev]
769 769 return ctx.manifest().keys()
770 770
771 771 def filedata(self, name, decode):
772 772 rev = self._state[1]
773 773 data = self._repo[rev][name].data()
774 774 if decode:
775 775 data = self._repo.wwritedata(name, data)
776 776 return data
777 777
778 778 def fileflags(self, name):
779 779 rev = self._state[1]
780 780 ctx = self._repo[rev]
781 781 return ctx.flags(name)
782 782
783 783 @annotatesubrepoerror
784 784 def printfiles(self, ui, m, fm, fmt, subrepos):
785 785 # If the parent context is a workingctx, use the workingctx here for
786 786 # consistency.
787 787 if self._ctx.rev() is None:
788 788 ctx = self._repo[None]
789 789 else:
790 790 rev = self._state[1]
791 791 ctx = self._repo[rev]
792 792 return cmdutil.files(ui, ctx, m, fm, fmt, subrepos)
793 793
794 794 @annotatesubrepoerror
795 def getfileset(self, expr):
795 def matchfileset(self, expr, badfn=None):
796 repo = self._repo
796 797 if self._ctx.rev() is None:
797 ctx = self._repo[None]
798 ctx = repo[None]
798 799 else:
799 800 rev = self._state[1]
800 ctx = self._repo[rev]
801 ctx = repo[rev]
801 802
802 files = ctx.getfileset(expr)
803 matchers = [ctx.matchfileset(expr, badfn=badfn)]
803 804
804 805 for subpath in ctx.substate:
805 806 sub = ctx.sub(subpath)
806 807
807 808 try:
808 files.extend(subpath + '/' + f for f in sub.getfileset(expr))
809 sm = sub.matchfileset(expr, badfn=badfn)
810 pm = matchmod.prefixdirmatcher(repo.root, repo.getcwd(),
811 subpath, sm, badfn=badfn)
812 matchers.append(pm)
809 813 except error.LookupError:
810 814 self.ui.status(_("skipping missing subrepository: %s\n")
811 815 % self.wvfs.reljoin(reporelpath(self), subpath))
812 return files
816 if len(matchers) == 1:
817 return matchers[0]
818 return matchmod.unionmatcher(matchers)
813 819
814 820 def walk(self, match):
815 821 ctx = self._repo[None]
816 822 return ctx.walk(match)
817 823
818 824 @annotatesubrepoerror
819 825 def forget(self, match, prefix, dryrun, interactive):
820 826 return cmdutil.forget(self.ui, self._repo, match,
821 827 self.wvfs.reljoin(prefix, self._path),
822 828 True, dryrun=dryrun, interactive=interactive)
823 829
824 830 @annotatesubrepoerror
825 831 def removefiles(self, matcher, prefix, after, force, subrepos,
826 832 dryrun, warnings):
827 833 return cmdutil.remove(self.ui, self._repo, matcher,
828 834 self.wvfs.reljoin(prefix, self._path),
829 835 after, force, subrepos, dryrun)
830 836
831 837 @annotatesubrepoerror
832 838 def revert(self, substate, *pats, **opts):
833 839 # reverting a subrepo is a 2 step process:
834 840 # 1. if the no_backup is not set, revert all modified
835 841 # files inside the subrepo
836 842 # 2. update the subrepo to the revision specified in
837 843 # the corresponding substate dictionary
838 844 self.ui.status(_('reverting subrepo %s\n') % substate[0])
839 845 if not opts.get(r'no_backup'):
840 846 # Revert all files on the subrepo, creating backups
841 847 # Note that this will not recursively revert subrepos
842 848 # We could do it if there was a set:subrepos() predicate
843 849 opts = opts.copy()
844 850 opts[r'date'] = None
845 851 opts[r'rev'] = substate[1]
846 852
847 853 self.filerevert(*pats, **opts)
848 854
849 855 # Update the repo to the revision specified in the given substate
850 856 if not opts.get(r'dry_run'):
851 857 self.get(substate, overwrite=True)
852 858
853 859 def filerevert(self, *pats, **opts):
854 860 ctx = self._repo[opts[r'rev']]
855 861 parents = self._repo.dirstate.parents()
856 862 if opts.get(r'all'):
857 863 pats = ['set:modified()']
858 864 else:
859 865 pats = []
860 866 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
861 867
862 868 def shortid(self, revid):
863 869 return revid[:12]
864 870
865 871 @annotatesubrepoerror
866 872 def unshare(self):
867 873 # subrepo inherently violates our import layering rules
868 874 # because it wants to make repo objects from deep inside the stack
869 875 # so we manually delay the circular imports to not break
870 876 # scripts that don't use our demand-loading
871 877 global hg
872 878 from . import hg as h
873 879 hg = h
874 880
875 881 # Nothing prevents a user from sharing in a repo, and then making that a
876 882 # subrepo. Alternately, the previous unshare attempt may have failed
877 883 # part way through. So recurse whether or not this layer is shared.
878 884 if self._repo.shared():
879 885 self.ui.status(_("unsharing subrepo '%s'\n") % self._relpath)
880 886
881 887 hg.unshare(self.ui, self._repo)
882 888
883 889 def verify(self):
884 890 try:
885 891 rev = self._state[1]
886 892 ctx = self._repo.unfiltered()[rev]
887 893 if ctx.hidden():
888 894 # Since hidden revisions aren't pushed/pulled, it seems worth an
889 895 # explicit warning.
890 896 ui = self._repo.ui
891 897 ui.warn(_("subrepo '%s' is hidden in revision %s\n") %
892 898 (self._relpath, node.short(self._ctx.node())))
893 899 return 0
894 900 except error.RepoLookupError:
895 901 # A missing subrepo revision may be a case of needing to pull it, so
896 902 # don't treat this as an error.
897 903 self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") %
898 904 (self._relpath, node.short(self._ctx.node())))
899 905 return 0
900 906
901 907 @propertycache
902 908 def wvfs(self):
903 909 """return own wvfs for efficiency and consistency
904 910 """
905 911 return self._repo.wvfs
906 912
907 913 @propertycache
908 914 def _relpath(self):
909 915 """return path to this subrepository as seen from outermost repository
910 916 """
911 917 # Keep consistent dir separators by avoiding vfs.join(self._path)
912 918 return reporelpath(self._repo)
913 919
914 920 class svnsubrepo(abstractsubrepo):
915 921 def __init__(self, ctx, path, state, allowcreate):
916 922 super(svnsubrepo, self).__init__(ctx, path)
917 923 self._state = state
918 924 self._exe = procutil.findexe('svn')
919 925 if not self._exe:
920 926 raise error.Abort(_("'svn' executable not found for subrepo '%s'")
921 927 % self._path)
922 928
923 929 def _svncommand(self, commands, filename='', failok=False):
924 930 cmd = [self._exe]
925 931 extrakw = {}
926 932 if not self.ui.interactive():
927 933 # Making stdin be a pipe should prevent svn from behaving
928 934 # interactively even if we can't pass --non-interactive.
929 935 extrakw[r'stdin'] = subprocess.PIPE
930 936 # Starting in svn 1.5 --non-interactive is a global flag
931 937 # instead of being per-command, but we need to support 1.4 so
932 938 # we have to be intelligent about what commands take
933 939 # --non-interactive.
934 940 if commands[0] in ('update', 'checkout', 'commit'):
935 941 cmd.append('--non-interactive')
936 942 cmd.extend(commands)
937 943 if filename is not None:
938 944 path = self.wvfs.reljoin(self._ctx.repo().origroot,
939 945 self._path, filename)
940 946 cmd.append(path)
941 947 env = dict(encoding.environ)
942 948 # Avoid localized output, preserve current locale for everything else.
943 949 lc_all = env.get('LC_ALL')
944 950 if lc_all:
945 951 env['LANG'] = lc_all
946 952 del env['LC_ALL']
947 953 env['LC_MESSAGES'] = 'C'
948 954 p = subprocess.Popen(cmd, bufsize=-1, close_fds=procutil.closefds,
949 955 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
950 956 universal_newlines=True, env=env, **extrakw)
951 957 stdout, stderr = p.communicate()
952 958 stderr = stderr.strip()
953 959 if not failok:
954 960 if p.returncode:
955 961 raise error.Abort(stderr or 'exited with code %d'
956 962 % p.returncode)
957 963 if stderr:
958 964 self.ui.warn(stderr + '\n')
959 965 return stdout, stderr
960 966
961 967 @propertycache
962 968 def _svnversion(self):
963 969 output, err = self._svncommand(['--version', '--quiet'], filename=None)
964 970 m = re.search(br'^(\d+)\.(\d+)', output)
965 971 if not m:
966 972 raise error.Abort(_('cannot retrieve svn tool version'))
967 973 return (int(m.group(1)), int(m.group(2)))
968 974
969 975 def _svnmissing(self):
970 976 return not self.wvfs.exists('.svn')
971 977
972 978 def _wcrevs(self):
973 979 # Get the working directory revision as well as the last
974 980 # commit revision so we can compare the subrepo state with
975 981 # both. We used to store the working directory one.
976 982 output, err = self._svncommand(['info', '--xml'])
977 983 doc = xml.dom.minidom.parseString(output)
978 984 entries = doc.getElementsByTagName('entry')
979 985 lastrev, rev = '0', '0'
980 986 if entries:
981 987 rev = str(entries[0].getAttribute('revision')) or '0'
982 988 commits = entries[0].getElementsByTagName('commit')
983 989 if commits:
984 990 lastrev = str(commits[0].getAttribute('revision')) or '0'
985 991 return (lastrev, rev)
986 992
987 993 def _wcrev(self):
988 994 return self._wcrevs()[0]
989 995
990 996 def _wcchanged(self):
991 997 """Return (changes, extchanges, missing) where changes is True
992 998 if the working directory was changed, extchanges is
993 999 True if any of these changes concern an external entry and missing
994 1000 is True if any change is a missing entry.
995 1001 """
996 1002 output, err = self._svncommand(['status', '--xml'])
997 1003 externals, changes, missing = [], [], []
998 1004 doc = xml.dom.minidom.parseString(output)
999 1005 for e in doc.getElementsByTagName('entry'):
1000 1006 s = e.getElementsByTagName('wc-status')
1001 1007 if not s:
1002 1008 continue
1003 1009 item = s[0].getAttribute('item')
1004 1010 props = s[0].getAttribute('props')
1005 1011 path = e.getAttribute('path')
1006 1012 if item == 'external':
1007 1013 externals.append(path)
1008 1014 elif item == 'missing':
1009 1015 missing.append(path)
1010 1016 if (item not in ('', 'normal', 'unversioned', 'external')
1011 1017 or props not in ('', 'none', 'normal')):
1012 1018 changes.append(path)
1013 1019 for path in changes:
1014 1020 for ext in externals:
1015 1021 if path == ext or path.startswith(ext + pycompat.ossep):
1016 1022 return True, True, bool(missing)
1017 1023 return bool(changes), False, bool(missing)
1018 1024
1019 1025 @annotatesubrepoerror
1020 1026 def dirty(self, ignoreupdate=False, missing=False):
1021 1027 if self._svnmissing():
1022 1028 return self._state[1] != ''
1023 1029 wcchanged = self._wcchanged()
1024 1030 changed = wcchanged[0] or (missing and wcchanged[2])
1025 1031 if not changed:
1026 1032 if self._state[1] in self._wcrevs() or ignoreupdate:
1027 1033 return False
1028 1034 return True
1029 1035
1030 1036 def basestate(self):
1031 1037 lastrev, rev = self._wcrevs()
1032 1038 if lastrev != rev:
1033 1039 # Last committed rev is not the same than rev. We would
1034 1040 # like to take lastrev but we do not know if the subrepo
1035 1041 # URL exists at lastrev. Test it and fallback to rev it
1036 1042 # is not there.
1037 1043 try:
1038 1044 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1039 1045 return lastrev
1040 1046 except error.Abort:
1041 1047 pass
1042 1048 return rev
1043 1049
1044 1050 @annotatesubrepoerror
1045 1051 def commit(self, text, user, date):
1046 1052 # user and date are out of our hands since svn is centralized
1047 1053 changed, extchanged, missing = self._wcchanged()
1048 1054 if not changed:
1049 1055 return self.basestate()
1050 1056 if extchanged:
1051 1057 # Do not try to commit externals
1052 1058 raise error.Abort(_('cannot commit svn externals'))
1053 1059 if missing:
1054 1060 # svn can commit with missing entries but aborting like hg
1055 1061 # seems a better approach.
1056 1062 raise error.Abort(_('cannot commit missing svn entries'))
1057 1063 commitinfo, err = self._svncommand(['commit', '-m', text])
1058 1064 self.ui.status(commitinfo)
1059 1065 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1060 1066 if not newrev:
1061 1067 if not commitinfo.strip():
1062 1068 # Sometimes, our definition of "changed" differs from
1063 1069 # svn one. For instance, svn ignores missing files
1064 1070 # when committing. If there are only missing files, no
1065 1071 # commit is made, no output and no error code.
1066 1072 raise error.Abort(_('failed to commit svn changes'))
1067 1073 raise error.Abort(commitinfo.splitlines()[-1])
1068 1074 newrev = newrev.groups()[0]
1069 1075 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1070 1076 return newrev
1071 1077
1072 1078 @annotatesubrepoerror
1073 1079 def remove(self):
1074 1080 if self.dirty():
1075 1081 self.ui.warn(_('not removing repo %s because '
1076 1082 'it has changes.\n') % self._path)
1077 1083 return
1078 1084 self.ui.note(_('removing subrepo %s\n') % self._path)
1079 1085
1080 1086 self.wvfs.rmtree(forcibly=True)
1081 1087 try:
1082 1088 pwvfs = self._ctx.repo().wvfs
1083 1089 pwvfs.removedirs(pwvfs.dirname(self._path))
1084 1090 except OSError:
1085 1091 pass
1086 1092
1087 1093 @annotatesubrepoerror
1088 1094 def get(self, state, overwrite=False):
1089 1095 if overwrite:
1090 1096 self._svncommand(['revert', '--recursive'])
1091 1097 args = ['checkout']
1092 1098 if self._svnversion >= (1, 5):
1093 1099 args.append('--force')
1094 1100 # The revision must be specified at the end of the URL to properly
1095 1101 # update to a directory which has since been deleted and recreated.
1096 1102 args.append('%s@%s' % (state[0], state[1]))
1097 1103
1098 1104 # SEC: check that the ssh url is safe
1099 1105 util.checksafessh(state[0])
1100 1106
1101 1107 status, err = self._svncommand(args, failok=True)
1102 1108 _sanitize(self.ui, self.wvfs, '.svn')
1103 1109 if not re.search('Checked out revision [0-9]+.', status):
1104 1110 if ('is already a working copy for a different URL' in err
1105 1111 and (self._wcchanged()[:2] == (False, False))):
1106 1112 # obstructed but clean working copy, so just blow it away.
1107 1113 self.remove()
1108 1114 self.get(state, overwrite=False)
1109 1115 return
1110 1116 raise error.Abort((status or err).splitlines()[-1])
1111 1117 self.ui.status(status)
1112 1118
1113 1119 @annotatesubrepoerror
1114 1120 def merge(self, state):
1115 1121 old = self._state[1]
1116 1122 new = state[1]
1117 1123 wcrev = self._wcrev()
1118 1124 if new != wcrev:
1119 1125 dirty = old == wcrev or self._wcchanged()[0]
1120 1126 if _updateprompt(self.ui, self, dirty, wcrev, new):
1121 1127 self.get(state, False)
1122 1128
1123 1129 def push(self, opts):
1124 1130 # push is a no-op for SVN
1125 1131 return True
1126 1132
1127 1133 @annotatesubrepoerror
1128 1134 def files(self):
1129 1135 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1130 1136 doc = xml.dom.minidom.parseString(output)
1131 1137 paths = []
1132 1138 for e in doc.getElementsByTagName('entry'):
1133 1139 kind = pycompat.bytestr(e.getAttribute('kind'))
1134 1140 if kind != 'file':
1135 1141 continue
1136 1142 name = ''.join(c.data for c
1137 1143 in e.getElementsByTagName('name')[0].childNodes
1138 1144 if c.nodeType == c.TEXT_NODE)
1139 1145 paths.append(name.encode('utf-8'))
1140 1146 return paths
1141 1147
1142 1148 def filedata(self, name, decode):
1143 1149 return self._svncommand(['cat'], name)[0]
1144 1150
1145 1151
1146 1152 class gitsubrepo(abstractsubrepo):
1147 1153 def __init__(self, ctx, path, state, allowcreate):
1148 1154 super(gitsubrepo, self).__init__(ctx, path)
1149 1155 self._state = state
1150 1156 self._abspath = ctx.repo().wjoin(path)
1151 1157 self._subparent = ctx.repo()
1152 1158 self._ensuregit()
1153 1159
1154 1160 def _ensuregit(self):
1155 1161 try:
1156 1162 self._gitexecutable = 'git'
1157 1163 out, err = self._gitnodir(['--version'])
1158 1164 except OSError as e:
1159 1165 genericerror = _("error executing git for subrepo '%s': %s")
1160 1166 notfoundhint = _("check git is installed and in your PATH")
1161 1167 if e.errno != errno.ENOENT:
1162 1168 raise error.Abort(genericerror % (
1163 1169 self._path, encoding.strtolocal(e.strerror)))
1164 1170 elif pycompat.iswindows:
1165 1171 try:
1166 1172 self._gitexecutable = 'git.cmd'
1167 1173 out, err = self._gitnodir(['--version'])
1168 1174 except OSError as e2:
1169 1175 if e2.errno == errno.ENOENT:
1170 1176 raise error.Abort(_("couldn't find 'git' or 'git.cmd'"
1171 1177 " for subrepo '%s'") % self._path,
1172 1178 hint=notfoundhint)
1173 1179 else:
1174 1180 raise error.Abort(genericerror % (self._path,
1175 1181 encoding.strtolocal(e2.strerror)))
1176 1182 else:
1177 1183 raise error.Abort(_("couldn't find git for subrepo '%s'")
1178 1184 % self._path, hint=notfoundhint)
1179 1185 versionstatus = self._checkversion(out)
1180 1186 if versionstatus == 'unknown':
1181 1187 self.ui.warn(_('cannot retrieve git version\n'))
1182 1188 elif versionstatus == 'abort':
1183 1189 raise error.Abort(_('git subrepo requires at least 1.6.0 or later'))
1184 1190 elif versionstatus == 'warning':
1185 1191 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1186 1192
1187 1193 @staticmethod
1188 1194 def _gitversion(out):
1189 1195 m = re.search(br'^git version (\d+)\.(\d+)\.(\d+)', out)
1190 1196 if m:
1191 1197 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1192 1198
1193 1199 m = re.search(br'^git version (\d+)\.(\d+)', out)
1194 1200 if m:
1195 1201 return (int(m.group(1)), int(m.group(2)), 0)
1196 1202
1197 1203 return -1
1198 1204
1199 1205 @staticmethod
1200 1206 def _checkversion(out):
1201 1207 '''ensure git version is new enough
1202 1208
1203 1209 >>> _checkversion = gitsubrepo._checkversion
1204 1210 >>> _checkversion(b'git version 1.6.0')
1205 1211 'ok'
1206 1212 >>> _checkversion(b'git version 1.8.5')
1207 1213 'ok'
1208 1214 >>> _checkversion(b'git version 1.4.0')
1209 1215 'abort'
1210 1216 >>> _checkversion(b'git version 1.5.0')
1211 1217 'warning'
1212 1218 >>> _checkversion(b'git version 1.9-rc0')
1213 1219 'ok'
1214 1220 >>> _checkversion(b'git version 1.9.0.265.g81cdec2')
1215 1221 'ok'
1216 1222 >>> _checkversion(b'git version 1.9.0.GIT')
1217 1223 'ok'
1218 1224 >>> _checkversion(b'git version 12345')
1219 1225 'unknown'
1220 1226 >>> _checkversion(b'no')
1221 1227 'unknown'
1222 1228 '''
1223 1229 version = gitsubrepo._gitversion(out)
1224 1230 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1225 1231 # despite the docstring comment. For now, error on 1.4.0, warn on
1226 1232 # 1.5.0 but attempt to continue.
1227 1233 if version == -1:
1228 1234 return 'unknown'
1229 1235 if version < (1, 5, 0):
1230 1236 return 'abort'
1231 1237 elif version < (1, 6, 0):
1232 1238 return 'warning'
1233 1239 return 'ok'
1234 1240
1235 1241 def _gitcommand(self, commands, env=None, stream=False):
1236 1242 return self._gitdir(commands, env=env, stream=stream)[0]
1237 1243
1238 1244 def _gitdir(self, commands, env=None, stream=False):
1239 1245 return self._gitnodir(commands, env=env, stream=stream,
1240 1246 cwd=self._abspath)
1241 1247
1242 1248 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1243 1249 """Calls the git command
1244 1250
1245 1251 The methods tries to call the git command. versions prior to 1.6.0
1246 1252 are not supported and very probably fail.
1247 1253 """
1248 1254 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1249 1255 if env is None:
1250 1256 env = encoding.environ.copy()
1251 1257 # disable localization for Git output (issue5176)
1252 1258 env['LC_ALL'] = 'C'
1253 1259 # fix for Git CVE-2015-7545
1254 1260 if 'GIT_ALLOW_PROTOCOL' not in env:
1255 1261 env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh'
1256 1262 # unless ui.quiet is set, print git's stderr,
1257 1263 # which is mostly progress and useful info
1258 1264 errpipe = None
1259 1265 if self.ui.quiet:
1260 1266 errpipe = open(os.devnull, 'w')
1261 1267 if self.ui._colormode and len(commands) and commands[0] == "diff":
1262 1268 # insert the argument in the front,
1263 1269 # the end of git diff arguments is used for paths
1264 1270 commands.insert(1, '--color')
1265 1271 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
1266 1272 cwd=cwd, env=env, close_fds=procutil.closefds,
1267 1273 stdout=subprocess.PIPE, stderr=errpipe)
1268 1274 if stream:
1269 1275 return p.stdout, None
1270 1276
1271 1277 retdata = p.stdout.read().strip()
1272 1278 # wait for the child to exit to avoid race condition.
1273 1279 p.wait()
1274 1280
1275 1281 if p.returncode != 0 and p.returncode != 1:
1276 1282 # there are certain error codes that are ok
1277 1283 command = commands[0]
1278 1284 if command in ('cat-file', 'symbolic-ref'):
1279 1285 return retdata, p.returncode
1280 1286 # for all others, abort
1281 1287 raise error.Abort(_('git %s error %d in %s') %
1282 1288 (command, p.returncode, self._relpath))
1283 1289
1284 1290 return retdata, p.returncode
1285 1291
1286 1292 def _gitmissing(self):
1287 1293 return not self.wvfs.exists('.git')
1288 1294
1289 1295 def _gitstate(self):
1290 1296 return self._gitcommand(['rev-parse', 'HEAD'])
1291 1297
1292 1298 def _gitcurrentbranch(self):
1293 1299 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1294 1300 if err:
1295 1301 current = None
1296 1302 return current
1297 1303
1298 1304 def _gitremote(self, remote):
1299 1305 out = self._gitcommand(['remote', 'show', '-n', remote])
1300 1306 line = out.split('\n')[1]
1301 1307 i = line.index('URL: ') + len('URL: ')
1302 1308 return line[i:]
1303 1309
1304 1310 def _githavelocally(self, revision):
1305 1311 out, code = self._gitdir(['cat-file', '-e', revision])
1306 1312 return code == 0
1307 1313
1308 1314 def _gitisancestor(self, r1, r2):
1309 1315 base = self._gitcommand(['merge-base', r1, r2])
1310 1316 return base == r1
1311 1317
1312 1318 def _gitisbare(self):
1313 1319 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1314 1320
1315 1321 def _gitupdatestat(self):
1316 1322 """This must be run before git diff-index.
1317 1323 diff-index only looks at changes to file stat;
1318 1324 this command looks at file contents and updates the stat."""
1319 1325 self._gitcommand(['update-index', '-q', '--refresh'])
1320 1326
1321 1327 def _gitbranchmap(self):
1322 1328 '''returns 2 things:
1323 1329 a map from git branch to revision
1324 1330 a map from revision to branches'''
1325 1331 branch2rev = {}
1326 1332 rev2branch = {}
1327 1333
1328 1334 out = self._gitcommand(['for-each-ref', '--format',
1329 1335 '%(objectname) %(refname)'])
1330 1336 for line in out.split('\n'):
1331 1337 revision, ref = line.split(' ')
1332 1338 if (not ref.startswith('refs/heads/') and
1333 1339 not ref.startswith('refs/remotes/')):
1334 1340 continue
1335 1341 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1336 1342 continue # ignore remote/HEAD redirects
1337 1343 branch2rev[ref] = revision
1338 1344 rev2branch.setdefault(revision, []).append(ref)
1339 1345 return branch2rev, rev2branch
1340 1346
1341 1347 def _gittracking(self, branches):
1342 1348 'return map of remote branch to local tracking branch'
1343 1349 # assumes no more than one local tracking branch for each remote
1344 1350 tracking = {}
1345 1351 for b in branches:
1346 1352 if b.startswith('refs/remotes/'):
1347 1353 continue
1348 1354 bname = b.split('/', 2)[2]
1349 1355 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1350 1356 if remote:
1351 1357 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1352 1358 tracking['refs/remotes/%s/%s' %
1353 1359 (remote, ref.split('/', 2)[2])] = b
1354 1360 return tracking
1355 1361
1356 1362 def _abssource(self, source):
1357 1363 if '://' not in source:
1358 1364 # recognize the scp syntax as an absolute source
1359 1365 colon = source.find(':')
1360 1366 if colon != -1 and '/' not in source[:colon]:
1361 1367 return source
1362 1368 self._subsource = source
1363 1369 return _abssource(self)
1364 1370
1365 1371 def _fetch(self, source, revision):
1366 1372 if self._gitmissing():
1367 1373 # SEC: check for safe ssh url
1368 1374 util.checksafessh(source)
1369 1375
1370 1376 source = self._abssource(source)
1371 1377 self.ui.status(_('cloning subrepo %s from %s\n') %
1372 1378 (self._relpath, source))
1373 1379 self._gitnodir(['clone', source, self._abspath])
1374 1380 if self._githavelocally(revision):
1375 1381 return
1376 1382 self.ui.status(_('pulling subrepo %s from %s\n') %
1377 1383 (self._relpath, self._gitremote('origin')))
1378 1384 # try only origin: the originally cloned repo
1379 1385 self._gitcommand(['fetch'])
1380 1386 if not self._githavelocally(revision):
1381 1387 raise error.Abort(_('revision %s does not exist in subrepository '
1382 1388 '"%s"\n') % (revision, self._relpath))
1383 1389
1384 1390 @annotatesubrepoerror
1385 1391 def dirty(self, ignoreupdate=False, missing=False):
1386 1392 if self._gitmissing():
1387 1393 return self._state[1] != ''
1388 1394 if self._gitisbare():
1389 1395 return True
1390 1396 if not ignoreupdate and self._state[1] != self._gitstate():
1391 1397 # different version checked out
1392 1398 return True
1393 1399 # check for staged changes or modified files; ignore untracked files
1394 1400 self._gitupdatestat()
1395 1401 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1396 1402 return code == 1
1397 1403
1398 1404 def basestate(self):
1399 1405 return self._gitstate()
1400 1406
1401 1407 @annotatesubrepoerror
1402 1408 def get(self, state, overwrite=False):
1403 1409 source, revision, kind = state
1404 1410 if not revision:
1405 1411 self.remove()
1406 1412 return
1407 1413 self._fetch(source, revision)
1408 1414 # if the repo was set to be bare, unbare it
1409 1415 if self._gitisbare():
1410 1416 self._gitcommand(['config', 'core.bare', 'false'])
1411 1417 if self._gitstate() == revision:
1412 1418 self._gitcommand(['reset', '--hard', 'HEAD'])
1413 1419 return
1414 1420 elif self._gitstate() == revision:
1415 1421 if overwrite:
1416 1422 # first reset the index to unmark new files for commit, because
1417 1423 # reset --hard will otherwise throw away files added for commit,
1418 1424 # not just unmark them.
1419 1425 self._gitcommand(['reset', 'HEAD'])
1420 1426 self._gitcommand(['reset', '--hard', 'HEAD'])
1421 1427 return
1422 1428 branch2rev, rev2branch = self._gitbranchmap()
1423 1429
1424 1430 def checkout(args):
1425 1431 cmd = ['checkout']
1426 1432 if overwrite:
1427 1433 # first reset the index to unmark new files for commit, because
1428 1434 # the -f option will otherwise throw away files added for
1429 1435 # commit, not just unmark them.
1430 1436 self._gitcommand(['reset', 'HEAD'])
1431 1437 cmd.append('-f')
1432 1438 self._gitcommand(cmd + args)
1433 1439 _sanitize(self.ui, self.wvfs, '.git')
1434 1440
1435 1441 def rawcheckout():
1436 1442 # no branch to checkout, check it out with no branch
1437 1443 self.ui.warn(_('checking out detached HEAD in '
1438 1444 'subrepository "%s"\n') % self._relpath)
1439 1445 self.ui.warn(_('check out a git branch if you intend '
1440 1446 'to make changes\n'))
1441 1447 checkout(['-q', revision])
1442 1448
1443 1449 if revision not in rev2branch:
1444 1450 rawcheckout()
1445 1451 return
1446 1452 branches = rev2branch[revision]
1447 1453 firstlocalbranch = None
1448 1454 for b in branches:
1449 1455 if b == 'refs/heads/master':
1450 1456 # master trumps all other branches
1451 1457 checkout(['refs/heads/master'])
1452 1458 return
1453 1459 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1454 1460 firstlocalbranch = b
1455 1461 if firstlocalbranch:
1456 1462 checkout([firstlocalbranch])
1457 1463 return
1458 1464
1459 1465 tracking = self._gittracking(branch2rev.keys())
1460 1466 # choose a remote branch already tracked if possible
1461 1467 remote = branches[0]
1462 1468 if remote not in tracking:
1463 1469 for b in branches:
1464 1470 if b in tracking:
1465 1471 remote = b
1466 1472 break
1467 1473
1468 1474 if remote not in tracking:
1469 1475 # create a new local tracking branch
1470 1476 local = remote.split('/', 3)[3]
1471 1477 checkout(['-b', local, remote])
1472 1478 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1473 1479 # When updating to a tracked remote branch,
1474 1480 # if the local tracking branch is downstream of it,
1475 1481 # a normal `git pull` would have performed a "fast-forward merge"
1476 1482 # which is equivalent to updating the local branch to the remote.
1477 1483 # Since we are only looking at branching at update, we need to
1478 1484 # detect this situation and perform this action lazily.
1479 1485 if tracking[remote] != self._gitcurrentbranch():
1480 1486 checkout([tracking[remote]])
1481 1487 self._gitcommand(['merge', '--ff', remote])
1482 1488 _sanitize(self.ui, self.wvfs, '.git')
1483 1489 else:
1484 1490 # a real merge would be required, just checkout the revision
1485 1491 rawcheckout()
1486 1492
1487 1493 @annotatesubrepoerror
1488 1494 def commit(self, text, user, date):
1489 1495 if self._gitmissing():
1490 1496 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1491 1497 cmd = ['commit', '-a', '-m', text]
1492 1498 env = encoding.environ.copy()
1493 1499 if user:
1494 1500 cmd += ['--author', user]
1495 1501 if date:
1496 1502 # git's date parser silently ignores when seconds < 1e9
1497 1503 # convert to ISO8601
1498 1504 env['GIT_AUTHOR_DATE'] = dateutil.datestr(date,
1499 1505 '%Y-%m-%dT%H:%M:%S %1%2')
1500 1506 self._gitcommand(cmd, env=env)
1501 1507 # make sure commit works otherwise HEAD might not exist under certain
1502 1508 # circumstances
1503 1509 return self._gitstate()
1504 1510
1505 1511 @annotatesubrepoerror
1506 1512 def merge(self, state):
1507 1513 source, revision, kind = state
1508 1514 self._fetch(source, revision)
1509 1515 base = self._gitcommand(['merge-base', revision, self._state[1]])
1510 1516 self._gitupdatestat()
1511 1517 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1512 1518
1513 1519 def mergefunc():
1514 1520 if base == revision:
1515 1521 self.get(state) # fast forward merge
1516 1522 elif base != self._state[1]:
1517 1523 self._gitcommand(['merge', '--no-commit', revision])
1518 1524 _sanitize(self.ui, self.wvfs, '.git')
1519 1525
1520 1526 if self.dirty():
1521 1527 if self._gitstate() != revision:
1522 1528 dirty = self._gitstate() == self._state[1] or code != 0
1523 1529 if _updateprompt(self.ui, self, dirty,
1524 1530 self._state[1][:7], revision[:7]):
1525 1531 mergefunc()
1526 1532 else:
1527 1533 mergefunc()
1528 1534
1529 1535 @annotatesubrepoerror
1530 1536 def push(self, opts):
1531 1537 force = opts.get('force')
1532 1538
1533 1539 if not self._state[1]:
1534 1540 return True
1535 1541 if self._gitmissing():
1536 1542 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1537 1543 # if a branch in origin contains the revision, nothing to do
1538 1544 branch2rev, rev2branch = self._gitbranchmap()
1539 1545 if self._state[1] in rev2branch:
1540 1546 for b in rev2branch[self._state[1]]:
1541 1547 if b.startswith('refs/remotes/origin/'):
1542 1548 return True
1543 1549 for b, revision in branch2rev.iteritems():
1544 1550 if b.startswith('refs/remotes/origin/'):
1545 1551 if self._gitisancestor(self._state[1], revision):
1546 1552 return True
1547 1553 # otherwise, try to push the currently checked out branch
1548 1554 cmd = ['push']
1549 1555 if force:
1550 1556 cmd.append('--force')
1551 1557
1552 1558 current = self._gitcurrentbranch()
1553 1559 if current:
1554 1560 # determine if the current branch is even useful
1555 1561 if not self._gitisancestor(self._state[1], current):
1556 1562 self.ui.warn(_('unrelated git branch checked out '
1557 1563 'in subrepository "%s"\n') % self._relpath)
1558 1564 return False
1559 1565 self.ui.status(_('pushing branch %s of subrepository "%s"\n') %
1560 1566 (current.split('/', 2)[2], self._relpath))
1561 1567 ret = self._gitdir(cmd + ['origin', current])
1562 1568 return ret[1] == 0
1563 1569 else:
1564 1570 self.ui.warn(_('no branch checked out in subrepository "%s"\n'
1565 1571 'cannot push revision %s\n') %
1566 1572 (self._relpath, self._state[1]))
1567 1573 return False
1568 1574
1569 1575 @annotatesubrepoerror
1570 1576 def add(self, ui, match, prefix, explicitonly, **opts):
1571 1577 if self._gitmissing():
1572 1578 return []
1573 1579
1574 1580 (modified, added, removed,
1575 1581 deleted, unknown, ignored, clean) = self.status(None, unknown=True,
1576 1582 clean=True)
1577 1583
1578 1584 tracked = set()
1579 1585 # dirstates 'amn' warn, 'r' is added again
1580 1586 for l in (modified, added, deleted, clean):
1581 1587 tracked.update(l)
1582 1588
1583 1589 # Unknown files not of interest will be rejected by the matcher
1584 1590 files = unknown
1585 1591 files.extend(match.files())
1586 1592
1587 1593 rejected = []
1588 1594
1589 1595 files = [f for f in sorted(set(files)) if match(f)]
1590 1596 for f in files:
1591 1597 exact = match.exact(f)
1592 1598 command = ["add"]
1593 1599 if exact:
1594 1600 command.append("-f") #should be added, even if ignored
1595 1601 if ui.verbose or not exact:
1596 1602 ui.status(_('adding %s\n') % match.rel(f))
1597 1603
1598 1604 if f in tracked: # hg prints 'adding' even if already tracked
1599 1605 if exact:
1600 1606 rejected.append(f)
1601 1607 continue
1602 1608 if not opts.get(r'dry_run'):
1603 1609 self._gitcommand(command + [f])
1604 1610
1605 1611 for f in rejected:
1606 1612 ui.warn(_("%s already tracked!\n") % match.abs(f))
1607 1613
1608 1614 return rejected
1609 1615
1610 1616 @annotatesubrepoerror
1611 1617 def remove(self):
1612 1618 if self._gitmissing():
1613 1619 return
1614 1620 if self.dirty():
1615 1621 self.ui.warn(_('not removing repo %s because '
1616 1622 'it has changes.\n') % self._relpath)
1617 1623 return
1618 1624 # we can't fully delete the repository as it may contain
1619 1625 # local-only history
1620 1626 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1621 1627 self._gitcommand(['config', 'core.bare', 'true'])
1622 1628 for f, kind in self.wvfs.readdir():
1623 1629 if f == '.git':
1624 1630 continue
1625 1631 if kind == stat.S_IFDIR:
1626 1632 self.wvfs.rmtree(f)
1627 1633 else:
1628 1634 self.wvfs.unlink(f)
1629 1635
1630 1636 def archive(self, archiver, prefix, match=None, decode=True):
1631 1637 total = 0
1632 1638 source, revision = self._state
1633 1639 if not revision:
1634 1640 return total
1635 1641 self._fetch(source, revision)
1636 1642
1637 1643 # Parse git's native archive command.
1638 1644 # This should be much faster than manually traversing the trees
1639 1645 # and objects with many subprocess calls.
1640 1646 tarstream = self._gitcommand(['archive', revision], stream=True)
1641 1647 tar = tarfile.open(fileobj=tarstream, mode=r'r|')
1642 1648 relpath = subrelpath(self)
1643 1649 progress = self.ui.makeprogress(_('archiving (%s)') % relpath,
1644 1650 unit=_('files'))
1645 1651 progress.update(0)
1646 1652 for info in tar:
1647 1653 if info.isdir():
1648 1654 continue
1649 1655 if match and not match(info.name):
1650 1656 continue
1651 1657 if info.issym():
1652 1658 data = info.linkname
1653 1659 else:
1654 1660 data = tar.extractfile(info).read()
1655 1661 archiver.addfile(prefix + self._path + '/' + info.name,
1656 1662 info.mode, info.issym(), data)
1657 1663 total += 1
1658 1664 progress.increment()
1659 1665 progress.complete()
1660 1666 return total
1661 1667
1662 1668
1663 1669 @annotatesubrepoerror
1664 1670 def cat(self, match, fm, fntemplate, prefix, **opts):
1665 1671 rev = self._state[1]
1666 1672 if match.anypats():
1667 1673 return 1 #No support for include/exclude yet
1668 1674
1669 1675 if not match.files():
1670 1676 return 1
1671 1677
1672 1678 # TODO: add support for non-plain formatter (see cmdutil.cat())
1673 1679 for f in match.files():
1674 1680 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1675 1681 fp = cmdutil.makefileobj(self._ctx, fntemplate,
1676 1682 pathname=self.wvfs.reljoin(prefix, f))
1677 1683 fp.write(output)
1678 1684 fp.close()
1679 1685 return 0
1680 1686
1681 1687
1682 1688 @annotatesubrepoerror
1683 1689 def status(self, rev2, **opts):
1684 1690 rev1 = self._state[1]
1685 1691 if self._gitmissing() or not rev1:
1686 1692 # if the repo is missing, return no results
1687 1693 return scmutil.status([], [], [], [], [], [], [])
1688 1694 modified, added, removed = [], [], []
1689 1695 self._gitupdatestat()
1690 1696 if rev2:
1691 1697 command = ['diff-tree', '--no-renames', '-r', rev1, rev2]
1692 1698 else:
1693 1699 command = ['diff-index', '--no-renames', rev1]
1694 1700 out = self._gitcommand(command)
1695 1701 for line in out.split('\n'):
1696 1702 tab = line.find('\t')
1697 1703 if tab == -1:
1698 1704 continue
1699 1705 status, f = line[tab - 1:tab], line[tab + 1:]
1700 1706 if status == 'M':
1701 1707 modified.append(f)
1702 1708 elif status == 'A':
1703 1709 added.append(f)
1704 1710 elif status == 'D':
1705 1711 removed.append(f)
1706 1712
1707 1713 deleted, unknown, ignored, clean = [], [], [], []
1708 1714
1709 1715 command = ['status', '--porcelain', '-z']
1710 1716 if opts.get(r'unknown'):
1711 1717 command += ['--untracked-files=all']
1712 1718 if opts.get(r'ignored'):
1713 1719 command += ['--ignored']
1714 1720 out = self._gitcommand(command)
1715 1721
1716 1722 changedfiles = set()
1717 1723 changedfiles.update(modified)
1718 1724 changedfiles.update(added)
1719 1725 changedfiles.update(removed)
1720 1726 for line in out.split('\0'):
1721 1727 if not line:
1722 1728 continue
1723 1729 st = line[0:2]
1724 1730 #moves and copies show 2 files on one line
1725 1731 if line.find('\0') >= 0:
1726 1732 filename1, filename2 = line[3:].split('\0')
1727 1733 else:
1728 1734 filename1 = line[3:]
1729 1735 filename2 = None
1730 1736
1731 1737 changedfiles.add(filename1)
1732 1738 if filename2:
1733 1739 changedfiles.add(filename2)
1734 1740
1735 1741 if st == '??':
1736 1742 unknown.append(filename1)
1737 1743 elif st == '!!':
1738 1744 ignored.append(filename1)
1739 1745
1740 1746 if opts.get(r'clean'):
1741 1747 out = self._gitcommand(['ls-files'])
1742 1748 for f in out.split('\n'):
1743 1749 if not f in changedfiles:
1744 1750 clean.append(f)
1745 1751
1746 1752 return scmutil.status(modified, added, removed, deleted,
1747 1753 unknown, ignored, clean)
1748 1754
1749 1755 @annotatesubrepoerror
1750 1756 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1751 1757 node1 = self._state[1]
1752 1758 cmd = ['diff', '--no-renames']
1753 1759 if opts[r'stat']:
1754 1760 cmd.append('--stat')
1755 1761 else:
1756 1762 # for Git, this also implies '-p'
1757 1763 cmd.append('-U%d' % diffopts.context)
1758 1764
1759 1765 gitprefix = self.wvfs.reljoin(prefix, self._path)
1760 1766
1761 1767 if diffopts.noprefix:
1762 1768 cmd.extend(['--src-prefix=%s/' % gitprefix,
1763 1769 '--dst-prefix=%s/' % gitprefix])
1764 1770 else:
1765 1771 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1766 1772 '--dst-prefix=b/%s/' % gitprefix])
1767 1773
1768 1774 if diffopts.ignorews:
1769 1775 cmd.append('--ignore-all-space')
1770 1776 if diffopts.ignorewsamount:
1771 1777 cmd.append('--ignore-space-change')
1772 1778 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1773 1779 and diffopts.ignoreblanklines:
1774 1780 cmd.append('--ignore-blank-lines')
1775 1781
1776 1782 cmd.append(node1)
1777 1783 if node2:
1778 1784 cmd.append(node2)
1779 1785
1780 1786 output = ""
1781 1787 if match.always():
1782 1788 output += self._gitcommand(cmd) + '\n'
1783 1789 else:
1784 1790 st = self.status(node2)[:3]
1785 1791 files = [f for sublist in st for f in sublist]
1786 1792 for f in files:
1787 1793 if match(f):
1788 1794 output += self._gitcommand(cmd + ['--', f]) + '\n'
1789 1795
1790 1796 if output.strip():
1791 1797 ui.write(output)
1792 1798
1793 1799 @annotatesubrepoerror
1794 1800 def revert(self, substate, *pats, **opts):
1795 1801 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1796 1802 if not opts.get(r'no_backup'):
1797 1803 status = self.status(None)
1798 1804 names = status.modified
1799 1805 for name in names:
1800 1806 bakname = scmutil.origpath(self.ui, self._subparent, name)
1801 1807 self.ui.note(_('saving current version of %s as %s\n') %
1802 1808 (name, bakname))
1803 1809 self.wvfs.rename(name, bakname)
1804 1810
1805 1811 if not opts.get(r'dry_run'):
1806 1812 self.get(substate, overwrite=True)
1807 1813 return []
1808 1814
1809 1815 def shortid(self, revid):
1810 1816 return revid[:7]
1811 1817
1812 1818 types = {
1813 1819 'hg': hgsubrepo,
1814 1820 'svn': svnsubrepo,
1815 1821 'git': gitsubrepo,
1816 1822 }
@@ -1,3147 +1,3158 b''
1 1 @ (34) head
2 2 |
3 3 | o (33) head
4 4 | |
5 5 o | (32) expand
6 6 |\ \
7 7 | o \ (31) expand
8 8 | |\ \
9 9 | | o \ (30) expand
10 10 | | |\ \
11 11 | | | o | (29) regular commit
12 12 | | | | |
13 13 | | o | | (28) merge zero known
14 14 | | |\ \ \
15 15 o | | | | | (27) collapse
16 16 |/ / / / /
17 17 | | o---+ (26) merge one known; far right
18 18 | | | | |
19 19 +---o | | (25) merge one known; far left
20 20 | | | | |
21 21 | | o | | (24) merge one known; immediate right
22 22 | | |\| |
23 23 | | o | | (23) merge one known; immediate left
24 24 | |/| | |
25 25 +---o---+ (22) merge two known; one far left, one far right
26 26 | | / /
27 27 o | | | (21) expand
28 28 |\ \ \ \
29 29 | o---+-+ (20) merge two known; two far right
30 30 | / / /
31 31 o | | | (19) expand
32 32 |\ \ \ \
33 33 +---+---o (18) merge two known; two far left
34 34 | | | |
35 35 | o | | (17) expand
36 36 | |\ \ \
37 37 | | o---+ (16) merge two known; one immediate right, one near right
38 38 | | |/ /
39 39 o | | | (15) expand
40 40 |\ \ \ \
41 41 | o-----+ (14) merge two known; one immediate right, one far right
42 42 | |/ / /
43 43 o | | | (13) expand
44 44 |\ \ \ \
45 45 +---o | | (12) merge two known; one immediate right, one far left
46 46 | | |/ /
47 47 | o | | (11) expand
48 48 | |\ \ \
49 49 | | o---+ (10) merge two known; one immediate left, one near right
50 50 | |/ / /
51 51 o | | | (9) expand
52 52 |\ \ \ \
53 53 | o-----+ (8) merge two known; one immediate left, one far right
54 54 |/ / / /
55 55 o | | | (7) expand
56 56 |\ \ \ \
57 57 +---o | | (6) merge two known; one immediate left, one far left
58 58 | |/ / /
59 59 | o | | (5) expand
60 60 | |\ \ \
61 61 | | o | | (4) merge two known; one immediate left, one immediate right
62 62 | |/|/ /
63 63 | o / / (3) collapse
64 64 |/ / /
65 65 o / / (2) collapse
66 66 |/ /
67 67 o / (1) collapse
68 68 |/
69 69 o (0) root
70 70
71 71 $ commit()
72 72 > {
73 73 > rev=$1
74 74 > msg=$2
75 75 > shift 2
76 76 > if [ "$#" -gt 0 ]; then
77 77 > hg debugsetparents "$@"
78 78 > fi
79 79 > echo $rev > a
80 80 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
81 81 > }
82 82
83 83 $ cat > printrevset.py <<EOF
84 84 > from __future__ import absolute_import
85 85 > from mercurial import (
86 86 > cmdutil,
87 87 > commands,
88 88 > extensions,
89 89 > logcmdutil,
90 90 > revsetlang,
91 91 > smartset,
92 92 > )
93 93 >
94 94 > from mercurial.utils import (
95 95 > stringutil,
96 96 > )
97 97 >
98 98 > def logrevset(repo, pats, opts):
99 99 > revs = logcmdutil._initialrevs(repo, opts)
100 100 > if not revs:
101 101 > return None
102 102 > match, pats, slowpath = logcmdutil._makematcher(repo, revs, pats, opts)
103 103 > return logcmdutil._makerevset(repo, match, pats, slowpath, opts)
104 104 >
105 105 > def uisetup(ui):
106 106 > def printrevset(orig, repo, pats, opts):
107 107 > revs, filematcher = orig(repo, pats, opts)
108 108 > if opts.get(b'print_revset'):
109 109 > expr = logrevset(repo, pats, opts)
110 110 > if expr:
111 111 > tree = revsetlang.parse(expr)
112 112 > tree = revsetlang.analyze(tree)
113 113 > else:
114 114 > tree = []
115 115 > ui = repo.ui
116 116 > ui.write(b'%r\n' % (opts.get(b'rev', []),))
117 117 > ui.write(revsetlang.prettyformat(tree) + b'\n')
118 118 > ui.write(stringutil.prettyrepr(revs) + b'\n')
119 119 > revs = smartset.baseset() # display no revisions
120 120 > return revs, filematcher
121 121 > extensions.wrapfunction(logcmdutil, 'getrevs', printrevset)
122 122 > aliases, entry = cmdutil.findcmd(b'log', commands.table)
123 123 > entry[1].append((b'', b'print-revset', False,
124 124 > b'print generated revset and exit (DEPRECATED)'))
125 125 > EOF
126 126
127 127 $ echo "[extensions]" >> $HGRCPATH
128 128 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
129 129 $ echo "beautifygraph=" >> $HGRCPATH
130 130
131 131 Set a default of narrow-text UTF-8.
132 132
133 133 $ HGENCODING=UTF-8; export HGENCODING
134 134 $ HGENCODINGAMBIGUOUS=narrow; export HGENCODINGAMBIGUOUS
135 135
136 136 Empty repo:
137 137
138 138 $ hg init repo
139 139 $ cd repo
140 140 $ hg log -G
141 141
142 142 Building DAG:
143 143
144 144 $ commit 0 "root"
145 145 $ commit 1 "collapse" 0
146 146 $ commit 2 "collapse" 1
147 147 $ commit 3 "collapse" 2
148 148 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
149 149 $ commit 5 "expand" 3 4
150 150 $ commit 6 "merge two known; one immediate left, one far left" 2 5
151 151 $ commit 7 "expand" 2 5
152 152 $ commit 8 "merge two known; one immediate left, one far right" 0 7
153 153 $ commit 9 "expand" 7 8
154 154 $ commit 10 "merge two known; one immediate left, one near right" 0 6
155 155 $ commit 11 "expand" 6 10
156 156 $ commit 12 "merge two known; one immediate right, one far left" 1 9
157 157 $ commit 13 "expand" 9 11
158 158 $ commit 14 "merge two known; one immediate right, one far right" 0 12
159 159 $ commit 15 "expand" 13 14
160 160 $ commit 16 "merge two known; one immediate right, one near right" 0 1
161 161 $ commit 17 "expand" 12 16
162 162 $ commit 18 "merge two known; two far left" 1 15
163 163 $ commit 19 "expand" 15 17
164 164 $ commit 20 "merge two known; two far right" 0 18
165 165 $ commit 21 "expand" 19 20
166 166 $ commit 22 "merge two known; one far left, one far right" 18 21
167 167 $ commit 23 "merge one known; immediate left" 1 22
168 168 $ commit 24 "merge one known; immediate right" 0 23
169 169 $ commit 25 "merge one known; far left" 21 24
170 170 $ commit 26 "merge one known; far right" 18 25
171 171 $ commit 27 "collapse" 21
172 172 $ commit 28 "merge zero known" 1 26
173 173 $ commit 29 "regular commit" 0
174 174 $ commit 30 "expand" 28 29
175 175 $ commit 31 "expand" 21 30
176 176 $ commit 32 "expand" 27 31
177 177 $ commit 33 "head" 18
178 178 $ commit 34 "head" 32
179 179
180 180 The extension should not turn on unless we're in UTF-8.
181 181
182 182 $ HGENCODING=latin1 hg log -G -q
183 183 beautifygraph: unsupported encoding, UTF-8 required
184 184 @ 34:fea3ac5810e0
185 185 |
186 186 | o 33:68608f5145f9
187 187 | |
188 188 o | 32:d06dffa21a31
189 189 |\ \
190 190 | o \ 31:621d83e11f67
191 191 | |\ \
192 192 | | o \ 30:6e11cd4b648f
193 193 | | |\ \
194 194 | | | o | 29:cd9bb2be7593
195 195 | | | | |
196 196 | | o | | 28:44ecd0b9ae99
197 197 | | |\ \ \
198 198 o | | | | | 27:886ed638191b
199 199 |/ / / / /
200 200 | | o---+ 26:7f25b6c2f0b9
201 201 | | | | |
202 202 +---o | | 25:91da8ed57247
203 203 | | | | |
204 204 | | o | | 24:a9c19a3d96b7
205 205 | | |\| |
206 206 | | o | | 23:a01cddf0766d
207 207 | |/| | |
208 208 +---o---+ 22:e0d9cccacb5d
209 209 | | / /
210 210 o | | | 21:d42a756af44d
211 211 |\ \ \ \
212 212 | o---+-+ 20:d30ed6450e32
213 213 | / / /
214 214 o | | | 19:31ddc2c1573b
215 215 |\ \ \ \
216 216 +---+---o 18:1aa84d96232a
217 217 | | | |
218 218 | o | | 17:44765d7c06e0
219 219 | |\ \ \
220 220 | | o---+ 16:3677d192927d
221 221 | | |/ /
222 222 o | | | 15:1dda3f72782d
223 223 |\ \ \ \
224 224 | o-----+ 14:8eac370358ef
225 225 | |/ / /
226 226 o | | | 13:22d8966a97e3
227 227 |\ \ \ \
228 228 +---o | | 12:86b91144a6e9
229 229 | | |/ /
230 230 | o | | 11:832d76e6bdf2
231 231 | |\ \ \
232 232 | | o---+ 10:74c64d036d72
233 233 | |/ / /
234 234 o | | | 9:7010c0af0a35
235 235 |\ \ \ \
236 236 | o-----+ 8:7a0b11f71937
237 237 |/ / / /
238 238 o | | | 7:b632bb1b1224
239 239 |\ \ \ \
240 240 +---o | | 6:b105a072e251
241 241 | |/ / /
242 242 | o | | 5:4409d547b708
243 243 | |\ \ \
244 244 | | o | | 4:26a8bac39d9f
245 245 | |/|/ /
246 246 | o / / 3:27eef8ed80b4
247 247 |/ / /
248 248 o / / 2:3d9a33b8d1e1
249 249 |/ /
250 250 o / 1:6db2ef61d156
251 251 |/
252 252 o 0:e6eb3150255d
253 253
254 254
255 255 The extension should not turn on if we're using wide text.
256 256
257 257 $ HGENCODINGAMBIGUOUS=wide hg log -G -q
258 258 beautifygraph: unsupported terminal settings, monospace narrow text required
259 259 @ 34:fea3ac5810e0
260 260 |
261 261 | o 33:68608f5145f9
262 262 | |
263 263 o | 32:d06dffa21a31
264 264 |\ \
265 265 | o \ 31:621d83e11f67
266 266 | |\ \
267 267 | | o \ 30:6e11cd4b648f
268 268 | | |\ \
269 269 | | | o | 29:cd9bb2be7593
270 270 | | | | |
271 271 | | o | | 28:44ecd0b9ae99
272 272 | | |\ \ \
273 273 o | | | | | 27:886ed638191b
274 274 |/ / / / /
275 275 | | o---+ 26:7f25b6c2f0b9
276 276 | | | | |
277 277 +---o | | 25:91da8ed57247
278 278 | | | | |
279 279 | | o | | 24:a9c19a3d96b7
280 280 | | |\| |
281 281 | | o | | 23:a01cddf0766d
282 282 | |/| | |
283 283 +---o---+ 22:e0d9cccacb5d
284 284 | | / /
285 285 o | | | 21:d42a756af44d
286 286 |\ \ \ \
287 287 | o---+-+ 20:d30ed6450e32
288 288 | / / /
289 289 o | | | 19:31ddc2c1573b
290 290 |\ \ \ \
291 291 +---+---o 18:1aa84d96232a
292 292 | | | |
293 293 | o | | 17:44765d7c06e0
294 294 | |\ \ \
295 295 | | o---+ 16:3677d192927d
296 296 | | |/ /
297 297 o | | | 15:1dda3f72782d
298 298 |\ \ \ \
299 299 | o-----+ 14:8eac370358ef
300 300 | |/ / /
301 301 o | | | 13:22d8966a97e3
302 302 |\ \ \ \
303 303 +---o | | 12:86b91144a6e9
304 304 | | |/ /
305 305 | o | | 11:832d76e6bdf2
306 306 | |\ \ \
307 307 | | o---+ 10:74c64d036d72
308 308 | |/ / /
309 309 o | | | 9:7010c0af0a35
310 310 |\ \ \ \
311 311 | o-----+ 8:7a0b11f71937
312 312 |/ / / /
313 313 o | | | 7:b632bb1b1224
314 314 |\ \ \ \
315 315 +---o | | 6:b105a072e251
316 316 | |/ / /
317 317 | o | | 5:4409d547b708
318 318 | |\ \ \
319 319 | | o | | 4:26a8bac39d9f
320 320 | |/|/ /
321 321 | o / / 3:27eef8ed80b4
322 322 |/ / /
323 323 o / / 2:3d9a33b8d1e1
324 324 |/ /
325 325 o / 1:6db2ef61d156
326 326 |/
327 327 o 0:e6eb3150255d
328 328
329 329
330 330 The rest of our tests will use the default narrow text UTF-8.
331 331
332 332 $ hg log -G -q
333 333 \xe2\x97\x8d 34:fea3ac5810e0 (esc)
334 334 \xe2\x94\x82 (esc)
335 335 \xe2\x94\x82 \xe2\x97\x8b 33:68608f5145f9 (esc)
336 336 \xe2\x94\x82 \xe2\x94\x82 (esc)
337 337 \xe2\x97\x8b \xe2\x94\x82 32:d06dffa21a31 (esc)
338 338 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 (esc)
339 339 \xe2\x94\x82 \xe2\x97\x8b \xe2\x95\xb2 31:621d83e11f67 (esc)
340 340 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 (esc)
341 341 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x95\xb2 30:6e11cd4b648f (esc)
342 342 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 (esc)
343 343 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 29:cd9bb2be7593 (esc)
344 344 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
345 345 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 28:44ecd0b9ae99 (esc)
346 346 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
347 347 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 27:886ed638191b (esc)
348 348 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
349 349 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 26:7f25b6c2f0b9 (esc)
350 350 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
351 351 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 25:91da8ed57247 (esc)
352 352 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
353 353 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 24:a9c19a3d96b7 (esc)
354 354 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 (esc)
355 355 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 23:a01cddf0766d (esc)
356 356 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
357 357 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 22:e0d9cccacb5d (esc)
358 358 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
359 359 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 21:d42a756af44d (esc)
360 360 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
361 361 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 20:d30ed6450e32 (esc)
362 362 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
363 363 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 19:31ddc2c1573b (esc)
364 364 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
365 365 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b 18:1aa84d96232a (esc)
366 366 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
367 367 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 17:44765d7c06e0 (esc)
368 368 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
369 369 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 16:3677d192927d (esc)
370 370 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc)
371 371 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 15:1dda3f72782d (esc)
372 372 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
373 373 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 14:8eac370358ef (esc)
374 374 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
375 375 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 13:22d8966a97e3 (esc)
376 376 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
377 377 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 12:86b91144a6e9 (esc)
378 378 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc)
379 379 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 11:832d76e6bdf2 (esc)
380 380 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
381 381 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 10:74c64d036d72 (esc)
382 382 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
383 383 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 9:7010c0af0a35 (esc)
384 384 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
385 385 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 8:7a0b11f71937 (esc)
386 386 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
387 387 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 7:b632bb1b1224 (esc)
388 388 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
389 389 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 6:b105a072e251 (esc)
390 390 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
391 391 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 5:4409d547b708 (esc)
392 392 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
393 393 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 4:26a8bac39d9f (esc)
394 394 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc)
395 395 \xe2\x94\x82 \xe2\x97\x8b \xe2\x95\xb1 \xe2\x95\xb1 3:27eef8ed80b4 (esc)
396 396 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
397 397 \xe2\x97\x8b \xe2\x95\xb1 \xe2\x95\xb1 2:3d9a33b8d1e1 (esc)
398 398 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc)
399 399 \xe2\x97\x8b \xe2\x95\xb1 1:6db2ef61d156 (esc)
400 400 \xe2\x94\x82\xe2\x95\xb1 (esc)
401 401 \xe2\x97\x8b 0:e6eb3150255d (esc)
402 402
403 403
404 404 $ hg log -G
405 405 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
406 406 \xe2\x94\x82 tag: tip (esc)
407 407 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
408 408 \xe2\x94\x82 user: test (esc)
409 409 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
410 410 \xe2\x94\x82 summary: (34) head (esc)
411 411 \xe2\x94\x82 (esc)
412 412 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
413 413 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
414 414 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
415 415 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
416 416 \xe2\x94\x82 \xe2\x94\x82 summary: (33) head (esc)
417 417 \xe2\x94\x82 \xe2\x94\x82 (esc)
418 418 \xe2\x97\x8b \xe2\x94\x82 changeset: 32:d06dffa21a31 (esc)
419 419 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 27:886ed638191b (esc)
420 420 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
421 421 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
422 422 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
423 423 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (32) expand (esc)
424 424 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
425 425 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 31:621d83e11f67 (esc)
426 426 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 21:d42a756af44d (esc)
427 427 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 30:6e11cd4b648f (esc)
428 428 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
429 429 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
430 430 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (31) expand (esc)
431 431 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
432 432 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 30:6e11cd4b648f (esc)
433 433 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
434 434 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 29:cd9bb2be7593 (esc)
435 435 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
436 436 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
437 437 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (30) expand (esc)
438 438 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
439 439 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 29:cd9bb2be7593 (esc)
440 440 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
441 441 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
442 442 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:29 1970 +0000 (esc)
443 443 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (29) regular commit (esc)
444 444 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
445 445 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 28:44ecd0b9ae99 (esc)
446 446 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
447 447 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 26:7f25b6c2f0b9 (esc)
448 448 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
449 449 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
450 450 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (28) merge zero known (esc)
451 451 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
452 452 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 27:886ed638191b (esc)
453 453 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
454 454 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
455 455 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:27 1970 +0000 (esc)
456 456 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (27) collapse (esc)
457 457 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
458 458 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 26:7f25b6c2f0b9 (esc)
459 459 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
460 460 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 25:91da8ed57247 (esc)
461 461 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
462 462 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
463 463 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (26) merge one known; far right (esc)
464 464 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
465 465 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 25:91da8ed57247 (esc)
466 466 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 21:d42a756af44d (esc)
467 467 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 24:a9c19a3d96b7 (esc)
468 468 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
469 469 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
470 470 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (25) merge one known; far left (esc)
471 471 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
472 472 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 24:a9c19a3d96b7 (esc)
473 473 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
474 474 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 23:a01cddf0766d (esc)
475 475 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
476 476 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
477 477 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (24) merge one known; immediate right (esc)
478 478 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
479 479 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 23:a01cddf0766d (esc)
480 480 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
481 481 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 22:e0d9cccacb5d (esc)
482 482 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
483 483 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
484 484 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (23) merge one known; immediate left (esc)
485 485 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
486 486 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 22:e0d9cccacb5d (esc)
487 487 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
488 488 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
489 489 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
490 490 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
491 491 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (22) merge two known; one far left, one far right (esc)
492 492 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
493 493 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 21:d42a756af44d (esc)
494 494 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
495 495 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
496 496 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
497 497 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
498 498 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
499 499 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
500 500 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 changeset: 20:d30ed6450e32 (esc)
501 501 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
502 502 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
503 503 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
504 504 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
505 505 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
506 506 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
507 507 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 19:31ddc2c1573b (esc)
508 508 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
509 509 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
510 510 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
511 511 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
512 512 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
513 513 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
514 514 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 18:1aa84d96232a (esc)
515 515 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
516 516 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
517 517 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
518 518 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
519 519 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
520 520 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
521 521 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 17:44765d7c06e0 (esc)
522 522 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
523 523 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
524 524 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
525 525 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
526 526 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
527 527 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
528 528 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 16:3677d192927d (esc)
529 529 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
530 530 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
531 531 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
532 532 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
533 533 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
534 534 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
535 535 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
536 536 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
537 537 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
538 538 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
539 539 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
540 540 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
541 541 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
542 542 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 14:8eac370358ef (esc)
543 543 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
544 544 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 12:86b91144a6e9 (esc)
545 545 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
546 546 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
547 547 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
548 548 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
549 549 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
550 550 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
551 551 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
552 552 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
553 553 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
554 554 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
555 555 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
556 556 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 12:86b91144a6e9 (esc)
557 557 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
558 558 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 9:7010c0af0a35 (esc)
559 559 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
560 560 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
561 561 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
562 562 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
563 563 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 11:832d76e6bdf2 (esc)
564 564 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 6:b105a072e251 (esc)
565 565 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
566 566 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
567 567 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
568 568 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
569 569 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
570 570 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 10:74c64d036d72 (esc)
571 571 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
572 572 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 6:b105a072e251 (esc)
573 573 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
574 574 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
575 575 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
576 576 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
577 577 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
578 578 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
579 579 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
580 580 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
581 581 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
582 582 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
583 583 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
584 584 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 8:7a0b11f71937 (esc)
585 585 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
586 586 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 7:b632bb1b1224 (esc)
587 587 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
588 588 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
589 589 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
590 590 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
591 591 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
592 592 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
593 593 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
594 594 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
595 595 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
596 596 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
597 597 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
598 598 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 6:b105a072e251 (esc)
599 599 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 2:3d9a33b8d1e1 (esc)
600 600 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
601 601 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
602 602 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
603 603 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
604 604 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
605 605 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 5:4409d547b708 (esc)
606 606 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
607 607 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 4:26a8bac39d9f (esc)
608 608 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
609 609 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
610 610 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (5) expand (esc)
611 611 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
612 612 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 4:26a8bac39d9f (esc)
613 613 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
614 614 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 3:27eef8ed80b4 (esc)
615 615 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
616 616 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:04 1970 +0000 (esc)
617 617 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (4) merge two known; one immediate left, one immediate right (esc)
618 618 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
619 619 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 3:27eef8ed80b4 (esc)
620 620 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
621 621 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:03 1970 +0000 (esc)
622 622 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (3) collapse (esc)
623 623 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
624 624 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 2:3d9a33b8d1e1 (esc)
625 625 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
626 626 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:02 1970 +0000 (esc)
627 627 \xe2\x94\x82 \xe2\x94\x82 summary: (2) collapse (esc)
628 628 \xe2\x94\x82 \xe2\x94\x82 (esc)
629 629 \xe2\x97\x8b \xe2\x94\x82 changeset: 1:6db2ef61d156 (esc)
630 630 \xe2\x94\x82\xe2\x95\xb1 user: test (esc)
631 631 \xe2\x94\x82 date: Thu Jan 01 00:00:01 1970 +0000 (esc)
632 632 \xe2\x94\x82 summary: (1) collapse (esc)
633 633 \xe2\x94\x82 (esc)
634 634 \xe2\x97\x8b changeset: 0:e6eb3150255d (esc)
635 635 user: test
636 636 date: Thu Jan 01 00:00:00 1970 +0000
637 637 summary: (0) root
638 638
639 639 File glog:
640 640 $ hg log -G a
641 641 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
642 642 \xe2\x94\x82 tag: tip (esc)
643 643 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
644 644 \xe2\x94\x82 user: test (esc)
645 645 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
646 646 \xe2\x94\x82 summary: (34) head (esc)
647 647 \xe2\x94\x82 (esc)
648 648 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
649 649 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
650 650 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
651 651 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
652 652 \xe2\x94\x82 \xe2\x94\x82 summary: (33) head (esc)
653 653 \xe2\x94\x82 \xe2\x94\x82 (esc)
654 654 \xe2\x97\x8b \xe2\x94\x82 changeset: 32:d06dffa21a31 (esc)
655 655 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 27:886ed638191b (esc)
656 656 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
657 657 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
658 658 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
659 659 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (32) expand (esc)
660 660 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
661 661 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 31:621d83e11f67 (esc)
662 662 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 21:d42a756af44d (esc)
663 663 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 30:6e11cd4b648f (esc)
664 664 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
665 665 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
666 666 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (31) expand (esc)
667 667 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
668 668 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 30:6e11cd4b648f (esc)
669 669 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
670 670 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 29:cd9bb2be7593 (esc)
671 671 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
672 672 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
673 673 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (30) expand (esc)
674 674 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
675 675 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 29:cd9bb2be7593 (esc)
676 676 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
677 677 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
678 678 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:29 1970 +0000 (esc)
679 679 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (29) regular commit (esc)
680 680 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
681 681 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 28:44ecd0b9ae99 (esc)
682 682 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
683 683 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 26:7f25b6c2f0b9 (esc)
684 684 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
685 685 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
686 686 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (28) merge zero known (esc)
687 687 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
688 688 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 27:886ed638191b (esc)
689 689 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
690 690 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
691 691 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:27 1970 +0000 (esc)
692 692 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (27) collapse (esc)
693 693 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
694 694 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 26:7f25b6c2f0b9 (esc)
695 695 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
696 696 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 25:91da8ed57247 (esc)
697 697 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
698 698 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
699 699 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (26) merge one known; far right (esc)
700 700 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
701 701 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 25:91da8ed57247 (esc)
702 702 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 21:d42a756af44d (esc)
703 703 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 24:a9c19a3d96b7 (esc)
704 704 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
705 705 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
706 706 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (25) merge one known; far left (esc)
707 707 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
708 708 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 24:a9c19a3d96b7 (esc)
709 709 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
710 710 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 23:a01cddf0766d (esc)
711 711 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
712 712 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
713 713 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (24) merge one known; immediate right (esc)
714 714 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
715 715 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 23:a01cddf0766d (esc)
716 716 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
717 717 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 22:e0d9cccacb5d (esc)
718 718 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
719 719 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
720 720 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (23) merge one known; immediate left (esc)
721 721 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
722 722 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 22:e0d9cccacb5d (esc)
723 723 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
724 724 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
725 725 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
726 726 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
727 727 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (22) merge two known; one far left, one far right (esc)
728 728 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
729 729 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 21:d42a756af44d (esc)
730 730 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
731 731 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
732 732 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
733 733 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
734 734 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
735 735 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
736 736 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 changeset: 20:d30ed6450e32 (esc)
737 737 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
738 738 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
739 739 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
740 740 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
741 741 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
742 742 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
743 743 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 19:31ddc2c1573b (esc)
744 744 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
745 745 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
746 746 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
747 747 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
748 748 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
749 749 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
750 750 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 18:1aa84d96232a (esc)
751 751 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
752 752 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
753 753 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
754 754 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
755 755 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
756 756 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
757 757 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 17:44765d7c06e0 (esc)
758 758 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
759 759 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
760 760 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
761 761 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
762 762 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
763 763 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
764 764 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 16:3677d192927d (esc)
765 765 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
766 766 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
767 767 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
768 768 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
769 769 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
770 770 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
771 771 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
772 772 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
773 773 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
774 774 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
775 775 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
776 776 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
777 777 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
778 778 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 14:8eac370358ef (esc)
779 779 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
780 780 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 12:86b91144a6e9 (esc)
781 781 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
782 782 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
783 783 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
784 784 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
785 785 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
786 786 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
787 787 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
788 788 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
789 789 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
790 790 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
791 791 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
792 792 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 12:86b91144a6e9 (esc)
793 793 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
794 794 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 9:7010c0af0a35 (esc)
795 795 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
796 796 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
797 797 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
798 798 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
799 799 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 11:832d76e6bdf2 (esc)
800 800 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 6:b105a072e251 (esc)
801 801 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
802 802 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
803 803 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
804 804 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
805 805 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
806 806 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 10:74c64d036d72 (esc)
807 807 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
808 808 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 6:b105a072e251 (esc)
809 809 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
810 810 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
811 811 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
812 812 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
813 813 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
814 814 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
815 815 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
816 816 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
817 817 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
818 818 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
819 819 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
820 820 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 8:7a0b11f71937 (esc)
821 821 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
822 822 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 7:b632bb1b1224 (esc)
823 823 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
824 824 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
825 825 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
826 826 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
827 827 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
828 828 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
829 829 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
830 830 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
831 831 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
832 832 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
833 833 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
834 834 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 6:b105a072e251 (esc)
835 835 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 2:3d9a33b8d1e1 (esc)
836 836 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
837 837 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
838 838 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
839 839 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
840 840 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
841 841 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 5:4409d547b708 (esc)
842 842 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
843 843 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 4:26a8bac39d9f (esc)
844 844 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
845 845 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
846 846 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (5) expand (esc)
847 847 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
848 848 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 4:26a8bac39d9f (esc)
849 849 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
850 850 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 3:27eef8ed80b4 (esc)
851 851 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
852 852 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:04 1970 +0000 (esc)
853 853 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (4) merge two known; one immediate left, one immediate right (esc)
854 854 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
855 855 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 3:27eef8ed80b4 (esc)
856 856 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
857 857 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:03 1970 +0000 (esc)
858 858 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (3) collapse (esc)
859 859 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
860 860 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 2:3d9a33b8d1e1 (esc)
861 861 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
862 862 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:02 1970 +0000 (esc)
863 863 \xe2\x94\x82 \xe2\x94\x82 summary: (2) collapse (esc)
864 864 \xe2\x94\x82 \xe2\x94\x82 (esc)
865 865 \xe2\x97\x8b \xe2\x94\x82 changeset: 1:6db2ef61d156 (esc)
866 866 \xe2\x94\x82\xe2\x95\xb1 user: test (esc)
867 867 \xe2\x94\x82 date: Thu Jan 01 00:00:01 1970 +0000 (esc)
868 868 \xe2\x94\x82 summary: (1) collapse (esc)
869 869 \xe2\x94\x82 (esc)
870 870 \xe2\x97\x8b changeset: 0:e6eb3150255d (esc)
871 871 user: test
872 872 date: Thu Jan 01 00:00:00 1970 +0000
873 873 summary: (0) root
874 874
875 875 File glog per revset:
876 876
877 877 $ hg log -G -r 'file("a")'
878 878 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
879 879 \xe2\x94\x82 tag: tip (esc)
880 880 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
881 881 \xe2\x94\x82 user: test (esc)
882 882 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
883 883 \xe2\x94\x82 summary: (34) head (esc)
884 884 \xe2\x94\x82 (esc)
885 885 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
886 886 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
887 887 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
888 888 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
889 889 \xe2\x94\x82 \xe2\x94\x82 summary: (33) head (esc)
890 890 \xe2\x94\x82 \xe2\x94\x82 (esc)
891 891 \xe2\x97\x8b \xe2\x94\x82 changeset: 32:d06dffa21a31 (esc)
892 892 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 27:886ed638191b (esc)
893 893 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
894 894 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
895 895 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
896 896 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (32) expand (esc)
897 897 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
898 898 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 31:621d83e11f67 (esc)
899 899 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 21:d42a756af44d (esc)
900 900 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 30:6e11cd4b648f (esc)
901 901 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
902 902 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
903 903 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (31) expand (esc)
904 904 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
905 905 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 30:6e11cd4b648f (esc)
906 906 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
907 907 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 29:cd9bb2be7593 (esc)
908 908 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
909 909 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
910 910 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (30) expand (esc)
911 911 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
912 912 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 29:cd9bb2be7593 (esc)
913 913 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
914 914 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
915 915 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:29 1970 +0000 (esc)
916 916 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (29) regular commit (esc)
917 917 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
918 918 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 28:44ecd0b9ae99 (esc)
919 919 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
920 920 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 26:7f25b6c2f0b9 (esc)
921 921 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
922 922 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
923 923 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (28) merge zero known (esc)
924 924 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
925 925 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 27:886ed638191b (esc)
926 926 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
927 927 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
928 928 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:27 1970 +0000 (esc)
929 929 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (27) collapse (esc)
930 930 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
931 931 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 26:7f25b6c2f0b9 (esc)
932 932 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
933 933 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 25:91da8ed57247 (esc)
934 934 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
935 935 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
936 936 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (26) merge one known; far right (esc)
937 937 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
938 938 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 25:91da8ed57247 (esc)
939 939 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 21:d42a756af44d (esc)
940 940 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 24:a9c19a3d96b7 (esc)
941 941 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
942 942 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
943 943 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (25) merge one known; far left (esc)
944 944 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
945 945 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 24:a9c19a3d96b7 (esc)
946 946 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
947 947 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 23:a01cddf0766d (esc)
948 948 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
949 949 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
950 950 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (24) merge one known; immediate right (esc)
951 951 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
952 952 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 23:a01cddf0766d (esc)
953 953 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
954 954 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 22:e0d9cccacb5d (esc)
955 955 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
956 956 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
957 957 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (23) merge one known; immediate left (esc)
958 958 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
959 959 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 22:e0d9cccacb5d (esc)
960 960 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
961 961 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
962 962 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
963 963 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
964 964 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (22) merge two known; one far left, one far right (esc)
965 965 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
966 966 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 21:d42a756af44d (esc)
967 967 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
968 968 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
969 969 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
970 970 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
971 971 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
972 972 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
973 973 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 changeset: 20:d30ed6450e32 (esc)
974 974 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
975 975 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
976 976 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
977 977 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
978 978 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
979 979 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
980 980 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 19:31ddc2c1573b (esc)
981 981 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
982 982 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
983 983 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
984 984 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
985 985 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
986 986 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
987 987 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 18:1aa84d96232a (esc)
988 988 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
989 989 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
990 990 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
991 991 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
992 992 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
993 993 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
994 994 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 17:44765d7c06e0 (esc)
995 995 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
996 996 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
997 997 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
998 998 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
999 999 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
1000 1000 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1001 1001 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 16:3677d192927d (esc)
1002 1002 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1003 1003 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
1004 1004 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1005 1005 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
1006 1006 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
1007 1007 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1008 1008 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
1009 1009 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
1010 1010 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
1011 1011 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1012 1012 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
1013 1013 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
1014 1014 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1015 1015 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 14:8eac370358ef (esc)
1016 1016 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1017 1017 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 12:86b91144a6e9 (esc)
1018 1018 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1019 1019 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
1020 1020 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
1021 1021 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1022 1022 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
1023 1023 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
1024 1024 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
1025 1025 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1026 1026 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
1027 1027 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
1028 1028 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1029 1029 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 12:86b91144a6e9 (esc)
1030 1030 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
1031 1031 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 9:7010c0af0a35 (esc)
1032 1032 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1033 1033 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
1034 1034 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
1035 1035 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1036 1036 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 11:832d76e6bdf2 (esc)
1037 1037 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 6:b105a072e251 (esc)
1038 1038 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
1039 1039 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1040 1040 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
1041 1041 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
1042 1042 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1043 1043 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 10:74c64d036d72 (esc)
1044 1044 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1045 1045 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 6:b105a072e251 (esc)
1046 1046 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1047 1047 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
1048 1048 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
1049 1049 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1050 1050 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
1051 1051 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
1052 1052 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
1053 1053 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1054 1054 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
1055 1055 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
1056 1056 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1057 1057 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 8:7a0b11f71937 (esc)
1058 1058 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1059 1059 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 7:b632bb1b1224 (esc)
1060 1060 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1061 1061 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
1062 1062 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
1063 1063 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1064 1064 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
1065 1065 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
1066 1066 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
1067 1067 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1068 1068 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
1069 1069 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
1070 1070 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1071 1071 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 6:b105a072e251 (esc)
1072 1072 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 2:3d9a33b8d1e1 (esc)
1073 1073 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
1074 1074 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1075 1075 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
1076 1076 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
1077 1077 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1078 1078 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 5:4409d547b708 (esc)
1079 1079 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
1080 1080 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 4:26a8bac39d9f (esc)
1081 1081 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1082 1082 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
1083 1083 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (5) expand (esc)
1084 1084 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1085 1085 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 4:26a8bac39d9f (esc)
1086 1086 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
1087 1087 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 3:27eef8ed80b4 (esc)
1088 1088 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1089 1089 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:04 1970 +0000 (esc)
1090 1090 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (4) merge two known; one immediate left, one immediate right (esc)
1091 1091 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1092 1092 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 3:27eef8ed80b4 (esc)
1093 1093 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
1094 1094 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:03 1970 +0000 (esc)
1095 1095 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (3) collapse (esc)
1096 1096 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1097 1097 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 2:3d9a33b8d1e1 (esc)
1098 1098 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
1099 1099 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:02 1970 +0000 (esc)
1100 1100 \xe2\x94\x82 \xe2\x94\x82 summary: (2) collapse (esc)
1101 1101 \xe2\x94\x82 \xe2\x94\x82 (esc)
1102 1102 \xe2\x97\x8b \xe2\x94\x82 changeset: 1:6db2ef61d156 (esc)
1103 1103 \xe2\x94\x82\xe2\x95\xb1 user: test (esc)
1104 1104 \xe2\x94\x82 date: Thu Jan 01 00:00:01 1970 +0000 (esc)
1105 1105 \xe2\x94\x82 summary: (1) collapse (esc)
1106 1106 \xe2\x94\x82 (esc)
1107 1107 \xe2\x97\x8b changeset: 0:e6eb3150255d (esc)
1108 1108 user: test
1109 1109 date: Thu Jan 01 00:00:00 1970 +0000
1110 1110 summary: (0) root
1111 1111
1112 1112
1113 1113 File glog per revset (only merges):
1114 1114
1115 1115 $ hg log -G -r 'file("a")' -m
1116 1116 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1117 1117 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
1118 1118 \xe2\x94\x82 \xe2\x94\x86 parent: 31:621d83e11f67 (esc)
1119 1119 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1120 1120 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
1121 1121 \xe2\x94\x82 \xe2\x94\x86 summary: (32) expand (esc)
1122 1122 \xe2\x94\x82 \xe2\x94\x86 (esc)
1123 1123 \xe2\x97\x8b \xe2\x94\x86 changeset: 31:621d83e11f67 (esc)
1124 1124 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc)
1125 1125 \xe2\x94\x82 \xe2\x94\x86 parent: 30:6e11cd4b648f (esc)
1126 1126 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1127 1127 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
1128 1128 \xe2\x94\x82 \xe2\x94\x86 summary: (31) expand (esc)
1129 1129 \xe2\x94\x82 \xe2\x94\x86 (esc)
1130 1130 \xe2\x97\x8b \xe2\x94\x86 changeset: 30:6e11cd4b648f (esc)
1131 1131 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
1132 1132 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 29:cd9bb2be7593 (esc)
1133 1133 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1134 1134 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
1135 1135 \xe2\x94\x82 \xe2\x94\x86 summary: (30) expand (esc)
1136 1136 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1137 1137 \xe2\x97\x8b \xe2\x94\x86 changeset: 28:44ecd0b9ae99 (esc)
1138 1138 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
1139 1139 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 26:7f25b6c2f0b9 (esc)
1140 1140 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1141 1141 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
1142 1142 \xe2\x94\x82 \xe2\x94\x86 summary: (28) merge zero known (esc)
1143 1143 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1144 1144 \xe2\x97\x8b \xe2\x94\x86 changeset: 26:7f25b6c2f0b9 (esc)
1145 1145 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 18:1aa84d96232a (esc)
1146 1146 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 25:91da8ed57247 (esc)
1147 1147 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1148 1148 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
1149 1149 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (26) merge one known; far right (esc)
1150 1150 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc)
1151 1151 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 25:91da8ed57247 (esc)
1152 1152 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc)
1153 1153 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 24:a9c19a3d96b7 (esc)
1154 1154 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1155 1155 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
1156 1156 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (25) merge one known; far left (esc)
1157 1157 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc)
1158 1158 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 24:a9c19a3d96b7 (esc)
1159 1159 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
1160 1160 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 23:a01cddf0766d (esc)
1161 1161 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1162 1162 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
1163 1163 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (24) merge one known; immediate right (esc)
1164 1164 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1165 1165 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 23:a01cddf0766d (esc)
1166 1166 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
1167 1167 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 22:e0d9cccacb5d (esc)
1168 1168 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1169 1169 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
1170 1170 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (23) merge one known; immediate left (esc)
1171 1171 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1172 1172 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 22:e0d9cccacb5d (esc)
1173 1173 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x86\xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
1174 1174 \xe2\x94\x82 \xe2\x94\x86 parent: 21:d42a756af44d (esc)
1175 1175 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1176 1176 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
1177 1177 \xe2\x94\x82 \xe2\x94\x86 summary: (22) merge two known; one far left, one far right (esc)
1178 1178 \xe2\x94\x82 \xe2\x94\x86 (esc)
1179 1179 \xe2\x94\x82 \xe2\x97\x8b changeset: 21:d42a756af44d (esc)
1180 1180 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
1181 1181 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
1182 1182 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1183 1183 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
1184 1184 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
1185 1185 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1186 1186 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 20:d30ed6450e32 (esc)
1187 1187 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1188 1188 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 18:1aa84d96232a (esc)
1189 1189 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1190 1190 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
1191 1191 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
1192 1192 \xe2\x94\x82 \xe2\x94\x82 (esc)
1193 1193 \xe2\x94\x82 \xe2\x97\x8b changeset: 19:31ddc2c1573b (esc)
1194 1194 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
1195 1195 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
1196 1196 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1197 1197 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
1198 1198 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
1199 1199 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1200 1200 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 18:1aa84d96232a (esc)
1201 1201 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
1202 1202 \xe2\x95\xa7 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
1203 1203 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1204 1204 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
1205 1205 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
1206 1206 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
1207 1207 \xe2\x94\x82 \xe2\x97\x8b changeset: 17:44765d7c06e0 (esc)
1208 1208 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
1209 1209 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
1210 1210 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1211 1211 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
1212 1212 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
1213 1213 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1214 1214 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 16:3677d192927d (esc)
1215 1215 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
1216 1216 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x95\xa7 parent: 1:6db2ef61d156 (esc)
1217 1217 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1218 1218 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
1219 1219 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
1220 1220 \xe2\x94\x82 \xe2\x94\x82 (esc)
1221 1221 \xe2\x97\x8b \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
1222 1222 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
1223 1223 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
1224 1224 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1225 1225 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
1226 1226 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
1227 1227 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1228 1228 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 14:8eac370358ef (esc)
1229 1229 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1230 1230 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 12:86b91144a6e9 (esc)
1231 1231 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1232 1232 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
1233 1233 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
1234 1234 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1235 1235 \xe2\x97\x8b \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
1236 1236 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
1237 1237 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
1238 1238 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1239 1239 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
1240 1240 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
1241 1241 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1242 1242 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 12:86b91144a6e9 (esc)
1243 1243 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
1244 1244 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 9:7010c0af0a35 (esc)
1245 1245 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1246 1246 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
1247 1247 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
1248 1248 \xe2\x94\x82 \xe2\x94\x82 (esc)
1249 1249 \xe2\x94\x82 \xe2\x97\x8b changeset: 11:832d76e6bdf2 (esc)
1250 1250 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 6:b105a072e251 (esc)
1251 1251 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
1252 1252 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1253 1253 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
1254 1254 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
1255 1255 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1256 1256 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 10:74c64d036d72 (esc)
1257 1257 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1258 1258 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 6:b105a072e251 (esc)
1259 1259 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1260 1260 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
1261 1261 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
1262 1262 \xe2\x94\x82 \xe2\x94\x82 (esc)
1263 1263 \xe2\x97\x8b \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
1264 1264 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
1265 1265 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
1266 1266 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1267 1267 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
1268 1268 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
1269 1269 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1270 1270 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 8:7a0b11f71937 (esc)
1271 1271 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1272 1272 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 7:b632bb1b1224 (esc)
1273 1273 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1274 1274 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
1275 1275 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
1276 1276 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1277 1277 \xe2\x97\x8b \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
1278 1278 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
1279 1279 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
1280 1280 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1281 1281 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
1282 1282 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
1283 1283 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1284 1284 \xe2\x94\x82 \xe2\x97\x8b changeset: 6:b105a072e251 (esc)
1285 1285 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 2:3d9a33b8d1e1 (esc)
1286 1286 \xe2\x94\x82 \xe2\x95\xa7 parent: 5:4409d547b708 (esc)
1287 1287 \xe2\x94\x82 user: test (esc)
1288 1288 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
1289 1289 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
1290 1290 \xe2\x94\x82 (esc)
1291 1291 \xe2\x97\x8b changeset: 5:4409d547b708 (esc)
1292 1292 \xe2\x94\x82\xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
1293 1293 \xe2\x94\x82 \xe2\x95\xa7 parent: 4:26a8bac39d9f (esc)
1294 1294 \xe2\x94\x82 user: test (esc)
1295 1295 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
1296 1296 \xe2\x94\x82 summary: (5) expand (esc)
1297 1297 \xe2\x94\x82 (esc)
1298 1298 \xe2\x97\x8b changeset: 4:26a8bac39d9f (esc)
1299 1299 \xe2\x94\x82\xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
1300 1300 \xe2\x95\xa7 \xe2\x95\xa7 parent: 3:27eef8ed80b4 (esc)
1301 1301 user: test
1302 1302 date: Thu Jan 01 00:00:04 1970 +0000
1303 1303 summary: (4) merge two known; one immediate left, one immediate right
1304 1304
1305 1305
1306 1306 Empty revision range - display nothing:
1307 1307 $ hg log -G -r 1..0
1308 1308
1309 1309 $ cd ..
1310 1310
1311 1311 #if no-outer-repo
1312 1312
1313 1313 From outer space:
1314 1314 $ hg log -G -l1 repo
1315 1315 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
1316 1316 \xe2\x94\x82 tag: tip (esc)
1317 1317 \xe2\x95\xa7 parent: 32:d06dffa21a31 (esc)
1318 1318 user: test
1319 1319 date: Thu Jan 01 00:00:34 1970 +0000
1320 1320 summary: (34) head
1321 1321
1322 1322 $ hg log -G -l1 repo/a
1323 1323 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
1324 1324 \xe2\x94\x82 tag: tip (esc)
1325 1325 \xe2\x95\xa7 parent: 32:d06dffa21a31 (esc)
1326 1326 user: test
1327 1327 date: Thu Jan 01 00:00:34 1970 +0000
1328 1328 summary: (34) head
1329 1329
1330 1330 $ hg log -G -l1 repo/missing
1331 1331
1332 1332 #endif
1333 1333
1334 1334 File log with revs != cset revs:
1335 1335 $ hg init flog
1336 1336 $ cd flog
1337 1337 $ echo one >one
1338 1338 $ hg add one
1339 1339 $ hg commit -mone
1340 1340 $ echo two >two
1341 1341 $ hg add two
1342 1342 $ hg commit -mtwo
1343 1343 $ echo more >two
1344 1344 $ hg commit -mmore
1345 1345 $ hg log -G two
1346 1346 \xe2\x97\x8d changeset: 2:12c28321755b (esc)
1347 1347 \xe2\x94\x82 tag: tip (esc)
1348 1348 \xe2\x94\x82 user: test (esc)
1349 1349 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
1350 1350 \xe2\x94\x82 summary: more (esc)
1351 1351 \xe2\x94\x82 (esc)
1352 1352 \xe2\x97\x8b changeset: 1:5ac72c0599bf (esc)
1353 1353 \xe2\x94\x82 user: test (esc)
1354 1354 \xe2\x95\xa7 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
1355 1355 summary: two
1356 1356
1357 1357
1358 1358 Issue1896: File log with explicit style
1359 1359 $ hg log -G --style=default one
1360 1360 \xe2\x97\x8b changeset: 0:3d578b4a1f53 (esc)
1361 1361 user: test
1362 1362 date: Thu Jan 01 00:00:00 1970 +0000
1363 1363 summary: one
1364 1364
1365 1365 Issue2395: glog --style header and footer
1366 1366 $ hg log -G --style=xml one
1367 1367 <?xml version="1.0"?>
1368 1368 <log>
1369 1369 \xe2\x97\x8b <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1"> (esc)
1370 1370 <author email="test">test</author>
1371 1371 <date>1970-01-01T00:00:00+00:00</date>
1372 1372 <msg xml:space="preserve">one</msg>
1373 1373 </logentry>
1374 1374 </log>
1375 1375
1376 1376 $ cd ..
1377 1377
1378 1378 Incoming and outgoing:
1379 1379
1380 1380 $ hg clone -U -r31 repo repo2
1381 1381 adding changesets
1382 1382 adding manifests
1383 1383 adding file changes
1384 1384 added 31 changesets with 31 changes to 1 files
1385 1385 new changesets e6eb3150255d:621d83e11f67
1386 1386 $ cd repo2
1387 1387
1388 1388 $ hg incoming --graph ../repo
1389 1389 comparing with ../repo
1390 1390 searching for changes
1391 1391 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1392 1392 \xe2\x94\x82 tag: tip (esc)
1393 1393 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1394 1394 \xe2\x94\x82 user: test (esc)
1395 1395 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
1396 1396 \xe2\x94\x82 summary: (34) head (esc)
1397 1397 \xe2\x94\x82 (esc)
1398 1398 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1399 1399 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1400 1400 \xe2\x94\x82 user: test (esc)
1401 1401 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
1402 1402 \xe2\x94\x82 summary: (33) head (esc)
1403 1403 \xe2\x94\x82 (esc)
1404 1404 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1405 1405 \xe2\x94\x82 parent: 27:886ed638191b (esc)
1406 1406 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
1407 1407 \xe2\x94\x82 user: test (esc)
1408 1408 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
1409 1409 \xe2\x94\x82 summary: (32) expand (esc)
1410 1410 \xe2\x94\x82 (esc)
1411 1411 \xe2\x97\x8b changeset: 27:886ed638191b (esc)
1412 1412 parent: 21:d42a756af44d
1413 1413 user: test
1414 1414 date: Thu Jan 01 00:00:27 1970 +0000
1415 1415 summary: (27) collapse
1416 1416
1417 1417 $ cd ..
1418 1418
1419 1419 $ hg -R repo outgoing --graph repo2
1420 1420 comparing with repo2
1421 1421 searching for changes
1422 1422 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
1423 1423 \xe2\x94\x82 tag: tip (esc)
1424 1424 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1425 1425 \xe2\x94\x82 user: test (esc)
1426 1426 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
1427 1427 \xe2\x94\x82 summary: (34) head (esc)
1428 1428 \xe2\x94\x82 (esc)
1429 1429 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1430 1430 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1431 1431 \xe2\x94\x82 user: test (esc)
1432 1432 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
1433 1433 \xe2\x94\x82 summary: (33) head (esc)
1434 1434 \xe2\x94\x82 (esc)
1435 1435 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1436 1436 \xe2\x94\x82 parent: 27:886ed638191b (esc)
1437 1437 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
1438 1438 \xe2\x94\x82 user: test (esc)
1439 1439 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
1440 1440 \xe2\x94\x82 summary: (32) expand (esc)
1441 1441 \xe2\x94\x82 (esc)
1442 1442 \xe2\x97\x8b changeset: 27:886ed638191b (esc)
1443 1443 parent: 21:d42a756af44d
1444 1444 user: test
1445 1445 date: Thu Jan 01 00:00:27 1970 +0000
1446 1446 summary: (27) collapse
1447 1447
1448 1448
1449 1449 File + limit with revs != cset revs:
1450 1450 $ cd repo
1451 1451 $ touch b
1452 1452 $ hg ci -Aqm0
1453 1453 $ hg log -G -l2 a
1454 1454 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1455 1455 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1456 1456 \xe2\x95\xa7 user: test (esc)
1457 1457 date: Thu Jan 01 00:00:34 1970 +0000
1458 1458 summary: (34) head
1459 1459
1460 1460 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1461 1461 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1462 1462 \xe2\x95\xa7 user: test (esc)
1463 1463 date: Thu Jan 01 00:00:33 1970 +0000
1464 1464 summary: (33) head
1465 1465
1466 1466
1467 1467 File + limit + -ra:b, (b - a) < limit:
1468 1468 $ hg log -G -l3000 -r32:tip a
1469 1469 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1470 1470 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1471 1471 \xe2\x94\x82 user: test (esc)
1472 1472 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
1473 1473 \xe2\x94\x82 summary: (34) head (esc)
1474 1474 \xe2\x94\x82 (esc)
1475 1475 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1476 1476 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1477 1477 \xe2\x94\x82 \xe2\x95\xa7 user: test (esc)
1478 1478 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
1479 1479 \xe2\x94\x82 summary: (33) head (esc)
1480 1480 \xe2\x94\x82 (esc)
1481 1481 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1482 1482 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
1483 1483 \xe2\x95\xa7 \xe2\x95\xa7 parent: 31:621d83e11f67 (esc)
1484 1484 user: test
1485 1485 date: Thu Jan 01 00:00:32 1970 +0000
1486 1486 summary: (32) expand
1487 1487
1488 1488
1489 1489 Point out a common and an uncommon unshown parent
1490 1490
1491 1491 $ hg log -G -r 'rev(8) or rev(9)'
1492 1492 \xe2\x97\x8b changeset: 9:7010c0af0a35 (esc)
1493 1493 \xe2\x94\x82\xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
1494 1494 \xe2\x94\x82 \xe2\x95\xa7 parent: 8:7a0b11f71937 (esc)
1495 1495 \xe2\x94\x82 user: test (esc)
1496 1496 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
1497 1497 \xe2\x94\x82 summary: (9) expand (esc)
1498 1498 \xe2\x94\x82 (esc)
1499 1499 \xe2\x97\x8b changeset: 8:7a0b11f71937 (esc)
1500 1500 \xe2\x94\x82\xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
1501 1501 \xe2\x95\xa7 \xe2\x95\xa7 parent: 7:b632bb1b1224 (esc)
1502 1502 user: test
1503 1503 date: Thu Jan 01 00:00:08 1970 +0000
1504 1504 summary: (8) merge two known; one immediate left, one far right
1505 1505
1506 1506
1507 1507 File + limit + -ra:b, b < tip:
1508 1508
1509 1509 $ hg log -G -l1 -r32:34 a
1510 1510 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1511 1511 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1512 1512 \xe2\x95\xa7 user: test (esc)
1513 1513 date: Thu Jan 01 00:00:34 1970 +0000
1514 1514 summary: (34) head
1515 1515
1516 1516
1517 1517 file(File) + limit + -ra:b, b < tip:
1518 1518
1519 1519 $ hg log -G -l1 -r32:34 -r 'file("a")'
1520 1520 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1521 1521 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1522 1522 \xe2\x95\xa7 user: test (esc)
1523 1523 date: Thu Jan 01 00:00:34 1970 +0000
1524 1524 summary: (34) head
1525 1525
1526 1526
1527 1527 limit(file(File) and a::b), b < tip:
1528 1528
1529 1529 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1530 1530 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1531 1531 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
1532 1532 \xe2\x95\xa7 \xe2\x95\xa7 parent: 31:621d83e11f67 (esc)
1533 1533 user: test
1534 1534 date: Thu Jan 01 00:00:32 1970 +0000
1535 1535 summary: (32) expand
1536 1536
1537 1537
1538 1538 File + limit + -ra:b, b < tip:
1539 1539
1540 1540 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1541 1541
1542 1542 File + limit + -ra:b, b < tip, (b - a) < limit:
1543 1543
1544 1544 $ hg log -G -l10 -r33:34 a
1545 1545 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1546 1546 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1547 1547 \xe2\x95\xa7 user: test (esc)
1548 1548 date: Thu Jan 01 00:00:34 1970 +0000
1549 1549 summary: (34) head
1550 1550
1551 1551 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1552 1552 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1553 1553 \xe2\x95\xa7 user: test (esc)
1554 1554 date: Thu Jan 01 00:00:33 1970 +0000
1555 1555 summary: (33) head
1556 1556
1557 1557
1558 1558 Do not crash or produce strange graphs if history is buggy
1559 1559
1560 1560 $ hg branch branch
1561 1561 marked working directory as branch branch
1562 1562 (branches are permanent and global, did you want a bookmark?)
1563 1563 $ commit 36 "buggy merge: identical parents" 35 35
1564 1564 $ hg log -G -l5
1565 1565 \xe2\x97\x8d changeset: 36:08a19a744424 (esc)
1566 1566 \xe2\x94\x82 branch: branch (esc)
1567 1567 \xe2\x94\x82 tag: tip (esc)
1568 1568 \xe2\x94\x82 parent: 35:9159c3644c5e (esc)
1569 1569 \xe2\x94\x82 parent: 35:9159c3644c5e (esc)
1570 1570 \xe2\x94\x82 user: test (esc)
1571 1571 \xe2\x94\x82 date: Thu Jan 01 00:00:36 1970 +0000 (esc)
1572 1572 \xe2\x94\x82 summary: (36) buggy merge: identical parents (esc)
1573 1573 \xe2\x94\x82 (esc)
1574 1574 \xe2\x97\x8b changeset: 35:9159c3644c5e (esc)
1575 1575 \xe2\x94\x82 user: test (esc)
1576 1576 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
1577 1577 \xe2\x94\x82 summary: 0 (esc)
1578 1578 \xe2\x94\x82 (esc)
1579 1579 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1580 1580 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1581 1581 \xe2\x94\x82 user: test (esc)
1582 1582 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
1583 1583 \xe2\x94\x82 summary: (34) head (esc)
1584 1584 \xe2\x94\x82 (esc)
1585 1585 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1586 1586 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1587 1587 \xe2\x94\x82 \xe2\x95\xa7 user: test (esc)
1588 1588 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
1589 1589 \xe2\x94\x82 summary: (33) head (esc)
1590 1590 \xe2\x94\x82 (esc)
1591 1591 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1592 1592 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
1593 1593 \xe2\x95\xa7 \xe2\x95\xa7 parent: 31:621d83e11f67 (esc)
1594 1594 user: test
1595 1595 date: Thu Jan 01 00:00:32 1970 +0000
1596 1596 summary: (32) expand
1597 1597
1598 1598
1599 1599 Test log -G options
1600 1600
1601 1601 $ testlog() {
1602 1602 > hg log -G --print-revset "$@"
1603 1603 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1604 1604 > | sed 's/.*nodetag/nodetag/' > log.nodes
1605 1605 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1606 1606 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1607 1607 > (cmp log.nodes glog.nodes || diff -u log.nodes glog.nodes) \
1608 1608 > | grep '^[-+@ ]' || :
1609 1609 > }
1610 1610
1611 1611 glog always reorders nodes which explains the difference with log
1612 1612
1613 1613 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1614 1614 ['27', '25', '21', '34', '32', '31']
1615 1615 []
1616 1616 <baseset- [21, 25, 27, 31, 32, 34]>
1617 1617 --- log.nodes * (glob)
1618 1618 +++ glog.nodes * (glob)
1619 1619 @@ -1,6 +1,6 @@
1620 1620 -nodetag 27
1621 1621 -nodetag 25
1622 1622 -nodetag 21
1623 1623 nodetag 34
1624 1624 nodetag 32
1625 1625 nodetag 31
1626 1626 +nodetag 27
1627 1627 +nodetag 25
1628 1628 +nodetag 21
1629 1629 $ testlog -u test -u not-a-user
1630 1630 []
1631 1631 (or
1632 1632 (list
1633 1633 (func
1634 1634 (symbol 'user')
1635 1635 (string 'test'))
1636 1636 (func
1637 1637 (symbol 'user')
1638 1638 (string 'not-a-user'))))
1639 1639 <filteredset
1640 1640 <spanset- 0:37>,
1641 1641 <addset
1642 1642 <filteredset
1643 1643 <fullreposet+ 0:37>,
1644 1644 <user 'test'>>,
1645 1645 <filteredset
1646 1646 <fullreposet+ 0:37>,
1647 1647 <user 'not-a-user'>>>>
1648 1648 $ testlog -b not-a-branch
1649 1649 abort: unknown revision 'not-a-branch'!
1650 1650 abort: unknown revision 'not-a-branch'!
1651 1651 abort: unknown revision 'not-a-branch'!
1652 1652 $ testlog -b 35 -b 36 --only-branch branch
1653 1653 []
1654 1654 (or
1655 1655 (list
1656 1656 (func
1657 1657 (symbol 'branch')
1658 1658 (string 'default'))
1659 1659 (or
1660 1660 (list
1661 1661 (func
1662 1662 (symbol 'branch')
1663 1663 (string 'branch'))
1664 1664 (func
1665 1665 (symbol 'branch')
1666 1666 (string 'branch'))))))
1667 1667 <filteredset
1668 1668 <spanset- 0:37>,
1669 1669 <addset
1670 1670 <filteredset
1671 1671 <fullreposet+ 0:37>,
1672 1672 <branch 'default'>>,
1673 1673 <addset
1674 1674 <filteredset
1675 1675 <fullreposet+ 0:37>,
1676 1676 <branch 'branch'>>,
1677 1677 <filteredset
1678 1678 <fullreposet+ 0:37>,
1679 1679 <branch 'branch'>>>>>
1680 1680 $ testlog -k expand -k merge
1681 1681 []
1682 1682 (or
1683 1683 (list
1684 1684 (func
1685 1685 (symbol 'keyword')
1686 1686 (string 'expand'))
1687 1687 (func
1688 1688 (symbol 'keyword')
1689 1689 (string 'merge'))))
1690 1690 <filteredset
1691 1691 <spanset- 0:37>,
1692 1692 <addset
1693 1693 <filteredset
1694 1694 <fullreposet+ 0:37>,
1695 1695 <keyword 'expand'>>,
1696 1696 <filteredset
1697 1697 <fullreposet+ 0:37>,
1698 1698 <keyword 'merge'>>>>
1699 1699 $ testlog --only-merges
1700 1700 []
1701 1701 (func
1702 1702 (symbol 'merge')
1703 1703 None)
1704 1704 <filteredset
1705 1705 <spanset- 0:37>,
1706 1706 <merge>>
1707 1707 $ testlog --no-merges
1708 1708 []
1709 1709 (not
1710 1710 (func
1711 1711 (symbol 'merge')
1712 1712 None))
1713 1713 <filteredset
1714 1714 <spanset- 0:37>,
1715 1715 <not
1716 1716 <filteredset
1717 1717 <spanset- 0:37>,
1718 1718 <merge>>>>
1719 1719 $ testlog --date '2 0 to 4 0'
1720 1720 []
1721 1721 (func
1722 1722 (symbol 'date')
1723 1723 (string '2 0 to 4 0'))
1724 1724 <filteredset
1725 1725 <spanset- 0:37>,
1726 1726 <date '2 0 to 4 0'>>
1727 1727 $ hg log -G -d 'brace ) in a date'
1728 1728 hg: parse error: invalid date: 'brace ) in a date'
1729 1729 [255]
1730 1730 $ testlog --prune 31 --prune 32
1731 1731 []
1732 1732 (not
1733 1733 (or
1734 1734 (list
1735 1735 (func
1736 1736 (symbol 'ancestors')
1737 1737 (string '31'))
1738 1738 (func
1739 1739 (symbol 'ancestors')
1740 1740 (string '32')))))
1741 1741 <filteredset
1742 1742 <spanset- 0:37>,
1743 1743 <not
1744 1744 <addset
1745 1745 <filteredset
1746 1746 <spanset- 0:37>,
1747 1747 <generatorsetdesc+>>,
1748 1748 <filteredset
1749 1749 <spanset- 0:37>,
1750 1750 <generatorsetdesc+>>>>>
1751 1751
1752 1752 Dedicated repo for --follow and paths filtering. The g is crafted to
1753 1753 have 2 filelog topological heads in a linear changeset graph.
1754 1754
1755 1755 $ cd ..
1756 1756 $ hg init follow
1757 1757 $ cd follow
1758 1758 $ testlog --follow
1759 1759 []
1760 1760 []
1761 1761 <baseset []>
1762 1762 $ testlog -rnull
1763 1763 ['null']
1764 1764 []
1765 1765 <baseset [-1]>
1766 1766 $ echo a > a
1767 1767 $ echo aa > aa
1768 1768 $ echo f > f
1769 1769 $ hg ci -Am "add a" a aa f
1770 1770 $ hg cp a b
1771 1771 $ hg cp f g
1772 1772 $ hg ci -m "copy a b"
1773 1773 $ mkdir dir
1774 1774 $ hg mv b dir
1775 1775 $ echo g >> g
1776 1776 $ echo f >> f
1777 1777 $ hg ci -m "mv b dir/b"
1778 1778 $ hg mv a b
1779 1779 $ hg cp -f f g
1780 1780 $ echo a > d
1781 1781 $ hg add d
1782 1782 $ hg ci -m "mv a b; add d"
1783 1783 $ hg mv dir/b e
1784 1784 $ hg ci -m "mv dir/b e"
1785 1785 $ hg log -G --template '({rev}) {desc|firstline}\n'
1786 1786 \xe2\x97\x8d (4) mv dir/b e (esc)
1787 1787 \xe2\x94\x82 (esc)
1788 1788 \xe2\x97\x8b (3) mv a b; add d (esc)
1789 1789 \xe2\x94\x82 (esc)
1790 1790 \xe2\x97\x8b (2) mv b dir/b (esc)
1791 1791 \xe2\x94\x82 (esc)
1792 1792 \xe2\x97\x8b (1) copy a b (esc)
1793 1793 \xe2\x94\x82 (esc)
1794 1794 \xe2\x97\x8b (0) add a (esc)
1795 1795
1796 1796
1797 1797 $ testlog a
1798 1798 []
1799 1799 (func
1800 1800 (symbol 'filelog')
1801 1801 (string 'a'))
1802 1802 <filteredset
1803 1803 <spanset- 0:5>, set([0])>
1804 1804 $ testlog a b
1805 1805 []
1806 1806 (or
1807 1807 (list
1808 1808 (func
1809 1809 (symbol 'filelog')
1810 1810 (string 'a'))
1811 1811 (func
1812 1812 (symbol 'filelog')
1813 1813 (string 'b'))))
1814 1814 <filteredset
1815 1815 <spanset- 0:5>,
1816 1816 <addset
1817 1817 <baseset+ [0]>,
1818 1818 <baseset+ [1]>>>
1819 1819
1820 1820 Test falling back to slow path for non-existing files
1821 1821
1822 1822 $ testlog a c
1823 1823 []
1824 1824 (func
1825 1825 (symbol '_matchfiles')
1826 1826 (list
1827 1827 (string 'r:')
1828 1828 (string 'd:relpath')
1829 1829 (string 'p:a')
1830 1830 (string 'p:c')))
1831 1831 <filteredset
1832 1832 <spanset- 0:5>,
1833 1833 <matchfiles patterns=['a', 'c'], include=[] exclude=[], default='relpath', rev=2147483647>>
1834 1834
1835 1835 Test multiple --include/--exclude/paths
1836 1836
1837 1837 $ testlog --include a --include e --exclude b --exclude e a e
1838 1838 []
1839 1839 (func
1840 1840 (symbol '_matchfiles')
1841 1841 (list
1842 1842 (string 'r:')
1843 1843 (string 'd:relpath')
1844 1844 (string 'p:a')
1845 1845 (string 'p:e')
1846 1846 (string 'i:a')
1847 1847 (string 'i:e')
1848 1848 (string 'x:b')
1849 1849 (string 'x:e')))
1850 1850 <filteredset
1851 1851 <spanset- 0:5>,
1852 1852 <matchfiles patterns=['a', 'e'], include=['a', 'e'] exclude=['b', 'e'], default='relpath', rev=2147483647>>
1853 1853
1854 1854 Test glob expansion of pats
1855 1855
1856 1856 $ expandglobs=`$PYTHON -c "import mercurial.util; \
1857 1857 > print(mercurial.util.expandglobs and 'true' or 'false')"`
1858 1858 $ if [ $expandglobs = "true" ]; then
1859 1859 > testlog 'a*';
1860 1860 > else
1861 1861 > testlog a*;
1862 1862 > fi;
1863 1863 []
1864 1864 (func
1865 1865 (symbol 'filelog')
1866 1866 (string 'aa'))
1867 1867 <filteredset
1868 1868 <spanset- 0:5>, set([0])>
1869 1869
1870 1870 Test --follow on a non-existent directory
1871 1871
1872 1872 $ testlog -f dir
1873 1873 abort: cannot follow file not in parent revision: "dir"
1874 1874 abort: cannot follow file not in parent revision: "dir"
1875 1875 abort: cannot follow file not in parent revision: "dir"
1876 1876
1877 1877 Test --follow on a directory
1878 1878
1879 1879 $ hg up -q '.^'
1880 1880 $ testlog -f dir
1881 1881 []
1882 1882 (func
1883 1883 (symbol '_matchfiles')
1884 1884 (list
1885 1885 (string 'r:')
1886 1886 (string 'd:relpath')
1887 1887 (string 'p:dir')))
1888 1888 <filteredset
1889 1889 <generatorsetdesc->,
1890 1890 <matchfiles patterns=['dir'], include=[] exclude=[], default='relpath', rev=2147483647>>
1891 1891 $ hg up -q tip
1892 1892
1893 1893 Test --follow on file not in parent revision
1894 1894
1895 1895 $ testlog -f a
1896 1896 abort: cannot follow file not in parent revision: "a"
1897 1897 abort: cannot follow file not in parent revision: "a"
1898 1898 abort: cannot follow file not in parent revision: "a"
1899 1899
1900 1900 Test --follow and patterns
1901 1901
1902 1902 $ testlog -f 'glob:*'
1903 1903 []
1904 1904 (func
1905 1905 (symbol '_matchfiles')
1906 1906 (list
1907 1907 (string 'r:')
1908 1908 (string 'd:relpath')
1909 1909 (string 'p:glob:*')))
1910 1910 <filteredset
1911 1911 <generatorsetdesc->,
1912 1912 <matchfiles patterns=['glob:*'], include=[] exclude=[], default='relpath', rev=2147483647>>
1913 1913
1914 1914 Test --follow on a single rename
1915 1915
1916 1916 $ hg up -q 2
1917 1917 $ testlog -f a
1918 1918 []
1919 1919 []
1920 1920 <generatorsetdesc->
1921 1921
1922 1922 Test --follow and multiple renames
1923 1923
1924 1924 $ hg up -q tip
1925 1925 $ testlog -f e
1926 1926 []
1927 1927 []
1928 1928 <generatorsetdesc->
1929 1929
1930 1930 Test --follow and multiple filelog heads
1931 1931
1932 1932 $ hg up -q 2
1933 1933 $ testlog -f g
1934 1934 []
1935 1935 []
1936 1936 <generatorsetdesc->
1937 1937 $ cat log.nodes
1938 1938 nodetag 2
1939 1939 nodetag 1
1940 1940 nodetag 0
1941 1941 $ hg up -q tip
1942 1942 $ testlog -f g
1943 1943 []
1944 1944 []
1945 1945 <generatorsetdesc->
1946 1946 $ cat log.nodes
1947 1947 nodetag 3
1948 1948 nodetag 2
1949 1949 nodetag 0
1950 1950
1951 1951 Test --follow and multiple files
1952 1952
1953 1953 $ testlog -f g e
1954 1954 []
1955 1955 []
1956 1956 <generatorsetdesc->
1957 1957 $ cat log.nodes
1958 1958 nodetag 4
1959 1959 nodetag 3
1960 1960 nodetag 2
1961 1961 nodetag 1
1962 1962 nodetag 0
1963 1963
1964 1964 Test --follow null parent
1965 1965
1966 1966 $ hg up -q null
1967 1967 $ testlog -f
1968 1968 []
1969 1969 []
1970 1970 <baseset []>
1971 1971
1972 1972 Test --follow-first
1973 1973
1974 1974 $ hg up -q 3
1975 1975 $ echo ee > e
1976 1976 $ hg ci -Am "add another e" e
1977 1977 created new head
1978 1978 $ hg merge --tool internal:other 4
1979 1979 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1980 1980 (branch merge, don't forget to commit)
1981 1981 $ echo merge > e
1982 1982 $ hg ci -m "merge 5 and 4"
1983 1983 $ testlog --follow-first
1984 1984 []
1985 1985 []
1986 1986 <generatorsetdesc->
1987 1987
1988 1988 Cannot compare with log --follow-first FILE as it never worked
1989 1989
1990 1990 $ hg log -G --print-revset --follow-first e
1991 1991 []
1992 1992 []
1993 1993 <generatorsetdesc->
1994 1994 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1995 1995 \xe2\x97\x8d 6 merge 5 and 4 (esc)
1996 1996 \xe2\x94\x82\xe2\x95\xb2 (esc)
1997 1997 \xe2\x94\x82 \xe2\x95\xa7 (esc)
1998 1998 \xe2\x97\x8b 5 add another e (esc)
1999 1999 \xe2\x94\x82 (esc)
2000 2000 \xe2\x95\xa7 (esc)
2001 2001
2002 2002 Test --copies
2003 2003
2004 2004 $ hg log -G --copies --template "{rev} {desc|firstline} \
2005 2005 > copies: {file_copies_switch}\n"
2006 2006 \xe2\x97\x8d 6 merge 5 and 4 copies: (esc)
2007 2007 \xe2\x94\x82\xe2\x95\xb2 (esc)
2008 2008 \xe2\x94\x82 \xe2\x97\x8b 5 add another e copies: (esc)
2009 2009 \xe2\x94\x82 \xe2\x94\x82 (esc)
2010 2010 \xe2\x97\x8b \xe2\x94\x82 4 mv dir/b e copies: e (dir/b) (esc)
2011 2011 \xe2\x94\x82\xe2\x95\xb1 (esc)
2012 2012 \xe2\x97\x8b 3 mv a b; add d copies: b (a)g (f) (esc)
2013 2013 \xe2\x94\x82 (esc)
2014 2014 \xe2\x97\x8b 2 mv b dir/b copies: dir/b (b) (esc)
2015 2015 \xe2\x94\x82 (esc)
2016 2016 \xe2\x97\x8b 1 copy a b copies: b (a)g (f) (esc)
2017 2017 \xe2\x94\x82 (esc)
2018 2018 \xe2\x97\x8b 0 add a copies: (esc)
2019 2019
2020 2020 Test "set:..." and parent revision
2021 2021
2022 2022 $ hg up -q 4
2023 2023 $ testlog "set:copied()"
2024 2024 []
2025 2025 (func
2026 (symbol 'filelog')
2027 (string 'set:copied()'))
2026 (symbol '_matchfiles')
2027 (list
2028 (string 'r:')
2029 (string 'd:relpath')
2030 (string 'p:set:copied()')))
2028 2031 <filteredset
2029 <spanset- 0:7>, set([])>
2032 <spanset- 0:7>,
2033 <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='relpath', rev=2147483647>>
2030 2034 $ testlog --include "set:copied()"
2031 2035 []
2032 []
2033 <spanset- 0:7>
2036 (func
2037 (symbol '_matchfiles')
2038 (list
2039 (string 'r:')
2040 (string 'd:relpath')
2041 (string 'i:set:copied()')))
2042 <filteredset
2043 <spanset- 0:7>,
2044 <matchfiles patterns=[], include=['set:copied()'] exclude=[], default='relpath', rev=2147483647>>
2034 2045 $ testlog -r "sort(file('set:copied()'), -rev)"
2035 2046 ["sort(file('set:copied()'), -rev)"]
2036 2047 []
2037 2048 <filteredset
2038 2049 <fullreposet- 0:7>,
2039 2050 <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='glob', rev=None>>
2040 2051
2041 2052 Test --removed
2042 2053
2043 2054 $ testlog --removed
2044 2055 []
2045 2056 []
2046 2057 <spanset- 0:7>
2047 2058 $ testlog --removed a
2048 2059 []
2049 2060 (func
2050 2061 (symbol '_matchfiles')
2051 2062 (list
2052 2063 (string 'r:')
2053 2064 (string 'd:relpath')
2054 2065 (string 'p:a')))
2055 2066 <filteredset
2056 2067 <spanset- 0:7>,
2057 2068 <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=2147483647>>
2058 2069 $ testlog --removed --follow a
2059 2070 []
2060 2071 (func
2061 2072 (symbol '_matchfiles')
2062 2073 (list
2063 2074 (string 'r:')
2064 2075 (string 'd:relpath')
2065 2076 (string 'p:a')))
2066 2077 <filteredset
2067 2078 <generatorsetdesc->,
2068 2079 <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=2147483647>>
2069 2080
2070 2081 Test --patch and --stat with --follow and --follow-first
2071 2082
2072 2083 $ hg up -q 3
2073 2084 $ hg log -G --git --patch b
2074 2085 \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc)
2075 2086 \xe2\x94\x82 user: test (esc)
2076 2087 \xe2\x95\xa7 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2077 2088 summary: copy a b
2078 2089
2079 2090 diff --git a/a b/b
2080 2091 copy from a
2081 2092 copy to b
2082 2093
2083 2094
2084 2095 $ hg log -G --git --stat b
2085 2096 \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc)
2086 2097 \xe2\x94\x82 user: test (esc)
2087 2098 \xe2\x95\xa7 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2088 2099 summary: copy a b
2089 2100
2090 2101 b | 0
2091 2102 1 files changed, 0 insertions(+), 0 deletions(-)
2092 2103
2093 2104
2094 2105 $ hg log -G --git --patch --follow b
2095 2106 \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc)
2096 2107 \xe2\x94\x82 user: test (esc)
2097 2108 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2098 2109 \xe2\x94\x82 summary: copy a b (esc)
2099 2110 \xe2\x94\x82 (esc)
2100 2111 \xe2\x94\x82 diff --git a/a b/b (esc)
2101 2112 \xe2\x94\x82 copy from a (esc)
2102 2113 \xe2\x94\x82 copy to b (esc)
2103 2114 \xe2\x94\x82 (esc)
2104 2115 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2105 2116 user: test
2106 2117 date: Thu Jan 01 00:00:00 1970 +0000
2107 2118 summary: add a
2108 2119
2109 2120 diff --git a/a b/a
2110 2121 new file mode 100644
2111 2122 --- /dev/null
2112 2123 +++ b/a
2113 2124 @@ -0,0 +1,1 @@
2114 2125 +a
2115 2126
2116 2127
2117 2128 $ hg log -G --git --stat --follow b
2118 2129 \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc)
2119 2130 \xe2\x94\x82 user: test (esc)
2120 2131 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2121 2132 \xe2\x94\x82 summary: copy a b (esc)
2122 2133 \xe2\x94\x82 (esc)
2123 2134 \xe2\x94\x82 b | 0 (esc)
2124 2135 \xe2\x94\x82 1 files changed, 0 insertions(+), 0 deletions(-) (esc)
2125 2136 \xe2\x94\x82 (esc)
2126 2137 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2127 2138 user: test
2128 2139 date: Thu Jan 01 00:00:00 1970 +0000
2129 2140 summary: add a
2130 2141
2131 2142 a | 1 +
2132 2143 1 files changed, 1 insertions(+), 0 deletions(-)
2133 2144
2134 2145
2135 2146 $ hg up -q 6
2136 2147 $ hg log -G --git --patch --follow-first e
2137 2148 \xe2\x97\x8d changeset: 6:fc281d8ff18d (esc)
2138 2149 \xe2\x94\x82\xe2\x95\xb2 tag: tip (esc)
2139 2150 \xe2\x94\x82 \xe2\x95\xa7 parent: 5:99b31f1c2782 (esc)
2140 2151 \xe2\x94\x82 parent: 4:17d952250a9d (esc)
2141 2152 \xe2\x94\x82 user: test (esc)
2142 2153 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2143 2154 \xe2\x94\x82 summary: merge 5 and 4 (esc)
2144 2155 \xe2\x94\x82 (esc)
2145 2156 \xe2\x94\x82 diff --git a/e b/e (esc)
2146 2157 \xe2\x94\x82 --- a/e (esc)
2147 2158 \xe2\x94\x82 +++ b/e (esc)
2148 2159 \xe2\x94\x82 @@ -1,1 +1,1 @@ (esc)
2149 2160 \xe2\x94\x82 -ee (esc)
2150 2161 \xe2\x94\x82 +merge (esc)
2151 2162 \xe2\x94\x82 (esc)
2152 2163 \xe2\x97\x8b changeset: 5:99b31f1c2782 (esc)
2153 2164 \xe2\x94\x82 parent: 3:5918b8d165d1 (esc)
2154 2165 \xe2\x95\xa7 user: test (esc)
2155 2166 date: Thu Jan 01 00:00:00 1970 +0000
2156 2167 summary: add another e
2157 2168
2158 2169 diff --git a/e b/e
2159 2170 new file mode 100644
2160 2171 --- /dev/null
2161 2172 +++ b/e
2162 2173 @@ -0,0 +1,1 @@
2163 2174 +ee
2164 2175
2165 2176
2166 2177 Test old-style --rev
2167 2178
2168 2179 $ hg tag 'foo-bar'
2169 2180 $ testlog -r 'foo-bar'
2170 2181 ['foo-bar']
2171 2182 []
2172 2183 <baseset [6]>
2173 2184
2174 2185 Test --follow and forward --rev
2175 2186
2176 2187 $ hg up -q 6
2177 2188 $ echo g > g
2178 2189 $ hg ci -Am 'add g' g
2179 2190 created new head
2180 2191 $ hg up -q 2
2181 2192 $ hg log -G --template "{rev} {desc|firstline}\n"
2182 2193 \xe2\x97\x8b 8 add g (esc)
2183 2194 \xe2\x94\x82 (esc)
2184 2195 \xe2\x94\x82 \xe2\x97\x8b 7 Added tag foo-bar for changeset fc281d8ff18d (esc)
2185 2196 \xe2\x94\x82\xe2\x95\xb1 (esc)
2186 2197 \xe2\x97\x8b 6 merge 5 and 4 (esc)
2187 2198 \xe2\x94\x82\xe2\x95\xb2 (esc)
2188 2199 \xe2\x94\x82 \xe2\x97\x8b 5 add another e (esc)
2189 2200 \xe2\x94\x82 \xe2\x94\x82 (esc)
2190 2201 \xe2\x97\x8b \xe2\x94\x82 4 mv dir/b e (esc)
2191 2202 \xe2\x94\x82\xe2\x95\xb1 (esc)
2192 2203 \xe2\x97\x8b 3 mv a b; add d (esc)
2193 2204 \xe2\x94\x82 (esc)
2194 2205 \xe2\x97\x8d 2 mv b dir/b (esc)
2195 2206 \xe2\x94\x82 (esc)
2196 2207 \xe2\x97\x8b 1 copy a b (esc)
2197 2208 \xe2\x94\x82 (esc)
2198 2209 \xe2\x97\x8b 0 add a (esc)
2199 2210
2200 2211 $ hg archive -r 7 archive
2201 2212 $ grep changessincelatesttag archive/.hg_archival.txt
2202 2213 changessincelatesttag: 1
2203 2214 $ rm -r archive
2204 2215
2205 2216 changessincelatesttag with no prior tag
2206 2217 $ hg archive -r 4 archive
2207 2218 $ grep changessincelatesttag archive/.hg_archival.txt
2208 2219 changessincelatesttag: 5
2209 2220
2210 2221 $ hg export 'all()'
2211 2222 # HG changeset patch
2212 2223 # User test
2213 2224 # Date 0 0
2214 2225 # Thu Jan 01 00:00:00 1970 +0000
2215 2226 # Node ID f8035bb17114da16215af3436ec5222428ace8ee
2216 2227 # Parent 0000000000000000000000000000000000000000
2217 2228 add a
2218 2229
2219 2230 diff -r 000000000000 -r f8035bb17114 a
2220 2231 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2221 2232 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2222 2233 @@ -0,0 +1,1 @@
2223 2234 +a
2224 2235 diff -r 000000000000 -r f8035bb17114 aa
2225 2236 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2226 2237 +++ b/aa Thu Jan 01 00:00:00 1970 +0000
2227 2238 @@ -0,0 +1,1 @@
2228 2239 +aa
2229 2240 diff -r 000000000000 -r f8035bb17114 f
2230 2241 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2231 2242 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2232 2243 @@ -0,0 +1,1 @@
2233 2244 +f
2234 2245 # HG changeset patch
2235 2246 # User test
2236 2247 # Date 0 0
2237 2248 # Thu Jan 01 00:00:00 1970 +0000
2238 2249 # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92
2239 2250 # Parent f8035bb17114da16215af3436ec5222428ace8ee
2240 2251 copy a b
2241 2252
2242 2253 diff -r f8035bb17114 -r 216d4c92cf98 b
2243 2254 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2244 2255 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2245 2256 @@ -0,0 +1,1 @@
2246 2257 +a
2247 2258 diff -r f8035bb17114 -r 216d4c92cf98 g
2248 2259 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2249 2260 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2250 2261 @@ -0,0 +1,1 @@
2251 2262 +f
2252 2263 # HG changeset patch
2253 2264 # User test
2254 2265 # Date 0 0
2255 2266 # Thu Jan 01 00:00:00 1970 +0000
2256 2267 # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2257 2268 # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92
2258 2269 mv b dir/b
2259 2270
2260 2271 diff -r 216d4c92cf98 -r bb573313a9e8 b
2261 2272 --- a/b Thu Jan 01 00:00:00 1970 +0000
2262 2273 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2263 2274 @@ -1,1 +0,0 @@
2264 2275 -a
2265 2276 diff -r 216d4c92cf98 -r bb573313a9e8 dir/b
2266 2277 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2267 2278 +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000
2268 2279 @@ -0,0 +1,1 @@
2269 2280 +a
2270 2281 diff -r 216d4c92cf98 -r bb573313a9e8 f
2271 2282 --- a/f Thu Jan 01 00:00:00 1970 +0000
2272 2283 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2273 2284 @@ -1,1 +1,2 @@
2274 2285 f
2275 2286 +f
2276 2287 diff -r 216d4c92cf98 -r bb573313a9e8 g
2277 2288 --- a/g Thu Jan 01 00:00:00 1970 +0000
2278 2289 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2279 2290 @@ -1,1 +1,2 @@
2280 2291 f
2281 2292 +g
2282 2293 # HG changeset patch
2283 2294 # User test
2284 2295 # Date 0 0
2285 2296 # Thu Jan 01 00:00:00 1970 +0000
2286 2297 # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2287 2298 # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2288 2299 mv a b; add d
2289 2300
2290 2301 diff -r bb573313a9e8 -r 5918b8d165d1 a
2291 2302 --- a/a Thu Jan 01 00:00:00 1970 +0000
2292 2303 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2293 2304 @@ -1,1 +0,0 @@
2294 2305 -a
2295 2306 diff -r bb573313a9e8 -r 5918b8d165d1 b
2296 2307 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2297 2308 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2298 2309 @@ -0,0 +1,1 @@
2299 2310 +a
2300 2311 diff -r bb573313a9e8 -r 5918b8d165d1 d
2301 2312 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2302 2313 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2303 2314 @@ -0,0 +1,1 @@
2304 2315 +a
2305 2316 diff -r bb573313a9e8 -r 5918b8d165d1 g
2306 2317 --- a/g Thu Jan 01 00:00:00 1970 +0000
2307 2318 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2308 2319 @@ -1,2 +1,2 @@
2309 2320 f
2310 2321 -g
2311 2322 +f
2312 2323 # HG changeset patch
2313 2324 # User test
2314 2325 # Date 0 0
2315 2326 # Thu Jan 01 00:00:00 1970 +0000
2316 2327 # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260
2317 2328 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2318 2329 mv dir/b e
2319 2330
2320 2331 diff -r 5918b8d165d1 -r 17d952250a9d dir/b
2321 2332 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2322 2333 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2323 2334 @@ -1,1 +0,0 @@
2324 2335 -a
2325 2336 diff -r 5918b8d165d1 -r 17d952250a9d e
2326 2337 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2327 2338 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2328 2339 @@ -0,0 +1,1 @@
2329 2340 +a
2330 2341 # HG changeset patch
2331 2342 # User test
2332 2343 # Date 0 0
2333 2344 # Thu Jan 01 00:00:00 1970 +0000
2334 2345 # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b
2335 2346 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2336 2347 add another e
2337 2348
2338 2349 diff -r 5918b8d165d1 -r 99b31f1c2782 e
2339 2350 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2340 2351 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2341 2352 @@ -0,0 +1,1 @@
2342 2353 +ee
2343 2354 # HG changeset patch
2344 2355 # User test
2345 2356 # Date 0 0
2346 2357 # Thu Jan 01 00:00:00 1970 +0000
2347 2358 # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
2348 2359 # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b
2349 2360 # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260
2350 2361 merge 5 and 4
2351 2362
2352 2363 diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
2353 2364 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2354 2365 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2355 2366 @@ -1,1 +0,0 @@
2356 2367 -a
2357 2368 diff -r 99b31f1c2782 -r fc281d8ff18d e
2358 2369 --- a/e Thu Jan 01 00:00:00 1970 +0000
2359 2370 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2360 2371 @@ -1,1 +1,1 @@
2361 2372 -ee
2362 2373 +merge
2363 2374 # HG changeset patch
2364 2375 # User test
2365 2376 # Date 0 0
2366 2377 # Thu Jan 01 00:00:00 1970 +0000
2367 2378 # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
2368 2379 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2369 2380 Added tag foo-bar for changeset fc281d8ff18d
2370 2381
2371 2382 diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
2372 2383 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2373 2384 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2374 2385 @@ -0,0 +1,1 @@
2375 2386 +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
2376 2387 # HG changeset patch
2377 2388 # User test
2378 2389 # Date 0 0
2379 2390 # Thu Jan 01 00:00:00 1970 +0000
2380 2391 # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
2381 2392 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2382 2393 add g
2383 2394
2384 2395 diff -r fc281d8ff18d -r 24c2e826ddeb g
2385 2396 --- a/g Thu Jan 01 00:00:00 1970 +0000
2386 2397 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2387 2398 @@ -1,2 +1,1 @@
2388 2399 -f
2389 2400 -f
2390 2401 +g
2391 2402 $ testlog --follow -r6 -r8 -r5 -r7 -r4
2392 2403 ['6', '8', '5', '7', '4']
2393 2404 []
2394 2405 <generatorsetdesc->
2395 2406
2396 2407 Test --follow-first and forward --rev
2397 2408
2398 2409 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
2399 2410 ['6', '8', '5', '7', '4']
2400 2411 []
2401 2412 <generatorsetdesc->
2402 2413
2403 2414 Test --follow and backward --rev
2404 2415
2405 2416 $ testlog --follow -r6 -r5 -r7 -r8 -r4
2406 2417 ['6', '5', '7', '8', '4']
2407 2418 []
2408 2419 <generatorsetdesc->
2409 2420
2410 2421 Test --follow-first and backward --rev
2411 2422
2412 2423 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2413 2424 ['6', '5', '7', '8', '4']
2414 2425 []
2415 2426 <generatorsetdesc->
2416 2427
2417 2428 Test --follow with --rev of graphlog extension
2418 2429
2419 2430 $ hg --config extensions.graphlog= glog -qfr1
2420 2431 \xe2\x97\x8b 1:216d4c92cf98 (esc)
2421 2432 \xe2\x94\x82 (esc)
2422 2433 \xe2\x97\x8b 0:f8035bb17114 (esc)
2423 2434
2424 2435
2425 2436 Test subdir
2426 2437
2427 2438 $ hg up -q 3
2428 2439 $ cd dir
2429 2440 $ testlog .
2430 2441 []
2431 2442 (func
2432 2443 (symbol '_matchfiles')
2433 2444 (list
2434 2445 (string 'r:')
2435 2446 (string 'd:relpath')
2436 2447 (string 'p:.')))
2437 2448 <filteredset
2438 2449 <spanset- 0:9>,
2439 2450 <matchfiles patterns=['.'], include=[] exclude=[], default='relpath', rev=2147483647>>
2440 2451 $ testlog ../b
2441 2452 []
2442 2453 (func
2443 2454 (symbol 'filelog')
2444 2455 (string '../b'))
2445 2456 <filteredset
2446 2457 <spanset- 0:9>, set([1])>
2447 2458 $ testlog -f ../b
2448 2459 []
2449 2460 []
2450 2461 <generatorsetdesc->
2451 2462 $ cd ..
2452 2463
2453 2464 Test --hidden
2454 2465 (enable obsolete)
2455 2466
2456 2467 $ cat >> $HGRCPATH << EOF
2457 2468 > [experimental]
2458 2469 > evolution.createmarkers=True
2459 2470 > EOF
2460 2471
2461 2472 $ hg debugobsolete `hg id --debug -i -r 8`
2462 2473 obsoleted 1 changesets
2463 2474 $ testlog
2464 2475 []
2465 2476 []
2466 2477 <spanset- 0:9>
2467 2478 $ testlog --hidden
2468 2479 []
2469 2480 []
2470 2481 <spanset- 0:9>
2471 2482 $ hg log -G --template '{rev} {desc}\n'
2472 2483 \xe2\x97\x8b 7 Added tag foo-bar for changeset fc281d8ff18d (esc)
2473 2484 \xe2\x94\x82 (esc)
2474 2485 \xe2\x97\x8b 6 merge 5 and 4 (esc)
2475 2486 \xe2\x94\x82\xe2\x95\xb2 (esc)
2476 2487 \xe2\x94\x82 \xe2\x97\x8b 5 add another e (esc)
2477 2488 \xe2\x94\x82 \xe2\x94\x82 (esc)
2478 2489 \xe2\x97\x8b \xe2\x94\x82 4 mv dir/b e (esc)
2479 2490 \xe2\x94\x82\xe2\x95\xb1 (esc)
2480 2491 \xe2\x97\x8d 3 mv a b; add d (esc)
2481 2492 \xe2\x94\x82 (esc)
2482 2493 \xe2\x97\x8b 2 mv b dir/b (esc)
2483 2494 \xe2\x94\x82 (esc)
2484 2495 \xe2\x97\x8b 1 copy a b (esc)
2485 2496 \xe2\x94\x82 (esc)
2486 2497 \xe2\x97\x8b 0 add a (esc)
2487 2498
2488 2499
2489 2500 A template without trailing newline should do something sane
2490 2501
2491 2502 $ hg log -G -r ::2 --template '{rev} {desc}'
2492 2503 \xe2\x97\x8b 2 mv b dir/b (esc)
2493 2504 \xe2\x94\x82 (esc)
2494 2505 \xe2\x97\x8b 1 copy a b (esc)
2495 2506 \xe2\x94\x82 (esc)
2496 2507 \xe2\x97\x8b 0 add a (esc)
2497 2508
2498 2509
2499 2510 Extra newlines must be preserved
2500 2511
2501 2512 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2502 2513 \xe2\x97\x8b (esc)
2503 2514 \xe2\x94\x82 2 mv b dir/b (esc)
2504 2515 \xe2\x94\x82 (esc)
2505 2516 \xe2\x97\x8b (esc)
2506 2517 \xe2\x94\x82 1 copy a b (esc)
2507 2518 \xe2\x94\x82 (esc)
2508 2519 \xe2\x97\x8b (esc)
2509 2520 0 add a
2510 2521
2511 2522
2512 2523 The almost-empty template should do something sane too ...
2513 2524
2514 2525 $ hg log -G -r ::2 --template '\n'
2515 2526 \xe2\x97\x8b (esc)
2516 2527 \xe2\x94\x82 (esc)
2517 2528 \xe2\x97\x8b (esc)
2518 2529 \xe2\x94\x82 (esc)
2519 2530 \xe2\x97\x8b (esc)
2520 2531
2521 2532
2522 2533 issue3772
2523 2534
2524 2535 $ hg log -G -r :null
2525 2536 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2526 2537 \xe2\x94\x82 user: test (esc)
2527 2538 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2528 2539 \xe2\x94\x82 summary: add a (esc)
2529 2540 \xe2\x94\x82 (esc)
2530 2541 \xe2\x97\x8b changeset: -1:000000000000 (esc)
2531 2542 user:
2532 2543 date: Thu Jan 01 00:00:00 1970 +0000
2533 2544
2534 2545 $ hg log -G -r null:null
2535 2546 \xe2\x97\x8b changeset: -1:000000000000 (esc)
2536 2547 user:
2537 2548 date: Thu Jan 01 00:00:00 1970 +0000
2538 2549
2539 2550
2540 2551 should not draw line down to null due to the magic of fullreposet
2541 2552
2542 2553 $ hg log -G -r 'all()' | tail -6
2543 2554 \xe2\x94\x82 (esc)
2544 2555 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2545 2556 user: test
2546 2557 date: Thu Jan 01 00:00:00 1970 +0000
2547 2558 summary: add a
2548 2559
2549 2560
2550 2561 $ hg log -G -r 'branch(default)' | tail -6
2551 2562 \xe2\x94\x82 (esc)
2552 2563 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2553 2564 user: test
2554 2565 date: Thu Jan 01 00:00:00 1970 +0000
2555 2566 summary: add a
2556 2567
2557 2568
2558 2569 working-directory revision
2559 2570
2560 2571 $ hg log -G -qr '. + wdir()'
2561 2572 \xe2\x97\x8b 2147483647:ffffffffffff (esc)
2562 2573 \xe2\x94\x82 (esc)
2563 2574 \xe2\x97\x8d 3:5918b8d165d1 (esc)
2564 2575 \xe2\x94\x82 (esc)
2565 2576 \xe2\x95\xa7 (esc)
2566 2577
2567 2578 node template with changesetprinter:
2568 2579
2569 2580 $ hg log -Gqr 5:7 --config ui.graphnodetemplate='"{rev}"'
2570 2581 7 7:02dbb8e276b8
2571 2582 \xe2\x94\x82 (esc)
2572 2583 6 6:fc281d8ff18d
2573 2584 \xe2\x94\x82\xe2\x95\xb2 (esc)
2574 2585 \xe2\x94\x82 \xe2\x95\xa7 (esc)
2575 2586 5 5:99b31f1c2782
2576 2587 \xe2\x94\x82 (esc)
2577 2588 \xe2\x95\xa7 (esc)
2578 2589
2579 2590 node template with changesettemplater (shared cache variable):
2580 2591
2581 2592 $ hg log -Gr 5:7 -T '{latesttag % "{rev} {tag}+{distance}"}\n' \
2582 2593 > --config ui.graphnodetemplate='{ifeq(latesttagdistance, 0, "#", graphnode)}'
2583 2594 \xe2\x97\x8b 7 foo-bar+1 (esc)
2584 2595 \xe2\x94\x82 (esc)
2585 2596 # 6 foo-bar+0
2586 2597 \xe2\x94\x82\xe2\x95\xb2 (esc)
2587 2598 \xe2\x94\x82 \xe2\x95\xa7 (esc)
2588 2599 \xe2\x97\x8b 5 null+5 (esc)
2589 2600 \xe2\x94\x82 (esc)
2590 2601 \xe2\x95\xa7 (esc)
2591 2602
2592 2603 label() should just work in node template:
2593 2604
2594 2605 $ hg log -Gqr 7 --config extensions.color= --color=debug \
2595 2606 > --config ui.graphnodetemplate='{label("branch.{branch}", rev)}'
2596 2607 [branch.default\xe2\x94\x827] [log.node|7:02dbb8e276b8] (esc)
2597 2608 \xe2\x94\x82 (esc)
2598 2609 \xe2\x95\xa7 (esc)
2599 2610
2600 2611 $ cd ..
2601 2612
2602 2613 change graph edge styling
2603 2614
2604 2615 $ cd repo
2605 2616
2606 2617 Setting HGPLAIN ignores graphmod styling:
2607 2618
2608 2619 $ HGPLAIN=1 hg log -G -r 'file("a")' -m
2609 2620 @ changeset: 36:08a19a744424
2610 2621 | branch: branch
2611 2622 | tag: tip
2612 2623 | parent: 35:9159c3644c5e
2613 2624 | parent: 35:9159c3644c5e
2614 2625 | user: test
2615 2626 | date: Thu Jan 01 00:00:36 1970 +0000
2616 2627 | summary: (36) buggy merge: identical parents
2617 2628 |
2618 2629 o changeset: 32:d06dffa21a31
2619 2630 |\ parent: 27:886ed638191b
2620 2631 | | parent: 31:621d83e11f67
2621 2632 | | user: test
2622 2633 | | date: Thu Jan 01 00:00:32 1970 +0000
2623 2634 | | summary: (32) expand
2624 2635 | |
2625 2636 o | changeset: 31:621d83e11f67
2626 2637 |\| parent: 21:d42a756af44d
2627 2638 | | parent: 30:6e11cd4b648f
2628 2639 | | user: test
2629 2640 | | date: Thu Jan 01 00:00:31 1970 +0000
2630 2641 | | summary: (31) expand
2631 2642 | |
2632 2643 o | changeset: 30:6e11cd4b648f
2633 2644 |\ \ parent: 28:44ecd0b9ae99
2634 2645 | | | parent: 29:cd9bb2be7593
2635 2646 | | | user: test
2636 2647 | | | date: Thu Jan 01 00:00:30 1970 +0000
2637 2648 | | | summary: (30) expand
2638 2649 | | |
2639 2650 o | | changeset: 28:44ecd0b9ae99
2640 2651 |\ \ \ parent: 1:6db2ef61d156
2641 2652 | | | | parent: 26:7f25b6c2f0b9
2642 2653 | | | | user: test
2643 2654 | | | | date: Thu Jan 01 00:00:28 1970 +0000
2644 2655 | | | | summary: (28) merge zero known
2645 2656 | | | |
2646 2657 o | | | changeset: 26:7f25b6c2f0b9
2647 2658 |\ \ \ \ parent: 18:1aa84d96232a
2648 2659 | | | | | parent: 25:91da8ed57247
2649 2660 | | | | | user: test
2650 2661 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
2651 2662 | | | | | summary: (26) merge one known; far right
2652 2663 | | | | |
2653 2664 | o-----+ changeset: 25:91da8ed57247
2654 2665 | | | | | parent: 21:d42a756af44d
2655 2666 | | | | | parent: 24:a9c19a3d96b7
2656 2667 | | | | | user: test
2657 2668 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
2658 2669 | | | | | summary: (25) merge one known; far left
2659 2670 | | | | |
2660 2671 | o | | | changeset: 24:a9c19a3d96b7
2661 2672 | |\ \ \ \ parent: 0:e6eb3150255d
2662 2673 | | | | | | parent: 23:a01cddf0766d
2663 2674 | | | | | | user: test
2664 2675 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
2665 2676 | | | | | | summary: (24) merge one known; immediate right
2666 2677 | | | | | |
2667 2678 | o---+ | | changeset: 23:a01cddf0766d
2668 2679 | | | | | | parent: 1:6db2ef61d156
2669 2680 | | | | | | parent: 22:e0d9cccacb5d
2670 2681 | | | | | | user: test
2671 2682 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
2672 2683 | | | | | | summary: (23) merge one known; immediate left
2673 2684 | | | | | |
2674 2685 | o-------+ changeset: 22:e0d9cccacb5d
2675 2686 | | | | | | parent: 18:1aa84d96232a
2676 2687 |/ / / / / parent: 21:d42a756af44d
2677 2688 | | | | | user: test
2678 2689 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
2679 2690 | | | | | summary: (22) merge two known; one far left, one far right
2680 2691 | | | | |
2681 2692 | | | | o changeset: 21:d42a756af44d
2682 2693 | | | | |\ parent: 19:31ddc2c1573b
2683 2694 | | | | | | parent: 20:d30ed6450e32
2684 2695 | | | | | | user: test
2685 2696 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
2686 2697 | | | | | | summary: (21) expand
2687 2698 | | | | | |
2688 2699 +-+-------o changeset: 20:d30ed6450e32
2689 2700 | | | | | parent: 0:e6eb3150255d
2690 2701 | | | | | parent: 18:1aa84d96232a
2691 2702 | | | | | user: test
2692 2703 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
2693 2704 | | | | | summary: (20) merge two known; two far right
2694 2705 | | | | |
2695 2706 | | | | o changeset: 19:31ddc2c1573b
2696 2707 | | | | |\ parent: 15:1dda3f72782d
2697 2708 | | | | | | parent: 17:44765d7c06e0
2698 2709 | | | | | | user: test
2699 2710 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
2700 2711 | | | | | | summary: (19) expand
2701 2712 | | | | | |
2702 2713 o---+---+ | changeset: 18:1aa84d96232a
2703 2714 | | | | | parent: 1:6db2ef61d156
2704 2715 / / / / / parent: 15:1dda3f72782d
2705 2716 | | | | | user: test
2706 2717 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
2707 2718 | | | | | summary: (18) merge two known; two far left
2708 2719 | | | | |
2709 2720 | | | | o changeset: 17:44765d7c06e0
2710 2721 | | | | |\ parent: 12:86b91144a6e9
2711 2722 | | | | | | parent: 16:3677d192927d
2712 2723 | | | | | | user: test
2713 2724 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
2714 2725 | | | | | | summary: (17) expand
2715 2726 | | | | | |
2716 2727 +-+-------o changeset: 16:3677d192927d
2717 2728 | | | | | parent: 0:e6eb3150255d
2718 2729 | | | | | parent: 1:6db2ef61d156
2719 2730 | | | | | user: test
2720 2731 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
2721 2732 | | | | | summary: (16) merge two known; one immediate right, one near right
2722 2733 | | | | |
2723 2734 | | | o | changeset: 15:1dda3f72782d
2724 2735 | | | |\ \ parent: 13:22d8966a97e3
2725 2736 | | | | | | parent: 14:8eac370358ef
2726 2737 | | | | | | user: test
2727 2738 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
2728 2739 | | | | | | summary: (15) expand
2729 2740 | | | | | |
2730 2741 +-------o | changeset: 14:8eac370358ef
2731 2742 | | | | |/ parent: 0:e6eb3150255d
2732 2743 | | | | | parent: 12:86b91144a6e9
2733 2744 | | | | | user: test
2734 2745 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
2735 2746 | | | | | summary: (14) merge two known; one immediate right, one far right
2736 2747 | | | | |
2737 2748 | | | o | changeset: 13:22d8966a97e3
2738 2749 | | | |\ \ parent: 9:7010c0af0a35
2739 2750 | | | | | | parent: 11:832d76e6bdf2
2740 2751 | | | | | | user: test
2741 2752 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
2742 2753 | | | | | | summary: (13) expand
2743 2754 | | | | | |
2744 2755 | +---+---o changeset: 12:86b91144a6e9
2745 2756 | | | | | parent: 1:6db2ef61d156
2746 2757 | | | | | parent: 9:7010c0af0a35
2747 2758 | | | | | user: test
2748 2759 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
2749 2760 | | | | | summary: (12) merge two known; one immediate right, one far left
2750 2761 | | | | |
2751 2762 | | | | o changeset: 11:832d76e6bdf2
2752 2763 | | | | |\ parent: 6:b105a072e251
2753 2764 | | | | | | parent: 10:74c64d036d72
2754 2765 | | | | | | user: test
2755 2766 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
2756 2767 | | | | | | summary: (11) expand
2757 2768 | | | | | |
2758 2769 +---------o changeset: 10:74c64d036d72
2759 2770 | | | | |/ parent: 0:e6eb3150255d
2760 2771 | | | | | parent: 6:b105a072e251
2761 2772 | | | | | user: test
2762 2773 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
2763 2774 | | | | | summary: (10) merge two known; one immediate left, one near right
2764 2775 | | | | |
2765 2776 | | | o | changeset: 9:7010c0af0a35
2766 2777 | | | |\ \ parent: 7:b632bb1b1224
2767 2778 | | | | | | parent: 8:7a0b11f71937
2768 2779 | | | | | | user: test
2769 2780 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
2770 2781 | | | | | | summary: (9) expand
2771 2782 | | | | | |
2772 2783 +-------o | changeset: 8:7a0b11f71937
2773 2784 | | | |/ / parent: 0:e6eb3150255d
2774 2785 | | | | | parent: 7:b632bb1b1224
2775 2786 | | | | | user: test
2776 2787 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
2777 2788 | | | | | summary: (8) merge two known; one immediate left, one far right
2778 2789 | | | | |
2779 2790 | | | o | changeset: 7:b632bb1b1224
2780 2791 | | | |\ \ parent: 2:3d9a33b8d1e1
2781 2792 | | | | | | parent: 5:4409d547b708
2782 2793 | | | | | | user: test
2783 2794 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
2784 2795 | | | | | | summary: (7) expand
2785 2796 | | | | | |
2786 2797 | | | +---o changeset: 6:b105a072e251
2787 2798 | | | | |/ parent: 2:3d9a33b8d1e1
2788 2799 | | | | | parent: 5:4409d547b708
2789 2800 | | | | | user: test
2790 2801 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
2791 2802 | | | | | summary: (6) merge two known; one immediate left, one far left
2792 2803 | | | | |
2793 2804 | | | o | changeset: 5:4409d547b708
2794 2805 | | | |\ \ parent: 3:27eef8ed80b4
2795 2806 | | | | | | parent: 4:26a8bac39d9f
2796 2807 | | | | | | user: test
2797 2808 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
2798 2809 | | | | | | summary: (5) expand
2799 2810 | | | | | |
2800 2811 | +---o | | changeset: 4:26a8bac39d9f
2801 2812 | | | |/ / parent: 1:6db2ef61d156
2802 2813 | | | | | parent: 3:27eef8ed80b4
2803 2814 | | | | | user: test
2804 2815 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
2805 2816 | | | | | summary: (4) merge two known; one immediate left, one immediate right
2806 2817 | | | | |
2807 2818
2808 2819 .. unless HGPLAINEXCEPT=graph is set:
2809 2820
2810 2821 $ HGPLAIN=1 HGPLAINEXCEPT=graph hg log -G -r 'file("a")' -m
2811 2822 \xe2\x97\x8d changeset: 36:08a19a744424 (esc)
2812 2823 \xe2\x94\x86 branch: branch (esc)
2813 2824 \xe2\x94\x86 tag: tip (esc)
2814 2825 \xe2\x94\x86 parent: 35:9159c3644c5e (esc)
2815 2826 \xe2\x94\x86 parent: 35:9159c3644c5e (esc)
2816 2827 \xe2\x94\x86 user: test (esc)
2817 2828 \xe2\x94\x86 date: Thu Jan 01 00:00:36 1970 +0000 (esc)
2818 2829 \xe2\x94\x86 summary: (36) buggy merge: identical parents (esc)
2819 2830 \xe2\x94\x86 (esc)
2820 2831 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
2821 2832 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
2822 2833 \xe2\x94\x82 \xe2\x94\x86 parent: 31:621d83e11f67 (esc)
2823 2834 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2824 2835 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
2825 2836 \xe2\x94\x82 \xe2\x94\x86 summary: (32) expand (esc)
2826 2837 \xe2\x94\x82 \xe2\x94\x86 (esc)
2827 2838 \xe2\x97\x8b \xe2\x94\x86 changeset: 31:621d83e11f67 (esc)
2828 2839 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc)
2829 2840 \xe2\x94\x82 \xe2\x94\x86 parent: 30:6e11cd4b648f (esc)
2830 2841 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2831 2842 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
2832 2843 \xe2\x94\x82 \xe2\x94\x86 summary: (31) expand (esc)
2833 2844 \xe2\x94\x82 \xe2\x94\x86 (esc)
2834 2845 \xe2\x97\x8b \xe2\x94\x86 changeset: 30:6e11cd4b648f (esc)
2835 2846 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
2836 2847 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 29:cd9bb2be7593 (esc)
2837 2848 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2838 2849 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
2839 2850 \xe2\x94\x82 \xe2\x94\x86 summary: (30) expand (esc)
2840 2851 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2841 2852 \xe2\x97\x8b \xe2\x94\x86 changeset: 28:44ecd0b9ae99 (esc)
2842 2853 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
2843 2854 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 26:7f25b6c2f0b9 (esc)
2844 2855 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2845 2856 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
2846 2857 \xe2\x94\x82 \xe2\x94\x86 summary: (28) merge zero known (esc)
2847 2858 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2848 2859 \xe2\x97\x8b \xe2\x94\x86 changeset: 26:7f25b6c2f0b9 (esc)
2849 2860 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 18:1aa84d96232a (esc)
2850 2861 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 25:91da8ed57247 (esc)
2851 2862 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2852 2863 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
2853 2864 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (26) merge one known; far right (esc)
2854 2865 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc)
2855 2866 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 25:91da8ed57247 (esc)
2856 2867 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc)
2857 2868 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 24:a9c19a3d96b7 (esc)
2858 2869 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2859 2870 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
2860 2871 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (25) merge one known; far left (esc)
2861 2872 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc)
2862 2873 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 24:a9c19a3d96b7 (esc)
2863 2874 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
2864 2875 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 23:a01cddf0766d (esc)
2865 2876 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2866 2877 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
2867 2878 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (24) merge one known; immediate right (esc)
2868 2879 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2869 2880 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 23:a01cddf0766d (esc)
2870 2881 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
2871 2882 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 22:e0d9cccacb5d (esc)
2872 2883 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2873 2884 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
2874 2885 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (23) merge one known; immediate left (esc)
2875 2886 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2876 2887 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 22:e0d9cccacb5d (esc)
2877 2888 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x86\xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
2878 2889 \xe2\x94\x82 \xe2\x94\x86 parent: 21:d42a756af44d (esc)
2879 2890 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2880 2891 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
2881 2892 \xe2\x94\x82 \xe2\x94\x86 summary: (22) merge two known; one far left, one far right (esc)
2882 2893 \xe2\x94\x82 \xe2\x94\x86 (esc)
2883 2894 \xe2\x94\x82 \xe2\x97\x8b changeset: 21:d42a756af44d (esc)
2884 2895 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
2885 2896 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
2886 2897 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2887 2898 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
2888 2899 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
2889 2900 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2890 2901 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 20:d30ed6450e32 (esc)
2891 2902 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
2892 2903 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 18:1aa84d96232a (esc)
2893 2904 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2894 2905 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
2895 2906 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
2896 2907 \xe2\x94\x82 \xe2\x94\x82 (esc)
2897 2908 \xe2\x94\x82 \xe2\x97\x8b changeset: 19:31ddc2c1573b (esc)
2898 2909 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
2899 2910 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
2900 2911 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2901 2912 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
2902 2913 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
2903 2914 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2904 2915 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 18:1aa84d96232a (esc)
2905 2916 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
2906 2917 \xe2\x95\xa7 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
2907 2918 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2908 2919 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
2909 2920 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
2910 2921 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
2911 2922 \xe2\x94\x82 \xe2\x97\x8b changeset: 17:44765d7c06e0 (esc)
2912 2923 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
2913 2924 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
2914 2925 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2915 2926 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
2916 2927 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
2917 2928 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2918 2929 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 16:3677d192927d (esc)
2919 2930 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
2920 2931 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x95\xa7 parent: 1:6db2ef61d156 (esc)
2921 2932 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2922 2933 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
2923 2934 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
2924 2935 \xe2\x94\x82 \xe2\x94\x82 (esc)
2925 2936 \xe2\x97\x8b \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
2926 2937 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
2927 2938 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
2928 2939 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2929 2940 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
2930 2941 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
2931 2942 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2932 2943 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 14:8eac370358ef (esc)
2933 2944 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 parent: 0:e6eb3150255d (esc)
2934 2945 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 12:86b91144a6e9 (esc)
2935 2946 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2936 2947 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
2937 2948 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
2938 2949 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2939 2950 \xe2\x97\x8b \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
2940 2951 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
2941 2952 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
2942 2953 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2943 2954 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
2944 2955 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
2945 2956 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2946 2957 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 12:86b91144a6e9 (esc)
2947 2958 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
2948 2959 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 9:7010c0af0a35 (esc)
2949 2960 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2950 2961 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
2951 2962 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
2952 2963 \xe2\x94\x82 \xe2\x94\x82 (esc)
2953 2964 \xe2\x94\x82 \xe2\x97\x8b changeset: 11:832d76e6bdf2 (esc)
2954 2965 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 6:b105a072e251 (esc)
2955 2966 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
2956 2967 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2957 2968 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
2958 2969 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
2959 2970 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2960 2971 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 10:74c64d036d72 (esc)
2961 2972 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 0:e6eb3150255d (esc)
2962 2973 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 6:b105a072e251 (esc)
2963 2974 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2964 2975 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
2965 2976 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
2966 2977 \xe2\x94\x82 \xe2\x94\x82 (esc)
2967 2978 \xe2\x97\x8b \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
2968 2979 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
2969 2980 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
2970 2981 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2971 2982 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
2972 2983 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
2973 2984 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2974 2985 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 8:7a0b11f71937 (esc)
2975 2986 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
2976 2987 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 7:b632bb1b1224 (esc)
2977 2988 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2978 2989 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
2979 2990 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
2980 2991 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2981 2992 \xe2\x97\x8b \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
2982 2993 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
2983 2994 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
2984 2995 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2985 2996 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
2986 2997 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
2987 2998 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2988 2999 \xe2\x94\x82 \xe2\x97\x8b changeset: 6:b105a072e251 (esc)
2989 3000 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 2:3d9a33b8d1e1 (esc)
2990 3001 \xe2\x94\x82 \xe2\x95\xa7 parent: 5:4409d547b708 (esc)
2991 3002 \xe2\x94\x82 user: test (esc)
2992 3003 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
2993 3004 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
2994 3005 \xe2\x94\x82 (esc)
2995 3006 \xe2\x97\x8b changeset: 5:4409d547b708 (esc)
2996 3007 \xe2\x94\x82\xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
2997 3008 \xe2\x94\x82 \xe2\x95\xa7 parent: 4:26a8bac39d9f (esc)
2998 3009 \xe2\x94\x82 user: test (esc)
2999 3010 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
3000 3011 \xe2\x94\x82 summary: (5) expand (esc)
3001 3012 \xe2\x94\x82 (esc)
3002 3013 \xe2\x97\x8b changeset: 4:26a8bac39d9f (esc)
3003 3014 \xe2\x94\x82\xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
3004 3015 \xe2\x95\xa7 \xe2\x95\xa7 parent: 3:27eef8ed80b4 (esc)
3005 3016 user: test
3006 3017 date: Thu Jan 01 00:00:04 1970 +0000
3007 3018 summary: (4) merge two known; one immediate left, one immediate right
3008 3019
3009 3020 $ cd ..
3010 3021 $ cd repo
3011 3022
3012 3023 behavior with newlines
3013 3024
3014 3025 $ hg log -G -r ::2 -T '{rev} {desc}'
3015 3026 \xe2\x97\x8b 2 (2) collapse (esc)
3016 3027 \xe2\x94\x82 (esc)
3017 3028 \xe2\x97\x8b 1 (1) collapse (esc)
3018 3029 \xe2\x94\x82 (esc)
3019 3030 \xe2\x97\x8b 0 (0) root (esc)
3020 3031
3021 3032
3022 3033 $ hg log -G -r ::2 -T '{rev} {desc}\n'
3023 3034 \xe2\x97\x8b 2 (2) collapse (esc)
3024 3035 \xe2\x94\x82 (esc)
3025 3036 \xe2\x97\x8b 1 (1) collapse (esc)
3026 3037 \xe2\x94\x82 (esc)
3027 3038 \xe2\x97\x8b 0 (0) root (esc)
3028 3039
3029 3040
3030 3041 $ hg log -G -r ::2 -T '{rev} {desc}\n\n'
3031 3042 \xe2\x97\x8b 2 (2) collapse (esc)
3032 3043 \xe2\x94\x82 (esc)
3033 3044 \xe2\x97\x8b 1 (1) collapse (esc)
3034 3045 \xe2\x94\x82 (esc)
3035 3046 \xe2\x97\x8b 0 (0) root (esc)
3036 3047
3037 3048
3038 3049 $ hg log -G -r ::2 -T '\n{rev} {desc}'
3039 3050 \xe2\x97\x8b (esc)
3040 3051 \xe2\x94\x82 2 (2) collapse (esc)
3041 3052 \xe2\x97\x8b (esc)
3042 3053 \xe2\x94\x82 1 (1) collapse (esc)
3043 3054 \xe2\x97\x8b (esc)
3044 3055 0 (0) root
3045 3056
3046 3057 $ hg log -G -r ::2 -T '{rev} {desc}\n\n\n'
3047 3058 \xe2\x97\x8b 2 (2) collapse (esc)
3048 3059 \xe2\x94\x82 (esc)
3049 3060 \xe2\x94\x82 (esc)
3050 3061 \xe2\x97\x8b 1 (1) collapse (esc)
3051 3062 \xe2\x94\x82 (esc)
3052 3063 \xe2\x94\x82 (esc)
3053 3064 \xe2\x97\x8b 0 (0) root (esc)
3054 3065
3055 3066
3056 3067 $ cd ..
3057 3068
3058 3069 When inserting extra line nodes to handle more than 2 parents, ensure that
3059 3070 the right node styles are used (issue5174):
3060 3071
3061 3072 $ hg init repo-issue5174
3062 3073 $ cd repo-issue5174
3063 3074 $ echo a > f0
3064 3075 $ hg ci -Aqm 0
3065 3076 $ echo a > f1
3066 3077 $ hg ci -Aqm 1
3067 3078 $ echo a > f2
3068 3079 $ hg ci -Aqm 2
3069 3080 $ hg co ".^"
3070 3081 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
3071 3082 $ echo a > f3
3072 3083 $ hg ci -Aqm 3
3073 3084 $ hg co ".^^"
3074 3085 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
3075 3086 $ echo a > f4
3076 3087 $ hg ci -Aqm 4
3077 3088 $ hg merge -r 2
3078 3089 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
3079 3090 (branch merge, don't forget to commit)
3080 3091 $ hg ci -qm 5
3081 3092 $ hg merge -r 3
3082 3093 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3083 3094 (branch merge, don't forget to commit)
3084 3095 $ hg ci -qm 6
3085 3096 $ hg log -G -r '0 | 1 | 2 | 6'
3086 3097 \xe2\x97\x8d changeset: 6:851fe89689ad (esc)
3087 3098 \xe2\x94\x86\xe2\x95\xb2 tag: tip (esc)
3088 3099 \xe2\x94\x86 \xe2\x94\x86 parent: 5:4f1e3cf15f5d (esc)
3089 3100 \xe2\x94\x86 \xe2\x94\x86 parent: 3:b74ba7084d2d (esc)
3090 3101 \xe2\x94\x86 \xe2\x94\x86 user: test (esc)
3091 3102 \xe2\x94\x86 \xe2\x94\x86 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
3092 3103 \xe2\x94\x86 \xe2\x94\x86 summary: 6 (esc)
3093 3104 \xe2\x94\x86 \xe2\x94\x86 (esc)
3094 3105 \xe2\x94\x86 \xe2\x95\xb2 (esc)
3095 3106 \xe2\x94\x86 \xe2\x94\x86\xe2\x95\xb2 (esc)
3096 3107 \xe2\x94\x86 \xe2\x97\x8b \xe2\x94\x86 changeset: 2:3e6599df4cce (esc)
3097 3108 \xe2\x94\x86 \xe2\x94\x86\xe2\x95\xb1 user: test (esc)
3098 3109 \xe2\x94\x86 \xe2\x94\x86 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
3099 3110 \xe2\x94\x86 \xe2\x94\x86 summary: 2 (esc)
3100 3111 \xe2\x94\x86 \xe2\x94\x86 (esc)
3101 3112 \xe2\x94\x86 \xe2\x97\x8b changeset: 1:bd9a55143933 (esc)
3102 3113 \xe2\x94\x86\xe2\x95\xb1 user: test (esc)
3103 3114 \xe2\x94\x86 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
3104 3115 \xe2\x94\x86 summary: 1 (esc)
3105 3116 \xe2\x94\x86 (esc)
3106 3117 \xe2\x97\x8b changeset: 0:870a5edc339c (esc)
3107 3118 user: test
3108 3119 date: Thu Jan 01 00:00:00 1970 +0000
3109 3120 summary: 0
3110 3121
3111 3122
3112 3123 $ cd ..
3113 3124
3114 3125 Multiple roots (issue5440):
3115 3126
3116 3127 $ hg init multiroots
3117 3128 $ cd multiroots
3118 3129 $ cat <<EOF > .hg/hgrc
3119 3130 > [ui]
3120 3131 > logtemplate = '{rev} {desc}\n\n'
3121 3132 > EOF
3122 3133
3123 3134 $ touch foo
3124 3135 $ hg ci -Aqm foo
3125 3136 $ hg co -q null
3126 3137 $ touch bar
3127 3138 $ hg ci -Aqm bar
3128 3139
3129 3140 $ hg log -Gr null:
3130 3141 \xe2\x97\x8d 1 bar (esc)
3131 3142 \xe2\x94\x82 (esc)
3132 3143 \xe2\x94\x82 \xe2\x97\x8b 0 foo (esc)
3133 3144 \xe2\x94\x82\xe2\x95\xb1 (esc)
3134 3145 \xe2\x97\x8b -1 (esc)
3135 3146
3136 3147 $ hg log -Gr null+0
3137 3148 \xe2\x97\x8b 0 foo (esc)
3138 3149 \xe2\x94\x82 (esc)
3139 3150 \xe2\x97\x8b -1 (esc)
3140 3151
3141 3152 $ hg log -Gr null+1
3142 3153 \xe2\x97\x8d 1 bar (esc)
3143 3154 \xe2\x94\x82 (esc)
3144 3155 \xe2\x97\x8b -1 (esc)
3145 3156
3146 3157
3147 3158 $ cd ..
@@ -1,3477 +1,3488 b''
1 1 @ (34) head
2 2 |
3 3 | o (33) head
4 4 | |
5 5 o | (32) expand
6 6 |\ \
7 7 | o \ (31) expand
8 8 | |\ \
9 9 | | o \ (30) expand
10 10 | | |\ \
11 11 | | | o | (29) regular commit
12 12 | | | | |
13 13 | | o | | (28) merge zero known
14 14 | | |\ \ \
15 15 o | | | | | (27) collapse
16 16 |/ / / / /
17 17 | | o---+ (26) merge one known; far right
18 18 | | | | |
19 19 +---o | | (25) merge one known; far left
20 20 | | | | |
21 21 | | o | | (24) merge one known; immediate right
22 22 | | |\| |
23 23 | | o | | (23) merge one known; immediate left
24 24 | |/| | |
25 25 +---o---+ (22) merge two known; one far left, one far right
26 26 | | / /
27 27 o | | | (21) expand
28 28 |\ \ \ \
29 29 | o---+-+ (20) merge two known; two far right
30 30 | / / /
31 31 o | | | (19) expand
32 32 |\ \ \ \
33 33 +---+---o (18) merge two known; two far left
34 34 | | | |
35 35 | o | | (17) expand
36 36 | |\ \ \
37 37 | | o---+ (16) merge two known; one immediate right, one near right
38 38 | | |/ /
39 39 o | | | (15) expand
40 40 |\ \ \ \
41 41 | o-----+ (14) merge two known; one immediate right, one far right
42 42 | |/ / /
43 43 o | | | (13) expand
44 44 |\ \ \ \
45 45 +---o | | (12) merge two known; one immediate right, one far left
46 46 | | |/ /
47 47 | o | | (11) expand
48 48 | |\ \ \
49 49 | | o---+ (10) merge two known; one immediate left, one near right
50 50 | |/ / /
51 51 o | | | (9) expand
52 52 |\ \ \ \
53 53 | o-----+ (8) merge two known; one immediate left, one far right
54 54 |/ / / /
55 55 o | | | (7) expand
56 56 |\ \ \ \
57 57 +---o | | (6) merge two known; one immediate left, one far left
58 58 | |/ / /
59 59 | o | | (5) expand
60 60 | |\ \ \
61 61 | | o | | (4) merge two known; one immediate left, one immediate right
62 62 | |/|/ /
63 63 | o / / (3) collapse
64 64 |/ / /
65 65 o / / (2) collapse
66 66 |/ /
67 67 o / (1) collapse
68 68 |/
69 69 o (0) root
70 70
71 71
72 72 $ commit()
73 73 > {
74 74 > rev=$1
75 75 > msg=$2
76 76 > shift 2
77 77 > if [ "$#" -gt 0 ]; then
78 78 > hg debugsetparents "$@"
79 79 > fi
80 80 > echo $rev > a
81 81 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
82 82 > }
83 83
84 84 $ cat > printrevset.py <<EOF
85 85 > from __future__ import absolute_import
86 86 > from mercurial import (
87 87 > cmdutil,
88 88 > commands,
89 89 > extensions,
90 90 > logcmdutil,
91 91 > revsetlang,
92 92 > smartset,
93 93 > )
94 94 > from mercurial.utils import stringutil
95 95 >
96 96 > def logrevset(repo, pats, opts):
97 97 > revs = logcmdutil._initialrevs(repo, opts)
98 98 > if not revs:
99 99 > return None
100 100 > match, pats, slowpath = logcmdutil._makematcher(repo, revs, pats, opts)
101 101 > return logcmdutil._makerevset(repo, match, pats, slowpath, opts)
102 102 >
103 103 > def uisetup(ui):
104 104 > def printrevset(orig, repo, pats, opts):
105 105 > revs, filematcher = orig(repo, pats, opts)
106 106 > if opts.get(b'print_revset'):
107 107 > expr = logrevset(repo, pats, opts)
108 108 > if expr:
109 109 > tree = revsetlang.parse(expr)
110 110 > tree = revsetlang.analyze(tree)
111 111 > else:
112 112 > tree = []
113 113 > ui = repo.ui
114 114 > ui.write(b'%r\n' % (opts.get(b'rev', []),))
115 115 > ui.write(revsetlang.prettyformat(tree) + b'\n')
116 116 > ui.write(stringutil.prettyrepr(revs) + b'\n')
117 117 > revs = smartset.baseset() # display no revisions
118 118 > return revs, filematcher
119 119 > extensions.wrapfunction(logcmdutil, 'getrevs', printrevset)
120 120 > aliases, entry = cmdutil.findcmd(b'log', commands.table)
121 121 > entry[1].append((b'', b'print-revset', False,
122 122 > b'print generated revset and exit (DEPRECATED)'))
123 123 > EOF
124 124
125 125 $ echo "[extensions]" >> $HGRCPATH
126 126 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
127 127
128 128 $ hg init repo
129 129 $ cd repo
130 130
131 131 Empty repo:
132 132
133 133 $ hg log -G
134 134
135 135
136 136 Building DAG:
137 137
138 138 $ commit 0 "root"
139 139 $ commit 1 "collapse" 0
140 140 $ commit 2 "collapse" 1
141 141 $ commit 3 "collapse" 2
142 142 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
143 143 $ commit 5 "expand" 3 4
144 144 $ commit 6 "merge two known; one immediate left, one far left" 2 5
145 145 $ commit 7 "expand" 2 5
146 146 $ commit 8 "merge two known; one immediate left, one far right" 0 7
147 147 $ commit 9 "expand" 7 8
148 148 $ commit 10 "merge two known; one immediate left, one near right" 0 6
149 149 $ commit 11 "expand" 6 10
150 150 $ commit 12 "merge two known; one immediate right, one far left" 1 9
151 151 $ commit 13 "expand" 9 11
152 152 $ commit 14 "merge two known; one immediate right, one far right" 0 12
153 153 $ commit 15 "expand" 13 14
154 154 $ commit 16 "merge two known; one immediate right, one near right" 0 1
155 155 $ commit 17 "expand" 12 16
156 156 $ commit 18 "merge two known; two far left" 1 15
157 157 $ commit 19 "expand" 15 17
158 158 $ commit 20 "merge two known; two far right" 0 18
159 159 $ commit 21 "expand" 19 20
160 160 $ commit 22 "merge two known; one far left, one far right" 18 21
161 161 $ commit 23 "merge one known; immediate left" 1 22
162 162 $ commit 24 "merge one known; immediate right" 0 23
163 163 $ commit 25 "merge one known; far left" 21 24
164 164 $ commit 26 "merge one known; far right" 18 25
165 165 $ commit 27 "collapse" 21
166 166 $ commit 28 "merge zero known" 1 26
167 167 $ commit 29 "regular commit" 0
168 168 $ commit 30 "expand" 28 29
169 169 $ commit 31 "expand" 21 30
170 170 $ commit 32 "expand" 27 31
171 171 $ commit 33 "head" 18
172 172 $ commit 34 "head" 32
173 173
174 174
175 175 $ hg log -G -q
176 176 @ 34:fea3ac5810e0
177 177 |
178 178 | o 33:68608f5145f9
179 179 | |
180 180 o | 32:d06dffa21a31
181 181 |\ \
182 182 | o \ 31:621d83e11f67
183 183 | |\ \
184 184 | | o \ 30:6e11cd4b648f
185 185 | | |\ \
186 186 | | | o | 29:cd9bb2be7593
187 187 | | | | |
188 188 | | o | | 28:44ecd0b9ae99
189 189 | | |\ \ \
190 190 o | | | | | 27:886ed638191b
191 191 |/ / / / /
192 192 | | o---+ 26:7f25b6c2f0b9
193 193 | | | | |
194 194 +---o | | 25:91da8ed57247
195 195 | | | | |
196 196 | | o | | 24:a9c19a3d96b7
197 197 | | |\| |
198 198 | | o | | 23:a01cddf0766d
199 199 | |/| | |
200 200 +---o---+ 22:e0d9cccacb5d
201 201 | | / /
202 202 o | | | 21:d42a756af44d
203 203 |\ \ \ \
204 204 | o---+-+ 20:d30ed6450e32
205 205 | / / /
206 206 o | | | 19:31ddc2c1573b
207 207 |\ \ \ \
208 208 +---+---o 18:1aa84d96232a
209 209 | | | |
210 210 | o | | 17:44765d7c06e0
211 211 | |\ \ \
212 212 | | o---+ 16:3677d192927d
213 213 | | |/ /
214 214 o | | | 15:1dda3f72782d
215 215 |\ \ \ \
216 216 | o-----+ 14:8eac370358ef
217 217 | |/ / /
218 218 o | | | 13:22d8966a97e3
219 219 |\ \ \ \
220 220 +---o | | 12:86b91144a6e9
221 221 | | |/ /
222 222 | o | | 11:832d76e6bdf2
223 223 | |\ \ \
224 224 | | o---+ 10:74c64d036d72
225 225 | |/ / /
226 226 o | | | 9:7010c0af0a35
227 227 |\ \ \ \
228 228 | o-----+ 8:7a0b11f71937
229 229 |/ / / /
230 230 o | | | 7:b632bb1b1224
231 231 |\ \ \ \
232 232 +---o | | 6:b105a072e251
233 233 | |/ / /
234 234 | o | | 5:4409d547b708
235 235 | |\ \ \
236 236 | | o | | 4:26a8bac39d9f
237 237 | |/|/ /
238 238 | o / / 3:27eef8ed80b4
239 239 |/ / /
240 240 o / / 2:3d9a33b8d1e1
241 241 |/ /
242 242 o / 1:6db2ef61d156
243 243 |/
244 244 o 0:e6eb3150255d
245 245
246 246
247 247 $ hg log -G
248 248 @ changeset: 34:fea3ac5810e0
249 249 | tag: tip
250 250 | parent: 32:d06dffa21a31
251 251 | user: test
252 252 | date: Thu Jan 01 00:00:34 1970 +0000
253 253 | summary: (34) head
254 254 |
255 255 | o changeset: 33:68608f5145f9
256 256 | | parent: 18:1aa84d96232a
257 257 | | user: test
258 258 | | date: Thu Jan 01 00:00:33 1970 +0000
259 259 | | summary: (33) head
260 260 | |
261 261 o | changeset: 32:d06dffa21a31
262 262 |\ \ parent: 27:886ed638191b
263 263 | | | parent: 31:621d83e11f67
264 264 | | | user: test
265 265 | | | date: Thu Jan 01 00:00:32 1970 +0000
266 266 | | | summary: (32) expand
267 267 | | |
268 268 | o | changeset: 31:621d83e11f67
269 269 | |\ \ parent: 21:d42a756af44d
270 270 | | | | parent: 30:6e11cd4b648f
271 271 | | | | user: test
272 272 | | | | date: Thu Jan 01 00:00:31 1970 +0000
273 273 | | | | summary: (31) expand
274 274 | | | |
275 275 | | o | changeset: 30:6e11cd4b648f
276 276 | | |\ \ parent: 28:44ecd0b9ae99
277 277 | | | | | parent: 29:cd9bb2be7593
278 278 | | | | | user: test
279 279 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
280 280 | | | | | summary: (30) expand
281 281 | | | | |
282 282 | | | o | changeset: 29:cd9bb2be7593
283 283 | | | | | parent: 0:e6eb3150255d
284 284 | | | | | user: test
285 285 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
286 286 | | | | | summary: (29) regular commit
287 287 | | | | |
288 288 | | o | | changeset: 28:44ecd0b9ae99
289 289 | | |\ \ \ parent: 1:6db2ef61d156
290 290 | | | | | | parent: 26:7f25b6c2f0b9
291 291 | | | | | | user: test
292 292 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
293 293 | | | | | | summary: (28) merge zero known
294 294 | | | | | |
295 295 o | | | | | changeset: 27:886ed638191b
296 296 |/ / / / / parent: 21:d42a756af44d
297 297 | | | | | user: test
298 298 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
299 299 | | | | | summary: (27) collapse
300 300 | | | | |
301 301 | | o---+ changeset: 26:7f25b6c2f0b9
302 302 | | | | | parent: 18:1aa84d96232a
303 303 | | | | | parent: 25:91da8ed57247
304 304 | | | | | user: test
305 305 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
306 306 | | | | | summary: (26) merge one known; far right
307 307 | | | | |
308 308 +---o | | changeset: 25:91da8ed57247
309 309 | | | | | parent: 21:d42a756af44d
310 310 | | | | | parent: 24:a9c19a3d96b7
311 311 | | | | | user: test
312 312 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
313 313 | | | | | summary: (25) merge one known; far left
314 314 | | | | |
315 315 | | o | | changeset: 24:a9c19a3d96b7
316 316 | | |\| | parent: 0:e6eb3150255d
317 317 | | | | | parent: 23:a01cddf0766d
318 318 | | | | | user: test
319 319 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
320 320 | | | | | summary: (24) merge one known; immediate right
321 321 | | | | |
322 322 | | o | | changeset: 23:a01cddf0766d
323 323 | |/| | | parent: 1:6db2ef61d156
324 324 | | | | | parent: 22:e0d9cccacb5d
325 325 | | | | | user: test
326 326 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
327 327 | | | | | summary: (23) merge one known; immediate left
328 328 | | | | |
329 329 +---o---+ changeset: 22:e0d9cccacb5d
330 330 | | | | parent: 18:1aa84d96232a
331 331 | | / / parent: 21:d42a756af44d
332 332 | | | | user: test
333 333 | | | | date: Thu Jan 01 00:00:22 1970 +0000
334 334 | | | | summary: (22) merge two known; one far left, one far right
335 335 | | | |
336 336 o | | | changeset: 21:d42a756af44d
337 337 |\ \ \ \ parent: 19:31ddc2c1573b
338 338 | | | | | parent: 20:d30ed6450e32
339 339 | | | | | user: test
340 340 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
341 341 | | | | | summary: (21) expand
342 342 | | | | |
343 343 | o---+-+ changeset: 20:d30ed6450e32
344 344 | | | | parent: 0:e6eb3150255d
345 345 | / / / parent: 18:1aa84d96232a
346 346 | | | | user: test
347 347 | | | | date: Thu Jan 01 00:00:20 1970 +0000
348 348 | | | | summary: (20) merge two known; two far right
349 349 | | | |
350 350 o | | | changeset: 19:31ddc2c1573b
351 351 |\ \ \ \ parent: 15:1dda3f72782d
352 352 | | | | | parent: 17:44765d7c06e0
353 353 | | | | | user: test
354 354 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
355 355 | | | | | summary: (19) expand
356 356 | | | | |
357 357 +---+---o changeset: 18:1aa84d96232a
358 358 | | | | parent: 1:6db2ef61d156
359 359 | | | | parent: 15:1dda3f72782d
360 360 | | | | user: test
361 361 | | | | date: Thu Jan 01 00:00:18 1970 +0000
362 362 | | | | summary: (18) merge two known; two far left
363 363 | | | |
364 364 | o | | changeset: 17:44765d7c06e0
365 365 | |\ \ \ parent: 12:86b91144a6e9
366 366 | | | | | parent: 16:3677d192927d
367 367 | | | | | user: test
368 368 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
369 369 | | | | | summary: (17) expand
370 370 | | | | |
371 371 | | o---+ changeset: 16:3677d192927d
372 372 | | | | | parent: 0:e6eb3150255d
373 373 | | |/ / parent: 1:6db2ef61d156
374 374 | | | | user: test
375 375 | | | | date: Thu Jan 01 00:00:16 1970 +0000
376 376 | | | | summary: (16) merge two known; one immediate right, one near right
377 377 | | | |
378 378 o | | | changeset: 15:1dda3f72782d
379 379 |\ \ \ \ parent: 13:22d8966a97e3
380 380 | | | | | parent: 14:8eac370358ef
381 381 | | | | | user: test
382 382 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
383 383 | | | | | summary: (15) expand
384 384 | | | | |
385 385 | o-----+ changeset: 14:8eac370358ef
386 386 | | | | | parent: 0:e6eb3150255d
387 387 | |/ / / parent: 12:86b91144a6e9
388 388 | | | | user: test
389 389 | | | | date: Thu Jan 01 00:00:14 1970 +0000
390 390 | | | | summary: (14) merge two known; one immediate right, one far right
391 391 | | | |
392 392 o | | | changeset: 13:22d8966a97e3
393 393 |\ \ \ \ parent: 9:7010c0af0a35
394 394 | | | | | parent: 11:832d76e6bdf2
395 395 | | | | | user: test
396 396 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
397 397 | | | | | summary: (13) expand
398 398 | | | | |
399 399 +---o | | changeset: 12:86b91144a6e9
400 400 | | |/ / parent: 1:6db2ef61d156
401 401 | | | | parent: 9:7010c0af0a35
402 402 | | | | user: test
403 403 | | | | date: Thu Jan 01 00:00:12 1970 +0000
404 404 | | | | summary: (12) merge two known; one immediate right, one far left
405 405 | | | |
406 406 | o | | changeset: 11:832d76e6bdf2
407 407 | |\ \ \ parent: 6:b105a072e251
408 408 | | | | | parent: 10:74c64d036d72
409 409 | | | | | user: test
410 410 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
411 411 | | | | | summary: (11) expand
412 412 | | | | |
413 413 | | o---+ changeset: 10:74c64d036d72
414 414 | | | | | parent: 0:e6eb3150255d
415 415 | |/ / / parent: 6:b105a072e251
416 416 | | | | user: test
417 417 | | | | date: Thu Jan 01 00:00:10 1970 +0000
418 418 | | | | summary: (10) merge two known; one immediate left, one near right
419 419 | | | |
420 420 o | | | changeset: 9:7010c0af0a35
421 421 |\ \ \ \ parent: 7:b632bb1b1224
422 422 | | | | | parent: 8:7a0b11f71937
423 423 | | | | | user: test
424 424 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
425 425 | | | | | summary: (9) expand
426 426 | | | | |
427 427 | o-----+ changeset: 8:7a0b11f71937
428 428 | | | | | parent: 0:e6eb3150255d
429 429 |/ / / / parent: 7:b632bb1b1224
430 430 | | | | user: test
431 431 | | | | date: Thu Jan 01 00:00:08 1970 +0000
432 432 | | | | summary: (8) merge two known; one immediate left, one far right
433 433 | | | |
434 434 o | | | changeset: 7:b632bb1b1224
435 435 |\ \ \ \ parent: 2:3d9a33b8d1e1
436 436 | | | | | parent: 5:4409d547b708
437 437 | | | | | user: test
438 438 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
439 439 | | | | | summary: (7) expand
440 440 | | | | |
441 441 +---o | | changeset: 6:b105a072e251
442 442 | |/ / / parent: 2:3d9a33b8d1e1
443 443 | | | | parent: 5:4409d547b708
444 444 | | | | user: test
445 445 | | | | date: Thu Jan 01 00:00:06 1970 +0000
446 446 | | | | summary: (6) merge two known; one immediate left, one far left
447 447 | | | |
448 448 | o | | changeset: 5:4409d547b708
449 449 | |\ \ \ parent: 3:27eef8ed80b4
450 450 | | | | | parent: 4:26a8bac39d9f
451 451 | | | | | user: test
452 452 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
453 453 | | | | | summary: (5) expand
454 454 | | | | |
455 455 | | o | | changeset: 4:26a8bac39d9f
456 456 | |/|/ / parent: 1:6db2ef61d156
457 457 | | | | parent: 3:27eef8ed80b4
458 458 | | | | user: test
459 459 | | | | date: Thu Jan 01 00:00:04 1970 +0000
460 460 | | | | summary: (4) merge two known; one immediate left, one immediate right
461 461 | | | |
462 462 | o | | changeset: 3:27eef8ed80b4
463 463 |/ / / user: test
464 464 | | | date: Thu Jan 01 00:00:03 1970 +0000
465 465 | | | summary: (3) collapse
466 466 | | |
467 467 o | | changeset: 2:3d9a33b8d1e1
468 468 |/ / user: test
469 469 | | date: Thu Jan 01 00:00:02 1970 +0000
470 470 | | summary: (2) collapse
471 471 | |
472 472 o | changeset: 1:6db2ef61d156
473 473 |/ user: test
474 474 | date: Thu Jan 01 00:00:01 1970 +0000
475 475 | summary: (1) collapse
476 476 |
477 477 o changeset: 0:e6eb3150255d
478 478 user: test
479 479 date: Thu Jan 01 00:00:00 1970 +0000
480 480 summary: (0) root
481 481
482 482
483 483 File glog:
484 484 $ hg log -G a
485 485 @ changeset: 34:fea3ac5810e0
486 486 | tag: tip
487 487 | parent: 32:d06dffa21a31
488 488 | user: test
489 489 | date: Thu Jan 01 00:00:34 1970 +0000
490 490 | summary: (34) head
491 491 |
492 492 | o changeset: 33:68608f5145f9
493 493 | | parent: 18:1aa84d96232a
494 494 | | user: test
495 495 | | date: Thu Jan 01 00:00:33 1970 +0000
496 496 | | summary: (33) head
497 497 | |
498 498 o | changeset: 32:d06dffa21a31
499 499 |\ \ parent: 27:886ed638191b
500 500 | | | parent: 31:621d83e11f67
501 501 | | | user: test
502 502 | | | date: Thu Jan 01 00:00:32 1970 +0000
503 503 | | | summary: (32) expand
504 504 | | |
505 505 | o | changeset: 31:621d83e11f67
506 506 | |\ \ parent: 21:d42a756af44d
507 507 | | | | parent: 30:6e11cd4b648f
508 508 | | | | user: test
509 509 | | | | date: Thu Jan 01 00:00:31 1970 +0000
510 510 | | | | summary: (31) expand
511 511 | | | |
512 512 | | o | changeset: 30:6e11cd4b648f
513 513 | | |\ \ parent: 28:44ecd0b9ae99
514 514 | | | | | parent: 29:cd9bb2be7593
515 515 | | | | | user: test
516 516 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
517 517 | | | | | summary: (30) expand
518 518 | | | | |
519 519 | | | o | changeset: 29:cd9bb2be7593
520 520 | | | | | parent: 0:e6eb3150255d
521 521 | | | | | user: test
522 522 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
523 523 | | | | | summary: (29) regular commit
524 524 | | | | |
525 525 | | o | | changeset: 28:44ecd0b9ae99
526 526 | | |\ \ \ parent: 1:6db2ef61d156
527 527 | | | | | | parent: 26:7f25b6c2f0b9
528 528 | | | | | | user: test
529 529 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
530 530 | | | | | | summary: (28) merge zero known
531 531 | | | | | |
532 532 o | | | | | changeset: 27:886ed638191b
533 533 |/ / / / / parent: 21:d42a756af44d
534 534 | | | | | user: test
535 535 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
536 536 | | | | | summary: (27) collapse
537 537 | | | | |
538 538 | | o---+ changeset: 26:7f25b6c2f0b9
539 539 | | | | | parent: 18:1aa84d96232a
540 540 | | | | | parent: 25:91da8ed57247
541 541 | | | | | user: test
542 542 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
543 543 | | | | | summary: (26) merge one known; far right
544 544 | | | | |
545 545 +---o | | changeset: 25:91da8ed57247
546 546 | | | | | parent: 21:d42a756af44d
547 547 | | | | | parent: 24:a9c19a3d96b7
548 548 | | | | | user: test
549 549 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
550 550 | | | | | summary: (25) merge one known; far left
551 551 | | | | |
552 552 | | o | | changeset: 24:a9c19a3d96b7
553 553 | | |\| | parent: 0:e6eb3150255d
554 554 | | | | | parent: 23:a01cddf0766d
555 555 | | | | | user: test
556 556 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
557 557 | | | | | summary: (24) merge one known; immediate right
558 558 | | | | |
559 559 | | o | | changeset: 23:a01cddf0766d
560 560 | |/| | | parent: 1:6db2ef61d156
561 561 | | | | | parent: 22:e0d9cccacb5d
562 562 | | | | | user: test
563 563 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
564 564 | | | | | summary: (23) merge one known; immediate left
565 565 | | | | |
566 566 +---o---+ changeset: 22:e0d9cccacb5d
567 567 | | | | parent: 18:1aa84d96232a
568 568 | | / / parent: 21:d42a756af44d
569 569 | | | | user: test
570 570 | | | | date: Thu Jan 01 00:00:22 1970 +0000
571 571 | | | | summary: (22) merge two known; one far left, one far right
572 572 | | | |
573 573 o | | | changeset: 21:d42a756af44d
574 574 |\ \ \ \ parent: 19:31ddc2c1573b
575 575 | | | | | parent: 20:d30ed6450e32
576 576 | | | | | user: test
577 577 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
578 578 | | | | | summary: (21) expand
579 579 | | | | |
580 580 | o---+-+ changeset: 20:d30ed6450e32
581 581 | | | | parent: 0:e6eb3150255d
582 582 | / / / parent: 18:1aa84d96232a
583 583 | | | | user: test
584 584 | | | | date: Thu Jan 01 00:00:20 1970 +0000
585 585 | | | | summary: (20) merge two known; two far right
586 586 | | | |
587 587 o | | | changeset: 19:31ddc2c1573b
588 588 |\ \ \ \ parent: 15:1dda3f72782d
589 589 | | | | | parent: 17:44765d7c06e0
590 590 | | | | | user: test
591 591 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
592 592 | | | | | summary: (19) expand
593 593 | | | | |
594 594 +---+---o changeset: 18:1aa84d96232a
595 595 | | | | parent: 1:6db2ef61d156
596 596 | | | | parent: 15:1dda3f72782d
597 597 | | | | user: test
598 598 | | | | date: Thu Jan 01 00:00:18 1970 +0000
599 599 | | | | summary: (18) merge two known; two far left
600 600 | | | |
601 601 | o | | changeset: 17:44765d7c06e0
602 602 | |\ \ \ parent: 12:86b91144a6e9
603 603 | | | | | parent: 16:3677d192927d
604 604 | | | | | user: test
605 605 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
606 606 | | | | | summary: (17) expand
607 607 | | | | |
608 608 | | o---+ changeset: 16:3677d192927d
609 609 | | | | | parent: 0:e6eb3150255d
610 610 | | |/ / parent: 1:6db2ef61d156
611 611 | | | | user: test
612 612 | | | | date: Thu Jan 01 00:00:16 1970 +0000
613 613 | | | | summary: (16) merge two known; one immediate right, one near right
614 614 | | | |
615 615 o | | | changeset: 15:1dda3f72782d
616 616 |\ \ \ \ parent: 13:22d8966a97e3
617 617 | | | | | parent: 14:8eac370358ef
618 618 | | | | | user: test
619 619 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
620 620 | | | | | summary: (15) expand
621 621 | | | | |
622 622 | o-----+ changeset: 14:8eac370358ef
623 623 | | | | | parent: 0:e6eb3150255d
624 624 | |/ / / parent: 12:86b91144a6e9
625 625 | | | | user: test
626 626 | | | | date: Thu Jan 01 00:00:14 1970 +0000
627 627 | | | | summary: (14) merge two known; one immediate right, one far right
628 628 | | | |
629 629 o | | | changeset: 13:22d8966a97e3
630 630 |\ \ \ \ parent: 9:7010c0af0a35
631 631 | | | | | parent: 11:832d76e6bdf2
632 632 | | | | | user: test
633 633 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
634 634 | | | | | summary: (13) expand
635 635 | | | | |
636 636 +---o | | changeset: 12:86b91144a6e9
637 637 | | |/ / parent: 1:6db2ef61d156
638 638 | | | | parent: 9:7010c0af0a35
639 639 | | | | user: test
640 640 | | | | date: Thu Jan 01 00:00:12 1970 +0000
641 641 | | | | summary: (12) merge two known; one immediate right, one far left
642 642 | | | |
643 643 | o | | changeset: 11:832d76e6bdf2
644 644 | |\ \ \ parent: 6:b105a072e251
645 645 | | | | | parent: 10:74c64d036d72
646 646 | | | | | user: test
647 647 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
648 648 | | | | | summary: (11) expand
649 649 | | | | |
650 650 | | o---+ changeset: 10:74c64d036d72
651 651 | | | | | parent: 0:e6eb3150255d
652 652 | |/ / / parent: 6:b105a072e251
653 653 | | | | user: test
654 654 | | | | date: Thu Jan 01 00:00:10 1970 +0000
655 655 | | | | summary: (10) merge two known; one immediate left, one near right
656 656 | | | |
657 657 o | | | changeset: 9:7010c0af0a35
658 658 |\ \ \ \ parent: 7:b632bb1b1224
659 659 | | | | | parent: 8:7a0b11f71937
660 660 | | | | | user: test
661 661 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
662 662 | | | | | summary: (9) expand
663 663 | | | | |
664 664 | o-----+ changeset: 8:7a0b11f71937
665 665 | | | | | parent: 0:e6eb3150255d
666 666 |/ / / / parent: 7:b632bb1b1224
667 667 | | | | user: test
668 668 | | | | date: Thu Jan 01 00:00:08 1970 +0000
669 669 | | | | summary: (8) merge two known; one immediate left, one far right
670 670 | | | |
671 671 o | | | changeset: 7:b632bb1b1224
672 672 |\ \ \ \ parent: 2:3d9a33b8d1e1
673 673 | | | | | parent: 5:4409d547b708
674 674 | | | | | user: test
675 675 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
676 676 | | | | | summary: (7) expand
677 677 | | | | |
678 678 +---o | | changeset: 6:b105a072e251
679 679 | |/ / / parent: 2:3d9a33b8d1e1
680 680 | | | | parent: 5:4409d547b708
681 681 | | | | user: test
682 682 | | | | date: Thu Jan 01 00:00:06 1970 +0000
683 683 | | | | summary: (6) merge two known; one immediate left, one far left
684 684 | | | |
685 685 | o | | changeset: 5:4409d547b708
686 686 | |\ \ \ parent: 3:27eef8ed80b4
687 687 | | | | | parent: 4:26a8bac39d9f
688 688 | | | | | user: test
689 689 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
690 690 | | | | | summary: (5) expand
691 691 | | | | |
692 692 | | o | | changeset: 4:26a8bac39d9f
693 693 | |/|/ / parent: 1:6db2ef61d156
694 694 | | | | parent: 3:27eef8ed80b4
695 695 | | | | user: test
696 696 | | | | date: Thu Jan 01 00:00:04 1970 +0000
697 697 | | | | summary: (4) merge two known; one immediate left, one immediate right
698 698 | | | |
699 699 | o | | changeset: 3:27eef8ed80b4
700 700 |/ / / user: test
701 701 | | | date: Thu Jan 01 00:00:03 1970 +0000
702 702 | | | summary: (3) collapse
703 703 | | |
704 704 o | | changeset: 2:3d9a33b8d1e1
705 705 |/ / user: test
706 706 | | date: Thu Jan 01 00:00:02 1970 +0000
707 707 | | summary: (2) collapse
708 708 | |
709 709 o | changeset: 1:6db2ef61d156
710 710 |/ user: test
711 711 | date: Thu Jan 01 00:00:01 1970 +0000
712 712 | summary: (1) collapse
713 713 |
714 714 o changeset: 0:e6eb3150255d
715 715 user: test
716 716 date: Thu Jan 01 00:00:00 1970 +0000
717 717 summary: (0) root
718 718
719 719
720 720 File glog per revset:
721 721
722 722 $ hg log -G -r 'file("a")'
723 723 @ changeset: 34:fea3ac5810e0
724 724 | tag: tip
725 725 | parent: 32:d06dffa21a31
726 726 | user: test
727 727 | date: Thu Jan 01 00:00:34 1970 +0000
728 728 | summary: (34) head
729 729 |
730 730 | o changeset: 33:68608f5145f9
731 731 | | parent: 18:1aa84d96232a
732 732 | | user: test
733 733 | | date: Thu Jan 01 00:00:33 1970 +0000
734 734 | | summary: (33) head
735 735 | |
736 736 o | changeset: 32:d06dffa21a31
737 737 |\ \ parent: 27:886ed638191b
738 738 | | | parent: 31:621d83e11f67
739 739 | | | user: test
740 740 | | | date: Thu Jan 01 00:00:32 1970 +0000
741 741 | | | summary: (32) expand
742 742 | | |
743 743 | o | changeset: 31:621d83e11f67
744 744 | |\ \ parent: 21:d42a756af44d
745 745 | | | | parent: 30:6e11cd4b648f
746 746 | | | | user: test
747 747 | | | | date: Thu Jan 01 00:00:31 1970 +0000
748 748 | | | | summary: (31) expand
749 749 | | | |
750 750 | | o | changeset: 30:6e11cd4b648f
751 751 | | |\ \ parent: 28:44ecd0b9ae99
752 752 | | | | | parent: 29:cd9bb2be7593
753 753 | | | | | user: test
754 754 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
755 755 | | | | | summary: (30) expand
756 756 | | | | |
757 757 | | | o | changeset: 29:cd9bb2be7593
758 758 | | | | | parent: 0:e6eb3150255d
759 759 | | | | | user: test
760 760 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
761 761 | | | | | summary: (29) regular commit
762 762 | | | | |
763 763 | | o | | changeset: 28:44ecd0b9ae99
764 764 | | |\ \ \ parent: 1:6db2ef61d156
765 765 | | | | | | parent: 26:7f25b6c2f0b9
766 766 | | | | | | user: test
767 767 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
768 768 | | | | | | summary: (28) merge zero known
769 769 | | | | | |
770 770 o | | | | | changeset: 27:886ed638191b
771 771 |/ / / / / parent: 21:d42a756af44d
772 772 | | | | | user: test
773 773 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
774 774 | | | | | summary: (27) collapse
775 775 | | | | |
776 776 | | o---+ changeset: 26:7f25b6c2f0b9
777 777 | | | | | parent: 18:1aa84d96232a
778 778 | | | | | parent: 25:91da8ed57247
779 779 | | | | | user: test
780 780 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
781 781 | | | | | summary: (26) merge one known; far right
782 782 | | | | |
783 783 +---o | | changeset: 25:91da8ed57247
784 784 | | | | | parent: 21:d42a756af44d
785 785 | | | | | parent: 24:a9c19a3d96b7
786 786 | | | | | user: test
787 787 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
788 788 | | | | | summary: (25) merge one known; far left
789 789 | | | | |
790 790 | | o | | changeset: 24:a9c19a3d96b7
791 791 | | |\| | parent: 0:e6eb3150255d
792 792 | | | | | parent: 23:a01cddf0766d
793 793 | | | | | user: test
794 794 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
795 795 | | | | | summary: (24) merge one known; immediate right
796 796 | | | | |
797 797 | | o | | changeset: 23:a01cddf0766d
798 798 | |/| | | parent: 1:6db2ef61d156
799 799 | | | | | parent: 22:e0d9cccacb5d
800 800 | | | | | user: test
801 801 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
802 802 | | | | | summary: (23) merge one known; immediate left
803 803 | | | | |
804 804 +---o---+ changeset: 22:e0d9cccacb5d
805 805 | | | | parent: 18:1aa84d96232a
806 806 | | / / parent: 21:d42a756af44d
807 807 | | | | user: test
808 808 | | | | date: Thu Jan 01 00:00:22 1970 +0000
809 809 | | | | summary: (22) merge two known; one far left, one far right
810 810 | | | |
811 811 o | | | changeset: 21:d42a756af44d
812 812 |\ \ \ \ parent: 19:31ddc2c1573b
813 813 | | | | | parent: 20:d30ed6450e32
814 814 | | | | | user: test
815 815 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
816 816 | | | | | summary: (21) expand
817 817 | | | | |
818 818 | o---+-+ changeset: 20:d30ed6450e32
819 819 | | | | parent: 0:e6eb3150255d
820 820 | / / / parent: 18:1aa84d96232a
821 821 | | | | user: test
822 822 | | | | date: Thu Jan 01 00:00:20 1970 +0000
823 823 | | | | summary: (20) merge two known; two far right
824 824 | | | |
825 825 o | | | changeset: 19:31ddc2c1573b
826 826 |\ \ \ \ parent: 15:1dda3f72782d
827 827 | | | | | parent: 17:44765d7c06e0
828 828 | | | | | user: test
829 829 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
830 830 | | | | | summary: (19) expand
831 831 | | | | |
832 832 +---+---o changeset: 18:1aa84d96232a
833 833 | | | | parent: 1:6db2ef61d156
834 834 | | | | parent: 15:1dda3f72782d
835 835 | | | | user: test
836 836 | | | | date: Thu Jan 01 00:00:18 1970 +0000
837 837 | | | | summary: (18) merge two known; two far left
838 838 | | | |
839 839 | o | | changeset: 17:44765d7c06e0
840 840 | |\ \ \ parent: 12:86b91144a6e9
841 841 | | | | | parent: 16:3677d192927d
842 842 | | | | | user: test
843 843 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
844 844 | | | | | summary: (17) expand
845 845 | | | | |
846 846 | | o---+ changeset: 16:3677d192927d
847 847 | | | | | parent: 0:e6eb3150255d
848 848 | | |/ / parent: 1:6db2ef61d156
849 849 | | | | user: test
850 850 | | | | date: Thu Jan 01 00:00:16 1970 +0000
851 851 | | | | summary: (16) merge two known; one immediate right, one near right
852 852 | | | |
853 853 o | | | changeset: 15:1dda3f72782d
854 854 |\ \ \ \ parent: 13:22d8966a97e3
855 855 | | | | | parent: 14:8eac370358ef
856 856 | | | | | user: test
857 857 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
858 858 | | | | | summary: (15) expand
859 859 | | | | |
860 860 | o-----+ changeset: 14:8eac370358ef
861 861 | | | | | parent: 0:e6eb3150255d
862 862 | |/ / / parent: 12:86b91144a6e9
863 863 | | | | user: test
864 864 | | | | date: Thu Jan 01 00:00:14 1970 +0000
865 865 | | | | summary: (14) merge two known; one immediate right, one far right
866 866 | | | |
867 867 o | | | changeset: 13:22d8966a97e3
868 868 |\ \ \ \ parent: 9:7010c0af0a35
869 869 | | | | | parent: 11:832d76e6bdf2
870 870 | | | | | user: test
871 871 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
872 872 | | | | | summary: (13) expand
873 873 | | | | |
874 874 +---o | | changeset: 12:86b91144a6e9
875 875 | | |/ / parent: 1:6db2ef61d156
876 876 | | | | parent: 9:7010c0af0a35
877 877 | | | | user: test
878 878 | | | | date: Thu Jan 01 00:00:12 1970 +0000
879 879 | | | | summary: (12) merge two known; one immediate right, one far left
880 880 | | | |
881 881 | o | | changeset: 11:832d76e6bdf2
882 882 | |\ \ \ parent: 6:b105a072e251
883 883 | | | | | parent: 10:74c64d036d72
884 884 | | | | | user: test
885 885 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
886 886 | | | | | summary: (11) expand
887 887 | | | | |
888 888 | | o---+ changeset: 10:74c64d036d72
889 889 | | | | | parent: 0:e6eb3150255d
890 890 | |/ / / parent: 6:b105a072e251
891 891 | | | | user: test
892 892 | | | | date: Thu Jan 01 00:00:10 1970 +0000
893 893 | | | | summary: (10) merge two known; one immediate left, one near right
894 894 | | | |
895 895 o | | | changeset: 9:7010c0af0a35
896 896 |\ \ \ \ parent: 7:b632bb1b1224
897 897 | | | | | parent: 8:7a0b11f71937
898 898 | | | | | user: test
899 899 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
900 900 | | | | | summary: (9) expand
901 901 | | | | |
902 902 | o-----+ changeset: 8:7a0b11f71937
903 903 | | | | | parent: 0:e6eb3150255d
904 904 |/ / / / parent: 7:b632bb1b1224
905 905 | | | | user: test
906 906 | | | | date: Thu Jan 01 00:00:08 1970 +0000
907 907 | | | | summary: (8) merge two known; one immediate left, one far right
908 908 | | | |
909 909 o | | | changeset: 7:b632bb1b1224
910 910 |\ \ \ \ parent: 2:3d9a33b8d1e1
911 911 | | | | | parent: 5:4409d547b708
912 912 | | | | | user: test
913 913 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
914 914 | | | | | summary: (7) expand
915 915 | | | | |
916 916 +---o | | changeset: 6:b105a072e251
917 917 | |/ / / parent: 2:3d9a33b8d1e1
918 918 | | | | parent: 5:4409d547b708
919 919 | | | | user: test
920 920 | | | | date: Thu Jan 01 00:00:06 1970 +0000
921 921 | | | | summary: (6) merge two known; one immediate left, one far left
922 922 | | | |
923 923 | o | | changeset: 5:4409d547b708
924 924 | |\ \ \ parent: 3:27eef8ed80b4
925 925 | | | | | parent: 4:26a8bac39d9f
926 926 | | | | | user: test
927 927 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
928 928 | | | | | summary: (5) expand
929 929 | | | | |
930 930 | | o | | changeset: 4:26a8bac39d9f
931 931 | |/|/ / parent: 1:6db2ef61d156
932 932 | | | | parent: 3:27eef8ed80b4
933 933 | | | | user: test
934 934 | | | | date: Thu Jan 01 00:00:04 1970 +0000
935 935 | | | | summary: (4) merge two known; one immediate left, one immediate right
936 936 | | | |
937 937 | o | | changeset: 3:27eef8ed80b4
938 938 |/ / / user: test
939 939 | | | date: Thu Jan 01 00:00:03 1970 +0000
940 940 | | | summary: (3) collapse
941 941 | | |
942 942 o | | changeset: 2:3d9a33b8d1e1
943 943 |/ / user: test
944 944 | | date: Thu Jan 01 00:00:02 1970 +0000
945 945 | | summary: (2) collapse
946 946 | |
947 947 o | changeset: 1:6db2ef61d156
948 948 |/ user: test
949 949 | date: Thu Jan 01 00:00:01 1970 +0000
950 950 | summary: (1) collapse
951 951 |
952 952 o changeset: 0:e6eb3150255d
953 953 user: test
954 954 date: Thu Jan 01 00:00:00 1970 +0000
955 955 summary: (0) root
956 956
957 957
958 958
959 959 File glog per revset (only merges):
960 960
961 961 $ hg log -G -r 'file("a")' -m
962 962 o changeset: 32:d06dffa21a31
963 963 |\ parent: 27:886ed638191b
964 964 | : parent: 31:621d83e11f67
965 965 | : user: test
966 966 | : date: Thu Jan 01 00:00:32 1970 +0000
967 967 | : summary: (32) expand
968 968 | :
969 969 o : changeset: 31:621d83e11f67
970 970 |\: parent: 21:d42a756af44d
971 971 | : parent: 30:6e11cd4b648f
972 972 | : user: test
973 973 | : date: Thu Jan 01 00:00:31 1970 +0000
974 974 | : summary: (31) expand
975 975 | :
976 976 o : changeset: 30:6e11cd4b648f
977 977 |\ \ parent: 28:44ecd0b9ae99
978 978 | ~ : parent: 29:cd9bb2be7593
979 979 | : user: test
980 980 | : date: Thu Jan 01 00:00:30 1970 +0000
981 981 | : summary: (30) expand
982 982 | /
983 983 o : changeset: 28:44ecd0b9ae99
984 984 |\ \ parent: 1:6db2ef61d156
985 985 | ~ : parent: 26:7f25b6c2f0b9
986 986 | : user: test
987 987 | : date: Thu Jan 01 00:00:28 1970 +0000
988 988 | : summary: (28) merge zero known
989 989 | /
990 990 o : changeset: 26:7f25b6c2f0b9
991 991 |\ \ parent: 18:1aa84d96232a
992 992 | | : parent: 25:91da8ed57247
993 993 | | : user: test
994 994 | | : date: Thu Jan 01 00:00:26 1970 +0000
995 995 | | : summary: (26) merge one known; far right
996 996 | | :
997 997 | o : changeset: 25:91da8ed57247
998 998 | |\: parent: 21:d42a756af44d
999 999 | | : parent: 24:a9c19a3d96b7
1000 1000 | | : user: test
1001 1001 | | : date: Thu Jan 01 00:00:25 1970 +0000
1002 1002 | | : summary: (25) merge one known; far left
1003 1003 | | :
1004 1004 | o : changeset: 24:a9c19a3d96b7
1005 1005 | |\ \ parent: 0:e6eb3150255d
1006 1006 | | ~ : parent: 23:a01cddf0766d
1007 1007 | | : user: test
1008 1008 | | : date: Thu Jan 01 00:00:24 1970 +0000
1009 1009 | | : summary: (24) merge one known; immediate right
1010 1010 | | /
1011 1011 | o : changeset: 23:a01cddf0766d
1012 1012 | |\ \ parent: 1:6db2ef61d156
1013 1013 | | ~ : parent: 22:e0d9cccacb5d
1014 1014 | | : user: test
1015 1015 | | : date: Thu Jan 01 00:00:23 1970 +0000
1016 1016 | | : summary: (23) merge one known; immediate left
1017 1017 | | /
1018 1018 | o : changeset: 22:e0d9cccacb5d
1019 1019 |/:/ parent: 18:1aa84d96232a
1020 1020 | : parent: 21:d42a756af44d
1021 1021 | : user: test
1022 1022 | : date: Thu Jan 01 00:00:22 1970 +0000
1023 1023 | : summary: (22) merge two known; one far left, one far right
1024 1024 | :
1025 1025 | o changeset: 21:d42a756af44d
1026 1026 | |\ parent: 19:31ddc2c1573b
1027 1027 | | | parent: 20:d30ed6450e32
1028 1028 | | | user: test
1029 1029 | | | date: Thu Jan 01 00:00:21 1970 +0000
1030 1030 | | | summary: (21) expand
1031 1031 | | |
1032 1032 +---o changeset: 20:d30ed6450e32
1033 1033 | | | parent: 0:e6eb3150255d
1034 1034 | | ~ parent: 18:1aa84d96232a
1035 1035 | | user: test
1036 1036 | | date: Thu Jan 01 00:00:20 1970 +0000
1037 1037 | | summary: (20) merge two known; two far right
1038 1038 | |
1039 1039 | o changeset: 19:31ddc2c1573b
1040 1040 | |\ parent: 15:1dda3f72782d
1041 1041 | | | parent: 17:44765d7c06e0
1042 1042 | | | user: test
1043 1043 | | | date: Thu Jan 01 00:00:19 1970 +0000
1044 1044 | | | summary: (19) expand
1045 1045 | | |
1046 1046 o | | changeset: 18:1aa84d96232a
1047 1047 |\| | parent: 1:6db2ef61d156
1048 1048 ~ | | parent: 15:1dda3f72782d
1049 1049 | | user: test
1050 1050 | | date: Thu Jan 01 00:00:18 1970 +0000
1051 1051 | | summary: (18) merge two known; two far left
1052 1052 / /
1053 1053 | o changeset: 17:44765d7c06e0
1054 1054 | |\ parent: 12:86b91144a6e9
1055 1055 | | | parent: 16:3677d192927d
1056 1056 | | | user: test
1057 1057 | | | date: Thu Jan 01 00:00:17 1970 +0000
1058 1058 | | | summary: (17) expand
1059 1059 | | |
1060 1060 | | o changeset: 16:3677d192927d
1061 1061 | | |\ parent: 0:e6eb3150255d
1062 1062 | | ~ ~ parent: 1:6db2ef61d156
1063 1063 | | user: test
1064 1064 | | date: Thu Jan 01 00:00:16 1970 +0000
1065 1065 | | summary: (16) merge two known; one immediate right, one near right
1066 1066 | |
1067 1067 o | changeset: 15:1dda3f72782d
1068 1068 |\ \ parent: 13:22d8966a97e3
1069 1069 | | | parent: 14:8eac370358ef
1070 1070 | | | user: test
1071 1071 | | | date: Thu Jan 01 00:00:15 1970 +0000
1072 1072 | | | summary: (15) expand
1073 1073 | | |
1074 1074 | o | changeset: 14:8eac370358ef
1075 1075 | |\| parent: 0:e6eb3150255d
1076 1076 | ~ | parent: 12:86b91144a6e9
1077 1077 | | user: test
1078 1078 | | date: Thu Jan 01 00:00:14 1970 +0000
1079 1079 | | summary: (14) merge two known; one immediate right, one far right
1080 1080 | /
1081 1081 o | changeset: 13:22d8966a97e3
1082 1082 |\ \ parent: 9:7010c0af0a35
1083 1083 | | | parent: 11:832d76e6bdf2
1084 1084 | | | user: test
1085 1085 | | | date: Thu Jan 01 00:00:13 1970 +0000
1086 1086 | | | summary: (13) expand
1087 1087 | | |
1088 1088 +---o changeset: 12:86b91144a6e9
1089 1089 | | | parent: 1:6db2ef61d156
1090 1090 | | ~ parent: 9:7010c0af0a35
1091 1091 | | user: test
1092 1092 | | date: Thu Jan 01 00:00:12 1970 +0000
1093 1093 | | summary: (12) merge two known; one immediate right, one far left
1094 1094 | |
1095 1095 | o changeset: 11:832d76e6bdf2
1096 1096 | |\ parent: 6:b105a072e251
1097 1097 | | | parent: 10:74c64d036d72
1098 1098 | | | user: test
1099 1099 | | | date: Thu Jan 01 00:00:11 1970 +0000
1100 1100 | | | summary: (11) expand
1101 1101 | | |
1102 1102 | | o changeset: 10:74c64d036d72
1103 1103 | |/| parent: 0:e6eb3150255d
1104 1104 | | ~ parent: 6:b105a072e251
1105 1105 | | user: test
1106 1106 | | date: Thu Jan 01 00:00:10 1970 +0000
1107 1107 | | summary: (10) merge two known; one immediate left, one near right
1108 1108 | |
1109 1109 o | changeset: 9:7010c0af0a35
1110 1110 |\ \ parent: 7:b632bb1b1224
1111 1111 | | | parent: 8:7a0b11f71937
1112 1112 | | | user: test
1113 1113 | | | date: Thu Jan 01 00:00:09 1970 +0000
1114 1114 | | | summary: (9) expand
1115 1115 | | |
1116 1116 | o | changeset: 8:7a0b11f71937
1117 1117 |/| | parent: 0:e6eb3150255d
1118 1118 | ~ | parent: 7:b632bb1b1224
1119 1119 | | user: test
1120 1120 | | date: Thu Jan 01 00:00:08 1970 +0000
1121 1121 | | summary: (8) merge two known; one immediate left, one far right
1122 1122 | /
1123 1123 o | changeset: 7:b632bb1b1224
1124 1124 |\ \ parent: 2:3d9a33b8d1e1
1125 1125 | ~ | parent: 5:4409d547b708
1126 1126 | | user: test
1127 1127 | | date: Thu Jan 01 00:00:07 1970 +0000
1128 1128 | | summary: (7) expand
1129 1129 | /
1130 1130 | o changeset: 6:b105a072e251
1131 1131 |/| parent: 2:3d9a33b8d1e1
1132 1132 | ~ parent: 5:4409d547b708
1133 1133 | user: test
1134 1134 | date: Thu Jan 01 00:00:06 1970 +0000
1135 1135 | summary: (6) merge two known; one immediate left, one far left
1136 1136 |
1137 1137 o changeset: 5:4409d547b708
1138 1138 |\ parent: 3:27eef8ed80b4
1139 1139 | ~ parent: 4:26a8bac39d9f
1140 1140 | user: test
1141 1141 | date: Thu Jan 01 00:00:05 1970 +0000
1142 1142 | summary: (5) expand
1143 1143 |
1144 1144 o changeset: 4:26a8bac39d9f
1145 1145 |\ parent: 1:6db2ef61d156
1146 1146 ~ ~ parent: 3:27eef8ed80b4
1147 1147 user: test
1148 1148 date: Thu Jan 01 00:00:04 1970 +0000
1149 1149 summary: (4) merge two known; one immediate left, one immediate right
1150 1150
1151 1151
1152 1152
1153 1153 Empty revision range - display nothing:
1154 1154 $ hg log -G -r 1..0
1155 1155
1156 1156 $ cd ..
1157 1157
1158 1158 #if no-outer-repo
1159 1159
1160 1160 From outer space:
1161 1161 $ hg log -G -l1 repo
1162 1162 @ changeset: 34:fea3ac5810e0
1163 1163 | tag: tip
1164 1164 ~ parent: 32:d06dffa21a31
1165 1165 user: test
1166 1166 date: Thu Jan 01 00:00:34 1970 +0000
1167 1167 summary: (34) head
1168 1168
1169 1169 $ hg log -G -l1 repo/a
1170 1170 @ changeset: 34:fea3ac5810e0
1171 1171 | tag: tip
1172 1172 ~ parent: 32:d06dffa21a31
1173 1173 user: test
1174 1174 date: Thu Jan 01 00:00:34 1970 +0000
1175 1175 summary: (34) head
1176 1176
1177 1177 $ hg log -G -l1 repo/missing
1178 1178
1179 1179 #endif
1180 1180
1181 1181 File log with revs != cset revs:
1182 1182 $ hg init flog
1183 1183 $ cd flog
1184 1184 $ echo one >one
1185 1185 $ hg add one
1186 1186 $ hg commit -mone
1187 1187 $ echo two >two
1188 1188 $ hg add two
1189 1189 $ hg commit -mtwo
1190 1190 $ echo more >two
1191 1191 $ hg commit -mmore
1192 1192 $ hg log -G two
1193 1193 @ changeset: 2:12c28321755b
1194 1194 | tag: tip
1195 1195 | user: test
1196 1196 | date: Thu Jan 01 00:00:00 1970 +0000
1197 1197 | summary: more
1198 1198 |
1199 1199 o changeset: 1:5ac72c0599bf
1200 1200 | user: test
1201 1201 ~ date: Thu Jan 01 00:00:00 1970 +0000
1202 1202 summary: two
1203 1203
1204 1204
1205 1205 Issue1896: File log with explicit style
1206 1206 $ hg log -G --style=default one
1207 1207 o changeset: 0:3d578b4a1f53
1208 1208 user: test
1209 1209 date: Thu Jan 01 00:00:00 1970 +0000
1210 1210 summary: one
1211 1211
1212 1212 Issue2395: glog --style header and footer
1213 1213 $ hg log -G --style=xml one
1214 1214 <?xml version="1.0"?>
1215 1215 <log>
1216 1216 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1217 1217 <author email="test">test</author>
1218 1218 <date>1970-01-01T00:00:00+00:00</date>
1219 1219 <msg xml:space="preserve">one</msg>
1220 1220 </logentry>
1221 1221 </log>
1222 1222
1223 1223 $ cd ..
1224 1224
1225 1225 Incoming and outgoing:
1226 1226
1227 1227 $ hg clone -U -r31 repo repo2
1228 1228 adding changesets
1229 1229 adding manifests
1230 1230 adding file changes
1231 1231 added 31 changesets with 31 changes to 1 files
1232 1232 new changesets e6eb3150255d:621d83e11f67
1233 1233 $ cd repo2
1234 1234
1235 1235 $ hg incoming --graph ../repo
1236 1236 comparing with ../repo
1237 1237 searching for changes
1238 1238 o changeset: 34:fea3ac5810e0
1239 1239 | tag: tip
1240 1240 | parent: 32:d06dffa21a31
1241 1241 | user: test
1242 1242 | date: Thu Jan 01 00:00:34 1970 +0000
1243 1243 | summary: (34) head
1244 1244 |
1245 1245 | o changeset: 33:68608f5145f9
1246 1246 | parent: 18:1aa84d96232a
1247 1247 | user: test
1248 1248 | date: Thu Jan 01 00:00:33 1970 +0000
1249 1249 | summary: (33) head
1250 1250 |
1251 1251 o changeset: 32:d06dffa21a31
1252 1252 | parent: 27:886ed638191b
1253 1253 | parent: 31:621d83e11f67
1254 1254 | user: test
1255 1255 | date: Thu Jan 01 00:00:32 1970 +0000
1256 1256 | summary: (32) expand
1257 1257 |
1258 1258 o changeset: 27:886ed638191b
1259 1259 parent: 21:d42a756af44d
1260 1260 user: test
1261 1261 date: Thu Jan 01 00:00:27 1970 +0000
1262 1262 summary: (27) collapse
1263 1263
1264 1264 $ cd ..
1265 1265
1266 1266 $ hg -R repo outgoing --graph repo2
1267 1267 comparing with repo2
1268 1268 searching for changes
1269 1269 @ changeset: 34:fea3ac5810e0
1270 1270 | tag: tip
1271 1271 | parent: 32:d06dffa21a31
1272 1272 | user: test
1273 1273 | date: Thu Jan 01 00:00:34 1970 +0000
1274 1274 | summary: (34) head
1275 1275 |
1276 1276 | o changeset: 33:68608f5145f9
1277 1277 | parent: 18:1aa84d96232a
1278 1278 | user: test
1279 1279 | date: Thu Jan 01 00:00:33 1970 +0000
1280 1280 | summary: (33) head
1281 1281 |
1282 1282 o changeset: 32:d06dffa21a31
1283 1283 | parent: 27:886ed638191b
1284 1284 | parent: 31:621d83e11f67
1285 1285 | user: test
1286 1286 | date: Thu Jan 01 00:00:32 1970 +0000
1287 1287 | summary: (32) expand
1288 1288 |
1289 1289 o changeset: 27:886ed638191b
1290 1290 parent: 21:d42a756af44d
1291 1291 user: test
1292 1292 date: Thu Jan 01 00:00:27 1970 +0000
1293 1293 summary: (27) collapse
1294 1294
1295 1295
1296 1296 File + limit with revs != cset revs:
1297 1297 $ cd repo
1298 1298 $ touch b
1299 1299 $ hg ci -Aqm0
1300 1300 $ hg log -G -l2 a
1301 1301 o changeset: 34:fea3ac5810e0
1302 1302 | parent: 32:d06dffa21a31
1303 1303 ~ user: test
1304 1304 date: Thu Jan 01 00:00:34 1970 +0000
1305 1305 summary: (34) head
1306 1306
1307 1307 o changeset: 33:68608f5145f9
1308 1308 | parent: 18:1aa84d96232a
1309 1309 ~ user: test
1310 1310 date: Thu Jan 01 00:00:33 1970 +0000
1311 1311 summary: (33) head
1312 1312
1313 1313
1314 1314 File + limit + -ra:b, (b - a) < limit:
1315 1315 $ hg log -G -l3000 -r32:tip a
1316 1316 o changeset: 34:fea3ac5810e0
1317 1317 | parent: 32:d06dffa21a31
1318 1318 | user: test
1319 1319 | date: Thu Jan 01 00:00:34 1970 +0000
1320 1320 | summary: (34) head
1321 1321 |
1322 1322 | o changeset: 33:68608f5145f9
1323 1323 | | parent: 18:1aa84d96232a
1324 1324 | ~ user: test
1325 1325 | date: Thu Jan 01 00:00:33 1970 +0000
1326 1326 | summary: (33) head
1327 1327 |
1328 1328 o changeset: 32:d06dffa21a31
1329 1329 |\ parent: 27:886ed638191b
1330 1330 ~ ~ parent: 31:621d83e11f67
1331 1331 user: test
1332 1332 date: Thu Jan 01 00:00:32 1970 +0000
1333 1333 summary: (32) expand
1334 1334
1335 1335
1336 1336 Point out a common and an uncommon unshown parent
1337 1337
1338 1338 $ hg log -G -r 'rev(8) or rev(9)'
1339 1339 o changeset: 9:7010c0af0a35
1340 1340 |\ parent: 7:b632bb1b1224
1341 1341 | ~ parent: 8:7a0b11f71937
1342 1342 | user: test
1343 1343 | date: Thu Jan 01 00:00:09 1970 +0000
1344 1344 | summary: (9) expand
1345 1345 |
1346 1346 o changeset: 8:7a0b11f71937
1347 1347 |\ parent: 0:e6eb3150255d
1348 1348 ~ ~ parent: 7:b632bb1b1224
1349 1349 user: test
1350 1350 date: Thu Jan 01 00:00:08 1970 +0000
1351 1351 summary: (8) merge two known; one immediate left, one far right
1352 1352
1353 1353
1354 1354 File + limit + -ra:b, b < tip:
1355 1355
1356 1356 $ hg log -G -l1 -r32:34 a
1357 1357 o changeset: 34:fea3ac5810e0
1358 1358 | parent: 32:d06dffa21a31
1359 1359 ~ user: test
1360 1360 date: Thu Jan 01 00:00:34 1970 +0000
1361 1361 summary: (34) head
1362 1362
1363 1363
1364 1364 file(File) + limit + -ra:b, b < tip:
1365 1365
1366 1366 $ hg log -G -l1 -r32:34 -r 'file("a")'
1367 1367 o changeset: 34:fea3ac5810e0
1368 1368 | parent: 32:d06dffa21a31
1369 1369 ~ user: test
1370 1370 date: Thu Jan 01 00:00:34 1970 +0000
1371 1371 summary: (34) head
1372 1372
1373 1373
1374 1374 limit(file(File) and a::b), b < tip:
1375 1375
1376 1376 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1377 1377 o changeset: 32:d06dffa21a31
1378 1378 |\ parent: 27:886ed638191b
1379 1379 ~ ~ parent: 31:621d83e11f67
1380 1380 user: test
1381 1381 date: Thu Jan 01 00:00:32 1970 +0000
1382 1382 summary: (32) expand
1383 1383
1384 1384
1385 1385 File + limit + -ra:b, b < tip:
1386 1386
1387 1387 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1388 1388
1389 1389 File + limit + -ra:b, b < tip, (b - a) < limit:
1390 1390
1391 1391 $ hg log -G -l10 -r33:34 a
1392 1392 o changeset: 34:fea3ac5810e0
1393 1393 | parent: 32:d06dffa21a31
1394 1394 ~ user: test
1395 1395 date: Thu Jan 01 00:00:34 1970 +0000
1396 1396 summary: (34) head
1397 1397
1398 1398 o changeset: 33:68608f5145f9
1399 1399 | parent: 18:1aa84d96232a
1400 1400 ~ user: test
1401 1401 date: Thu Jan 01 00:00:33 1970 +0000
1402 1402 summary: (33) head
1403 1403
1404 1404
1405 1405 Do not crash or produce strange graphs if history is buggy
1406 1406
1407 1407 $ hg branch branch
1408 1408 marked working directory as branch branch
1409 1409 (branches are permanent and global, did you want a bookmark?)
1410 1410 $ commit 36 "buggy merge: identical parents" 35 35
1411 1411 $ hg log -G -l5
1412 1412 @ changeset: 36:08a19a744424
1413 1413 | branch: branch
1414 1414 | tag: tip
1415 1415 | parent: 35:9159c3644c5e
1416 1416 | parent: 35:9159c3644c5e
1417 1417 | user: test
1418 1418 | date: Thu Jan 01 00:00:36 1970 +0000
1419 1419 | summary: (36) buggy merge: identical parents
1420 1420 |
1421 1421 o changeset: 35:9159c3644c5e
1422 1422 | user: test
1423 1423 | date: Thu Jan 01 00:00:00 1970 +0000
1424 1424 | summary: 0
1425 1425 |
1426 1426 o changeset: 34:fea3ac5810e0
1427 1427 | parent: 32:d06dffa21a31
1428 1428 | user: test
1429 1429 | date: Thu Jan 01 00:00:34 1970 +0000
1430 1430 | summary: (34) head
1431 1431 |
1432 1432 | o changeset: 33:68608f5145f9
1433 1433 | | parent: 18:1aa84d96232a
1434 1434 | ~ user: test
1435 1435 | date: Thu Jan 01 00:00:33 1970 +0000
1436 1436 | summary: (33) head
1437 1437 |
1438 1438 o changeset: 32:d06dffa21a31
1439 1439 |\ parent: 27:886ed638191b
1440 1440 ~ ~ parent: 31:621d83e11f67
1441 1441 user: test
1442 1442 date: Thu Jan 01 00:00:32 1970 +0000
1443 1443 summary: (32) expand
1444 1444
1445 1445
1446 1446 Test log -G options
1447 1447
1448 1448 $ testlog() {
1449 1449 > hg log -G --print-revset "$@"
1450 1450 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1451 1451 > | sed 's/.*nodetag/nodetag/' > log.nodes
1452 1452 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1453 1453 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1454 1454 > (cmp log.nodes glog.nodes || diff -u log.nodes glog.nodes) \
1455 1455 > | grep '^[-+@ ]' || :
1456 1456 > }
1457 1457
1458 1458 glog always reorders nodes which explains the difference with log
1459 1459
1460 1460 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1461 1461 ['27', '25', '21', '34', '32', '31']
1462 1462 []
1463 1463 <baseset- [21, 25, 27, 31, 32, 34]>
1464 1464 --- log.nodes * (glob)
1465 1465 +++ glog.nodes * (glob)
1466 1466 @@ -1,6 +1,6 @@
1467 1467 -nodetag 27
1468 1468 -nodetag 25
1469 1469 -nodetag 21
1470 1470 nodetag 34
1471 1471 nodetag 32
1472 1472 nodetag 31
1473 1473 +nodetag 27
1474 1474 +nodetag 25
1475 1475 +nodetag 21
1476 1476 $ testlog -u test -u not-a-user
1477 1477 []
1478 1478 (or
1479 1479 (list
1480 1480 (func
1481 1481 (symbol 'user')
1482 1482 (string 'test'))
1483 1483 (func
1484 1484 (symbol 'user')
1485 1485 (string 'not-a-user'))))
1486 1486 <filteredset
1487 1487 <spanset- 0:37>,
1488 1488 <addset
1489 1489 <filteredset
1490 1490 <fullreposet+ 0:37>,
1491 1491 <user 'test'>>,
1492 1492 <filteredset
1493 1493 <fullreposet+ 0:37>,
1494 1494 <user 'not-a-user'>>>>
1495 1495 $ testlog -b not-a-branch
1496 1496 abort: unknown revision 'not-a-branch'!
1497 1497 abort: unknown revision 'not-a-branch'!
1498 1498 abort: unknown revision 'not-a-branch'!
1499 1499 $ testlog -b 35 -b 36 --only-branch branch
1500 1500 []
1501 1501 (or
1502 1502 (list
1503 1503 (func
1504 1504 (symbol 'branch')
1505 1505 (string 'default'))
1506 1506 (or
1507 1507 (list
1508 1508 (func
1509 1509 (symbol 'branch')
1510 1510 (string 'branch'))
1511 1511 (func
1512 1512 (symbol 'branch')
1513 1513 (string 'branch'))))))
1514 1514 <filteredset
1515 1515 <spanset- 0:37>,
1516 1516 <addset
1517 1517 <filteredset
1518 1518 <fullreposet+ 0:37>,
1519 1519 <branch 'default'>>,
1520 1520 <addset
1521 1521 <filteredset
1522 1522 <fullreposet+ 0:37>,
1523 1523 <branch 'branch'>>,
1524 1524 <filteredset
1525 1525 <fullreposet+ 0:37>,
1526 1526 <branch 'branch'>>>>>
1527 1527 $ testlog -k expand -k merge
1528 1528 []
1529 1529 (or
1530 1530 (list
1531 1531 (func
1532 1532 (symbol 'keyword')
1533 1533 (string 'expand'))
1534 1534 (func
1535 1535 (symbol 'keyword')
1536 1536 (string 'merge'))))
1537 1537 <filteredset
1538 1538 <spanset- 0:37>,
1539 1539 <addset
1540 1540 <filteredset
1541 1541 <fullreposet+ 0:37>,
1542 1542 <keyword 'expand'>>,
1543 1543 <filteredset
1544 1544 <fullreposet+ 0:37>,
1545 1545 <keyword 'merge'>>>>
1546 1546 $ testlog --only-merges
1547 1547 []
1548 1548 (func
1549 1549 (symbol 'merge')
1550 1550 None)
1551 1551 <filteredset
1552 1552 <spanset- 0:37>,
1553 1553 <merge>>
1554 1554 $ testlog --no-merges
1555 1555 []
1556 1556 (not
1557 1557 (func
1558 1558 (symbol 'merge')
1559 1559 None))
1560 1560 <filteredset
1561 1561 <spanset- 0:37>,
1562 1562 <not
1563 1563 <filteredset
1564 1564 <spanset- 0:37>,
1565 1565 <merge>>>>
1566 1566 $ testlog --date '2 0 to 4 0'
1567 1567 []
1568 1568 (func
1569 1569 (symbol 'date')
1570 1570 (string '2 0 to 4 0'))
1571 1571 <filteredset
1572 1572 <spanset- 0:37>,
1573 1573 <date '2 0 to 4 0'>>
1574 1574 $ hg log -G -d 'brace ) in a date'
1575 1575 hg: parse error: invalid date: 'brace ) in a date'
1576 1576 [255]
1577 1577 $ testlog --prune 31 --prune 32
1578 1578 []
1579 1579 (not
1580 1580 (or
1581 1581 (list
1582 1582 (func
1583 1583 (symbol 'ancestors')
1584 1584 (string '31'))
1585 1585 (func
1586 1586 (symbol 'ancestors')
1587 1587 (string '32')))))
1588 1588 <filteredset
1589 1589 <spanset- 0:37>,
1590 1590 <not
1591 1591 <addset
1592 1592 <filteredset
1593 1593 <spanset- 0:37>,
1594 1594 <generatorsetdesc+>>,
1595 1595 <filteredset
1596 1596 <spanset- 0:37>,
1597 1597 <generatorsetdesc+>>>>>
1598 1598
1599 1599 Dedicated repo for --follow and paths filtering. The g is crafted to
1600 1600 have 2 filelog topological heads in a linear changeset graph.
1601 1601
1602 1602 $ cd ..
1603 1603 $ hg init follow
1604 1604 $ cd follow
1605 1605 $ testlog --follow
1606 1606 []
1607 1607 []
1608 1608 <baseset []>
1609 1609 $ testlog -rnull
1610 1610 ['null']
1611 1611 []
1612 1612 <baseset [-1]>
1613 1613 $ echo a > a
1614 1614 $ echo aa > aa
1615 1615 $ echo f > f
1616 1616 $ hg ci -Am "add a" a aa f
1617 1617 $ hg cp a b
1618 1618 $ hg cp f g
1619 1619 $ hg ci -m "copy a b"
1620 1620 $ mkdir dir
1621 1621 $ hg mv b dir
1622 1622 $ echo g >> g
1623 1623 $ echo f >> f
1624 1624 $ hg ci -m "mv b dir/b"
1625 1625 $ hg mv a b
1626 1626 $ hg cp -f f g
1627 1627 $ echo a > d
1628 1628 $ hg add d
1629 1629 $ hg ci -m "mv a b; add d"
1630 1630 $ hg mv dir/b e
1631 1631 $ hg ci -m "mv dir/b e"
1632 1632 $ hg log -G --template '({rev}) {desc|firstline}\n'
1633 1633 @ (4) mv dir/b e
1634 1634 |
1635 1635 o (3) mv a b; add d
1636 1636 |
1637 1637 o (2) mv b dir/b
1638 1638 |
1639 1639 o (1) copy a b
1640 1640 |
1641 1641 o (0) add a
1642 1642
1643 1643
1644 1644 $ testlog a
1645 1645 []
1646 1646 (func
1647 1647 (symbol 'filelog')
1648 1648 (string 'a'))
1649 1649 <filteredset
1650 1650 <spanset- 0:5>, set([0])>
1651 1651 $ testlog a b
1652 1652 []
1653 1653 (or
1654 1654 (list
1655 1655 (func
1656 1656 (symbol 'filelog')
1657 1657 (string 'a'))
1658 1658 (func
1659 1659 (symbol 'filelog')
1660 1660 (string 'b'))))
1661 1661 <filteredset
1662 1662 <spanset- 0:5>,
1663 1663 <addset
1664 1664 <baseset+ [0]>,
1665 1665 <baseset+ [1]>>>
1666 1666
1667 1667 Test falling back to slow path for non-existing files
1668 1668
1669 1669 $ testlog a c
1670 1670 []
1671 1671 (func
1672 1672 (symbol '_matchfiles')
1673 1673 (list
1674 1674 (string 'r:')
1675 1675 (string 'd:relpath')
1676 1676 (string 'p:a')
1677 1677 (string 'p:c')))
1678 1678 <filteredset
1679 1679 <spanset- 0:5>,
1680 1680 <matchfiles patterns=['a', 'c'], include=[] exclude=[], default='relpath', rev=2147483647>>
1681 1681
1682 1682 Test multiple --include/--exclude/paths
1683 1683
1684 1684 $ testlog --include a --include e --exclude b --exclude e a e
1685 1685 []
1686 1686 (func
1687 1687 (symbol '_matchfiles')
1688 1688 (list
1689 1689 (string 'r:')
1690 1690 (string 'd:relpath')
1691 1691 (string 'p:a')
1692 1692 (string 'p:e')
1693 1693 (string 'i:a')
1694 1694 (string 'i:e')
1695 1695 (string 'x:b')
1696 1696 (string 'x:e')))
1697 1697 <filteredset
1698 1698 <spanset- 0:5>,
1699 1699 <matchfiles patterns=['a', 'e'], include=['a', 'e'] exclude=['b', 'e'], default='relpath', rev=2147483647>>
1700 1700
1701 1701 Test glob expansion of pats
1702 1702
1703 1703 $ expandglobs=`$PYTHON -c "import mercurial.util; \
1704 1704 > print(mercurial.util.expandglobs and 'true' or 'false')"`
1705 1705 $ if [ $expandglobs = "true" ]; then
1706 1706 > testlog 'a*';
1707 1707 > else
1708 1708 > testlog a*;
1709 1709 > fi;
1710 1710 []
1711 1711 (func
1712 1712 (symbol 'filelog')
1713 1713 (string 'aa'))
1714 1714 <filteredset
1715 1715 <spanset- 0:5>, set([0])>
1716 1716
1717 1717 Test --follow on a non-existent directory
1718 1718
1719 1719 $ testlog -f dir
1720 1720 abort: cannot follow file not in parent revision: "dir"
1721 1721 abort: cannot follow file not in parent revision: "dir"
1722 1722 abort: cannot follow file not in parent revision: "dir"
1723 1723
1724 1724 Test --follow on a directory
1725 1725
1726 1726 $ hg up -q '.^'
1727 1727 $ testlog -f dir
1728 1728 []
1729 1729 (func
1730 1730 (symbol '_matchfiles')
1731 1731 (list
1732 1732 (string 'r:')
1733 1733 (string 'd:relpath')
1734 1734 (string 'p:dir')))
1735 1735 <filteredset
1736 1736 <generatorsetdesc->,
1737 1737 <matchfiles patterns=['dir'], include=[] exclude=[], default='relpath', rev=2147483647>>
1738 1738 $ hg up -q tip
1739 1739
1740 1740 Test --follow on file not in parent revision
1741 1741
1742 1742 $ testlog -f a
1743 1743 abort: cannot follow file not in parent revision: "a"
1744 1744 abort: cannot follow file not in parent revision: "a"
1745 1745 abort: cannot follow file not in parent revision: "a"
1746 1746
1747 1747 Test --follow and patterns
1748 1748
1749 1749 $ testlog -f 'glob:*'
1750 1750 []
1751 1751 (func
1752 1752 (symbol '_matchfiles')
1753 1753 (list
1754 1754 (string 'r:')
1755 1755 (string 'd:relpath')
1756 1756 (string 'p:glob:*')))
1757 1757 <filteredset
1758 1758 <generatorsetdesc->,
1759 1759 <matchfiles patterns=['glob:*'], include=[] exclude=[], default='relpath', rev=2147483647>>
1760 1760
1761 1761 Test --follow on a single rename
1762 1762
1763 1763 $ hg up -q 2
1764 1764 $ testlog -f a
1765 1765 []
1766 1766 []
1767 1767 <generatorsetdesc->
1768 1768
1769 1769 Test --follow and multiple renames
1770 1770
1771 1771 $ hg up -q tip
1772 1772 $ testlog -f e
1773 1773 []
1774 1774 []
1775 1775 <generatorsetdesc->
1776 1776
1777 1777 Test --follow and multiple filelog heads
1778 1778
1779 1779 $ hg up -q 2
1780 1780 $ testlog -f g
1781 1781 []
1782 1782 []
1783 1783 <generatorsetdesc->
1784 1784 $ cat log.nodes
1785 1785 nodetag 2
1786 1786 nodetag 1
1787 1787 nodetag 0
1788 1788 $ hg up -q tip
1789 1789 $ testlog -f g
1790 1790 []
1791 1791 []
1792 1792 <generatorsetdesc->
1793 1793 $ cat log.nodes
1794 1794 nodetag 3
1795 1795 nodetag 2
1796 1796 nodetag 0
1797 1797
1798 1798 Test --follow and multiple files
1799 1799
1800 1800 $ testlog -f g e
1801 1801 []
1802 1802 []
1803 1803 <generatorsetdesc->
1804 1804 $ cat log.nodes
1805 1805 nodetag 4
1806 1806 nodetag 3
1807 1807 nodetag 2
1808 1808 nodetag 1
1809 1809 nodetag 0
1810 1810
1811 1811 Test --follow null parent
1812 1812
1813 1813 $ hg up -q null
1814 1814 $ testlog -f
1815 1815 []
1816 1816 []
1817 1817 <baseset []>
1818 1818
1819 1819 Test --follow-first
1820 1820
1821 1821 $ hg up -q 3
1822 1822 $ echo ee > e
1823 1823 $ hg ci -Am "add another e" e
1824 1824 created new head
1825 1825 $ hg merge --tool internal:other 4
1826 1826 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1827 1827 (branch merge, don't forget to commit)
1828 1828 $ echo merge > e
1829 1829 $ hg ci -m "merge 5 and 4"
1830 1830 $ testlog --follow-first
1831 1831 []
1832 1832 []
1833 1833 <generatorsetdesc->
1834 1834
1835 1835 Cannot compare with log --follow-first FILE as it never worked
1836 1836
1837 1837 $ hg log -G --print-revset --follow-first e
1838 1838 []
1839 1839 []
1840 1840 <generatorsetdesc->
1841 1841 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1842 1842 @ 6 merge 5 and 4
1843 1843 |\
1844 1844 | ~
1845 1845 o 5 add another e
1846 1846 |
1847 1847 ~
1848 1848
1849 1849 Test --copies
1850 1850
1851 1851 $ hg log -G --copies --template "{rev} {desc|firstline} \
1852 1852 > copies: {file_copies_switch}\n"
1853 1853 @ 6 merge 5 and 4 copies:
1854 1854 |\
1855 1855 | o 5 add another e copies:
1856 1856 | |
1857 1857 o | 4 mv dir/b e copies: e (dir/b)
1858 1858 |/
1859 1859 o 3 mv a b; add d copies: b (a)g (f)
1860 1860 |
1861 1861 o 2 mv b dir/b copies: dir/b (b)
1862 1862 |
1863 1863 o 1 copy a b copies: b (a)g (f)
1864 1864 |
1865 1865 o 0 add a copies:
1866 1866
1867 1867 Test "set:..." and parent revision
1868 1868
1869 1869 $ hg up -q 4
1870 1870 $ testlog "set:copied()"
1871 1871 []
1872 1872 (func
1873 (symbol 'filelog')
1874 (string 'set:copied()'))
1873 (symbol '_matchfiles')
1874 (list
1875 (string 'r:')
1876 (string 'd:relpath')
1877 (string 'p:set:copied()')))
1875 1878 <filteredset
1876 <spanset- 0:7>, set([])>
1879 <spanset- 0:7>,
1880 <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='relpath', rev=2147483647>>
1877 1881 $ testlog --include "set:copied()"
1878 1882 []
1879 []
1880 <spanset- 0:7>
1883 (func
1884 (symbol '_matchfiles')
1885 (list
1886 (string 'r:')
1887 (string 'd:relpath')
1888 (string 'i:set:copied()')))
1889 <filteredset
1890 <spanset- 0:7>,
1891 <matchfiles patterns=[], include=['set:copied()'] exclude=[], default='relpath', rev=2147483647>>
1881 1892 $ testlog -r "sort(file('set:copied()'), -rev)"
1882 1893 ["sort(file('set:copied()'), -rev)"]
1883 1894 []
1884 1895 <filteredset
1885 1896 <fullreposet- 0:7>,
1886 1897 <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='glob', rev=None>>
1887 1898
1888 1899 Test --removed
1889 1900
1890 1901 $ testlog --removed
1891 1902 []
1892 1903 []
1893 1904 <spanset- 0:7>
1894 1905 $ testlog --removed a
1895 1906 []
1896 1907 (func
1897 1908 (symbol '_matchfiles')
1898 1909 (list
1899 1910 (string 'r:')
1900 1911 (string 'd:relpath')
1901 1912 (string 'p:a')))
1902 1913 <filteredset
1903 1914 <spanset- 0:7>,
1904 1915 <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=2147483647>>
1905 1916 $ testlog --removed --follow a
1906 1917 []
1907 1918 (func
1908 1919 (symbol '_matchfiles')
1909 1920 (list
1910 1921 (string 'r:')
1911 1922 (string 'd:relpath')
1912 1923 (string 'p:a')))
1913 1924 <filteredset
1914 1925 <generatorsetdesc->,
1915 1926 <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=2147483647>>
1916 1927
1917 1928 Test --patch and --stat with --follow and --follow-first
1918 1929
1919 1930 $ hg up -q 3
1920 1931 $ hg log -G --git --patch b
1921 1932 o changeset: 1:216d4c92cf98
1922 1933 | user: test
1923 1934 ~ date: Thu Jan 01 00:00:00 1970 +0000
1924 1935 summary: copy a b
1925 1936
1926 1937 diff --git a/a b/b
1927 1938 copy from a
1928 1939 copy to b
1929 1940
1930 1941
1931 1942 $ hg log -G --git --stat b
1932 1943 o changeset: 1:216d4c92cf98
1933 1944 | user: test
1934 1945 ~ date: Thu Jan 01 00:00:00 1970 +0000
1935 1946 summary: copy a b
1936 1947
1937 1948 b | 0
1938 1949 1 files changed, 0 insertions(+), 0 deletions(-)
1939 1950
1940 1951
1941 1952 $ hg log -G --git --patch --follow b
1942 1953 o changeset: 1:216d4c92cf98
1943 1954 | user: test
1944 1955 | date: Thu Jan 01 00:00:00 1970 +0000
1945 1956 | summary: copy a b
1946 1957 |
1947 1958 | diff --git a/a b/b
1948 1959 | copy from a
1949 1960 | copy to b
1950 1961 |
1951 1962 o changeset: 0:f8035bb17114
1952 1963 user: test
1953 1964 date: Thu Jan 01 00:00:00 1970 +0000
1954 1965 summary: add a
1955 1966
1956 1967 diff --git a/a b/a
1957 1968 new file mode 100644
1958 1969 --- /dev/null
1959 1970 +++ b/a
1960 1971 @@ -0,0 +1,1 @@
1961 1972 +a
1962 1973
1963 1974
1964 1975 $ hg log -G --git --stat --follow b
1965 1976 o changeset: 1:216d4c92cf98
1966 1977 | user: test
1967 1978 | date: Thu Jan 01 00:00:00 1970 +0000
1968 1979 | summary: copy a b
1969 1980 |
1970 1981 | b | 0
1971 1982 | 1 files changed, 0 insertions(+), 0 deletions(-)
1972 1983 |
1973 1984 o changeset: 0:f8035bb17114
1974 1985 user: test
1975 1986 date: Thu Jan 01 00:00:00 1970 +0000
1976 1987 summary: add a
1977 1988
1978 1989 a | 1 +
1979 1990 1 files changed, 1 insertions(+), 0 deletions(-)
1980 1991
1981 1992
1982 1993 $ hg up -q 6
1983 1994 $ hg log -G --git --patch --follow-first e
1984 1995 @ changeset: 6:fc281d8ff18d
1985 1996 |\ tag: tip
1986 1997 | ~ parent: 5:99b31f1c2782
1987 1998 | parent: 4:17d952250a9d
1988 1999 | user: test
1989 2000 | date: Thu Jan 01 00:00:00 1970 +0000
1990 2001 | summary: merge 5 and 4
1991 2002 |
1992 2003 | diff --git a/e b/e
1993 2004 | --- a/e
1994 2005 | +++ b/e
1995 2006 | @@ -1,1 +1,1 @@
1996 2007 | -ee
1997 2008 | +merge
1998 2009 |
1999 2010 o changeset: 5:99b31f1c2782
2000 2011 | parent: 3:5918b8d165d1
2001 2012 ~ user: test
2002 2013 date: Thu Jan 01 00:00:00 1970 +0000
2003 2014 summary: add another e
2004 2015
2005 2016 diff --git a/e b/e
2006 2017 new file mode 100644
2007 2018 --- /dev/null
2008 2019 +++ b/e
2009 2020 @@ -0,0 +1,1 @@
2010 2021 +ee
2011 2022
2012 2023
2013 2024 Test old-style --rev
2014 2025
2015 2026 $ hg tag 'foo-bar'
2016 2027 $ testlog -r 'foo-bar'
2017 2028 ['foo-bar']
2018 2029 []
2019 2030 <baseset [6]>
2020 2031
2021 2032 Test --follow and forward --rev
2022 2033
2023 2034 $ hg up -q 6
2024 2035 $ echo g > g
2025 2036 $ hg ci -Am 'add g' g
2026 2037 created new head
2027 2038 $ hg up -q 2
2028 2039 $ hg log -G --template "{rev} {desc|firstline}\n"
2029 2040 o 8 add g
2030 2041 |
2031 2042 | o 7 Added tag foo-bar for changeset fc281d8ff18d
2032 2043 |/
2033 2044 o 6 merge 5 and 4
2034 2045 |\
2035 2046 | o 5 add another e
2036 2047 | |
2037 2048 o | 4 mv dir/b e
2038 2049 |/
2039 2050 o 3 mv a b; add d
2040 2051 |
2041 2052 @ 2 mv b dir/b
2042 2053 |
2043 2054 o 1 copy a b
2044 2055 |
2045 2056 o 0 add a
2046 2057
2047 2058 $ hg archive -r 7 archive
2048 2059 $ grep changessincelatesttag archive/.hg_archival.txt
2049 2060 changessincelatesttag: 1
2050 2061 $ rm -r archive
2051 2062
2052 2063 changessincelatesttag with no prior tag
2053 2064 $ hg archive -r 4 archive
2054 2065 $ grep changessincelatesttag archive/.hg_archival.txt
2055 2066 changessincelatesttag: 5
2056 2067
2057 2068 $ hg export 'all()'
2058 2069 # HG changeset patch
2059 2070 # User test
2060 2071 # Date 0 0
2061 2072 # Thu Jan 01 00:00:00 1970 +0000
2062 2073 # Node ID f8035bb17114da16215af3436ec5222428ace8ee
2063 2074 # Parent 0000000000000000000000000000000000000000
2064 2075 add a
2065 2076
2066 2077 diff -r 000000000000 -r f8035bb17114 a
2067 2078 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2068 2079 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2069 2080 @@ -0,0 +1,1 @@
2070 2081 +a
2071 2082 diff -r 000000000000 -r f8035bb17114 aa
2072 2083 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2073 2084 +++ b/aa Thu Jan 01 00:00:00 1970 +0000
2074 2085 @@ -0,0 +1,1 @@
2075 2086 +aa
2076 2087 diff -r 000000000000 -r f8035bb17114 f
2077 2088 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2078 2089 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2079 2090 @@ -0,0 +1,1 @@
2080 2091 +f
2081 2092 # HG changeset patch
2082 2093 # User test
2083 2094 # Date 0 0
2084 2095 # Thu Jan 01 00:00:00 1970 +0000
2085 2096 # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92
2086 2097 # Parent f8035bb17114da16215af3436ec5222428ace8ee
2087 2098 copy a b
2088 2099
2089 2100 diff -r f8035bb17114 -r 216d4c92cf98 b
2090 2101 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2091 2102 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2092 2103 @@ -0,0 +1,1 @@
2093 2104 +a
2094 2105 diff -r f8035bb17114 -r 216d4c92cf98 g
2095 2106 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2096 2107 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2097 2108 @@ -0,0 +1,1 @@
2098 2109 +f
2099 2110 # HG changeset patch
2100 2111 # User test
2101 2112 # Date 0 0
2102 2113 # Thu Jan 01 00:00:00 1970 +0000
2103 2114 # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2104 2115 # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92
2105 2116 mv b dir/b
2106 2117
2107 2118 diff -r 216d4c92cf98 -r bb573313a9e8 b
2108 2119 --- a/b Thu Jan 01 00:00:00 1970 +0000
2109 2120 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2110 2121 @@ -1,1 +0,0 @@
2111 2122 -a
2112 2123 diff -r 216d4c92cf98 -r bb573313a9e8 dir/b
2113 2124 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2114 2125 +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000
2115 2126 @@ -0,0 +1,1 @@
2116 2127 +a
2117 2128 diff -r 216d4c92cf98 -r bb573313a9e8 f
2118 2129 --- a/f Thu Jan 01 00:00:00 1970 +0000
2119 2130 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2120 2131 @@ -1,1 +1,2 @@
2121 2132 f
2122 2133 +f
2123 2134 diff -r 216d4c92cf98 -r bb573313a9e8 g
2124 2135 --- a/g Thu Jan 01 00:00:00 1970 +0000
2125 2136 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2126 2137 @@ -1,1 +1,2 @@
2127 2138 f
2128 2139 +g
2129 2140 # HG changeset patch
2130 2141 # User test
2131 2142 # Date 0 0
2132 2143 # Thu Jan 01 00:00:00 1970 +0000
2133 2144 # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2134 2145 # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2135 2146 mv a b; add d
2136 2147
2137 2148 diff -r bb573313a9e8 -r 5918b8d165d1 a
2138 2149 --- a/a Thu Jan 01 00:00:00 1970 +0000
2139 2150 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2140 2151 @@ -1,1 +0,0 @@
2141 2152 -a
2142 2153 diff -r bb573313a9e8 -r 5918b8d165d1 b
2143 2154 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2144 2155 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2145 2156 @@ -0,0 +1,1 @@
2146 2157 +a
2147 2158 diff -r bb573313a9e8 -r 5918b8d165d1 d
2148 2159 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2149 2160 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2150 2161 @@ -0,0 +1,1 @@
2151 2162 +a
2152 2163 diff -r bb573313a9e8 -r 5918b8d165d1 g
2153 2164 --- a/g Thu Jan 01 00:00:00 1970 +0000
2154 2165 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2155 2166 @@ -1,2 +1,2 @@
2156 2167 f
2157 2168 -g
2158 2169 +f
2159 2170 # HG changeset patch
2160 2171 # User test
2161 2172 # Date 0 0
2162 2173 # Thu Jan 01 00:00:00 1970 +0000
2163 2174 # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260
2164 2175 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2165 2176 mv dir/b e
2166 2177
2167 2178 diff -r 5918b8d165d1 -r 17d952250a9d dir/b
2168 2179 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2169 2180 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2170 2181 @@ -1,1 +0,0 @@
2171 2182 -a
2172 2183 diff -r 5918b8d165d1 -r 17d952250a9d e
2173 2184 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2174 2185 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2175 2186 @@ -0,0 +1,1 @@
2176 2187 +a
2177 2188 # HG changeset patch
2178 2189 # User test
2179 2190 # Date 0 0
2180 2191 # Thu Jan 01 00:00:00 1970 +0000
2181 2192 # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b
2182 2193 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2183 2194 add another e
2184 2195
2185 2196 diff -r 5918b8d165d1 -r 99b31f1c2782 e
2186 2197 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2187 2198 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2188 2199 @@ -0,0 +1,1 @@
2189 2200 +ee
2190 2201 # HG changeset patch
2191 2202 # User test
2192 2203 # Date 0 0
2193 2204 # Thu Jan 01 00:00:00 1970 +0000
2194 2205 # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
2195 2206 # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b
2196 2207 # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260
2197 2208 merge 5 and 4
2198 2209
2199 2210 diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
2200 2211 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2201 2212 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2202 2213 @@ -1,1 +0,0 @@
2203 2214 -a
2204 2215 diff -r 99b31f1c2782 -r fc281d8ff18d e
2205 2216 --- a/e Thu Jan 01 00:00:00 1970 +0000
2206 2217 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2207 2218 @@ -1,1 +1,1 @@
2208 2219 -ee
2209 2220 +merge
2210 2221 # HG changeset patch
2211 2222 # User test
2212 2223 # Date 0 0
2213 2224 # Thu Jan 01 00:00:00 1970 +0000
2214 2225 # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
2215 2226 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2216 2227 Added tag foo-bar for changeset fc281d8ff18d
2217 2228
2218 2229 diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
2219 2230 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2220 2231 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2221 2232 @@ -0,0 +1,1 @@
2222 2233 +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
2223 2234 # HG changeset patch
2224 2235 # User test
2225 2236 # Date 0 0
2226 2237 # Thu Jan 01 00:00:00 1970 +0000
2227 2238 # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
2228 2239 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2229 2240 add g
2230 2241
2231 2242 diff -r fc281d8ff18d -r 24c2e826ddeb g
2232 2243 --- a/g Thu Jan 01 00:00:00 1970 +0000
2233 2244 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2234 2245 @@ -1,2 +1,1 @@
2235 2246 -f
2236 2247 -f
2237 2248 +g
2238 2249 $ testlog --follow -r6 -r8 -r5 -r7 -r4
2239 2250 ['6', '8', '5', '7', '4']
2240 2251 []
2241 2252 <generatorsetdesc->
2242 2253
2243 2254 Test --follow-first and forward --rev
2244 2255
2245 2256 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
2246 2257 ['6', '8', '5', '7', '4']
2247 2258 []
2248 2259 <generatorsetdesc->
2249 2260
2250 2261 Test --follow and backward --rev
2251 2262
2252 2263 $ testlog --follow -r6 -r5 -r7 -r8 -r4
2253 2264 ['6', '5', '7', '8', '4']
2254 2265 []
2255 2266 <generatorsetdesc->
2256 2267
2257 2268 Test --follow-first and backward --rev
2258 2269
2259 2270 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2260 2271 ['6', '5', '7', '8', '4']
2261 2272 []
2262 2273 <generatorsetdesc->
2263 2274
2264 2275 Test --follow with --rev of graphlog extension
2265 2276
2266 2277 $ hg --config extensions.graphlog= glog -qfr1
2267 2278 o 1:216d4c92cf98
2268 2279 |
2269 2280 o 0:f8035bb17114
2270 2281
2271 2282
2272 2283 Test subdir
2273 2284
2274 2285 $ hg up -q 3
2275 2286 $ cd dir
2276 2287 $ testlog .
2277 2288 []
2278 2289 (func
2279 2290 (symbol '_matchfiles')
2280 2291 (list
2281 2292 (string 'r:')
2282 2293 (string 'd:relpath')
2283 2294 (string 'p:.')))
2284 2295 <filteredset
2285 2296 <spanset- 0:9>,
2286 2297 <matchfiles patterns=['.'], include=[] exclude=[], default='relpath', rev=2147483647>>
2287 2298 $ testlog ../b
2288 2299 []
2289 2300 (func
2290 2301 (symbol 'filelog')
2291 2302 (string '../b'))
2292 2303 <filteredset
2293 2304 <spanset- 0:9>, set([1])>
2294 2305 $ testlog -f ../b
2295 2306 []
2296 2307 []
2297 2308 <generatorsetdesc->
2298 2309 $ cd ..
2299 2310
2300 2311 Test --hidden
2301 2312 (enable obsolete)
2302 2313
2303 2314 $ cat >> $HGRCPATH << EOF
2304 2315 > [experimental]
2305 2316 > evolution.createmarkers=True
2306 2317 > EOF
2307 2318
2308 2319 $ hg debugobsolete `hg id --debug -i -r 8`
2309 2320 obsoleted 1 changesets
2310 2321 $ testlog
2311 2322 []
2312 2323 []
2313 2324 <spanset- 0:9>
2314 2325 $ testlog --hidden
2315 2326 []
2316 2327 []
2317 2328 <spanset- 0:9>
2318 2329 $ hg log -G --template '{rev} {desc}\n'
2319 2330 o 7 Added tag foo-bar for changeset fc281d8ff18d
2320 2331 |
2321 2332 o 6 merge 5 and 4
2322 2333 |\
2323 2334 | o 5 add another e
2324 2335 | |
2325 2336 o | 4 mv dir/b e
2326 2337 |/
2327 2338 @ 3 mv a b; add d
2328 2339 |
2329 2340 o 2 mv b dir/b
2330 2341 |
2331 2342 o 1 copy a b
2332 2343 |
2333 2344 o 0 add a
2334 2345
2335 2346
2336 2347 A template without trailing newline should do something sane
2337 2348
2338 2349 $ hg log -G -r ::2 --template '{rev} {desc}'
2339 2350 o 2 mv b dir/b
2340 2351 |
2341 2352 o 1 copy a b
2342 2353 |
2343 2354 o 0 add a
2344 2355
2345 2356
2346 2357 Extra newlines must be preserved
2347 2358
2348 2359 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2349 2360 o
2350 2361 | 2 mv b dir/b
2351 2362 |
2352 2363 o
2353 2364 | 1 copy a b
2354 2365 |
2355 2366 o
2356 2367 0 add a
2357 2368
2358 2369
2359 2370 The almost-empty template should do something sane too ...
2360 2371
2361 2372 $ hg log -G -r ::2 --template '\n'
2362 2373 o
2363 2374 |
2364 2375 o
2365 2376 |
2366 2377 o
2367 2378
2368 2379
2369 2380 issue3772
2370 2381
2371 2382 $ hg log -G -r :null
2372 2383 o changeset: 0:f8035bb17114
2373 2384 | user: test
2374 2385 | date: Thu Jan 01 00:00:00 1970 +0000
2375 2386 | summary: add a
2376 2387 |
2377 2388 o changeset: -1:000000000000
2378 2389 user:
2379 2390 date: Thu Jan 01 00:00:00 1970 +0000
2380 2391
2381 2392 $ hg log -G -r null:null
2382 2393 o changeset: -1:000000000000
2383 2394 user:
2384 2395 date: Thu Jan 01 00:00:00 1970 +0000
2385 2396
2386 2397
2387 2398 should not draw line down to null due to the magic of fullreposet
2388 2399
2389 2400 $ hg log -G -r 'all()' | tail -6
2390 2401 |
2391 2402 o changeset: 0:f8035bb17114
2392 2403 user: test
2393 2404 date: Thu Jan 01 00:00:00 1970 +0000
2394 2405 summary: add a
2395 2406
2396 2407
2397 2408 $ hg log -G -r 'branch(default)' | tail -6
2398 2409 |
2399 2410 o changeset: 0:f8035bb17114
2400 2411 user: test
2401 2412 date: Thu Jan 01 00:00:00 1970 +0000
2402 2413 summary: add a
2403 2414
2404 2415
2405 2416 working-directory revision
2406 2417
2407 2418 $ hg log -G -qr '. + wdir()'
2408 2419 o 2147483647:ffffffffffff
2409 2420 |
2410 2421 @ 3:5918b8d165d1
2411 2422 |
2412 2423 ~
2413 2424
2414 2425 node template with changesetprinter:
2415 2426
2416 2427 $ hg log -Gqr 5:7 --config ui.graphnodetemplate='"{rev}"'
2417 2428 7 7:02dbb8e276b8
2418 2429 |
2419 2430 6 6:fc281d8ff18d
2420 2431 |\
2421 2432 | ~
2422 2433 5 5:99b31f1c2782
2423 2434 |
2424 2435 ~
2425 2436
2426 2437 node template with changesettemplater (shared cache variable):
2427 2438
2428 2439 $ hg log -Gr 5:7 -T '{latesttag % "{rev} {tag}+{distance}"}\n' \
2429 2440 > --config ui.graphnodetemplate='{ifeq(latesttagdistance, 0, "#", graphnode)}'
2430 2441 o 7 foo-bar+1
2431 2442 |
2432 2443 # 6 foo-bar+0
2433 2444 |\
2434 2445 | ~
2435 2446 o 5 null+5
2436 2447 |
2437 2448 ~
2438 2449
2439 2450 label() should just work in node template:
2440 2451
2441 2452 $ hg log -Gqr 7 --config extensions.color= --color=debug \
2442 2453 > --config ui.graphnodetemplate='{label("branch.{branch}", rev)}'
2443 2454 [branch.default|7] [log.node|7:02dbb8e276b8]
2444 2455 |
2445 2456 ~
2446 2457
2447 2458 $ cd ..
2448 2459
2449 2460 change graph edge styling
2450 2461
2451 2462 $ cd repo
2452 2463 $ cat << EOF >> $HGRCPATH
2453 2464 > [experimental]
2454 2465 > graphstyle.parent = |
2455 2466 > graphstyle.grandparent = :
2456 2467 > graphstyle.missing =
2457 2468 > EOF
2458 2469 $ hg log -G -r 'file("a")' -m
2459 2470 @ changeset: 36:08a19a744424
2460 2471 : branch: branch
2461 2472 : tag: tip
2462 2473 : parent: 35:9159c3644c5e
2463 2474 : parent: 35:9159c3644c5e
2464 2475 : user: test
2465 2476 : date: Thu Jan 01 00:00:36 1970 +0000
2466 2477 : summary: (36) buggy merge: identical parents
2467 2478 :
2468 2479 o changeset: 32:d06dffa21a31
2469 2480 |\ parent: 27:886ed638191b
2470 2481 | : parent: 31:621d83e11f67
2471 2482 | : user: test
2472 2483 | : date: Thu Jan 01 00:00:32 1970 +0000
2473 2484 | : summary: (32) expand
2474 2485 | :
2475 2486 o : changeset: 31:621d83e11f67
2476 2487 |\: parent: 21:d42a756af44d
2477 2488 | : parent: 30:6e11cd4b648f
2478 2489 | : user: test
2479 2490 | : date: Thu Jan 01 00:00:31 1970 +0000
2480 2491 | : summary: (31) expand
2481 2492 | :
2482 2493 o : changeset: 30:6e11cd4b648f
2483 2494 |\ \ parent: 28:44ecd0b9ae99
2484 2495 | ~ : parent: 29:cd9bb2be7593
2485 2496 | : user: test
2486 2497 | : date: Thu Jan 01 00:00:30 1970 +0000
2487 2498 | : summary: (30) expand
2488 2499 | /
2489 2500 o : changeset: 28:44ecd0b9ae99
2490 2501 |\ \ parent: 1:6db2ef61d156
2491 2502 | ~ : parent: 26:7f25b6c2f0b9
2492 2503 | : user: test
2493 2504 | : date: Thu Jan 01 00:00:28 1970 +0000
2494 2505 | : summary: (28) merge zero known
2495 2506 | /
2496 2507 o : changeset: 26:7f25b6c2f0b9
2497 2508 |\ \ parent: 18:1aa84d96232a
2498 2509 | | : parent: 25:91da8ed57247
2499 2510 | | : user: test
2500 2511 | | : date: Thu Jan 01 00:00:26 1970 +0000
2501 2512 | | : summary: (26) merge one known; far right
2502 2513 | | :
2503 2514 | o : changeset: 25:91da8ed57247
2504 2515 | |\: parent: 21:d42a756af44d
2505 2516 | | : parent: 24:a9c19a3d96b7
2506 2517 | | : user: test
2507 2518 | | : date: Thu Jan 01 00:00:25 1970 +0000
2508 2519 | | : summary: (25) merge one known; far left
2509 2520 | | :
2510 2521 | o : changeset: 24:a9c19a3d96b7
2511 2522 | |\ \ parent: 0:e6eb3150255d
2512 2523 | | ~ : parent: 23:a01cddf0766d
2513 2524 | | : user: test
2514 2525 | | : date: Thu Jan 01 00:00:24 1970 +0000
2515 2526 | | : summary: (24) merge one known; immediate right
2516 2527 | | /
2517 2528 | o : changeset: 23:a01cddf0766d
2518 2529 | |\ \ parent: 1:6db2ef61d156
2519 2530 | | ~ : parent: 22:e0d9cccacb5d
2520 2531 | | : user: test
2521 2532 | | : date: Thu Jan 01 00:00:23 1970 +0000
2522 2533 | | : summary: (23) merge one known; immediate left
2523 2534 | | /
2524 2535 | o : changeset: 22:e0d9cccacb5d
2525 2536 |/:/ parent: 18:1aa84d96232a
2526 2537 | : parent: 21:d42a756af44d
2527 2538 | : user: test
2528 2539 | : date: Thu Jan 01 00:00:22 1970 +0000
2529 2540 | : summary: (22) merge two known; one far left, one far right
2530 2541 | :
2531 2542 | o changeset: 21:d42a756af44d
2532 2543 | |\ parent: 19:31ddc2c1573b
2533 2544 | | | parent: 20:d30ed6450e32
2534 2545 | | | user: test
2535 2546 | | | date: Thu Jan 01 00:00:21 1970 +0000
2536 2547 | | | summary: (21) expand
2537 2548 | | |
2538 2549 +---o changeset: 20:d30ed6450e32
2539 2550 | | | parent: 0:e6eb3150255d
2540 2551 | | ~ parent: 18:1aa84d96232a
2541 2552 | | user: test
2542 2553 | | date: Thu Jan 01 00:00:20 1970 +0000
2543 2554 | | summary: (20) merge two known; two far right
2544 2555 | |
2545 2556 | o changeset: 19:31ddc2c1573b
2546 2557 | |\ parent: 15:1dda3f72782d
2547 2558 | | | parent: 17:44765d7c06e0
2548 2559 | | | user: test
2549 2560 | | | date: Thu Jan 01 00:00:19 1970 +0000
2550 2561 | | | summary: (19) expand
2551 2562 | | |
2552 2563 o | | changeset: 18:1aa84d96232a
2553 2564 |\| | parent: 1:6db2ef61d156
2554 2565 ~ | | parent: 15:1dda3f72782d
2555 2566 | | user: test
2556 2567 | | date: Thu Jan 01 00:00:18 1970 +0000
2557 2568 | | summary: (18) merge two known; two far left
2558 2569 / /
2559 2570 | o changeset: 17:44765d7c06e0
2560 2571 | |\ parent: 12:86b91144a6e9
2561 2572 | | | parent: 16:3677d192927d
2562 2573 | | | user: test
2563 2574 | | | date: Thu Jan 01 00:00:17 1970 +0000
2564 2575 | | | summary: (17) expand
2565 2576 | | |
2566 2577 | | o changeset: 16:3677d192927d
2567 2578 | | |\ parent: 0:e6eb3150255d
2568 2579 | | ~ ~ parent: 1:6db2ef61d156
2569 2580 | | user: test
2570 2581 | | date: Thu Jan 01 00:00:16 1970 +0000
2571 2582 | | summary: (16) merge two known; one immediate right, one near right
2572 2583 | |
2573 2584 o | changeset: 15:1dda3f72782d
2574 2585 |\ \ parent: 13:22d8966a97e3
2575 2586 | | | parent: 14:8eac370358ef
2576 2587 | | | user: test
2577 2588 | | | date: Thu Jan 01 00:00:15 1970 +0000
2578 2589 | | | summary: (15) expand
2579 2590 | | |
2580 2591 | o | changeset: 14:8eac370358ef
2581 2592 | |\| parent: 0:e6eb3150255d
2582 2593 | ~ | parent: 12:86b91144a6e9
2583 2594 | | user: test
2584 2595 | | date: Thu Jan 01 00:00:14 1970 +0000
2585 2596 | | summary: (14) merge two known; one immediate right, one far right
2586 2597 | /
2587 2598 o | changeset: 13:22d8966a97e3
2588 2599 |\ \ parent: 9:7010c0af0a35
2589 2600 | | | parent: 11:832d76e6bdf2
2590 2601 | | | user: test
2591 2602 | | | date: Thu Jan 01 00:00:13 1970 +0000
2592 2603 | | | summary: (13) expand
2593 2604 | | |
2594 2605 +---o changeset: 12:86b91144a6e9
2595 2606 | | | parent: 1:6db2ef61d156
2596 2607 | | ~ parent: 9:7010c0af0a35
2597 2608 | | user: test
2598 2609 | | date: Thu Jan 01 00:00:12 1970 +0000
2599 2610 | | summary: (12) merge two known; one immediate right, one far left
2600 2611 | |
2601 2612 | o changeset: 11:832d76e6bdf2
2602 2613 | |\ parent: 6:b105a072e251
2603 2614 | | | parent: 10:74c64d036d72
2604 2615 | | | user: test
2605 2616 | | | date: Thu Jan 01 00:00:11 1970 +0000
2606 2617 | | | summary: (11) expand
2607 2618 | | |
2608 2619 | | o changeset: 10:74c64d036d72
2609 2620 | |/| parent: 0:e6eb3150255d
2610 2621 | | ~ parent: 6:b105a072e251
2611 2622 | | user: test
2612 2623 | | date: Thu Jan 01 00:00:10 1970 +0000
2613 2624 | | summary: (10) merge two known; one immediate left, one near right
2614 2625 | |
2615 2626 o | changeset: 9:7010c0af0a35
2616 2627 |\ \ parent: 7:b632bb1b1224
2617 2628 | | | parent: 8:7a0b11f71937
2618 2629 | | | user: test
2619 2630 | | | date: Thu Jan 01 00:00:09 1970 +0000
2620 2631 | | | summary: (9) expand
2621 2632 | | |
2622 2633 | o | changeset: 8:7a0b11f71937
2623 2634 |/| | parent: 0:e6eb3150255d
2624 2635 | ~ | parent: 7:b632bb1b1224
2625 2636 | | user: test
2626 2637 | | date: Thu Jan 01 00:00:08 1970 +0000
2627 2638 | | summary: (8) merge two known; one immediate left, one far right
2628 2639 | /
2629 2640 o | changeset: 7:b632bb1b1224
2630 2641 |\ \ parent: 2:3d9a33b8d1e1
2631 2642 | ~ | parent: 5:4409d547b708
2632 2643 | | user: test
2633 2644 | | date: Thu Jan 01 00:00:07 1970 +0000
2634 2645 | | summary: (7) expand
2635 2646 | /
2636 2647 | o changeset: 6:b105a072e251
2637 2648 |/| parent: 2:3d9a33b8d1e1
2638 2649 | ~ parent: 5:4409d547b708
2639 2650 | user: test
2640 2651 | date: Thu Jan 01 00:00:06 1970 +0000
2641 2652 | summary: (6) merge two known; one immediate left, one far left
2642 2653 |
2643 2654 o changeset: 5:4409d547b708
2644 2655 |\ parent: 3:27eef8ed80b4
2645 2656 | ~ parent: 4:26a8bac39d9f
2646 2657 | user: test
2647 2658 | date: Thu Jan 01 00:00:05 1970 +0000
2648 2659 | summary: (5) expand
2649 2660 |
2650 2661 o changeset: 4:26a8bac39d9f
2651 2662 |\ parent: 1:6db2ef61d156
2652 2663 ~ ~ parent: 3:27eef8ed80b4
2653 2664 user: test
2654 2665 date: Thu Jan 01 00:00:04 1970 +0000
2655 2666 summary: (4) merge two known; one immediate left, one immediate right
2656 2667
2657 2668
2658 2669 Setting HGPLAIN ignores graphmod styling:
2659 2670
2660 2671 $ HGPLAIN=1 hg log -G -r 'file("a")' -m
2661 2672 @ changeset: 36:08a19a744424
2662 2673 | branch: branch
2663 2674 | tag: tip
2664 2675 | parent: 35:9159c3644c5e
2665 2676 | parent: 35:9159c3644c5e
2666 2677 | user: test
2667 2678 | date: Thu Jan 01 00:00:36 1970 +0000
2668 2679 | summary: (36) buggy merge: identical parents
2669 2680 |
2670 2681 o changeset: 32:d06dffa21a31
2671 2682 |\ parent: 27:886ed638191b
2672 2683 | | parent: 31:621d83e11f67
2673 2684 | | user: test
2674 2685 | | date: Thu Jan 01 00:00:32 1970 +0000
2675 2686 | | summary: (32) expand
2676 2687 | |
2677 2688 o | changeset: 31:621d83e11f67
2678 2689 |\| parent: 21:d42a756af44d
2679 2690 | | parent: 30:6e11cd4b648f
2680 2691 | | user: test
2681 2692 | | date: Thu Jan 01 00:00:31 1970 +0000
2682 2693 | | summary: (31) expand
2683 2694 | |
2684 2695 o | changeset: 30:6e11cd4b648f
2685 2696 |\ \ parent: 28:44ecd0b9ae99
2686 2697 | | | parent: 29:cd9bb2be7593
2687 2698 | | | user: test
2688 2699 | | | date: Thu Jan 01 00:00:30 1970 +0000
2689 2700 | | | summary: (30) expand
2690 2701 | | |
2691 2702 o | | changeset: 28:44ecd0b9ae99
2692 2703 |\ \ \ parent: 1:6db2ef61d156
2693 2704 | | | | parent: 26:7f25b6c2f0b9
2694 2705 | | | | user: test
2695 2706 | | | | date: Thu Jan 01 00:00:28 1970 +0000
2696 2707 | | | | summary: (28) merge zero known
2697 2708 | | | |
2698 2709 o | | | changeset: 26:7f25b6c2f0b9
2699 2710 |\ \ \ \ parent: 18:1aa84d96232a
2700 2711 | | | | | parent: 25:91da8ed57247
2701 2712 | | | | | user: test
2702 2713 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
2703 2714 | | | | | summary: (26) merge one known; far right
2704 2715 | | | | |
2705 2716 | o-----+ changeset: 25:91da8ed57247
2706 2717 | | | | | parent: 21:d42a756af44d
2707 2718 | | | | | parent: 24:a9c19a3d96b7
2708 2719 | | | | | user: test
2709 2720 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
2710 2721 | | | | | summary: (25) merge one known; far left
2711 2722 | | | | |
2712 2723 | o | | | changeset: 24:a9c19a3d96b7
2713 2724 | |\ \ \ \ parent: 0:e6eb3150255d
2714 2725 | | | | | | parent: 23:a01cddf0766d
2715 2726 | | | | | | user: test
2716 2727 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
2717 2728 | | | | | | summary: (24) merge one known; immediate right
2718 2729 | | | | | |
2719 2730 | o---+ | | changeset: 23:a01cddf0766d
2720 2731 | | | | | | parent: 1:6db2ef61d156
2721 2732 | | | | | | parent: 22:e0d9cccacb5d
2722 2733 | | | | | | user: test
2723 2734 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
2724 2735 | | | | | | summary: (23) merge one known; immediate left
2725 2736 | | | | | |
2726 2737 | o-------+ changeset: 22:e0d9cccacb5d
2727 2738 | | | | | | parent: 18:1aa84d96232a
2728 2739 |/ / / / / parent: 21:d42a756af44d
2729 2740 | | | | | user: test
2730 2741 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
2731 2742 | | | | | summary: (22) merge two known; one far left, one far right
2732 2743 | | | | |
2733 2744 | | | | o changeset: 21:d42a756af44d
2734 2745 | | | | |\ parent: 19:31ddc2c1573b
2735 2746 | | | | | | parent: 20:d30ed6450e32
2736 2747 | | | | | | user: test
2737 2748 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
2738 2749 | | | | | | summary: (21) expand
2739 2750 | | | | | |
2740 2751 +-+-------o changeset: 20:d30ed6450e32
2741 2752 | | | | | parent: 0:e6eb3150255d
2742 2753 | | | | | parent: 18:1aa84d96232a
2743 2754 | | | | | user: test
2744 2755 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
2745 2756 | | | | | summary: (20) merge two known; two far right
2746 2757 | | | | |
2747 2758 | | | | o changeset: 19:31ddc2c1573b
2748 2759 | | | | |\ parent: 15:1dda3f72782d
2749 2760 | | | | | | parent: 17:44765d7c06e0
2750 2761 | | | | | | user: test
2751 2762 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
2752 2763 | | | | | | summary: (19) expand
2753 2764 | | | | | |
2754 2765 o---+---+ | changeset: 18:1aa84d96232a
2755 2766 | | | | | parent: 1:6db2ef61d156
2756 2767 / / / / / parent: 15:1dda3f72782d
2757 2768 | | | | | user: test
2758 2769 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
2759 2770 | | | | | summary: (18) merge two known; two far left
2760 2771 | | | | |
2761 2772 | | | | o changeset: 17:44765d7c06e0
2762 2773 | | | | |\ parent: 12:86b91144a6e9
2763 2774 | | | | | | parent: 16:3677d192927d
2764 2775 | | | | | | user: test
2765 2776 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
2766 2777 | | | | | | summary: (17) expand
2767 2778 | | | | | |
2768 2779 +-+-------o changeset: 16:3677d192927d
2769 2780 | | | | | parent: 0:e6eb3150255d
2770 2781 | | | | | parent: 1:6db2ef61d156
2771 2782 | | | | | user: test
2772 2783 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
2773 2784 | | | | | summary: (16) merge two known; one immediate right, one near right
2774 2785 | | | | |
2775 2786 | | | o | changeset: 15:1dda3f72782d
2776 2787 | | | |\ \ parent: 13:22d8966a97e3
2777 2788 | | | | | | parent: 14:8eac370358ef
2778 2789 | | | | | | user: test
2779 2790 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
2780 2791 | | | | | | summary: (15) expand
2781 2792 | | | | | |
2782 2793 +-------o | changeset: 14:8eac370358ef
2783 2794 | | | | |/ parent: 0:e6eb3150255d
2784 2795 | | | | | parent: 12:86b91144a6e9
2785 2796 | | | | | user: test
2786 2797 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
2787 2798 | | | | | summary: (14) merge two known; one immediate right, one far right
2788 2799 | | | | |
2789 2800 | | | o | changeset: 13:22d8966a97e3
2790 2801 | | | |\ \ parent: 9:7010c0af0a35
2791 2802 | | | | | | parent: 11:832d76e6bdf2
2792 2803 | | | | | | user: test
2793 2804 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
2794 2805 | | | | | | summary: (13) expand
2795 2806 | | | | | |
2796 2807 | +---+---o changeset: 12:86b91144a6e9
2797 2808 | | | | | parent: 1:6db2ef61d156
2798 2809 | | | | | parent: 9:7010c0af0a35
2799 2810 | | | | | user: test
2800 2811 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
2801 2812 | | | | | summary: (12) merge two known; one immediate right, one far left
2802 2813 | | | | |
2803 2814 | | | | o changeset: 11:832d76e6bdf2
2804 2815 | | | | |\ parent: 6:b105a072e251
2805 2816 | | | | | | parent: 10:74c64d036d72
2806 2817 | | | | | | user: test
2807 2818 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
2808 2819 | | | | | | summary: (11) expand
2809 2820 | | | | | |
2810 2821 +---------o changeset: 10:74c64d036d72
2811 2822 | | | | |/ parent: 0:e6eb3150255d
2812 2823 | | | | | parent: 6:b105a072e251
2813 2824 | | | | | user: test
2814 2825 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
2815 2826 | | | | | summary: (10) merge two known; one immediate left, one near right
2816 2827 | | | | |
2817 2828 | | | o | changeset: 9:7010c0af0a35
2818 2829 | | | |\ \ parent: 7:b632bb1b1224
2819 2830 | | | | | | parent: 8:7a0b11f71937
2820 2831 | | | | | | user: test
2821 2832 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
2822 2833 | | | | | | summary: (9) expand
2823 2834 | | | | | |
2824 2835 +-------o | changeset: 8:7a0b11f71937
2825 2836 | | | |/ / parent: 0:e6eb3150255d
2826 2837 | | | | | parent: 7:b632bb1b1224
2827 2838 | | | | | user: test
2828 2839 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
2829 2840 | | | | | summary: (8) merge two known; one immediate left, one far right
2830 2841 | | | | |
2831 2842 | | | o | changeset: 7:b632bb1b1224
2832 2843 | | | |\ \ parent: 2:3d9a33b8d1e1
2833 2844 | | | | | | parent: 5:4409d547b708
2834 2845 | | | | | | user: test
2835 2846 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
2836 2847 | | | | | | summary: (7) expand
2837 2848 | | | | | |
2838 2849 | | | +---o changeset: 6:b105a072e251
2839 2850 | | | | |/ parent: 2:3d9a33b8d1e1
2840 2851 | | | | | parent: 5:4409d547b708
2841 2852 | | | | | user: test
2842 2853 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
2843 2854 | | | | | summary: (6) merge two known; one immediate left, one far left
2844 2855 | | | | |
2845 2856 | | | o | changeset: 5:4409d547b708
2846 2857 | | | |\ \ parent: 3:27eef8ed80b4
2847 2858 | | | | | | parent: 4:26a8bac39d9f
2848 2859 | | | | | | user: test
2849 2860 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
2850 2861 | | | | | | summary: (5) expand
2851 2862 | | | | | |
2852 2863 | +---o | | changeset: 4:26a8bac39d9f
2853 2864 | | | |/ / parent: 1:6db2ef61d156
2854 2865 | | | | | parent: 3:27eef8ed80b4
2855 2866 | | | | | user: test
2856 2867 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
2857 2868 | | | | | summary: (4) merge two known; one immediate left, one immediate right
2858 2869 | | | | |
2859 2870
2860 2871 .. unless HGPLAINEXCEPT=graph is set:
2861 2872
2862 2873 $ HGPLAIN=1 HGPLAINEXCEPT=graph hg log -G -r 'file("a")' -m
2863 2874 @ changeset: 36:08a19a744424
2864 2875 : branch: branch
2865 2876 : tag: tip
2866 2877 : parent: 35:9159c3644c5e
2867 2878 : parent: 35:9159c3644c5e
2868 2879 : user: test
2869 2880 : date: Thu Jan 01 00:00:36 1970 +0000
2870 2881 : summary: (36) buggy merge: identical parents
2871 2882 :
2872 2883 o changeset: 32:d06dffa21a31
2873 2884 |\ parent: 27:886ed638191b
2874 2885 | : parent: 31:621d83e11f67
2875 2886 | : user: test
2876 2887 | : date: Thu Jan 01 00:00:32 1970 +0000
2877 2888 | : summary: (32) expand
2878 2889 | :
2879 2890 o : changeset: 31:621d83e11f67
2880 2891 |\: parent: 21:d42a756af44d
2881 2892 | : parent: 30:6e11cd4b648f
2882 2893 | : user: test
2883 2894 | : date: Thu Jan 01 00:00:31 1970 +0000
2884 2895 | : summary: (31) expand
2885 2896 | :
2886 2897 o : changeset: 30:6e11cd4b648f
2887 2898 |\ \ parent: 28:44ecd0b9ae99
2888 2899 | ~ : parent: 29:cd9bb2be7593
2889 2900 | : user: test
2890 2901 | : date: Thu Jan 01 00:00:30 1970 +0000
2891 2902 | : summary: (30) expand
2892 2903 | /
2893 2904 o : changeset: 28:44ecd0b9ae99
2894 2905 |\ \ parent: 1:6db2ef61d156
2895 2906 | ~ : parent: 26:7f25b6c2f0b9
2896 2907 | : user: test
2897 2908 | : date: Thu Jan 01 00:00:28 1970 +0000
2898 2909 | : summary: (28) merge zero known
2899 2910 | /
2900 2911 o : changeset: 26:7f25b6c2f0b9
2901 2912 |\ \ parent: 18:1aa84d96232a
2902 2913 | | : parent: 25:91da8ed57247
2903 2914 | | : user: test
2904 2915 | | : date: Thu Jan 01 00:00:26 1970 +0000
2905 2916 | | : summary: (26) merge one known; far right
2906 2917 | | :
2907 2918 | o : changeset: 25:91da8ed57247
2908 2919 | |\: parent: 21:d42a756af44d
2909 2920 | | : parent: 24:a9c19a3d96b7
2910 2921 | | : user: test
2911 2922 | | : date: Thu Jan 01 00:00:25 1970 +0000
2912 2923 | | : summary: (25) merge one known; far left
2913 2924 | | :
2914 2925 | o : changeset: 24:a9c19a3d96b7
2915 2926 | |\ \ parent: 0:e6eb3150255d
2916 2927 | | ~ : parent: 23:a01cddf0766d
2917 2928 | | : user: test
2918 2929 | | : date: Thu Jan 01 00:00:24 1970 +0000
2919 2930 | | : summary: (24) merge one known; immediate right
2920 2931 | | /
2921 2932 | o : changeset: 23:a01cddf0766d
2922 2933 | |\ \ parent: 1:6db2ef61d156
2923 2934 | | ~ : parent: 22:e0d9cccacb5d
2924 2935 | | : user: test
2925 2936 | | : date: Thu Jan 01 00:00:23 1970 +0000
2926 2937 | | : summary: (23) merge one known; immediate left
2927 2938 | | /
2928 2939 | o : changeset: 22:e0d9cccacb5d
2929 2940 |/:/ parent: 18:1aa84d96232a
2930 2941 | : parent: 21:d42a756af44d
2931 2942 | : user: test
2932 2943 | : date: Thu Jan 01 00:00:22 1970 +0000
2933 2944 | : summary: (22) merge two known; one far left, one far right
2934 2945 | :
2935 2946 | o changeset: 21:d42a756af44d
2936 2947 | |\ parent: 19:31ddc2c1573b
2937 2948 | | | parent: 20:d30ed6450e32
2938 2949 | | | user: test
2939 2950 | | | date: Thu Jan 01 00:00:21 1970 +0000
2940 2951 | | | summary: (21) expand
2941 2952 | | |
2942 2953 +---o changeset: 20:d30ed6450e32
2943 2954 | | | parent: 0:e6eb3150255d
2944 2955 | | ~ parent: 18:1aa84d96232a
2945 2956 | | user: test
2946 2957 | | date: Thu Jan 01 00:00:20 1970 +0000
2947 2958 | | summary: (20) merge two known; two far right
2948 2959 | |
2949 2960 | o changeset: 19:31ddc2c1573b
2950 2961 | |\ parent: 15:1dda3f72782d
2951 2962 | | | parent: 17:44765d7c06e0
2952 2963 | | | user: test
2953 2964 | | | date: Thu Jan 01 00:00:19 1970 +0000
2954 2965 | | | summary: (19) expand
2955 2966 | | |
2956 2967 o | | changeset: 18:1aa84d96232a
2957 2968 |\| | parent: 1:6db2ef61d156
2958 2969 ~ | | parent: 15:1dda3f72782d
2959 2970 | | user: test
2960 2971 | | date: Thu Jan 01 00:00:18 1970 +0000
2961 2972 | | summary: (18) merge two known; two far left
2962 2973 / /
2963 2974 | o changeset: 17:44765d7c06e0
2964 2975 | |\ parent: 12:86b91144a6e9
2965 2976 | | | parent: 16:3677d192927d
2966 2977 | | | user: test
2967 2978 | | | date: Thu Jan 01 00:00:17 1970 +0000
2968 2979 | | | summary: (17) expand
2969 2980 | | |
2970 2981 | | o changeset: 16:3677d192927d
2971 2982 | | |\ parent: 0:e6eb3150255d
2972 2983 | | ~ ~ parent: 1:6db2ef61d156
2973 2984 | | user: test
2974 2985 | | date: Thu Jan 01 00:00:16 1970 +0000
2975 2986 | | summary: (16) merge two known; one immediate right, one near right
2976 2987 | |
2977 2988 o | changeset: 15:1dda3f72782d
2978 2989 |\ \ parent: 13:22d8966a97e3
2979 2990 | | | parent: 14:8eac370358ef
2980 2991 | | | user: test
2981 2992 | | | date: Thu Jan 01 00:00:15 1970 +0000
2982 2993 | | | summary: (15) expand
2983 2994 | | |
2984 2995 | o | changeset: 14:8eac370358ef
2985 2996 | |\| parent: 0:e6eb3150255d
2986 2997 | ~ | parent: 12:86b91144a6e9
2987 2998 | | user: test
2988 2999 | | date: Thu Jan 01 00:00:14 1970 +0000
2989 3000 | | summary: (14) merge two known; one immediate right, one far right
2990 3001 | /
2991 3002 o | changeset: 13:22d8966a97e3
2992 3003 |\ \ parent: 9:7010c0af0a35
2993 3004 | | | parent: 11:832d76e6bdf2
2994 3005 | | | user: test
2995 3006 | | | date: Thu Jan 01 00:00:13 1970 +0000
2996 3007 | | | summary: (13) expand
2997 3008 | | |
2998 3009 +---o changeset: 12:86b91144a6e9
2999 3010 | | | parent: 1:6db2ef61d156
3000 3011 | | ~ parent: 9:7010c0af0a35
3001 3012 | | user: test
3002 3013 | | date: Thu Jan 01 00:00:12 1970 +0000
3003 3014 | | summary: (12) merge two known; one immediate right, one far left
3004 3015 | |
3005 3016 | o changeset: 11:832d76e6bdf2
3006 3017 | |\ parent: 6:b105a072e251
3007 3018 | | | parent: 10:74c64d036d72
3008 3019 | | | user: test
3009 3020 | | | date: Thu Jan 01 00:00:11 1970 +0000
3010 3021 | | | summary: (11) expand
3011 3022 | | |
3012 3023 | | o changeset: 10:74c64d036d72
3013 3024 | |/| parent: 0:e6eb3150255d
3014 3025 | | ~ parent: 6:b105a072e251
3015 3026 | | user: test
3016 3027 | | date: Thu Jan 01 00:00:10 1970 +0000
3017 3028 | | summary: (10) merge two known; one immediate left, one near right
3018 3029 | |
3019 3030 o | changeset: 9:7010c0af0a35
3020 3031 |\ \ parent: 7:b632bb1b1224
3021 3032 | | | parent: 8:7a0b11f71937
3022 3033 | | | user: test
3023 3034 | | | date: Thu Jan 01 00:00:09 1970 +0000
3024 3035 | | | summary: (9) expand
3025 3036 | | |
3026 3037 | o | changeset: 8:7a0b11f71937
3027 3038 |/| | parent: 0:e6eb3150255d
3028 3039 | ~ | parent: 7:b632bb1b1224
3029 3040 | | user: test
3030 3041 | | date: Thu Jan 01 00:00:08 1970 +0000
3031 3042 | | summary: (8) merge two known; one immediate left, one far right
3032 3043 | /
3033 3044 o | changeset: 7:b632bb1b1224
3034 3045 |\ \ parent: 2:3d9a33b8d1e1
3035 3046 | ~ | parent: 5:4409d547b708
3036 3047 | | user: test
3037 3048 | | date: Thu Jan 01 00:00:07 1970 +0000
3038 3049 | | summary: (7) expand
3039 3050 | /
3040 3051 | o changeset: 6:b105a072e251
3041 3052 |/| parent: 2:3d9a33b8d1e1
3042 3053 | ~ parent: 5:4409d547b708
3043 3054 | user: test
3044 3055 | date: Thu Jan 01 00:00:06 1970 +0000
3045 3056 | summary: (6) merge two known; one immediate left, one far left
3046 3057 |
3047 3058 o changeset: 5:4409d547b708
3048 3059 |\ parent: 3:27eef8ed80b4
3049 3060 | ~ parent: 4:26a8bac39d9f
3050 3061 | user: test
3051 3062 | date: Thu Jan 01 00:00:05 1970 +0000
3052 3063 | summary: (5) expand
3053 3064 |
3054 3065 o changeset: 4:26a8bac39d9f
3055 3066 |\ parent: 1:6db2ef61d156
3056 3067 ~ ~ parent: 3:27eef8ed80b4
3057 3068 user: test
3058 3069 date: Thu Jan 01 00:00:04 1970 +0000
3059 3070 summary: (4) merge two known; one immediate left, one immediate right
3060 3071
3061 3072 Draw only part of a grandparent line differently with "<N><char>"; only the
3062 3073 last N lines (for positive N) or everything but the first N lines (for
3063 3074 negative N) along the current node use the style, the rest of the edge uses
3064 3075 the parent edge styling.
3065 3076
3066 3077 Last 3 lines:
3067 3078
3068 3079 $ cat << EOF >> $HGRCPATH
3069 3080 > [experimental]
3070 3081 > graphstyle.parent = !
3071 3082 > graphstyle.grandparent = 3.
3072 3083 > graphstyle.missing =
3073 3084 > EOF
3074 3085 $ hg log -G -r '36:18 & file("a")' -m
3075 3086 @ changeset: 36:08a19a744424
3076 3087 ! branch: branch
3077 3088 ! tag: tip
3078 3089 ! parent: 35:9159c3644c5e
3079 3090 ! parent: 35:9159c3644c5e
3080 3091 ! user: test
3081 3092 . date: Thu Jan 01 00:00:36 1970 +0000
3082 3093 . summary: (36) buggy merge: identical parents
3083 3094 .
3084 3095 o changeset: 32:d06dffa21a31
3085 3096 !\ parent: 27:886ed638191b
3086 3097 ! ! parent: 31:621d83e11f67
3087 3098 ! ! user: test
3088 3099 ! . date: Thu Jan 01 00:00:32 1970 +0000
3089 3100 ! . summary: (32) expand
3090 3101 ! .
3091 3102 o ! changeset: 31:621d83e11f67
3092 3103 !\! parent: 21:d42a756af44d
3093 3104 ! ! parent: 30:6e11cd4b648f
3094 3105 ! ! user: test
3095 3106 ! ! date: Thu Jan 01 00:00:31 1970 +0000
3096 3107 ! ! summary: (31) expand
3097 3108 ! !
3098 3109 o ! changeset: 30:6e11cd4b648f
3099 3110 !\ \ parent: 28:44ecd0b9ae99
3100 3111 ! ~ ! parent: 29:cd9bb2be7593
3101 3112 ! ! user: test
3102 3113 ! ! date: Thu Jan 01 00:00:30 1970 +0000
3103 3114 ! ! summary: (30) expand
3104 3115 ! /
3105 3116 o ! changeset: 28:44ecd0b9ae99
3106 3117 !\ \ parent: 1:6db2ef61d156
3107 3118 ! ~ ! parent: 26:7f25b6c2f0b9
3108 3119 ! ! user: test
3109 3120 ! ! date: Thu Jan 01 00:00:28 1970 +0000
3110 3121 ! ! summary: (28) merge zero known
3111 3122 ! /
3112 3123 o ! changeset: 26:7f25b6c2f0b9
3113 3124 !\ \ parent: 18:1aa84d96232a
3114 3125 ! ! ! parent: 25:91da8ed57247
3115 3126 ! ! ! user: test
3116 3127 ! ! ! date: Thu Jan 01 00:00:26 1970 +0000
3117 3128 ! ! ! summary: (26) merge one known; far right
3118 3129 ! ! !
3119 3130 ! o ! changeset: 25:91da8ed57247
3120 3131 ! !\! parent: 21:d42a756af44d
3121 3132 ! ! ! parent: 24:a9c19a3d96b7
3122 3133 ! ! ! user: test
3123 3134 ! ! ! date: Thu Jan 01 00:00:25 1970 +0000
3124 3135 ! ! ! summary: (25) merge one known; far left
3125 3136 ! ! !
3126 3137 ! o ! changeset: 24:a9c19a3d96b7
3127 3138 ! !\ \ parent: 0:e6eb3150255d
3128 3139 ! ! ~ ! parent: 23:a01cddf0766d
3129 3140 ! ! ! user: test
3130 3141 ! ! ! date: Thu Jan 01 00:00:24 1970 +0000
3131 3142 ! ! ! summary: (24) merge one known; immediate right
3132 3143 ! ! /
3133 3144 ! o ! changeset: 23:a01cddf0766d
3134 3145 ! !\ \ parent: 1:6db2ef61d156
3135 3146 ! ! ~ ! parent: 22:e0d9cccacb5d
3136 3147 ! ! ! user: test
3137 3148 ! ! ! date: Thu Jan 01 00:00:23 1970 +0000
3138 3149 ! ! ! summary: (23) merge one known; immediate left
3139 3150 ! ! /
3140 3151 ! o ! changeset: 22:e0d9cccacb5d
3141 3152 !/!/ parent: 18:1aa84d96232a
3142 3153 ! ! parent: 21:d42a756af44d
3143 3154 ! ! user: test
3144 3155 ! ! date: Thu Jan 01 00:00:22 1970 +0000
3145 3156 ! ! summary: (22) merge two known; one far left, one far right
3146 3157 ! !
3147 3158 ! o changeset: 21:d42a756af44d
3148 3159 ! !\ parent: 19:31ddc2c1573b
3149 3160 ! ! ! parent: 20:d30ed6450e32
3150 3161 ! ! ! user: test
3151 3162 ! ! ! date: Thu Jan 01 00:00:21 1970 +0000
3152 3163 ! ! ! summary: (21) expand
3153 3164 ! ! !
3154 3165 +---o changeset: 20:d30ed6450e32
3155 3166 ! ! | parent: 0:e6eb3150255d
3156 3167 ! ! ~ parent: 18:1aa84d96232a
3157 3168 ! ! user: test
3158 3169 ! ! date: Thu Jan 01 00:00:20 1970 +0000
3159 3170 ! ! summary: (20) merge two known; two far right
3160 3171 ! !
3161 3172 ! o changeset: 19:31ddc2c1573b
3162 3173 ! |\ parent: 15:1dda3f72782d
3163 3174 ! ~ ~ parent: 17:44765d7c06e0
3164 3175 ! user: test
3165 3176 ! date: Thu Jan 01 00:00:19 1970 +0000
3166 3177 ! summary: (19) expand
3167 3178 !
3168 3179 o changeset: 18:1aa84d96232a
3169 3180 |\ parent: 1:6db2ef61d156
3170 3181 ~ ~ parent: 15:1dda3f72782d
3171 3182 user: test
3172 3183 date: Thu Jan 01 00:00:18 1970 +0000
3173 3184 summary: (18) merge two known; two far left
3174 3185
3175 3186 All but the first 3 lines:
3176 3187
3177 3188 $ cat << EOF >> $HGRCPATH
3178 3189 > [experimental]
3179 3190 > graphstyle.parent = !
3180 3191 > graphstyle.grandparent = -3.
3181 3192 > graphstyle.missing =
3182 3193 > EOF
3183 3194 $ hg log -G -r '36:18 & file("a")' -m
3184 3195 @ changeset: 36:08a19a744424
3185 3196 ! branch: branch
3186 3197 ! tag: tip
3187 3198 . parent: 35:9159c3644c5e
3188 3199 . parent: 35:9159c3644c5e
3189 3200 . user: test
3190 3201 . date: Thu Jan 01 00:00:36 1970 +0000
3191 3202 . summary: (36) buggy merge: identical parents
3192 3203 .
3193 3204 o changeset: 32:d06dffa21a31
3194 3205 !\ parent: 27:886ed638191b
3195 3206 ! ! parent: 31:621d83e11f67
3196 3207 ! . user: test
3197 3208 ! . date: Thu Jan 01 00:00:32 1970 +0000
3198 3209 ! . summary: (32) expand
3199 3210 ! .
3200 3211 o ! changeset: 31:621d83e11f67
3201 3212 !\! parent: 21:d42a756af44d
3202 3213 ! ! parent: 30:6e11cd4b648f
3203 3214 ! ! user: test
3204 3215 ! ! date: Thu Jan 01 00:00:31 1970 +0000
3205 3216 ! ! summary: (31) expand
3206 3217 ! !
3207 3218 o ! changeset: 30:6e11cd4b648f
3208 3219 !\ \ parent: 28:44ecd0b9ae99
3209 3220 ! ~ ! parent: 29:cd9bb2be7593
3210 3221 ! ! user: test
3211 3222 ! ! date: Thu Jan 01 00:00:30 1970 +0000
3212 3223 ! ! summary: (30) expand
3213 3224 ! /
3214 3225 o ! changeset: 28:44ecd0b9ae99
3215 3226 !\ \ parent: 1:6db2ef61d156
3216 3227 ! ~ ! parent: 26:7f25b6c2f0b9
3217 3228 ! ! user: test
3218 3229 ! ! date: Thu Jan 01 00:00:28 1970 +0000
3219 3230 ! ! summary: (28) merge zero known
3220 3231 ! /
3221 3232 o ! changeset: 26:7f25b6c2f0b9
3222 3233 !\ \ parent: 18:1aa84d96232a
3223 3234 ! ! ! parent: 25:91da8ed57247
3224 3235 ! ! ! user: test
3225 3236 ! ! ! date: Thu Jan 01 00:00:26 1970 +0000
3226 3237 ! ! ! summary: (26) merge one known; far right
3227 3238 ! ! !
3228 3239 ! o ! changeset: 25:91da8ed57247
3229 3240 ! !\! parent: 21:d42a756af44d
3230 3241 ! ! ! parent: 24:a9c19a3d96b7
3231 3242 ! ! ! user: test
3232 3243 ! ! ! date: Thu Jan 01 00:00:25 1970 +0000
3233 3244 ! ! ! summary: (25) merge one known; far left
3234 3245 ! ! !
3235 3246 ! o ! changeset: 24:a9c19a3d96b7
3236 3247 ! !\ \ parent: 0:e6eb3150255d
3237 3248 ! ! ~ ! parent: 23:a01cddf0766d
3238 3249 ! ! ! user: test
3239 3250 ! ! ! date: Thu Jan 01 00:00:24 1970 +0000
3240 3251 ! ! ! summary: (24) merge one known; immediate right
3241 3252 ! ! /
3242 3253 ! o ! changeset: 23:a01cddf0766d
3243 3254 ! !\ \ parent: 1:6db2ef61d156
3244 3255 ! ! ~ ! parent: 22:e0d9cccacb5d
3245 3256 ! ! ! user: test
3246 3257 ! ! ! date: Thu Jan 01 00:00:23 1970 +0000
3247 3258 ! ! ! summary: (23) merge one known; immediate left
3248 3259 ! ! /
3249 3260 ! o ! changeset: 22:e0d9cccacb5d
3250 3261 !/!/ parent: 18:1aa84d96232a
3251 3262 ! ! parent: 21:d42a756af44d
3252 3263 ! ! user: test
3253 3264 ! ! date: Thu Jan 01 00:00:22 1970 +0000
3254 3265 ! ! summary: (22) merge two known; one far left, one far right
3255 3266 ! !
3256 3267 ! o changeset: 21:d42a756af44d
3257 3268 ! !\ parent: 19:31ddc2c1573b
3258 3269 ! ! ! parent: 20:d30ed6450e32
3259 3270 ! ! ! user: test
3260 3271 ! ! ! date: Thu Jan 01 00:00:21 1970 +0000
3261 3272 ! ! ! summary: (21) expand
3262 3273 ! ! !
3263 3274 +---o changeset: 20:d30ed6450e32
3264 3275 ! ! | parent: 0:e6eb3150255d
3265 3276 ! ! ~ parent: 18:1aa84d96232a
3266 3277 ! ! user: test
3267 3278 ! ! date: Thu Jan 01 00:00:20 1970 +0000
3268 3279 ! ! summary: (20) merge two known; two far right
3269 3280 ! !
3270 3281 ! o changeset: 19:31ddc2c1573b
3271 3282 ! |\ parent: 15:1dda3f72782d
3272 3283 ! ~ ~ parent: 17:44765d7c06e0
3273 3284 ! user: test
3274 3285 ! date: Thu Jan 01 00:00:19 1970 +0000
3275 3286 ! summary: (19) expand
3276 3287 !
3277 3288 o changeset: 18:1aa84d96232a
3278 3289 |\ parent: 1:6db2ef61d156
3279 3290 ~ ~ parent: 15:1dda3f72782d
3280 3291 user: test
3281 3292 date: Thu Jan 01 00:00:18 1970 +0000
3282 3293 summary: (18) merge two known; two far left
3283 3294
3284 3295 $ cd ..
3285 3296
3286 3297 Change graph shorten, test better with graphstyle.missing not none
3287 3298
3288 3299 $ cd repo
3289 3300 $ cat << EOF >> $HGRCPATH
3290 3301 > [experimental]
3291 3302 > graphstyle.parent = |
3292 3303 > graphstyle.grandparent = :
3293 3304 > graphstyle.missing = '
3294 3305 > graphshorten = true
3295 3306 > EOF
3296 3307 $ hg log -G -r 'file("a")' -m -T '{rev} {desc}'
3297 3308 @ 36 (36) buggy merge: identical parents
3298 3309 o 32 (32) expand
3299 3310 |\
3300 3311 o : 31 (31) expand
3301 3312 |\:
3302 3313 o : 30 (30) expand
3303 3314 |\ \
3304 3315 o \ \ 28 (28) merge zero known
3305 3316 |\ \ \
3306 3317 o \ \ \ 26 (26) merge one known; far right
3307 3318 |\ \ \ \
3308 3319 | o-----+ 25 (25) merge one known; far left
3309 3320 | o ' ' : 24 (24) merge one known; immediate right
3310 3321 | |\ \ \ \
3311 3322 | o---+ ' : 23 (23) merge one known; immediate left
3312 3323 | o-------+ 22 (22) merge two known; one far left, one far right
3313 3324 |/ / / / /
3314 3325 | ' ' ' o 21 (21) expand
3315 3326 | ' ' ' |\
3316 3327 +-+-------o 20 (20) merge two known; two far right
3317 3328 | ' ' ' o 19 (19) expand
3318 3329 | ' ' ' |\
3319 3330 o---+---+ | 18 (18) merge two known; two far left
3320 3331 / / / / /
3321 3332 ' ' ' | o 17 (17) expand
3322 3333 ' ' ' | |\
3323 3334 +-+-------o 16 (16) merge two known; one immediate right, one near right
3324 3335 ' ' ' o | 15 (15) expand
3325 3336 ' ' ' |\ \
3326 3337 +-------o | 14 (14) merge two known; one immediate right, one far right
3327 3338 ' ' ' | |/
3328 3339 ' ' ' o | 13 (13) expand
3329 3340 ' ' ' |\ \
3330 3341 ' +---+---o 12 (12) merge two known; one immediate right, one far left
3331 3342 ' ' ' | o 11 (11) expand
3332 3343 ' ' ' | |\
3333 3344 +---------o 10 (10) merge two known; one immediate left, one near right
3334 3345 ' ' ' | |/
3335 3346 ' ' ' o | 9 (9) expand
3336 3347 ' ' ' |\ \
3337 3348 +-------o | 8 (8) merge two known; one immediate left, one far right
3338 3349 ' ' ' |/ /
3339 3350 ' ' ' o | 7 (7) expand
3340 3351 ' ' ' |\ \
3341 3352 ' ' ' +---o 6 (6) merge two known; one immediate left, one far left
3342 3353 ' ' ' | '/
3343 3354 ' ' ' o ' 5 (5) expand
3344 3355 ' ' ' |\ \
3345 3356 ' +---o ' ' 4 (4) merge two known; one immediate left, one immediate right
3346 3357 ' ' ' '/ /
3347 3358
3348 3359 behavior with newlines
3349 3360
3350 3361 $ hg log -G -r ::2 -T '{rev} {desc}'
3351 3362 o 2 (2) collapse
3352 3363 o 1 (1) collapse
3353 3364 o 0 (0) root
3354 3365
3355 3366 $ hg log -G -r ::2 -T '{rev} {desc}\n'
3356 3367 o 2 (2) collapse
3357 3368 o 1 (1) collapse
3358 3369 o 0 (0) root
3359 3370
3360 3371 $ hg log -G -r ::2 -T '{rev} {desc}\n\n'
3361 3372 o 2 (2) collapse
3362 3373 |
3363 3374 o 1 (1) collapse
3364 3375 |
3365 3376 o 0 (0) root
3366 3377
3367 3378
3368 3379 $ hg log -G -r ::2 -T '\n{rev} {desc}'
3369 3380 o
3370 3381 | 2 (2) collapse
3371 3382 o
3372 3383 | 1 (1) collapse
3373 3384 o
3374 3385 0 (0) root
3375 3386
3376 3387 $ hg log -G -r ::2 -T '{rev} {desc}\n\n\n'
3377 3388 o 2 (2) collapse
3378 3389 |
3379 3390 |
3380 3391 o 1 (1) collapse
3381 3392 |
3382 3393 |
3383 3394 o 0 (0) root
3384 3395
3385 3396
3386 3397 $ cd ..
3387 3398
3388 3399 When inserting extra line nodes to handle more than 2 parents, ensure that
3389 3400 the right node styles are used (issue5174):
3390 3401
3391 3402 $ hg init repo-issue5174
3392 3403 $ cd repo-issue5174
3393 3404 $ echo a > f0
3394 3405 $ hg ci -Aqm 0
3395 3406 $ echo a > f1
3396 3407 $ hg ci -Aqm 1
3397 3408 $ echo a > f2
3398 3409 $ hg ci -Aqm 2
3399 3410 $ hg co ".^"
3400 3411 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
3401 3412 $ echo a > f3
3402 3413 $ hg ci -Aqm 3
3403 3414 $ hg co ".^^"
3404 3415 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
3405 3416 $ echo a > f4
3406 3417 $ hg ci -Aqm 4
3407 3418 $ hg merge -r 2
3408 3419 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
3409 3420 (branch merge, don't forget to commit)
3410 3421 $ hg ci -qm 5
3411 3422 $ hg merge -r 3
3412 3423 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3413 3424 (branch merge, don't forget to commit)
3414 3425 $ hg ci -qm 6
3415 3426 $ hg log -G -r '0 | 1 | 2 | 6'
3416 3427 @ changeset: 6:851fe89689ad
3417 3428 :\ tag: tip
3418 3429 : : parent: 5:4f1e3cf15f5d
3419 3430 : : parent: 3:b74ba7084d2d
3420 3431 : : user: test
3421 3432 : : date: Thu Jan 01 00:00:00 1970 +0000
3422 3433 : : summary: 6
3423 3434 : :
3424 3435 : \
3425 3436 : :\
3426 3437 : o : changeset: 2:3e6599df4cce
3427 3438 : :/ user: test
3428 3439 : : date: Thu Jan 01 00:00:00 1970 +0000
3429 3440 : : summary: 2
3430 3441 : :
3431 3442 : o changeset: 1:bd9a55143933
3432 3443 :/ user: test
3433 3444 : date: Thu Jan 01 00:00:00 1970 +0000
3434 3445 : summary: 1
3435 3446 :
3436 3447 o changeset: 0:870a5edc339c
3437 3448 user: test
3438 3449 date: Thu Jan 01 00:00:00 1970 +0000
3439 3450 summary: 0
3440 3451
3441 3452
3442 3453 $ cd ..
3443 3454
3444 3455 Multiple roots (issue5440):
3445 3456
3446 3457 $ hg init multiroots
3447 3458 $ cd multiroots
3448 3459 $ cat <<EOF > .hg/hgrc
3449 3460 > [ui]
3450 3461 > logtemplate = '{rev} {desc}\n\n'
3451 3462 > EOF
3452 3463
3453 3464 $ touch foo
3454 3465 $ hg ci -Aqm foo
3455 3466 $ hg co -q null
3456 3467 $ touch bar
3457 3468 $ hg ci -Aqm bar
3458 3469
3459 3470 $ hg log -Gr null:
3460 3471 @ 1 bar
3461 3472 |
3462 3473 | o 0 foo
3463 3474 |/
3464 3475 o -1
3465 3476
3466 3477 $ hg log -Gr null+0
3467 3478 o 0 foo
3468 3479 |
3469 3480 o -1
3470 3481
3471 3482 $ hg log -Gr null+1
3472 3483 @ 1 bar
3473 3484 |
3474 3485 o -1
3475 3486
3476 3487
3477 3488 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now