graphlog: implement --follow with file arguments
authorPatrick Mezard <patrick@mezard.eu>
Sat, 25 Feb 2012 22:11:36 +0100
changeset 16173 9178d284b880
parent 16172 db75321c7a0e
child 16174 0a73c4bd9f47
graphlog: implement --follow with file arguments
hgext/graphlog.py
tests/test-glog.t
--- a/hgext/graphlog.py	Sat Feb 25 22:11:35 2012 +0100
+++ b/hgext/graphlog.py	Sat Feb 25 22:11:36 2012 +0100
@@ -241,9 +241,6 @@
         if op in opts and opts[op]:
             raise util.Abort(_("-G/--graph option is incompatible with --%s")
                              % op.replace("_", "-"))
-    if pats and opts.get('follow'):
-        raise util.Abort(_("-G/--graph option is incompatible with --follow "
-                           "with file argument"))
 
 def revset(repo, pats, opts):
     """Return revset str built of revisions, log options and file patterns.
@@ -256,6 +253,7 @@
         'date':        ('date(%(val)r)', None),
         'branch':      ('branch(%(val)r)', ' or '),
         '_patslog':    ('filelog(%(val)r)', ' or '),
+        '_patsfollow': ('follow(%(val)r)', ' or '),
         'keyword':     ('keyword(%(val)r)', ' or '),
         'prune':       ('not (%(val)r or ancestors(%(val)r))', ' and '),
         'user':        ('user(%(val)r)', ' or '),
@@ -268,22 +266,35 @@
     if 'branch' in opts and 'only_branch' in opts:
         opts['branch'] = opts['branch'] + opts.pop('only_branch')
 
+    follow = opts.get('follow')
+    if 'follow' in opts:
+        del opts['follow']
     # pats/include/exclude are passed to match.match() directly in
     # _matchfile() revset but walkchangerevs() builds its matcher with
     # scmutil.match(). The difference is input pats are globbed on
     # platforms without shell expansion (windows).
-    match, pats = scmutil.matchandpats(repo[None], pats, opts)
+    pctx = repo[None]
+    match, pats = scmutil.matchandpats(pctx, pats, opts)
     slowpath = match.anypats() or (match.files() and opts.get('removed'))
     if not slowpath:
         for f in match.files():
+            if follow and f not in pctx:
+                raise util.Abort(_('cannot follow file not in parent '
+                                   'revision: "%s"') % f)
             filelog = repo.file(f)
             if not len(filelog):
                 # A zero count may be a directory or deleted file, so
                 # try to find matching entries on the slow path.
+                if follow:
+                    raise util.Abort(
+                        _('cannot follow nonexistent file: "%s"') % f)
                 slowpath = True
     if slowpath:
         # See cmdutil.walkchangerevs() slow path.
         #
+        if follow:
+            raise util.Abort(_('can only follow copies/renames for explicit '
+                               'filenames'))
         # pats/include/exclude cannot be represented as separate
         # revset expressions as their filtering logic applies at file
         # level. For instance "-I a -X a" matches a revision touching
@@ -298,7 +309,13 @@
         matchargs = ','.join(('%r' % p) for p in matchargs)
         opts['rev'] = opts.get('rev', []) + ['_matchfiles(%s)' % matchargs]
     else:
-        opts['_patslog'] = list(pats)
+        if follow:
+            if pats:
+                opts['_patsfollow'] = list(pats)
+            else:
+                opts['follow'] = True
+        else:
+            opts['_patslog'] = list(pats)
 
     revset = []
     for op, val in opts.iteritems():
--- a/tests/test-glog.t	Sat Feb 25 22:11:35 2012 +0100
+++ b/tests/test-glog.t	Sat Feb 25 22:11:36 2012 +0100
@@ -1454,8 +1454,6 @@
   ('group', ('group', ('or', ('or', ('func', ('symbol', 'branch'), ('string', 'default')), ('func', ('symbol', 'branch'), ('string', 'branch'))), ('func', ('symbol', 'branch'), ('string', 'branch')))))
   $ testlog -k expand -k merge
   ('group', ('group', ('or', ('func', ('symbol', 'keyword'), ('string', 'expand')), ('func', ('symbol', 'keyword'), ('string', 'merge')))))
-  $ hg log -G --follow  --template 'nodetag {rev}\n' | grep nodetag | wc -l
-  \s*36 (re)
   $ hg log -G --removed --template 'nodetag {rev}\n' | grep nodetag | wc -l
   \s*0 (re)
   $ hg log -G --only-merges --template 'nodetag {rev}\n' | grep nodetag | wc -l
@@ -1492,27 +1490,30 @@
   [255]
   $ testlog --prune 31 --prune 32
   ('group', ('group', ('and', ('not', ('group', ('or', ('string', '31'), ('func', ('symbol', 'ancestors'), ('string', '31'))))), ('not', ('group', ('or', ('string', '32'), ('func', ('symbol', 'ancestors'), ('string', '32'))))))))
-  $ hg log -G --follow a
-  abort: -G/--graph option is incompatible with --follow with file argument
-  [255]
 
-
-Dedicated repo for --follow and paths filtering
+Dedicated repo for --follow and paths filtering. The g is crafted to
+have 2 filelog topological heads in a linear changeset graph.
 
   $ cd ..
   $ hg init follow
   $ cd follow
   $ echo a > a
   $ echo aa > aa
+  $ echo f > f
   $ hg ci -Am "add a"
   adding a
   adding aa
+  adding f
   $ hg cp a b
+  $ hg cp f g
   $ hg ci -m "copy a b"
   $ mkdir dir
   $ hg mv b dir
+  $ echo g >> g
+  $ echo f >> f
   $ hg ci -m "mv b dir/b"
   $ hg mv a b
+  $ hg cp -f f g
   $ echo a > d
   $ hg add d
   $ hg ci -m "mv a b; add d"
@@ -1555,3 +1556,65 @@
   >    testlog a*;
   > fi;
   ('group', ('group', ('func', ('symbol', 'filelog'), ('string', 'aa'))))
+
+Test --follow on a directory
+
+  $ testlog -f dir
+  abort: cannot follow file not in parent revision: "dir"
+  abort: cannot follow file not in parent revision: "dir"
+  abort: cannot follow file not in parent revision: "dir"
+
+Test --follow on file not in parent revision
+
+  $ testlog -f a
+  abort: cannot follow file not in parent revision: "a"
+  abort: cannot follow file not in parent revision: "a"
+  abort: cannot follow file not in parent revision: "a"
+
+Test --follow and patterns
+
+  $ testlog -f 'glob:*'
+  abort: can only follow copies/renames for explicit filenames
+  abort: can only follow copies/renames for explicit filenames
+  abort: can only follow copies/renames for explicit filenames
+
+Test --follow on a single rename
+
+  $ hg up -q 2
+  $ testlog -f a
+  ('group', ('group', ('func', ('symbol', 'follow'), ('string', 'a'))))
+
+Test --follow and multiple renames
+
+  $ hg up -q tip
+  $ testlog -f e
+  ('group', ('group', ('func', ('symbol', 'follow'), ('string', 'e'))))
+
+Test --follow and multiple filelog heads
+
+  $ hg up -q 2
+  $ testlog -f g
+  ('group', ('group', ('func', ('symbol', 'follow'), ('string', 'g'))))
+  $ cat log.nodes
+  nodetag 2
+  nodetag 1
+  nodetag 0
+  $ hg up -q tip
+  $ testlog -f g
+  ('group', ('group', ('func', ('symbol', 'follow'), ('string', 'g'))))
+  $ cat log.nodes
+  nodetag 3
+  nodetag 2
+  nodetag 0
+
+Test --follow and multiple files
+
+  $ testlog -f g e
+  ('group', ('group', ('or', ('func', ('symbol', 'follow'), ('string', 'g')), ('func', ('symbol', 'follow'), ('string', 'e')))))
+  $ cat log.nodes
+  nodetag 4
+  nodetag 3
+  nodetag 2
+  nodetag 1
+  nodetag 0
+