[PATCH] revset: introduce feature revset for tracking in-progress work (issue4968)
Andrew Halberstadt
halbersa at gmail.com
Thu Nov 26 22:31:23 UTC 2015
# HG changeset patch
# User Andrew Halberstadt <ahalberstadt at mozilla.com>
# Date 1448490626 18000
# Wed Nov 25 17:30:26 2015 -0500
# Node ID 3545b0234e4884f57dac44fd4e443deac5b9d673
# Parent 61fbf5dc12b23e7a2a30cf04ebd9f096c42a1f61
revset: introduce feature revset for tracking in-progress work (issue4968)
The revset "only(<rev>) and not public()" is often used to track wip features.
But this approach is simplistic and doesn't take things like merges,
obsolescence and bookmarks into account. This change formalizes a 'feature()'
revset that can turn a set of revision specifications into all the commits
within their associated feature branches.
A commit C is in the feature ending at revision R if all of the following
conditions are true:
1. C is R or C is an ancestor of R
2. C is not public
3. C is not a merge commit
4. C is not obsolete
5. no bookmarks exist in [C, R) for C != R
6. all commits in (C, R) are also within R for C != R
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -3,16 +3,17 @@
# Copyright 2010 Matt Mackall <mpm at selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from __future__ import absolute_import
import heapq
+import itertools
import re
from .i18n import _
from . import (
destutil,
encoding,
error,
hbisect,
@@ -926,16 +927,51 @@ def extra(repo, subset, x):
kind, value, matcher = util.stringmatcher(value)
def _matchvalue(r):
extra = repo[r].extra()
return label in extra and (value is None or matcher(extra[label]))
return subset.filter(lambda r: _matchvalue(r))
+def feature(repo, subset, x):
+ """``feature(set)``
+ Changesets that are in a feature of a revision specification in set.
+
+ Changeset X is in Y's feature if X is an ancestor of Y and they don't have
+ any merge commits, public changesets or changesets with a bookmark in
+ between them.
+ """
+ args = getargs(x, 1, 1, _("feature takes one argument"))
+ specs = getset(repo, fullreposet(repo), args[0])
+ specs = [formatspec('rev(%d)', s) if isinstance(s, int) else s
+ for s in specs]
+
+ m = matchany(repo.ui, specs, repo)
+ l = m(repo)
+
+ feature = set()
+ for i, root in enumerate(l):
+ nodes = itertools.chain([root], repo[root].ancestors())
+ for node in nodes:
+ ctx = repo[node]
+ # if public, a merge or obsolete, not in feature
+ if ctx.phase() == 0 or len(ctx.parents()) == 2 or ctx.obsolete():
+ break
+
+ # if a bookmark other than specs[i] exists, not in feature
+ marks = set(ctx.bookmarks()) - set(repo[root].bookmarks())
+ if specs[i] in marks:
+ marks.remove(specs[i])
+ if len(marks) > 0:
+ break
+ feature.add(ctx.rev())
+
+ return subset & feature
+
def filelog(repo, subset, x):
"""``filelog(pattern)``
Changesets connected to the specified filelog.
For performance reasons, visits only revisions mentioned in the file-level
filelog, rather than filtering through all changesets (much faster, but
doesn't include deletes or duplicate changes). For a slower, more accurate
result, use ``file()``.
@@ -2123,16 +2159,17 @@ symbols = {
"desc": desc,
"descendants": descendants,
"_firstdescendants": _firstdescendants,
"destination": destination,
"divergent": divergent,
"draft": draft,
"extinct": extinct,
"extra": extra,
+ "feature": feature,
"file": hasfile,
"filelog": filelog,
"first": first,
"follow": follow,
"_followfirst": _followfirst,
"grep": grep,
"head": head,
"heads": heads,
@@ -2200,16 +2237,17 @@ safesymbols = set([
"desc",
"descendants",
"_firstdescendants",
"destination",
"divergent",
"draft",
"extinct",
"extra",
+ "feature",
"file",
"filelog",
"first",
"follow",
"_followfirst",
"head",
"heads",
"hidden",
diff --git a/tests/test-revset.t b/tests/test-revset.t
--- a/tests/test-revset.t
+++ b/tests/test-revset.t
@@ -2006,16 +2006,38 @@ test or-ed indirect predicates (issue377
0
1
2
3
4
5
6
+test feature
+ $ log 'feature(7)'
+ 7
+ $ log 'feature(only)'
+ 0
+ 1
+ 2
+ 4
+ 8
+ 9
+ $ hg phase -f -p 8
+ $ log 'feature(only)'
+ 9
+ $ log 'feature(8)'
+ $ hg phase -f -d 8
+ $ hg bookmark -r 8 other
+ $ log 'feature(only)'
+ 9
+ $ log 'feature(only | 7)'
+ 7
+ 9
+
tests for 'remote()' predicate:
#. (csets in remote) (id) (remote)
1. less than local current branch "default"
2. same with local specified "default"
3. more than local specified specified
$ hg clone --quiet -U . ../remote3
$ cd ../remote3
More information about the Mercurial-devel
mailing list