Show copies in hg log.
authorBrendan Cully <brendan@kublai.com>
Fri, 29 Sep 2006 13:01:19 -0700
changeset 3197 e18c3d08528d
parent 3196 f3b939444c72
child 3198 a32f65549b9e
Show copies in hg log. The format is: copies: destination (source)...
mercurial/commands.py
mercurial/templater.py
templates/map-cmdline.default
templates/template-vars.txt
tests/test-log
tests/test-log.out
--- a/mercurial/commands.py	Fri Sep 29 13:00:54 2006 -0700
+++ b/mercurial/commands.py	Fri Sep 29 13:01:19 2006 -0700
@@ -312,7 +312,7 @@
         self.ui = ui
         self.repo = repo
 
-    def show(self, rev=0, changenode=None, brinfo=None):
+    def show(self, rev=0, changenode=None, brinfo=None, copies=None):
         '''show a single changeset or file revision'''
         log = self.repo.changelog
         if changenode is None:
@@ -359,6 +359,9 @@
                     self.ui.note("%-12s %s\n" % (key, " ".join(value)))
         else:
             self.ui.note(_("files:       %s\n") % " ".join(changes[3]))
+        if copies:
+            copies = ['%s (%s)' % c for c in copies]
+            self.ui.note(_("copies:      %s\n") % ' '.join(copies))
 
         description = changes[4].strip()
         if description:
@@ -1774,6 +1777,40 @@
         limit = sys.maxint
     count = 0
 
+    if opts['copies'] and opts['rev']:
+        endrev = max([int(i)
+                      for i in cmdutil.revrange(ui, repo, opts['rev'])]) + 1
+    else:
+        endrev = repo.changelog.count()
+    rcache = {}
+    ncache = {}
+    dcache = []
+    def getrenamed(fn, rev, man):
+        '''looks up all renames for a file (up to endrev) the first
+        time the file is given. It indexes on the changerev and only
+        parses the manifest if linkrev != changerev.
+        Returns rename info for fn at changerev rev.'''
+        if fn not in rcache:
+            rcache[fn] = {}
+            ncache[fn] = {}
+            fl = repo.file(fn)
+            for i in xrange(fl.count()):
+                node = fl.node(i)
+                lr = fl.linkrev(node)
+                renamed = fl.renamed(node)
+                rcache[fn][lr] = renamed
+                if renamed:
+                    ncache[fn][node] = renamed
+                if lr >= endrev:
+                    break
+        if rev in rcache[fn]:
+            return rcache[fn][rev]
+        if not dcache or dcache[0] != man:
+            dcache[:] = [man, repo.manifest.readdelta(man)]
+        if fn in dcache[1]:
+            return ncache[fn].get(dcache[1][fn])
+        return None
+
     displayer = show_changeset(ui, repo, opts)
     for st, rev, fns in changeiter:
         if st == 'window':
@@ -1805,7 +1842,14 @@
             if opts['branches']:
                 br = repo.branchlookup([repo.changelog.node(rev)])
 
-            displayer.show(rev, brinfo=br)
+            copies = []
+            if opts.get('copies') and rev:
+                mf = getchange(rev)[0]
+                for fn in getchange(rev)[3]:
+                    rename = getrenamed(fn, rev, mf)
+                    if rename:
+                        copies.append((fn, rename[0]))
+            displayer.show(rev, brinfo=br, copies=copies)
             if opts['patch']:
                 prev = (parents and parents[0]) or nullid
                 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
@@ -2889,6 +2933,7 @@
            _('follow changeset history, or file history across copies and renames')),
           ('', 'follow-first', None,
            _('only follow the first parent of merge changesets')),
+          ('C', 'copies', None, _('show copied files')),
           ('k', 'keyword', [], _('search for a keyword')),
           ('l', 'limit', '', _('limit number of changes displayed')),
           ('r', 'rev', [], _('show the specified revision or range')),
--- a/mercurial/templater.py	Fri Sep 29 13:00:54 2006 -0700
+++ b/mercurial/templater.py	Fri Sep 29 13:01:19 2006 -0700
@@ -330,7 +330,8 @@
     def __init__(self, ui, repo, mapfile, dest=None):
         self.t = templater(mapfile, common_filters,
                            cache={'parent': '{rev}:{node|short} ',
-                                  'manifest': '{rev}:{node|short}'})
+                                  'manifest': '{rev}:{node|short}',
+                                  'filecopy': '{name} ({source})'})
         self.ui = ui
         self.dest = dest
         self.repo = repo
