Patchwork D10083: tags: validate nodes in _getfnodes() and update cache in case of unknown nodes

login
register
mail settings
Submitter phabricator
Date March 1, 2021, 6:47 p.m.
Message ID <differential-rev-PHID-DREV-ltpuc3uytczyhbhustkc-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/48398/
State Superseded
Headers show

Comments

phabricator - March 1, 2021, 6:47 p.m.
pulkit created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  `hgtagsfnodescache` can contain unknown nodes due to cache corruption and this
  lead to a traceback on operations like `hg tags` as we don't validate nodes.
  
  This patch validates that all filenodes returned after `hgtagsfnodescache` are
  known to the repository. If there exists any unknown filenode, we force
  recompute it and update the cache.
  
  The test change demonstrates the fix.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D10083

AFFECTED FILES
  mercurial/tags.py
  tests/test-tags.t

CHANGE DETAILS




To: pulkit, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

diff --git a/tests/test-tags.t b/tests/test-tags.t
--- a/tests/test-tags.t
+++ b/tests/test-tags.t
@@ -452,8 +452,8 @@ 
   5 8dbfe60eff306a54259cfe007db9e330e7ecf866 0c04f2a8deadde17fab7422878ee5a2dadbc943d (unknown node)
 
   $ hg tags
-  abort: data/.hgtags.i@0c04f2a8deadde17fab7422878ee5a2dadbc943d: no match found
-  [50]
+  tip                                5:8dbfe60eff30
+  bar                                1:78391a272241
 
 BUG: Unless this file is restored, the `hg tags` in the next unix-permissions
 conditional will fail: "abort: data/.hgtags.i@0c04f2a8dead: no match found"
diff --git a/mercurial/tags.py b/mercurial/tags.py
--- a/mercurial/tags.py
+++ b/mercurial/tags.py
@@ -494,11 +494,25 @@ 
     starttime = util.timer()
     fnodescache = hgtagsfnodescache(repo.unfiltered())
     cachefnode = {}
+    validatedfnodes = set()
+    unknownentries = set()
     for node in nodes:
         fnode = fnodescache.getfnode(node)
         if fnode != nullid:
+            if fnode not in validatedfnodes:
+                try:
+                    repo.filectx(b'.hgtags', fileid=fnode).data()
+                    validatedfnodes.add(fnode)
+                except error.LookupError:
+                    unknownentries.add(node)
             cachefnode[node] = fnode
 
+    if unknownentries:
+        fixednodemap = fnodescache.recomputefnodes(unknownentries)
+        for node, fnode in pycompat.iteritems(fixednodemap):
+            if fnode != nullid:
+                cachefnode[node] = fnode
+
     fnodescache.write()
 
     duration = util.timer() - starttime
@@ -826,6 +840,16 @@ 
 
         self._writeentry(ctx.rev() * _fnodesrecsize, node[0:4], fnode)
 
+    def recomputefnodes(self, nodes):
+        """recomputes file nodes for given nodes as the current ones are
+        unknown and return a map of node -> recomputed fnode"""
+        fixednodemap = {}
+        for node in nodes:
+            fnode = self._computefnode(node)
+            fixednodemap[node] = fnode
+            self.setfnode(node, fnode)
+        return fixednodemap
+
     def _writeentry(self, offset, prefix, fnode):
         # Slices on array instances only accept other array.
         entry = bytearray(prefix + fnode)