[PATCH] graphlog: add glog --ancestors to see paths to common ancestor

Peter Arrenbrecht peter.arrenbrecht at gmail.com
Wed Apr 7 04:11:18 CDT 2010


# HG changeset patch
# User Peter Arrenbrecht <peter.arrenbrecht at gmail.com>
# Date 1270631415 -7200
graphlog: add glog --ancestors to see paths to common ancestor

Adds the --ancestors/-a option to glog. It plots just the paths
from two nodes to their common ancestor. It takes either two
explicit nodes as arguments, or a merge node which is graphed
together with its parents and the paths to their ancestor, or
'.' to graph the parents of an uncommitted merge.

diff --git a/hgext/graphlog.py b/hgext/graphlog.py
--- a/hgext/graphlog.py
+++ b/hgext/graphlog.py
@@ -17,9 +17,29 @@
 from mercurial.commands import templateopts
 from mercurial.i18n import _
 from mercurial.node import nullrev
+from mercurial.graphmod import CHANGESET
 from mercurial import bundlerepo, changegroup, cmdutil, commands, extensions
 from mercurial import hg, url, util, graphmod
 
+def ancestors(repo, a, b, child=None):
+    """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples
+
+    This generator function walks from the given nodes a and b back to their
+    closest common ancestor. If child is given, it must be a direct child of both
+    a and b and is displayed too.
+    """
+    cl = repo.changelog
+    commonnode = cl.ancestor(a, b)
+    nodes, _oh, _oc = cl.nodesbetween(roots=[commonnode], heads=[a, b])
+    revs = [cl.rev(n) for n in nodes]
+    if child:
+        revs.append(cl.rev(child))
+    revset = set(revs)
+    for r in reversed(revs):
+        ncx = repo[r]
+        parents = [p.rev() for p in ncx.parents() if p.rev() in revset]
+        yield (r, CHANGESET, ncx, sorted(parents))
+
 ASCIIDATA = 'ASC'
 
 def asciiedges(seen, rev, parents):
@@ -229,7 +249,7 @@
         lines = displayer.hunk.pop(rev).split('\n')[:-1]
         ascii(ui, state, type, char, lines, edgefn(seen, rev, parents))
 
-def graphlog(ui, repo, path=None, **opts):
+def graphlog(ui, repo, *pathOrRevs, **opts):
     """show revision history alongside an ASCII revision graph
 
     Print a revision history alongside a revision graph drawn with
@@ -237,22 +257,55 @@
 
     Nodes printed as an @ character are parents of the working
     directory.
+
+    If --ancestors is given, you must specify either two nodes, or a single merge
+    node, or . to select the two parents of the current uncommitted merge.
     """
 
     check_unsupported_flags(opts)
     limit = cmdutil.loglimit(opts)
-    start, stop = get_revs(repo, opts["rev"])
-    if start == nullrev:
-        return
 
-    if path:
-        path = util.canonpath(repo.root, os.getcwd(), path)
-    if path: # could be reset in canonpath
-        revdag = graphmod.filerevs(repo, path, start, stop, limit)
+    ancs = opts.get("ancestors")
+    if ancs:
+        revs = pathOrRevs
+        if len(revs) == 1:
+            if revs[0] == '.':
+                # dirstate must be merge
+                ds = repo.dirstate
+                ps = ds.parents()
+                if len(ps) != 2:
+                    raise util.Abort(_('not an uncommitted merge'))
+                a, b = ps
+                child = None
+            else:
+                # must be merge node
+                cx = repo[revs[0]]
+                ps = [p.node() for p in cx.parents()]
+                if len(ps) != 2:
+                    raise util.Abort(_('not a merge node'))
+                a, b = ps
+                child = cx.node()
+        elif len(revs) == 2:
+            a, b = [repo[a].node() for a in revs]
+            child = None
+        else:
+            raise util.Abort(_('must specify exactly two nodes (or one merge node)'))
+        revdag = ancestors(repo, a, b, child)
     else:
-        if limit is not None:
-            stop = max(stop, start - limit + 1)
-        revdag = graphmod.revisions(repo, start, stop)
+        start, stop = get_revs(repo, opts["rev"])
+        if start == nullrev:
+            return
+        path = None
+        if pathOrRevs:
+            if len(pathOrRevs) > 1:
+                raise util.Abort(_('specify exactly one file'))
+            path = util.canonpath(repo.root, os.getcwd(), pathOrRevs[0])
+        if path: # could be reset in canonpath
+            revdag = graphmod.filerevs(repo, path, start, stop, limit)
+        else:
+            if limit is not None:
+                stop = max(stop, start - limit + 1)
+            revdag = graphmod.revisions(repo, start, stop)
 
     displayer = show_changeset(ui, repo, opts, buffered=True)
     showparents = [ctx.node() for ctx in repo[None].parents()]
@@ -372,6 +425,7 @@
          [('l', 'limit', '', _('limit number of changes displayed')),
           ('p', 'patch', False, _('show patch')),
           ('r', 'rev', [], _('show the specified revision or range')),
+          ('a', 'ancestors', None, _('show path to common ancestor of two nodes')),
          ] + templateopts,