@@ -355,7 +356,7 @@
         self.write(thing, header=True)
 
     def show(self, rev=0, changenode=None, brinfo=None, changes=None,
-             **props):
+             copies=None, **props):
         '''show a single changeset or file revision'''
         log = self.repo.changelog
         if changenode is None:
@@ -472,6 +473,13 @@
             showadds = ''
             showdels = ''
 
+        copies = [{'name': x[0], 'source': x[1]}
+                  for x in copies]
+        def showcopies(**args):
+            for x in showlist('file_copy', copies, plural='file_copies',
+                              **args):
+                yield x
+
         defprops = {
             'author': changes[1],
             'branches': showbranches,
@@ -480,6 +488,7 @@
             'file_adds': showadds,
             'file_dels': showdels,
             'files': showfiles,
+            'file_copies': showcopies,
             'manifest': showmanifest,
             'node': hex(changenode),
             'parents': showparents,
--- a/templates/map-cmdline.default	Fri Sep 29 13:00:54 2006 -0700
+++ b/templates/map-cmdline.default	Fri Sep 29 13:01:19 2006 -0700
@@ -1,12 +1,15 @@
 changeset = 'changeset:   {rev}:{node|short}\n{tags}{short_parents}user:        {author}\ndate:        {date|date}\nsummary:     {desc|firstline}\n\n'
 changeset_quiet = '{rev}:{node|short}\n'
-changeset_verbose = 'changeset:   {rev}:{node}\n{tags}{parents}{manifest}user:        {author}\ndate:        {date|date}\nfiles:       {files}\n{file_adds}{file_dels}description:\n{desc|strip}\n\n\n'
+changeset_verbose = 'changeset:   {rev}:{node}\n{tags}{parents}{manifest}user:        {author}\ndate:        {date|date}\nfiles:       {files}\n{file_adds}{file_dels}{file_copies}description:\n{desc|strip}\n\n\n'
 start_file_adds = 'files+:     '
 file_add = ' {file_add}'
 end_file_adds = '\n'
 start_file_dels = 'files-:     '
 file_del = ' {file_del}'
 end_file_dels = '\n'
+start_file_copies = 'copies:     '
+file_copy = ' {name} ({source})'
+end_file_copies = '\n'
 short_parent = 'parent:      {rev}:{node|short}\n'
 parent = 'parent:      {rev}:{node}\n'
 manifest = 'manifest:    {rev}:{node}\n'
--- a/templates/template-vars.txt	Fri Sep 29 13:00:54 2006 -0700
+++ b/templates/template-vars.txt	Fri Sep 29 13:01:19 2006 -0700
@@ -25,6 +25,7 @@
 footer        the global page footer
 
 files         a list of file links
+file_copies   a list of pairs of name, source filenames
 dirs          a set of directory links
 diff          a diff of one or more files
 annotate      an annotated file
@@ -36,4 +37,4 @@
     filenodelink - jump to file diff
     fileellipses - printed after maxfiles
     changelogentry - an entry in the log
-  manifest - browse a manifest as a directory tree
\ No newline at end of file
+  manifest - browse a manifest as a directory tree
--- a/tests/test-log	Fri Sep 29 13:00:54 2006 -0700
+++ b/tests/test-log	Fri Sep 29 13:01:19 2006 -0700
@@ -29,6 +29,9 @@
 echo % many renames
 hg log -vf e
 
+echo % log copies
+hg log -vC --template '{rev} {file_copies%filecopy}\n'
+
 # log --follow tests
 hg init ../follow
 cd ../follow
--- a/tests/test-log.out	Fri Sep 29 13:00:54 2006 -0700
+++ b/tests/test-log.out	Fri Sep 29 13:01:19 2006 -0700
@@ -76,6 +76,12 @@
 a
 
 
+% log copies
+4 e (dir/b)
+3 b (a)
+2 dir/b (b)
+1 b (a)
+0 
 adding base
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 adding b1