This ensure that corrupted clone does not spread corruption to "fixed" version.
This might come at a performance cost, we will had a config option to control
this behavior in the next changesets.
pulkit |
hg-reviewers |
This ensure that corrupted clone does not spread corruption to "fixed" version.
This might come at a performance cost, we will had a config option to control
this behavior in the next changesets.
Automatic diff as part of commit; lint not applicable. |
Automatic diff as part of commit; unit tests not applicable. |
โ
refresh by Heptapod after a successful CI run (๐ ๐)
โ This patch is intended for stable โ
โ
refresh by Heptapod after a successful CI run (๐ ๐)
โ This patch is intended for stable โ
Path | Packages | |||
---|---|---|---|---|
M | mercurial/filelog.py (4 lines) | |||
M | mercurial/revlogutils/rewrite.py (84 lines) | |||
A | M | (bin) | tests/bundles/issue6528.hg-v1 | |
A | M | (bin) | tests/bundles/issue6528.hg-v2 | |
M | tests/test-issue6528.t (93 lines) |
) | ) | ||||
from .interfaces import ( | from .interfaces import ( | ||||
repository, | repository, | ||||
util as interfaceutil, | util as interfaceutil, | ||||
) | ) | ||||
from .utils import storageutil | from .utils import storageutil | ||||
from .revlogutils import ( | from .revlogutils import ( | ||||
constants as revlog_constants, | constants as revlog_constants, | ||||
rewrite, | |||||
) | ) | ||||
@interfaceutil.implementer(repository.ifilestorage) | @interfaceutil.implementer(repository.ifilestorage) | ||||
class filelog(object): | class filelog(object): | ||||
def __init__(self, opener, path): | def __init__(self, opener, path): | ||||
self._revlog = revlog.revlog( | self._revlog = revlog.revlog( | ||||
opener, | opener, | ||||
raise error.Abort( | raise error.Abort( | ||||
_( | _( | ||||
b'revlog storage does not support missing ' | b'revlog storage does not support missing ' | ||||
b'parents write mode' | b'parents write mode' | ||||
) | ) | ||||
) | ) | ||||
with self._revlog._writing(transaction): | with self._revlog._writing(transaction): | ||||
deltas = rewrite.filter_delta_issue6528(self._revlog, deltas) | |||||
return self._revlog.addgroup( | return self._revlog.addgroup( | ||||
deltas, | deltas, | ||||
linkmapper, | linkmapper, | ||||
transaction, | transaction, | ||||
addrevisioncb=addrevisioncb, | addrevisioncb=addrevisioncb, | ||||
duplicaterevisioncb=duplicaterevisioncb, | duplicaterevisioncb=duplicaterevisioncb, | ||||
) | ) | ||||
ENTRY_DELTA_BASE, | ENTRY_DELTA_BASE, | ||||
ENTRY_LINK_REV, | ENTRY_LINK_REV, | ||||
ENTRY_NODE_ID, | ENTRY_NODE_ID, | ||||
ENTRY_PARENT_1, | ENTRY_PARENT_1, | ||||
ENTRY_PARENT_2, | ENTRY_PARENT_2, | ||||
ENTRY_SIDEDATA_COMPRESSED_LENGTH, | ENTRY_SIDEDATA_COMPRESSED_LENGTH, | ||||
ENTRY_SIDEDATA_COMPRESSION_MODE, | ENTRY_SIDEDATA_COMPRESSION_MODE, | ||||
ENTRY_SIDEDATA_OFFSET, | ENTRY_SIDEDATA_OFFSET, | ||||
REVIDX_ISCENSORED, | |||||
REVLOGV0, | REVLOGV0, | ||||
REVLOGV1, | REVLOGV1, | ||||
) | ) | ||||
from ..i18n import _ | from ..i18n import _ | ||||
from .. import ( | from .. import ( | ||||
error, | error, | ||||
mdiff, | |||||
pycompat, | pycompat, | ||||
revlogutils, | revlogutils, | ||||
util, | util, | ||||
) | ) | ||||
from ..utils import ( | from ..utils import ( | ||||
storageutil, | storageutil, | ||||
) | ) | ||||
from . import ( | from . import ( | ||||
if not to_fix: | if not to_fix: | ||||
msg = _(b"no affected revisions were found for '%s'\n") | msg = _(b"no affected revisions were found for '%s'\n") | ||||
ui.write(msg % filename) | ui.write(msg % filename) | ||||
continue | continue | ||||
if not dry_run: | if not dry_run: | ||||
_reorder_filelog_parents(repo, fl, sorted(to_fix)) | _reorder_filelog_parents(repo, fl, sorted(to_fix)) | ||||
def filter_delta_issue6528(revlog, deltas_iter): | |||||
"""filter incomind deltas to repaire issue 6528 on the fly""" | |||||
metadata_cache = {} | |||||
deltacomputer = deltas.deltacomputer(revlog) | |||||
for rev, d in enumerate(deltas_iter, len(revlog)): | |||||
( | |||||
node, | |||||
p1_node, | |||||
p2_node, | |||||
linknode, | |||||
deltabase, | |||||
delta, | |||||
flags, | |||||
sidedata, | |||||
) = d | |||||
if not revlog.index.has_node(deltabase): | |||||
raise error.LookupError( | |||||
deltabase, revlog.radix, _(b'unknown parent') | |||||
) | |||||
base_rev = revlog.rev(deltabase) | |||||
if not revlog.index.has_node(p1_node): | |||||
raise error.LookupError(p1_node, revlog.radix, _(b'unknown parent')) | |||||
p1_rev = revlog.rev(p1_node) | |||||
if not revlog.index.has_node(p2_node): | |||||
raise error.LookupError(p2_node, revlog.radix, _(b'unknown parent')) | |||||
p2_rev = revlog.rev(p2_node) | |||||
is_censored = lambda: bool(flags & REVIDX_ISCENSORED) | |||||
delta_base = lambda: revlog.rev(delta_base) | |||||
delta_base = lambda: base_rev | |||||
parent_revs = lambda: (p1_rev, p2_rev) | |||||
def full_text(): | |||||
# note: being able to reuse the full text computation in the | |||||
# underlying addrevision would be useful however this is a bit too | |||||
# intrusive the for the "quick" issue6528 we are writing before the | |||||
# 5.8 release | |||||
textlen = mdiff.patchedsize(revlog.size(base_rev), delta) | |||||
revinfo = revlogutils.revisioninfo( | |||||
node, | |||||
p1_node, | |||||
p2_node, | |||||
[None], | |||||
textlen, | |||||
(base_rev, delta), | |||||
flags, | |||||
) | |||||
# cached by the global "writing" context | |||||
assert revlog._writinghandles is not None | |||||
if revlog._inline: | |||||
fh = revlog._writinghandles[0] | |||||
else: | |||||
fh = revlog._writinghandles[1] | |||||
return deltacomputer.buildtext(revinfo, fh) | |||||
is_affected = _is_revision_affected_fast_inner( | |||||
is_censored, | |||||
delta_base, | |||||
lambda: delta, | |||||
full_text, | |||||
parent_revs, | |||||
rev, | |||||
metadata_cache, | |||||
) | |||||
if is_affected: | |||||
d = ( | |||||
node, | |||||
p2_node, | |||||
p1_node, | |||||
linknode, | |||||
deltabase, | |||||
delta, | |||||
flags, | |||||
sidedata, | |||||
) | |||||
yield d | |||||
def repair_issue6528( | def repair_issue6528( | ||||
ui, repo, dry_run=False, to_report=None, from_report=None, paranoid=False | ui, repo, dry_run=False, to_report=None, from_report=None, paranoid=False | ||||
): | ): | ||||
from .. import store # avoid cycle | from .. import store # avoid cycle | ||||
@contextlib.contextmanager | @contextlib.contextmanager | ||||
def context(): | def context(): | ||||
if dry_run or to_report: # No need for locking | if dry_run or to_report: # No need for locking |
$ hg debugrevlogindex D.txt | $ hg debugrevlogindex D.txt | ||||
rev linkrev nodeid p1 p2 | rev linkrev nodeid p1 p2 | ||||
0 6 2a8d3833f2fb 000000000000 000000000000 | 0 6 2a8d3833f2fb 000000000000 000000000000 | ||||
1 7 2a80419dfc31 000000000000 2a8d3833f2fb | 1 7 2a80419dfc31 000000000000 2a8d3833f2fb | ||||
2 8 65aecc89bb5d 2a80419dfc31 000000000000 | 2 8 65aecc89bb5d 2a80419dfc31 000000000000 | ||||
$ hg debug-repair-issue6528 | $ hg debug-repair-issue6528 | ||||
no affected revisions were found | no affected revisions were found | ||||
$ hg st | $ hg st | ||||
$ cd .. | |||||
Applying a bad bundle should fix it on the fly | |||||
---------------------------------------------- | |||||
from a v1 bundle | |||||
~~~~~~~~~~~~~~~~ | |||||
$ hg debugbundle --spec "$TESTDIR"/bundles/issue6528.hg-v1 | |||||
bzip2-v1 | |||||
$ hg init unbundle-v1 | |||||
$ cd unbundle-v1 | |||||
$ hg unbundle "$TESTDIR"/bundles/issue6528.hg-v1 | |||||
adding changesets | |||||
adding manifests | |||||
adding file changes | |||||
added 8 changesets with 12 changes to 4 files | |||||
new changesets f5a5a568022f:3beabb508514 (8 drafts) | |||||
(run 'hg update' to get a working copy) | |||||
Check that revision were fixed on the fly | |||||
$ hg debugrevlogindex b.txt | |||||
rev linkrev nodeid p1 p2 | |||||
0 2 05b806ebe5ea 000000000000 000000000000 | |||||
1 3 a58b36ad6b65 000000000000 05b806ebe5ea | |||||
2 6 216a5fe8b8ed 000000000000 000000000000 | |||||
3 7 ea4f2f2463cc 000000000000 216a5fe8b8ed | |||||
$ hg debugrevlogindex D.txt | |||||
rev linkrev nodeid p1 p2 | |||||
0 6 2a8d3833f2fb 000000000000 000000000000 | |||||
1 7 2a80419dfc31 000000000000 2a8d3833f2fb | |||||
That we don't see the symptoms of the bug | |||||
$ hg up -- -1 | |||||
2 files updated, 0 files merged, 0 files removed, 0 files unresolved | |||||
$ hg status | |||||
And that the repair command does not find anything to fix | |||||
$ hg debug-repair-issue6528 | |||||
no affected revisions were found | |||||
$ cd .. | |||||
from a v2 bundle | |||||
~~~~~~~~~~~~~~~~ | |||||
$ hg debugbundle --spec "$TESTDIR"/bundles/issue6528.hg-v2 | |||||
bzip2-v2 | |||||
$ hg init unbundle-v2 | |||||
$ cd unbundle-v2 | |||||
$ hg unbundle "$TESTDIR"/bundles/issue6528.hg-v2 | |||||
adding changesets | |||||
adding manifests | |||||
adding file changes | |||||
added 8 changesets with 12 changes to 4 files | |||||
new changesets f5a5a568022f:3beabb508514 (8 drafts) | |||||
(run 'hg update' to get a working copy) | |||||
Check that revision were fixed on the fly | |||||
$ hg debugrevlogindex b.txt | |||||
rev linkrev nodeid p1 p2 | |||||
0 2 05b806ebe5ea 000000000000 000000000000 | |||||
1 3 a58b36ad6b65 000000000000 05b806ebe5ea | |||||
2 6 216a5fe8b8ed 000000000000 000000000000 | |||||
3 7 ea4f2f2463cc 000000000000 216a5fe8b8ed | |||||
$ hg debugrevlogindex D.txt | |||||
rev linkrev nodeid p1 p2 | |||||
0 6 2a8d3833f2fb 000000000000 000000000000 | |||||
1 7 2a80419dfc31 000000000000 2a8d3833f2fb | |||||
That we don't see the symptoms of the bug | |||||
$ hg up -- -1 | |||||
2 files updated, 0 files merged, 0 files removed, 0 files unresolved | |||||
$ hg status | |||||
And that the repair command does not find anything to fix | |||||
$ hg debug-repair-issue6528 | |||||
no affected revisions were found | |||||
$ cd .. |