# HG changeset patch # User Sean Farley # Date 2018-06-26 22:26:21 # Node ID 52f19a8405432472db74c46146671284866ab371 # Parent 5460926352ee95842e3d379ffd0ab3b7ef7e3ff4 revset: add optimization for heads(commonancestors()) 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: (python) hg perfrevset 'heads(commonancestors(a7cf55 + d8b15))' ! wall 0.002155 comb 0.000000 user 0.000000 sys 0.000000 (best of 1107) (C) hg perfrevset 'heads(commonancestors(a7cf55 + d8b15))' ! wall 0.000568 comb 0.000000 user 0.000000 sys 0.000000 (best of 4646) diff --git a/mercurial/revset.py b/mercurial/revset.py --- a/mercurial/revset.py +++ b/mercurial/revset.py @@ -608,6 +608,19 @@ def closed(repo, subset, x): return subset.filter(lambda r: repo[r].closesbranch(), condrepr='') +# for internal use +@predicate('_commonancestorheads(set)', safe=True) +def _commonancestorheads(repo, subset, x): + # This is an internal method is for quickly calculating "heads(::x and + # ::y)" + + # These greatest common ancestors are the same ones that the consesus bid + # merge will find. + h = heads(repo, fullreposet(repo), x, defineorder) + + ancs = repo.changelog._commonancestorsheads(*list(h)) + return subset & baseset(ancs) + @predicate('commonancestors(set)', safe=True) def commonancestors(repo, subset, x): """Returns all common ancestors of the set. diff --git a/mercurial/revsetlang.py b/mercurial/revsetlang.py --- a/mercurial/revsetlang.py +++ b/mercurial/revsetlang.py @@ -459,6 +459,12 @@ def _optimize(x): f = getsymbol(x[1]) wa, ta = _optimize(x[2]) w = getattr(symbols.get(f), '_weight', 1) + m = _match('commonancestors(_)', ta) + + # Optimize heads(commonancestors(_)) because we have a fast version + if f == 'heads' and m: + return w + wa, _build('_commonancestorheads(_)', m[1]) + return w + wa, (op, x[1], ta) raise ValueError('invalid operator %r' % op) diff --git a/tests/test-merge-criss-cross.t b/tests/test-merge-criss-cross.t --- a/tests/test-merge-criss-cross.t +++ b/tests/test-merge-criss-cross.t @@ -410,6 +410,21 @@ Verify how the output looks and and how 3 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) +Test the greatest common ancestor returning multiple changesets + + $ hg log -r 'heads(commonancestors(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 + + $ cd .. http://stackoverflow.com/questions/9350005/how-do-i-specify-a-merge-base-to-use-in-a-hg-merge/9430810 diff --git a/tests/test-revset2.t b/tests/test-revset2.t --- a/tests/test-revset2.t +++ b/tests/test-revset2.t @@ -1834,3 +1834,21 @@ Test `draft() & ::x` optimization (keyvalue (symbol 'depth') (symbol '1'))))) + +test commonancestors and its optimization + + $ hg debugrevspec --verify -p analyzed -p optimized 'heads(commonancestors(head()))' + * analyzed: + (func + (symbol 'heads') + (func + (symbol 'commonancestors') + (func + (symbol 'head') + None))) + * optimized: + (func + (symbol '_commonancestorheads') + (func + (symbol 'head') + None))