[PATCH 2 of 2] identify: add the --locate flag to display nearest surrounding tags
Gilles Moris
gilles.moris at free.fr
Fri Aug 28 07:38:28 UTC 2009
mercurial/cmdutil.py | 69 ++++++++++++++++++++++++++++++++++
mercurial/commands.py | 28 ++++++++++++--
tests/test-debugcomplete.out | 2 +-
tests/test-identify | 63 +++++++++++++++++++++++++++++++
tests/test-identify.out | 23 +++++++++++
5 files changed, 180 insertions(+), 5 deletions(-)
# HG changeset patch
# User Gilles Moris <gilles.moris at free.fr>
# Date 1234603377 -3600
# Node ID 1583cb23bd884b0bcdc473054b1076f7a20f0a43
# Parent 6fed19d04b0d859931837172ad974edb1e7b6b80
identify: add the --locate flag to display nearest surrounding tags
The goal is to give an idea of the position of the revision in the history
topology (where are the nearest tags? on which named branch?).
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1287,3 +1287,72 @@
ltmap[r] = ldate, ldist + 1, ltag
return ltmap
+
+def nearesttags(repo, rev):
+ # ctx.children() is way too slow compared to ctx.parents(), upto
+ # two orders of magnitude. So we build our own cache to replace it.
+ chldcache = [[]] * (len(repo))
+ for r in range(len(repo)):
+ for pr in [p.rev() for p in repo[r].parents() if p.rev() != nullrev]:
+ if not chldcache[pr]:
+ chldcache[pr] = [r]
+ else:
+ chldcache[pr].append(r)
+
+ # compute the DAG distance map from the seeded rev
+ # initialize with -1 meaning unreachable rev
+ dmap = [-1] * len(repo)
+ dmap[rev] = 0
+ # distance map for descendants
+ for r in range(rev+1, len(repo)):
+ dl = [dmap[p.rev()] for p in repo[r].parents()
+ if p.rev() != nullrev and dmap[p.rev()] >= 0]
+ if dl:
+ dmap[r] = max(dl) + 1
+
+ # distance map for ancestors
+ for r in range(rev-1, -1, -1):
+ # we know that ancestors cannot be > rev (DAG partial ordering)
+ dl = [dmap[p] for p in chldcache[r] if dmap[p] >= 0 and p <= rev]
+ if dl:
+ dmap[r] = max(dl) + 1
+
+ # get revisions of relevant tags in the repo
+ revtags = set([repo.changelog.rev(n) for t, n in repo.tags().items()
+ if repo.tagtype(t) and dmap[repo.changelog.rev(n)] >= 0])
+
+ def obliteratedeps(infants):
+ rl = set(infants(r))
+ while rl:
+ rr = rl.pop()
+ dmap[rr] = -1
+ for pr in infants(rr):
+ if dmap[pr] >= 0:
+ rl.add(pr)
+ # we just want the first tag on each branch to be reported so ...
+ for r in revtags:
+ # ... set ancestors (resp descendants) as unreachable
+ if r < rev:
+ obliteratedeps(lambda rr: [p.rev() for p in repo[rr].parents()
+ if p.rev() != nullrev])
+ elif r > rev:
+ obliteratedeps(lambda rr: chldcache[rr])
+
+ # dispatch remaining tags in last or next tags lists
+ lasttags = []
+ nexttags = []
+ for r in [rr for rr in revtags if dmap[rr] >= 0]:
+ c = repo[r]
+ tags = sorted(t for t in c.tags() if repo.tagtype(t))
+ b = c.branch()
+ if r <= rev:
+ lasttags.append((-c.date()[0], dmap[r], tags, r, b))
+ elif r >= rev:
+ nexttags.append((c.date()[0], dmap[r], tags, r, b))
+
+ # sort by nearest (i.e. highest, resp. lowest) date
+ # for comparison of tuples, see python tutorial 5.8
+ lasttags = [(r, d, t, b) for dt, d, t, r, b in sorted(lasttags)]
+ nexttags = [(r, d, t, b) for dt, d, t, r, b in sorted(nexttags)]
+
+ return lasttags, nexttags
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -1634,7 +1634,7 @@
ui.write("%s\n" % first)
def identify(ui, repo, source=None,
- rev=None, num=None, id=None, branch=None, tags=None):
+ rev=None, num=None, id=None, branch=None, tags=None, locate=None):
"""identify the working copy or specified revision
With no revision, print a summary of the current state of the
@@ -1667,7 +1667,7 @@
rev = revs[0]
if not rev:
rev = "tip"
- if num or branch or tags:
+ if num or branch or tags or locate:
raise util.Abort(
"can't query remote revision number, branch, or tags")
output = [hexfunc(repo.lookup(rev))]
@@ -1708,6 +1708,25 @@
ui.write("%s\n" % ' '.join(output))
+ if locate:
+ if isinstance(ctx, context.workingctx):
+ ctx = ctx.parents()[0]
+
+ def dpynsttags(fwd, nl, output):
+ for rev, dist, taglist, branch in nl:
+ if branch != 'default':
+ brname = ' ' + branch
+ else:
+ brname = ''
+ output.append('/'.join(taglist) +
+ '(%s%d%s)' % (fwd and '+' or '-', dist, brname))
+ if len(output) > 1:
+ ui.write("%s\n" % ' '.join(output))
+
+ lt, nt = cmdutil.nearesttags(repo, ctx.rev())
+ dpynsttags(False, lt, ['based on'])
+ dpynsttags(True, nt, ['went in'])
+
def import_(ui, repo, patch1, *patches, **opts):
"""import an ordered set of patches
@@ -3348,8 +3367,9 @@
('n', 'num', None, _('show local revision number')),
('i', 'id', None, _('show global revision id')),
('b', 'branch', None, _('show branch')),
- ('t', 'tags', None, _('show tags'))],
- _('[-nibt] [-r REV] [SOURCE]')),
+ ('t', 'tags', None, _('show tags')),
+ ('L', 'locate', None, _('show how far are the nearest tags'))],
+ _('[-nibtL] [-r REV] [SOURCE]')),
"import|patch":
(import_,
[('p', 'strip', 1,
diff --git a/tests/test-debugcomplete.out b/tests/test-debugcomplete.out
--- a/tests/test-debugcomplete.out
+++ b/tests/test-debugcomplete.out
@@ -207,7 +207,7 @@
grep: print0, all, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
heads: rev, active, closed, style, template
help:
-identify: rev, num, id, branch, tags
+identify: rev, num, id, branch, tags, locate
import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity
incoming: force, newest-first, bundle, rev, patch, git, limit, no-merges, style, template, ssh, remotecmd
locate: rev, print0, fullpath, include, exclude
diff --git a/tests/test-identify b/tests/test-identify
--- a/tests/test-identify
+++ b/tests/test-identify
@@ -43,4 +43,67 @@
echo % remote with tags?
hg id -t http://localhost:$HGPORT1/
+hg revert a
+hg branch -q stable
+echo fix1 > b
+hg add b
+hg ci -d '1 0' -mfix1
+hg up -q default
+echo feat1 >> a
+hg ci -d '2 0' -mfeat1
+echo feat2 >> a
+hg ci -d '3 0' -mfeat2
+hg merge -q stable
+hg ci -d '4 0' -mmerge1
+
+hg up -q stable
+hg merge -q default
+hg ci -d '5 0' -mmergeback
+
+echo fix2 >> b
+hg ci -d '6 0' -m fix2
+echo fix3 >> b
+hg ci -d '7 0' -m fix3
+hg up -q default
+echo feat3 >> a
+hg ci -d '8 0' -mfeat3
+hg merge -q stable
+hg ci -d '9 0' -mmerge2
+
+echo % test --locate
+echo "[extensions]" >> $HGRCPATH
+echo "mq =" >> $HGRCPATH
+hg qnew -d '10 0' patch1
+echo c > c
+hg add c
+hg qrefresh -d '10 0'
+hg qnew -d '11 0' patch2
+echo c >> c
+hg qrefresh -d '11 0'
+
+echo % at qtip
+hg id -L
+hg id -L -r 4
+
+echo % tags top and bottom
+hg tag -l -r 9 top
+hg tag -l -r 9 alttop
+hg tag -l -r 0 bottom
+hg id -L
+hg id -L -r 4
+
+echo % nearest tags by date
+hg up 4
+hg tag -l -r 2 exp1
+hg tag -l -r 7 st2
+hg id -L
+
+echo % closer tags by hops after
+hg tag -l -r 1 st1
+hg tag -l -r 8 exp2
+hg id -L
+
+echo % remote verbose
+hg id -L http://localhost:$HGPORT1/
+
true # ends with util.Abort -> returns 255
diff --git a/tests/test-identify.out b/tests/test-identify.out
--- a/tests/test-identify.out
+++ b/tests/test-identify.out
@@ -23,3 +23,26 @@
cb9a9f314b8b
% remote with tags?
abort: can't query remote revision number, branch, or tags
+created new head
+created new head
+% test --locate
+% at qtip
+04b2329333f9 qtip/patch2/tip
+7fe7ca622a6b
+% tags top and bottom
+04b2329333f9 qtip/tip/patch2
+based on alttop/top(-2)
+7fe7ca622a6b
+based on bottom(-3)
+went in alttop/top(+4)
+% nearest tags by date
+2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+7fe7ca622a6b
+based on exp1(-2)
+went in st2(+3 stable)
+% closer tags by hops after
+7fe7ca622a6b
+based on exp1(-2) st1(-1 stable)
+went in st2(+3 stable) exp2(+1)
+% remote verbose
+abort: can't query remote revision number, branch, or tags
More information about the Mercurial-devel
mailing list