-         _('hg glog [OPTION]... [FILE]')),
+         _('hg glog [OPTION]... [FILE] | [REV]...')),
 }
diff --git a/tests/test-glog b/tests/test-glog
--- a/tests/test-glog
+++ b/tests/test-glog
@@ -146,6 +146,19 @@
 echo % unused arguments
 hg glog -q foo bar || echo failed
 
+echo % ancestry of merge
+hg glog -a 21
+
+echo % ancestry of two nodes
+hg glog -a 20 19
+
+echo % ancestry of uncommitted merge
+hg clone . ../pendmerge
+cd ../pendmerge
+hg up 19
+HGMERGE=internal:local hg merge 20
+hg glog -a .
+
 echo % from outer space
 cd ..
 hg glog -l1 repo
diff --git a/tests/test-glog.out b/tests/test-glog.out
--- a/tests/test-glog.out
+++ b/tests/test-glog.out
@@ -543,11 +543,107 @@
    summary:     (0) root
 
 % unused arguments
-hg glog: invalid arguments
-hg glog [OPTION]... [FILE]
+abort: specify exactly one file
+failed
+% ancestry of merge
+o    changeset:   21:d42a756af44d
+|\   parent:      19:31ddc2c1573b
+| |  parent:      20:d30ed6450e32
+| |  user:        test
+| |  date:        Thu Jan 01 00:00:21 1970 +0000
+| |  summary:     (21) expand
+| |
+| o  changeset:   20:d30ed6450e32
+| |  parent:      0:e6eb3150255d
+| |  parent:      18:1aa84d96232a
+| |  user:        test
+| |  date:        Thu Jan 01 00:00:20 1970 +0000
+| |  summary:     (20) merge two known; two far right
+| |
+o |  changeset:   19:31ddc2c1573b
+| |  parent:      15:1dda3f72782d
+| |  parent:      17:44765d7c06e0
+| |  user:        test
+| |  date:        Thu Jan 01 00:00:19 1970 +0000
+| |  summary:     (19) expand
+| |
+| o  changeset:   18:1aa84d96232a
+|/   parent:      1:6db2ef61d156
+|    parent:      15:1dda3f72782d
+|    user:        test
+|    date:        Thu Jan 01 00:00:18 1970 +0000
+|    summary:     (18) merge two known; two far left
+|
+o  changeset:   15:1dda3f72782d
+   parent:      13:22d8966a97e3
+   parent:      14:8eac370358ef
+   user:        test
+   date:        Thu Jan 01 00:00:15 1970 +0000
+   summary:     (15) expand
 
-show revision history alongside an ASCII revision graph
-failed
+% ancestry of two nodes
+o  changeset:   20:d30ed6450e32
+|  parent:      0:e6eb3150255d
+|  parent:      18:1aa84d96232a
+|  user:        test
+|  date:        Thu Jan 01 00:00:20 1970 +0000
+|  summary:     (20) merge two known; two far right
+|
+| o  changeset:   19:31ddc2c1573b
+| |  parent:      15:1dda3f72782d
+| |  parent:      17:44765d7c06e0
+| |  user:        test
+| |  date:        Thu Jan 01 00:00:19 1970 +0000
+| |  summary:     (19) expand
+| |
+o |  changeset:   18:1aa84d96232a
+|/   parent:      1:6db2ef61d156
+|    parent:      15:1dda3f72782d
+|    user:        test
+|    date:        Thu Jan 01 00:00:18 1970 +0000
+|    summary:     (18) merge two known; two far left
+|
+o  changeset:   15:1dda3f72782d
+   parent:      13:22d8966a97e3
+   parent:      14:8eac370358ef
+   user:        test
+   date:        Thu Jan 01 00:00:15 1970 +0000
+   summary:     (15) expand
+
+% ancestry of uncommitted merge
+updating to branch default
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+@  changeset:   20:d30ed6450e32
+|  parent:      0:e6eb3150255d
+|  parent:      18:1aa84d96232a
+|  user:        test
+|  date:        Thu Jan 01 00:00:20 1970 +0000
+|  summary:     (20) merge two known; two far right
+|
+| @  changeset:   19:31ddc2c1573b
+| |  parent:      15:1dda3f72782d
+| |  parent:      17:44765d7c06e0
+| |  user:        test
+| |  date:        Thu Jan 01 00:00:19 1970 +0000
+| |  summary:     (19) expand
+| |
+o |  changeset:   18:1aa84d96232a
+|/   parent:      1:6db2ef61d156
+|    parent:      15:1dda3f72782d
+|    user:        test
+|    date:        Thu Jan 01 00:00:18 1970 +0000
+|    summary:     (18) merge two known; two far left
+|
+o  changeset:   15:1dda3f72782d
+   parent:      13:22d8966a97e3
+   parent:      14:8eac370358ef
+   user:        test
+   date:        Thu Jan 01 00:00:15 1970 +0000
+   summary:     (15) expand
+
 % from outer space
 @  changeset:   34:fea3ac5810e0
 |  tag:         tip


More information about the Mercurial-devel mailing list