[PATCH] in/out --summary

Alexis S. L. Carvalho alexis at cecm.usp.br
Tue Feb 12 19:18:59 CST 2008


Thus spake Jesse Glick:
> Alexis S. L. Carvalho wrote:
> >> There ought to be a command (out --summary?) which folds all
> >> non-merge outgoing changesets and displays the result as a patch.
> 
> By the way see
> 
> http://www.selenic.com/mercurial/bts/issue28
> 
> and
> 
> http://www.selenic.com/mercurial/bts/issue219

Hmm...

> > Maybe something like this?
> 
> It doesn't work for me; it folds in merge changesets too, making the 
> output misleading. E.g. in the following example I expected out -s to 
> show only the patch to r:

As I said, "lightly tested" ;) .  The version below should be better.
(I would've sent this faster, but my ISP decided to make my connection
"a bit" unstable for the last hour or so).

Alexis


diff -r ea34059b89de mercurial/cmdutil.py
--- a/mercurial/cmdutil.py	Tue Feb 12 11:35:06 2008 +0100
+++ b/mercurial/cmdutil.py	Tue Feb 12 23:17:13 2008 -0200
@@ -1157,3 +1157,105 @@ def commit(ui, repo, commitfunc, pats, o
         return commitfunc(ui, repo, files, message, match, opts)
     except ValueError, inst:
         raise util.Abort(str(inst))
+
+def summaryrevs(repo, nodes, opts={}):
+    '''returns (base, head) nodes that can be compared to summarize changes
+
+    See the docstring for summaryrevpair for information about the nodes
+    argument.
+
+    opts['base'] can be used to override the calculation of the base node.
+
+    opts['rev] can be used to override the calculation of the head node.
+    Note that opts['rev'] is expected to be a list with a single element.
+
+    '''
+    head = opts.get('rev')
+    if len(head) > 1:
+        raise util.Abort(_('please specify a single revision with --rev'))
+    elif head:
+        head = repo.lookup(head[0])
+
+    base = opts.get('base')
+    if base:
+        base = repo.lookup(base)
+
+    return summaryrevpair(repo, nodes, base, head)
+
+def summaryrevpair(repo, nodes, base=None, head=None):
+    '''returns (base, head) nodes that can be compared to summarize changes
+
+    nodes is usually a list of nodes returned by nodesbetween
+
+    The caller can specify base and head nodes that should be used.
+    '''
+    def show(revs):
+        displayer = show_changeset(ui, repo, {})
+        revs.sort()
+        for r in revs:
+            displayer.show(rev=r)
+
+    ui = repo.ui
+    cl = repo.changelog
+
+    if not base:
+        # The repo we're comparing with doesn't know anything about the
+        # nodes in the nodes list.
+        # So we first determine the "boundary" of this set of nodes by
+        # looking at the parents of each node and saving the ones that
+        # are not in the set.
+        # If there's only one such parent, we use it as the base.
+        # If there's more than one, we try to find one that is a descendant
+        # of all the others and use it as the base.  If there's no such
+        # parent, abort.
+        parents = {}
+        revs = dict.fromkeys([cl.rev(n) for n in nodes])
+        revs[nullrev] = 1
+        for r in revs:
+            for p in cl.parentrevs(r):
+                if p not in revs:
+                    parents[p] = 1
+        if len(parents) > 1:
+            seen = {}
+            for r in xrange(max(parents), min(parents) - 1, -1):
+                if r in seen and r in parents:
+                    del parents[r]
+                for p in cl.parentrevs(r):
+                    seen[p] = 1
+            if len(parents) > 1:
+                if not ui.quiet:
+                    ui.write("there's more than one possible base revision.\n")
+                    ui.write("please choose one using --base.\n")
+                    ui.write('base revisions found:\n')
+                    show(parents.keys())
+                raise util.Abort(_('unable to find a unique base revision'))
+        if not parents:
+            # other repo is empty
+            parents[nullrev] = 1
+        base = cl.node(parents.keys()[0])
+
+    if not head:
+        # just find all heads that descend from the base revision
+        heads = cl.heads(base)
+        if len(heads) > 1:
+            if not ui.quiet:
+                ui.write("there's more than one possible head revision.\n")
+                ui.write("please choose one using --rev.\n")
+                ui.write('head revisions found:\n')
+                show([cl.rev(n) for n in heads])
+            raise util.Abort(_('unable to find a unique head revision'))
+        head = heads[0]
+    else:
+        # sanity check: if a head revision was specified, it must be a
+        # descendant of whatever base revision we have.
+        seen = {}
+        baserev = cl.rev(base)
+        for r in xrange(cl.rev(head), baserev, -1):
+            for p in cl.parentrevs(r):
+                seen[p] = 1
+        if baserev not in seen:
+            raise util.Abort(_('the specified head revision is not a '
+                               'descendant of the base revision'))
+
+    return base, head
+
diff -r ea34059b89de mercurial/commands.py
--- a/mercurial/commands.py	Tue Feb 12 11:35:06 2008 +0100
+++ b/mercurial/commands.py	Tue Feb 12 23:17:13 2008 -0200
@@ -1562,6 +1562,10 @@ def incoming(ui, repo, source="default",
                 other = bundlerepo.bundlerepository(ui, repo.root, fname)
 
         o = other.changelog.nodesbetween(incoming, revs)[0]
+        if opts.get('summary'):
+            base, head = cmdutil.summaryrevs(other, o, opts)
+            patch.diff(other, base, head, opts=patch.diffopts(ui, opts))
+            return
         if opts['newest_first']:
             o.reverse()
         displayer = cmdutil.show_changeset(ui, other, opts)
@@ -1853,6 +1857,10 @@ def outgoing(ui, repo, dest=None, **opts
         ui.status(_("no changes found\n"))
         return 1
     o = repo.changelog.nodesbetween(o, revs)[0]
+    if opts.get('summary'):
+        base, head = cmdutil.summaryrevs(repo, o, opts)
+        patch.diff(repo, base, head, opts=patch.diffopts(ui, opts))
+        return
     if opts['newest_first']:
         o.reverse()
     displayer = cmdutil.show_changeset(ui, repo, opts)
@@ -2906,6 +2914,8 @@ table = {
           ('p', 'patch', None, _('show patch')),
           ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
           ('', 'template', '', _('display with template')),
+          ('s', 'summary', None, _('display only a single diff containing all changes')),
+          ('', 'base', '', _('force a base revision for --summary')),
          ] + remoteopts,
          _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
            ' [--bundle FILENAME] [SOURCE]')),
@@ -2964,6 +2974,8 @@ table = {
           ('r', 'rev', [], _('a specific revision you would like to push')),
           ('n', 'newest-first', None, _('show newest record first')),
           ('', 'template', '', _('display with template')),
+          ('s', 'summary', None, _('display only a single diff containing all changes')),
+          ('', 'base', '', _('force a base revision for --summary')),
          ] + remoteopts,
          _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
     "^parents":


More information about the Mercurial-devel mailing list