diff --git a/hgext/convert/hg.py b/hgext/convert/hg.py index db677b70a298ec488a72cd96e03eee4dd771b020_aGdleHQvY29udmVydC9oZy5weQ==..a75d24539aba3d8d94dda1d9cf5c24081768ed02_aGdleHQvY29udmVydC9oZy5weQ== 100644 --- a/hgext/convert/hg.py +++ b/hgext/convert/hg.py @@ -23,6 +23,7 @@ from mercurial.node import bin, hex, nullid from mercurial import hg, util, context, bookmarks, error, scmutil, exchange from mercurial import phases +from mercurial import merge as mergemod from common import NoRepo, commit, converter_source, converter_sink, mapfile @@ -176,8 +177,47 @@ return fp.getvalue() + def _calculatemergedfiles(self, source, p1ctx, p2ctx): + """Calculates the files from p2 that we need to pull in when merging p1 + and p2, given that the merge is coming from the given source. + + This prevents us from losing files that only exist in the target p2 and + that don't come from the source repo (like if you're merging multiple + repositories together). + """ + anc = [p1ctx.ancestor(p2ctx)] + # Calculate what files are coming from p2 + actions, diverge, rename = mergemod.calculateupdates( + self.repo, p1ctx, p2ctx, anc, + True, # branchmerge + True, # force + False, # partial + False, # acceptremote + False, # followcopies + ) + + for file, (action, info, msg) in actions.iteritems(): + if source.targetfilebelongstosource(file): + # If the file belongs to the source repo, ignore the p2 + # since it will be covered by the existing fileset. + continue + + # If the file requires actual merging, abort. We don't have enough + # context to resolve merges correctly. + if action in ['m', 'dm', 'cd', 'dc']: + raise util.Abort(_("unable to convert merge commit " + "since target parents do not merge cleanly (file " + "%s, parents %s and %s)") % (file, p1ctx, + p2ctx)) + elif action == 'k': + # 'keep' means nothing changed from p1 + continue + else: + # Any other change means we want to take the p2 version + yield file + def putcommit(self, files, copies, parents, commit, source, revmap, full, cleanp2): files = dict(files) def getfilectx(repo, memctx, f): @@ -179,9 +219,9 @@ def putcommit(self, files, copies, parents, commit, source, revmap, full, cleanp2): files = dict(files) def getfilectx(repo, memctx, f): - if p2ctx and f in cleanp2 and f not in copies: + if p2ctx and f in p2files and f not in copies: self.ui.debug('reusing %s from p2\n' % f) return p2ctx[f] try: @@ -255,6 +295,7 @@ while parents: p1 = p2 p2 = parents.pop(0) + p1ctx = self.repo[p1] p2ctx = None if p2 != nullid: p2ctx = self.repo[p2] @@ -262,6 +303,13 @@ if full: fileset.update(self.repo[p1]) fileset.update(self.repo[p2]) + + if p2ctx: + p2files = set(cleanp2) + for file in self._calculatemergedfiles(source, p1ctx, p2ctx): + p2files.add(file) + fileset.add(file) + ctx = context.memctx(self.repo, (p1, p2), text, fileset, getfilectx, commit.author, commit.date, extra) diff --git a/tests/test-convert-filemap.t b/tests/test-convert-filemap.t index db677b70a298ec488a72cd96e03eee4dd771b020_dGVzdHMvdGVzdC1jb252ZXJ0LWZpbGVtYXAudA==..a75d24539aba3d8d94dda1d9cf5c24081768ed02_dGVzdHMvdGVzdC1jb252ZXJ0LWZpbGVtYXAudA== 100644 --- a/tests/test-convert-filemap.t +++ b/tests/test-convert-filemap.t @@ -671,3 +671,61 @@ |/ o 0:c334dc3be0da@default "add" files: a + $ cd .. + +test converting merges into a repo that contains other files + + $ hg init merge-test1 + $ cd merge-test1 + $ touch a && hg commit -Aqm a + $ hg up -q null + $ touch b && hg commit -Aqm b + $ hg merge -q 0 && hg commit -qm merge + $ cd .. + $ hg init merge-test2 + $ cd merge-test2 + $ mkdir converted + $ touch converted/a && hg commit -Aqm 'a' + $ touch x && hg commit -Aqm 'x' + $ cd .. + $ hg log -G -T '{node}' -R merge-test1 + @ ea7c1a7ae9588677a715ce4f204cd89c28d5471f + |\ + | o d7486e00c6f1b633dcadc0582f78006d805c7a0f + | + o 3903775176ed42b1458a6281db4a0ccf4d9f287a + + $ hg log -G -T '{node}' -R merge-test2 + @ 34f1aa7da42559bae87920880b522d47b3ddbc0d + | + o e01a12b07b4fdfd61ff90a2a1b4560a7a776f323 + +- Build a shamap where the target converted/a is in on top of an unrelated +- change to 'x'. This simulates using convert to merge several repositories +- together. + $ cat >> merge-test2/.hg/shamap <<EOF + > 3903775176ed42b1458a6281db4a0ccf4d9f287a 34f1aa7da42559bae87920880b522d47b3ddbc0d + > EOF + $ cat >> merge-test-filemap <<EOF + > rename . converted/ + > EOF + $ hg convert --filemap merge-test-filemap merge-test1 merge-test2 --traceback + scanning source... + sorting... + converting... + 1 b + 0 merge + $ hg -R merge-test2 manifest -r tip + converted/a + converted/b + x + $ hg -R merge-test2 log -G -T '{node}\n{files % "{file}\n"}' + o 4b5e2f0218d3442a0c14892b18685bf9c8059c4a + |\ + | o 214325dd2e4cff981dcf00cb120cd39e1ea36dcc + | converted/b + @ 34f1aa7da42559bae87920880b522d47b3ddbc0d + | x + o e01a12b07b4fdfd61ff90a2a1b4560a7a776f323 + converted/a +