diff --git a/hgext/convert/__init__.py b/hgext/convert/__init__.py --- a/hgext/convert/__init__.py +++ b/hgext/convert/__init__.py @@ -439,6 +439,11 @@ :convert.hg.sourcename: records the given string as a 'convert_source' extra value on each commit made in the target repository. The default is None. + :convert.hg.preserve-hash: only works with mercurial sources. Make convert + prevent performance improvement to the list of modified files in commits + when such an improvement would cause the hash of a commit to change. + The default is False. + All Destinations ################ diff --git a/hgext/convert/common.py b/hgext/convert/common.py --- a/hgext/convert/common.py +++ b/hgext/convert/common.py @@ -114,7 +114,7 @@ class commit(object): def __init__(self, author, date, desc, parents, branch=None, rev=None, extra=None, sortkey=None, saverev=True, phase=phases.draft, - optparents=None): + optparents=None, ctx=None): self.author = author or 'unknown' self.date = date or '0 0' self.desc = desc @@ -126,6 +126,7 @@ self.sortkey = sortkey self.saverev = saverev self.phase = phase + self.ctx = ctx # for hg to hg conversions class converter_source(object): """Conversion source interface""" diff --git a/hgext/convert/hg.py b/hgext/convert/hg.py --- a/hgext/convert/hg.py +++ b/hgext/convert/hg.py @@ -339,7 +339,11 @@ phases.phasenames[commit.phase], 'convert') with self.repo.transaction("convert") as tr: - node = nodemod.hex(self.repo.commitctx(ctx)) + if self.repo.ui.config('convert', 'hg.preserve-hash'): + origctx = commit.ctx + else: + origctx = None + node = nodemod.hex(self.repo.commitctx(ctx, origctx=origctx)) # If the node value has changed, but the phase is lower than # draft, set it back to draft since it hasn't been exposed @@ -591,7 +595,8 @@ extra=ctx.extra(), sortkey=ctx.rev(), saverev=self.saverev, - phase=ctx.phase()) + phase=ctx.phase(), + ctx=ctx) def numcommits(self): return len(self.repo) diff --git a/hgext/lfs/__init__.py b/hgext/lfs/__init__.py --- a/hgext/lfs/__init__.py +++ b/hgext/lfs/__init__.py @@ -227,9 +227,9 @@ class lfsrepo(repo.__class__): @localrepo.unfilteredmethod - def commitctx(self, ctx, error=False): + def commitctx(self, ctx, error=False, origctx=None): repo.svfs.options['lfstrack'] = _trackedmatcher(self) - return super(lfsrepo, self).commitctx(ctx, error) + return super(lfsrepo, self).commitctx(ctx, error, origctx=origctx) repo.__class__ = lfsrepo diff --git a/mercurial/configitems.py b/mercurial/configitems.py --- a/mercurial/configitems.py +++ b/mercurial/configitems.py @@ -291,6 +291,9 @@ coreconfigitem('convert', 'hg.ignoreerrors', default=False, ) +coreconfigitem('convert', 'hg.preserve-hash', + default=False, +) coreconfigitem('convert', 'hg.revs', default=None, ) diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -2578,7 +2578,7 @@ return ret @unfilteredmethod - def commitctx(self, ctx, error=False): + def commitctx(self, ctx, error=False, origctx=None): """Add a new revision to current repository. Revision information is passed via the context argument. @@ -2586,6 +2586,12 @@ modified/added/removed files. On merge, it may be wider than the ctx.files() to be committed, since any file nodes derived directly from p1 or p2 are excluded from the committed ctx.files(). + + origctx is for convert to work around the problem that bug + fixes to the files list in changesets change hashes. For + convert to be the identity, it can pass an origctx and this + function will use the same files list when it makes sense to + do so. """ p1, p2 = ctx.p1(), ctx.p2() @@ -2701,6 +2707,9 @@ filesadded = filesadded or None filesremoved = filesremoved or None + if origctx and origctx.manifestnode() == mn: + files = origctx.files() + # update changelog self.ui.note(_("committing changelog\n")) self.changelog.delayupdate(tr) diff --git a/tests/test-convert-identity.t b/tests/test-convert-identity.t new file mode 100644 --- /dev/null +++ b/tests/test-convert-identity.t @@ -0,0 +1,40 @@ +Testing that convert.hg.preserve-hash=true can be used to make hg +convert from hg repo to hg repo preserve hashes, even if the +computation of the files list in commits change slightly between hg +versions. + + $ cat <<'EOF' >> "$HGRCPATH" + > [extensions] + > convert = + > EOF + $ cat <<'EOF' > changefileslist.py + > from mercurial import (changelog, extensions) + > def wrap(orig, clog, manifest, files, *args, **kwargs): + > return orig(clog, manifest, ["a"], *args, **kwargs) + > def extsetup(ui): + > extensions.wrapfunction(changelog.changelog, 'add', wrap) + > EOF + + $ hg init repo + $ cd repo + $ echo a > a; hg commit -qAm a + $ echo b > a; hg commit -qAm b + $ hg up -qr 0; echo c > c; hg commit -qAm c + $ hg merge -qr 1 + $ hg commit -m_ --config extensions.x=../changefileslist.py + $ hg log -r . -T '{node|short} {files|json}\n' + c085bbe93d59 ["a"] + +Now that we have a commit with a files list that's not what the +current hg version would create, check that convert either fixes it or +keeps it depending on config: + + $ hg convert -q . ../convert + $ hg --cwd ../convert log -r tip -T '{node|short} {files|json}\n' + b7c4d4bbacd3 [] + $ rm -rf ../convert + + $ hg convert -q . ../convert --config convert.hg.preserve-hash=true + $ hg --cwd ../convert log -r tip -T '{node|short} {files|json}\n' + c085bbe93d59 ["a"] + $ rm -rf ../convert diff --git a/tests/test-convert.t b/tests/test-convert.t --- a/tests/test-convert.t +++ b/tests/test-convert.t @@ -373,6 +373,11 @@ records the given string as a 'convert_source' extra value on each commit made in the target repository. The default is None. + convert.hg.preserve-hash + only works with mercurial sources. Make convert prevent + performance improvement to the list of modified files in + commits when such an improvement would cause the hash of a + commit to change. The default is False. All Destinations ################