Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
  • 3 commits
  • 2 files changed
  • 0 commit comments
  • 1 contributor
Showing with 125 additions and 21 deletions.
  1. +58 −0 loglinear-test.py
  2. +67 −21 loglinear.py
View
58 loglinear-test.py
@@ -0,0 +1,58 @@
+# loglinear - mercurial extension to display linearized log
+#
+# Copyright 2011 Arjen Stolk <simplyarjen@gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+import loglinear, unittest
+
+class revordertest (unittest.TestCase):
+ """Verify the general behaviour of the revorder class"""
+ def test_empty(self):
+ """Verify that a revorder backed by an empty iterator is empty."""
+ class empty_iter:
+ def __init__(self):
+ self.called = 0
+ def __iter__(self):
+ return self
+ def next(self):
+ self.called += 1
+ raise StopIteration
+ it = empty_iter()
+ r = loglinear.revorder(it)
+ self.assertEqual(0, it.called)
+ self.assertTrue(r.isempty())
+ self.assertEqual(1, it.called)
+ def test_iterate(self):
+ """Verify that a revorder is iterable in the same order as its
+ backing iterator, and can be iterated more than once."""
+ def gen():
+ yield 3
+ yield 2
+ yield 1
+ r = loglinear.revorder(gen())
+ self.assertEqual([3,2,1], list(iter(r)))
+ self.assertEqual([3,2,1], list(iter(r)))
+ def test_has(self):
+ """Verify that a revorder correctly reports which elements it
+ contains and only advances the backing iterator as for as it has to
+ to do so."""
+ yielded = []
+ def record(x):
+ yielded.append(x)
+ return x
+ def gen():
+ yield record(4)
+ yield record(3)
+ yield record(1)
+ yield record(0)
+ r = loglinear.revorder(gen())
+ self.assertEqual([], yielded)
+ self.assertTrue(r.has(4))
+ self.assertEqual([4], yielded)
+ self.assertFalse(r.has(2))
+ self.assertEqual([4,3,1], yielded)
+ self.assertTrue(r.has(3))
+ self.assertEqual([4,3,1], yielded)
+
View
88 loglinear.py
@@ -4,32 +4,81 @@
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
+"""show a threaded view of the repository history"""
-from mercurial import cmdutil, scmutil, util, node, error
-from mercurial.i18n import _
+from mercurial import util, node, error, i18n, cmdutil, scmutil
cmdtable = {}
-command = cmdutil.command(cmdtable)
-@command('^loglinear|ll', [
+def revrange(repo, revs):
+ return scmutil.revrange(repo, revs)
+
+def formatid(ctx):
+ return "%d:%s"%(ctx.rev(), node.short(ctx.node()))
+
+def formatdate(ctx):
+ return util.datestr(ctx.date())
+
+def _(msg):
+ return i18n._(msg)
+
+def extsetup(ui):
+ cmdutil.command(cmdtable)('^loglinear|ll', [
('r', 'rev', [],
_('show the specified revision or range'), _('REV')),
('l', 'limit', '',
- _('limit number of changes displayed'), _('NUM')),
+ _('limit number of changes displayed'), _('LIMIT')),
('d', 'max-depth', '',
- _('expand merges up to this depth'), _('NUM')),
+ _('expand merges up to this depth'), _('DEPTH')),
('b', 'branch', [],
_('show changesets within the given named branch'), _('BRANCH')),
('m', 'merge', '',
_('show revisions integrated in merge'), _('REV'))
- ], _('-r [REV]'))
+ ], _('-r [REV] -d [DEPTH] -l [LIMIT] -b [BRANCH]'))(loglinear)
+
def loglinear(ui, repo, *args, **opts):
- '''Linear log
+ '''show a threaded view of the repository history
+
+ Shows a thread of the repository history starting from the current
+ revision, the passed revision, or tipmost revision of the passed
+ revision set. Following each revision in the thread is one of its
+ parents. For merges a parent selection algorithm (described below) is
+ used. It aims to select the main line of development to go on the
+ thread.
+
+ For each revision, the revision number and short hash are shown,
+ together with commit date, user and description. The revision number
+ and short hash of each parent that is not on the thread are also shown.
+
+ When max-depth is specified, threads are recursively shown for the
+ other parent of merges in a displayed thread, up to the specified
+ depth. Threads on a different branch are not shown, unless this branch
+ is specified on the command line with the --branch option. Threads
+ that start outside the passed revision set are never shown.
+
+ Merges for which the thread is not displayed because it is too deep or
+ on a another branch will show the number of revisions merged in (i.e.
+ revisions that are ancestors of the merge but not of the selected
+ parent) and the branch if it is different from that of the merge.
+
+ Parent selection proceeds as follows.
+
+ :1.: Only parents in the passed revision set are eligible.
+ :2.: Only parents on the same branch as the merge are elgible.
+ :3.: If there are no eligible parents, the thread ends, if there is only
+ one, it is the selected parent.
+ :4.: If one parent has the same user as the merge and the other has a
+ different user, the one with the different user is the selected parent.
+ :5.: The first parent is the selected parent.
+
+ The idea of step 4 is that users will merge their own changes into the
+ main development branch, so the thread should follow the parent with
+ the different user.
'''
revs, merge, limit, depth, branches = parseopts(opts)
sub = findsub(repo, revs, merge)
if sub.isempty():
- raise util.Abort(_("empty revision set"))
+ raise error.Abort(_("empty revision set"))
ctx = repo[sub.max()]
if ctx.branch() not in branches:
branches.add(ctx.branch())
@@ -39,7 +88,7 @@ def parseopts(opts):
revs = opts.get('rev')
merge = opts.get('merge')
if (revs and merge):
- raise util.Abort(_('cannot specify both --rev and --merge'))
+ raise error.Abort(_('cannot specify both --rev and --merge'))
if not revs and not merge:
revs = ['.']
def _nopt(name):
@@ -48,9 +97,9 @@ def _nopt(name):
try:
value = int(value)
except ValueError:
- raise util.Abort(_('%s must be a positive integer')%name)
+ raise error.Abort(_('%s must be a positive integer')%name)
if value <= 0:
- raise util.Abort(_('%s must be positive')%name)
+ raise error.Abort(_('%s must be positive')%name)
else:
value = None
return value
@@ -72,12 +121,12 @@ def findsub(repo, revs, merge):
if single:
sub = ancestors(repo, single)
elif revs:
- sub = seqrevorder(scmutil.revrange(repo, revs))
+ sub = seqrevorder(revrange(repo, revs))
else:
rev = repo[merge].rev()
pars = [par for par in cl.parentrevs(rev) if par >= 0]
if len(pars) < 2:
- raise util.Abort(_('non-merge revision specified for --merge'))
+ raise error.Abort(_('non-merge revision specified for --merge'))
main = pickparent(repo, rev, *pars)
other = [x for x in pars if x != main][0]
sub = findmerged(repo, other, main, ancestors(repo, rev))
@@ -125,14 +174,12 @@ def printrecursive(ui, repo, sub, limit, depth, branches, main, prefix):
e = ''
if pctx.branch() != ctx.branch():
e = "(%s)"%pctx.branch()
- ui.write(_("%s Based on %d:%s %s\n")%
- (prefix, par, node.short(pctx.node()), e))
+ ui.write(_("%s Based on %s %s\n")%(prefix, formatid(pctx), e))
return limit
def printsingle(ui, ctx, prefix):
- ui.write("%s* %d:%s (%s) %s\n"%(
- prefix, ctx.rev(), node.short(ctx.node()),
- util.datestr(ctx.date()), ctx.user()))
+ ui.write("%s* %s (%s) %s\n"%(
+ prefix, formatid(ctx), formatdate(ctx), ctx.user()))
desc = ctx.description()
for dl in desc.split('\n'):
ui.write("%s %s\n"%(prefix, dl))
@@ -149,8 +196,7 @@ def printmerged(ui, pctx, branch, merged, prefix):
e = ''
if extra:
e = "(" + ", ".join(extra) + ")"
- ui.write(_("%s Merged in %d:%s %s\n")%
- (prefix, pctx.rev(), node.short(pctx.node()), e))
+ ui.write(_("%s Merged in %s %s\n")%(prefix, formatid(pctx), e))
def findmerged(repo, par, other, sub):
parents = repo.changelog.parentrevs

No commit comments for this range

Something went wrong with that request. Please try again.