diff mercurial/repair.py @ 47916:0fb328bb2459

debugrebuildfncache: add a cheaper option to rebuild the fncache On my repository, debugrebuildfncache takes 5-10min with the lock. With the flag added in this commit, it takes 10s. The tradeoff is that it only recovers from certain kinds of corruptions. It is intended to to recover faster from fncaches broken by a revlog split during a transaction that ends up being rolled back. Differential Revision: https://phab.mercurial-scm.org/D11265
author Valentin Gatien-Baron <vgatien-baron@janestreet.com>
date Fri, 06 Aug 2021 16:27:17 -0400
parents 7a430116f639
children 2174f54aab18
line wrap: on
line diff
--- a/mercurial/repair.py	Fri Aug 06 16:17:17 2021 -0400
+++ b/mercurial/repair.py	Fri Aug 06 16:27:17 2021 -0400
@@ -441,7 +441,7 @@
                 yield repo.manifestlog.getstorage(dir)
 
 
-def rebuildfncache(ui, repo):
+def rebuildfncache(ui, repo, only_data=False):
     """Rebuilds the fncache file from repo history.
 
     Missing entries will be added. Extra entries will be removed.
@@ -465,28 +465,40 @@
         newentries = set()
         seenfiles = set()
 
-        progress = ui.makeprogress(
-            _(b'rebuilding'), unit=_(b'changesets'), total=len(repo)
-        )
-        for rev in repo:
-            progress.update(rev)
+        if only_data:
+            # Trust the listing of .i from the fncache, but not the .d. This is
+            # much faster, because we only need to stat every possible .d files,
+            # instead of reading the full changelog
+            for f in fnc:
+                if f[:5] == b'data/' and f[-2:] == b'.i':
+                    seenfiles.add(f[5:-2])
+                    newentries.add(f)
+                    dataf = f[:-2] + b'.d'
+                    if repo.store._exists(dataf):
+                        newentries.add(dataf)
+        else:
+            progress = ui.makeprogress(
+                _(b'rebuilding'), unit=_(b'changesets'), total=len(repo)
+            )
+            for rev in repo:
+                progress.update(rev)
 
-            ctx = repo[rev]
-            for f in ctx.files():
-                # This is to minimize I/O.
-                if f in seenfiles:
-                    continue
-                seenfiles.add(f)
+                ctx = repo[rev]
+                for f in ctx.files():
+                    # This is to minimize I/O.
+                    if f in seenfiles:
+                        continue
+                    seenfiles.add(f)
 
-                i = b'data/%s.i' % f
-                d = b'data/%s.d' % f
+                    i = b'data/%s.i' % f
+                    d = b'data/%s.d' % f
 
-                if repo.store._exists(i):
-                    newentries.add(i)
-                if repo.store._exists(d):
-                    newentries.add(d)
+                    if repo.store._exists(i):
+                        newentries.add(i)
+                    if repo.store._exists(d):
+                        newentries.add(d)
 
-        progress.complete()
+            progress.complete()
 
         if requirements.TREEMANIFEST_REQUIREMENT in repo.requirements:
             # This logic is safe if treemanifest isn't enabled, but also