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
--- a/mercurial/debugcommands.py Fri Aug 06 16:17:17 2021 -0400
+++ b/mercurial/debugcommands.py Fri Aug 06 16:27:17 2021 -0400
@@ -2987,10 +2987,22 @@
dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
-@command(b'debugrebuildfncache', [], b'')
-def debugrebuildfncache(ui, repo):
+@command(
+ b'debugrebuildfncache',
+ [
+ (
+ b'',
+ b'only-data',
+ False,
+ _(b'only look for wrong .d files (much faster)'),
+ )
+ ],
+ b'',
+)
+def debugrebuildfncache(ui, repo, **opts):
"""rebuild the fncache file"""
- repair.rebuildfncache(ui, repo)
+ opts = pycompat.byteskwargs(opts)
+ repair.rebuildfncache(ui, repo, opts.get(b"only_data"))
@command(
--- 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
--- a/tests/test-completion.t Fri Aug 06 16:17:17 2021 -0400
+++ b/tests/test-completion.t Fri Aug 06 16:27:17 2021 -0400
@@ -316,7 +316,7 @@
debugpushkey:
debugpvec:
debugrebuilddirstate: rev, minimal
- debugrebuildfncache:
+ debugrebuildfncache: only-data
debugrename: rev
debugrequires:
debugrevlog: changelog, manifest, dir, dump
--- a/tests/test-transaction-rollback-on-revlog-split.t Fri Aug 06 16:17:17 2021 -0400
+++ b/tests/test-transaction-rollback-on-revlog-split.t Fri Aug 06 16:27:17 2021 -0400
@@ -86,6 +86,10 @@
warning: revlog 'data/file.d' not in fncache!
1 warnings encountered!
hint: run "hg debugrebuildfncache" to recover from corrupt fncache
+ $ hg debugrebuildfncache --only-data
+ adding data/file.d
+ 1 items added, 0 removed from fncache
+ $ hg verify -q
$ cd ..