[PATCH] debug: automate the process of truncating a damaged obsstore

Simon Farnsworth simonfar at fb.com
Thu Jun 30 14:04:09 UTC 2016


# HG changeset patch
# User Simon Farnsworth <simonfar at fb.com>
# Date 1467295436 25200
#      Thu Jun 30 07:03:56 2016 -0700
# Node ID 252cefa0326063bd664f47e3628942df30d08ccf
# Parent  c42a3fd5c1fc5193e5f45887bfddaf05ca977fa4
debug: automate the process of truncating a damaged obsstore

We occasionally see users who've had a system crash damage the obsstore file
in their .hg/store directory; this makes all `hg` commands fail until we go
in and remove the damaged section of the obsstore by hand.

Automate the process we use when this happens, as a debug command because it
loses the corrupted data. We only use it in rare circumstances when it's
important to retrieve a user's work and apply it to a fresh clone.

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -3702,6 +3702,46 @@
             displayer.show(repo[r], **props)
         displayer.close()
 
+ at command('debugtruncatestore',
+    [('', 'obsolete', None, _('truncate bad markers in obsstore'))],
+    _('[OPTION]'))
+def debugtruncatestore(ui, repo, **opts):
+    """Fix up repository corruption by truncating damaged files
+
+    Most on-disk data structures are designed to be append-only. A failed write
+    (e.g. due to an unexpected power failure) can leave the file corrupted.
+
+    This command attempts to recover from that situation by replacing the
+    corrupted file with a version that only contains the valid records from the
+    broken file.
+
+    You should normally use :hg:`recover` before resorting to this command.
+    """
+
+    if 'obsolete' in opts:
+        data = repo.svfs.tryread('obsstore')
+        if data:
+            # Slow algorithm - but this is an emergency debug operation
+            version = None
+            corrupt = False
+            while version is None:
+                try:
+                    (version, markers) = obsolete._readmarkers(data)
+                except ValueError:
+                    corrupt = True
+                    version = None
+                    data = data[:-1]
+                    continue
+                break
+            if corrupt:
+                repo.svfs.write('obsstore', data)
+                ui.write(_('truncated obsstore\n'))
+            else:
+                ui.write(_('no corruption\n'))
+
+        else:
+            ui.write(_('no obsstore\n'))
+
 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
 def debugwalk(ui, repo, *pats, **opts):
     """show how files match on given patterns"""


More information about the Mercurial-devel mailing list