linkrev-filelog: handle filtered linkrev with no visible children (issue4307)
authorPierre-Yves David <pierre-yves.david@fb.com>
Mon, 29 Dec 2014 18:35:23 -0800
changeset 23720 8ec03e0ef51a
parent 23719 34364a4b25eb
child 23722 5803bfeebbfe
linkrev-filelog: handle filtered linkrev with no visible children (issue4307) If the file revision with a filtered linkrev does not have any (unfiltered) children, we cannot use it to bound the search for another introduction. Instead, we have to look at the file revision used by each head changeset. If one of them uses this file revision, we know there is another occurrence and we have a starting point. See inline comments for details. Adding some kind of permanent reference of all the introductions of a file revision instead of just the first one would be much better. But this is more difficult. I hope to take that into account in the next repository format.
mercurial/revset.py
tests/test-log.t
--- a/mercurial/revset.py	Mon Dec 29 17:23:16 2014 -0800
+++ b/mercurial/revset.py	Mon Dec 29 18:35:23 2014 -0800
@@ -792,6 +792,7 @@
         backrevref = {}  # final value for: changerev -> filerev
         lowestchild = {} # lowest known filerev child of a filerev
         delayed = []     # filerev with filtered linkrev, for post-processing
+        lowesthead = None # cache for manifest content of all head revisions
         fl = repo.file(f)
         for fr in list(fl):
             lkr = rev = fl.linkrev(fr)
@@ -825,9 +826,24 @@
             child = lowestchild.get(fr)
 
             if child is None:
-                # XXX content could be linkrev-shadowed in a head, but lets
-                # ignore this case for now.
-                continue
+                # search for existence of this file revision in a head revision.
+                # There are three possibilities:
+                # - the revision exists in a head and we can find an
+                #   introduction from there,
+                # - the revision does not exist in a head because it has been
+                #   changed since its introduction: we would have found a child
+                #   and be in the other 'else' clause,
+                # - all versions of the revision are hidden.
+                if lowesthead is None:
+                    lowesthead = {}
+                    for h in repo.heads():
+                        fnode = repo[h].manifest()[f]
+                        lowesthead[fl.rev(fnode)] = h
+                headrev = lowesthead.get(fr)
+                if headrev is None:
+                    # content is nowhere unfiltered
+                    continue
+                rev = repo[headrev][f].introrev()
             else:
                 # the lowest known child is a good upper bound
                 childcrev = backrevref[child]
--- a/tests/test-log.t	Mon Dec 29 17:23:16 2014 -0800
+++ b/tests/test-log.t	Mon Dec 29 18:35:23 2014 -0800
@@ -1725,4 +1725,22 @@
      summary:     content1
   
 
+Even when a head revision is linkrev-shadowed.
+
+  $ hg log -T '{node}\n' -r 4
+  50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
+  $ hg debugobsolete 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
+  $ hg log -G a
+  @  changeset:   3:15b2327059e5
+  |  tag:         tip
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     content2
+  |
+  o  changeset:   0:ae0a3c9f9e95
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     content1
+  
+
   $ cd ..