[PATCH 3 of 3 gca-revset] revset: add a new commonancestorheads method

Sean Farley sean at farley.io
Fri Jun 15 03:10:25 EDT 2018


# HG changeset patch
# User Sean Farley <sean at farley.io>
# Date 1527357855 -7200
#      Sat May 26 20:04:15 2018 +0200
# Branch gca-revset
# Node ID d44266127f1a86af521df2b2c088528c3f3b803a
# Parent  ab29cfd39f48432d8e7c38cdceb62980d5c22f09
revset: add a new commonancestorheads method

This is a new revset that provides a public interface to the
ancestor.commonancestorsheads method. Currently, we have no fast revset
way to get the ancestors of a list of changesets as returned by the
consensus bid merge algorithm (the one needed by criss-cross merges).

Previously, the only way to get these commits were (tested on
mozilla-central):

hg perfrevset 'heads(::a7cf55 and ::d8b15)'
! wall 4.988366 comb 4.960000 user 4.780000 sys 0.180000 (best of 3)

After this patch:

hg perfrevset 'commonancestorheads(a7cf55, d8b15)'
! wall 0.001790 comb 0.000000 user 0.000000 sys 0.000000 (best of 1340)

I'm not a fan of added more revset methods for performance but, yet,
here we are.

diff --git a/mercurial/revset.py b/mercurial/revset.py
index de543df..4004f4b 100644
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -299,10 +299,34 @@ def adds(repo, subset, x):
     """
     # i18n: "adds" is a keyword
     pat = getstring(x, _("adds requires a pattern"))
     return checkstatus(repo, subset, pat, 1)
 
+ at predicate('commonancestorheads(*changeset)', safe=True, weight=0.5)
+def commonancestorheads(repo, subset, x):
+    """Returns all greatest common ancestors of the changesets.
+
+    Accepts 0 or more changesets. Will return empty list when passed no args.
+    Greatest common ancestor of a single changeset is that changeset.
+
+    These greatest common ancestors the same ones that the consesus bid merge
+    will find.
+
+    """
+    # i18n: "ancestor" is a keyword
+    from .ancestor import commonancestorsheads as cah
+    l = getlist(x)
+    rl = fullreposet(repo)
+    input_revs = []
+
+    # (getset(repo, rl, i) for i in l) generates a list of lists
+    for revs in (getset(repo, rl, i) for i in l):
+        for r in revs:
+            input_revs.append(r)
+
+    return subset & cah(repo.changelog.parentrevs, *input_revs)
+
 @predicate('ancestor(*changeset)', safe=True, weight=0.5)
 def ancestor(repo, subset, x):
     """A greatest common ancestor of the changesets.
 
     Accepts 0 or more changesets.
diff --git a/tests/test-merge-criss-cross.t b/tests/test-merge-criss-cross.t
index 4901da2..f2a96e7 100644
--- a/tests/test-merge-criss-cross.t
+++ b/tests/test-merge-criss-cross.t
@@ -159,10 +159,25 @@ Criss cross merging
   merging f2
   3 files updated, 0 files merged, 0 files removed, 1 files unresolved
   use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
   [1]
 
+Test the greatest common ancestor returning multiple changesets
+
+  $ hg log -r 'commonancestorheads(head())'
+  changeset:   1:0f6b37dbe527
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     1 first change f1
+  
+  changeset:   2:d1d156401c1b
+  parent:      0:40494bf2444c
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     2 first change f2
+  
+
 Redo merge with merge.preferancestor="*" to enable bid merge
 
   $ rm f*
   $ hg up -qC .
   $ hg merge -v --debug --tool internal:dump 5 --config merge.preferancestor="*"


More information about the Mercurial-devel mailing list