Submitter | Phillip Cohen |
---|---|
Date | March 17, 2017, 2:17 a.m. |
Message ID | <62ab2ddcf1683c3bf2e4.1489717038@phillco-mbp.dhcp.thefacebook.com> |
Download | mbox | patch |
Permalink | /patch/19414/ |
State | Changes Requested |
Headers | show |
Comments
Patch 3 looks good to me. In this patch, I'd fold the test additions into the new test case you made in patch 3 -- a separate test for corner cases doesn't really make sense to me. Otherwise, the tests look good to me. Looking forward to the next version of this series which I think will be in a queue-ready state :-) On 3/17/17 2:17 AM, Phil Cohen wrote: > # HG changeset patch > # User Phil Cohen <phillco@fb.com> > # Date 1489716623 25200 > # Thu Mar 16 19:10:23 2017 -0700 > # Node ID 62ab2ddcf1683c3bf2e4dbb21dbe11d065bff93b > # Parent 36d378ac43dbbf559655c70b7cad873768a0e794 > filemerge: add test cases for corner cases using internal:dumpjson > > diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py > --- a/mercurial/filemerge.py > +++ b/mercurial/filemerge.py > @@ -589,6 +589,8 @@ > # `up -n` or `merge -f`), "local" must reflect that, not the underlying > # commit. Those contents are available in the .orig version, so we look > # there and mock up the schema to look like the other contexts. > + # > + # Test cases affected in test-merge-conflict-cornercases.t: #0 > local = { > 'contents': util.readfile(origfile), > 'exists': True, > @@ -605,6 +607,8 @@ > # pre-merge properties including any local changes), so we can reuse > # that. > # > + # Affected test cases: #0b, #1, #6, #11, and #12. > + # > # Another alternative might be to use repo['.'][path] but that wouldn't > # have any dirty pre-merge changes. > # > diff --git a/tests/test-merge-conflict-cornercases.t b/tests/test-merge-conflict-cornercases.t > new file mode 100644 > --- /dev/null > +++ b/tests/test-merge-conflict-cornercases.t > @@ -0,0 +1,575 @@ > +Tests merge conflict corner cases (file-to-directory, binary-to-symlink, etc.) > +"other" == source > +"local" == dest > + > +Setup > + $ cat >> $HGRCPATH <<EOF > + > [extensions] > + > rebase= > + > EOF > + > + $ reset() { > + > rm -rf foo > + > mkdir foo > + > cd foo > + > hg init > + > echo "base" > file > + > hg commit -Aqm "base" > + > } > + $ logg() { > + > hg log -G -T '({rev}) {desc}\naffected: {files}\ndeleted: {file_dels}\n\n' > + > } > + > +Test case 0: A merge of just contents conflicts (not usually a corner case), > +but the user had local changes and ran `merge -f`. > + > +tldr: Since we can premerge, the working copy is backed up to an origfile. > + $ reset > + $ echo "change" > file > + $ hg commit -Aqm "dest" > + $ hg up -q 0 > + $ echo "other change" > file > + $ hg commit -Aqm "source" > + $ hg up -q 1 > + $ logg > + o (2) source > + | affected: file > + | deleted: > + | > + | @ (1) dest > + |/ affected: file > + | deleted: > + | > + o (0) base > + affected: file > + deleted: > + > + $ echo "some local changes" > file > + $ hg merge 2 -f > + merging file > + warning: conflicts while merging file! (edit, then use 'hg resolve --mark') > + 0 files updated, 0 files merged, 0 files removed, 1 files unresolved > + use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon > + [1] > + > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "some local changes\n", "exists": true, "isexec": false, "issymlink": false}, "other": {"contents": "other change\n", "exists": true, "isexec": false, "issymlink": false}, "output": {"contents": "<<<<<<<workingcopy:fd7d10c36158-test:dest\nsomelocalchanges\n=======\notherchange\n>>>>>>> merge rev: 9b65ba2922f0 - test: source\n", "exists": true, "isexec": false, "issymlink": false, "path": "$TESTTMP/foo/file"}, "path": "file"}] > + } > + ] > + > +Test case 0b: Like #0 but with a corner case: source deleted, local changed > +*and* had local changes using merge -f. > + > +tldr: Since we couldn't premerge, the working copy is left alone. > + $ reset > + $ echo "change" > file > + $ hg commit -Aqm "dest" > + $ hg up -q 0 > + $ hg rm file > + $ hg commit -Aqm "source" > + $ hg up -q 1 > + $ logg > + o (2) source > + | affected: file > + | deleted: file > + | > + | @ (1) dest > + |/ affected: file > + | deleted: > + | > + o (0) base > + affected: file > + deleted: > + > + $ echo "some local changes" > file > + $ chmod +x file > + $ hg merge 2 -f > + local [working copy] changed file which other [merge rev] deleted > + use (c)hanged version, (d)elete, or leave (u)nresolved? u > + 0 files updated, 0 files merged, 0 files removed, 1 files unresolved > + use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon > + [1] > + > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "some local changes\n", "exists": true, "isexec": true, "issymlink": false}, "other": {"exists": false}, "output": {"contents": "some local changes\n", "exists": true, "isexec": true, "issymlink": false, "path": "$TESTTMP/foo/foo/file"}, "path": "file"}] > + } > + ] > + > +Test case 1: Source deleted, dest changed > + $ reset > + $ echo "change" > file > + $ hg commit -Aqm "dest" > + $ hg up -q 0 > + $ hg rm file > + $ hg commit -Aqm "source" > + $ hg up -q 1 > + $ logg > + o (2) source > + | affected: file > + | deleted: file > + | > + | @ (1) dest > + |/ affected: file > + | deleted: > + | > + o (0) base > + affected: file > + deleted: > + > + > + $ hg rebase -d 1 -s 2 > + rebasing 2:25c2ef28f4c7 "source" (tip) > + local [dest] changed file which other [source] deleted > + use (c)hanged version, (d)elete, or leave (u)nresolved? u > + unresolved conflicts (see hg resolve, then hg rebase --continue) > + [1] > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "other": {"exists": false}, "output": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false, "path": "$TESTTMP/foo/foo/foo/file"}, "path": "file"}] > + } > + ] > +Test case 1b: Like #1 but with a merge, with local changes > + $ reset > + $ echo "change" > file > + $ hg commit -Aqm "dest" > + $ hg up -q 0 > + $ hg rm file > + $ hg commit -Aqm "source" > + $ hg up -q 1 > + $ logg > + o (2) source > + | affected: file > + | deleted: file > + | > + | @ (1) dest > + |/ affected: file > + | deleted: > + | > + o (0) base > + affected: file > + deleted: > + > + $ echo "some local changes" > file > + $ hg merge 2 -f > + local [working copy] changed file which other [merge rev] deleted > + use (c)hanged version, (d)elete, or leave (u)nresolved? u > + 0 files updated, 0 files merged, 0 files removed, 1 files unresolved > + use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon > + [1] > + > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "some local changes\n", "exists": true, "isexec": false, "issymlink": false}, "other": {"exists": false}, "output": {"contents": "some local changes\n", "exists": true, "isexec": false, "issymlink": false, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] > + } > + ] > +Test case 2: Source changed, dest deleted > + $ cd .. > + $ reset > + $ echo "change" > file > + $ hg commit -Aqm "source" > + $ hg up -q 0 > + $ hg rm file > + $ hg commit -Aqm "dest" > + $ hg up --q 2 > + $ logg > + @ (2) dest > + | affected: file > + | deleted: file > + | > + | o (1) source > + |/ affected: file > + | deleted: > + | > + o (0) base > + affected: file > + deleted: > + > + > + $ hg rebase -d 2 -s 1 > + rebasing 1:ec87889f5f90 "source" > + other [source] changed file which local [dest] deleted > + use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u > + unresolved conflicts (see hg resolve, then hg rebase --continue) > + [1] > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"exists": false}, "other": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "output": {"exists": false, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] > + } > + ] > +Test case 3: Source changed, dest moved > + $ cd .. > + $ reset > + $ echo "change" > file > + $ hg commit -Aqm "source" > + $ hg up -q 0 > + $ hg mv file file_newloc > + $ hg commit -Aqm "dest" > + $ hg up -q 2 > + $ logg > + @ (2) dest > + | affected: file file_newloc > + | deleted: file > + | > + | o (1) source > + |/ affected: file > + | deleted: > + | > + o (0) base > + affected: file > + deleted: > + > + > + $ hg rebase -d 2 -s 1 > + rebasing 1:ec87889f5f90 "source" > + merging file_newloc and file to file_newloc > + saved backup bundle to $TESTTMP/foo/foo/foo/foo/.hg/strip-backup/ec87889f5f90-e39a76b8-backup.hg (glob) > + $ hg up -q 2 # source > + $ cat file_newloc # Should follow: > + change > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [] > + } > + ] > +Test case 4: Source changed, dest moved (w/o copytracing) > + $ cd .. > + $ reset > + $ echo "change" > file > + $ hg commit -Aqm "source" > + $ hg up -q 0 > + $ hg mv file file_newloc > + $ hg commit -Aqm "dest" > + $ hg up -q 2 > + $ logg > + @ (2) dest > + | affected: file file_newloc > + | deleted: file > + | > + | o (1) source > + |/ affected: file > + | deleted: > + | > + o (0) base > + affected: file > + deleted: > + > + > + $ hg rebase -d 2 -s 1 --config experimental.disablecopytrace=True > + rebasing 1:ec87889f5f90 "source" > + other [source] changed file which local [dest] deleted > + use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u > + unresolved conflicts (see hg resolve, then hg rebase --continue) > + [1] > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"exists": false}, "other": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "output": {"exists": false, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] > + } > + ] > +Test case 5: Source moved, dest changed > + $ cd .. > + $ reset > + $ hg mv file file_newloc > + $ hg commit -Aqm "source" > + $ hg up -q 0 > + $ echo "change" > file > + $ hg commit -Aqm "dest" > + $ hg up -q 2 > + $ logg > + @ (2) dest > + | affected: file > + | deleted: > + | > + | o (1) source > + |/ affected: file file_newloc > + | deleted: file > + | > + o (0) base > + affected: file > + deleted: > + > + > + $ hg rebase -d 2 -s 1 > + rebasing 1:e6e7483a8950 "source" > + merging file and file_newloc to file_newloc > + saved backup bundle to $TESTTMP/foo/foo/foo/foo/.hg/strip-backup/e6e7483a8950-8e128ac2-backup.hg (glob) > + $ hg up 2 > + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved > + $ cat file_newloc > + change > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [] > + } > + ] > +Test case 6: Source moved, dest changed (w/o copytracing) > + $ cd .. > + $ reset > + $ hg mv file file_newloc > + $ hg commit -Aqm "source" > + $ hg up -q 0 > + $ echo "change" > file > + $ hg commit -Aqm "dest" > + $ hg up -q 2 > + $ logg > + @ (2) dest > + | affected: file > + | deleted: > + | > + | o (1) source > + |/ affected: file file_newloc > + | deleted: file > + | > + o (0) base > + affected: file > + deleted: > + > + > + $ hg rebase -d 2 -s 1 --config experimental.disablecopytrace=True > + rebasing 1:e6e7483a8950 "source" > + local [dest] changed file which other [source] deleted > + use (c)hanged version, (d)elete, or leave (u)nresolved? u > + unresolved conflicts (see hg resolve, then hg rebase --continue) > + [1] > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "other": {"exists": false}, "output": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] > + } > + ] > +Test case 7: Source is a directory, dest is a file (base is still a file) > + $ cd .. > + $ reset > + $ hg rm file > + $ mkdir file # "file" is a stretch > + $ echo "this will cause problems" >> file/subfile > + $ hg commit -Aqm "source" > + $ hg up -q 0 > + $ echo "change" > file > + $ hg commit -Aqm "dest" > + $ hg up -q 2 > + $ logg > + @ (2) dest > + | affected: file > + | deleted: > + | > + | o (1) source > + |/ affected: file file/subfile > + | deleted: file > + | > + o (0) base > + affected: file > + deleted: > + > + > + $ hg rebase -d 2 -s 1 > + rebasing 1:ed93aeac6b3c "source" > + abort: Not a directory: '$TESTTMP/foo/foo/foo/foo/file/subfile' > + [255] > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [] > + } > + ] > +Test case 8: Source is a file, dest is a directory (base is still a file) > + $ cd .. > + $ reset > + $ echo "change" > file > + $ hg commit -Aqm "source" > + $ hg up -q 0 > + $ hg rm file > + $ mkdir file # "file" > + $ echo "this will cause problems" >> file/subfile > + $ hg commit -Aqm "dest" > + $ hg up -q 2 > + $ logg > + @ (2) dest > + | affected: file file/subfile > + | deleted: file > + | > + | o (1) source > + |/ affected: file > + | deleted: > + | > + o (0) base > + affected: file > + deleted: > + > + > + $ hg rebase -d 2 -s 1 > + rebasing 1:ec87889f5f90 "source" > + abort: Operation not permitted: '$TESTTMP/foo/foo/foo/foo/file' > + [255] > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [] > + } > + ] > +Test case 9: Source is a binary file, dest is a file (base is still a file) > + $ cd .. > + $ reset > + $ python -c 'f = open("file", "w"); f.write("\x00")' > + $ hg commit -Aqm "source" > + $ hg up -q 0 > + $ echo "change" > file > + $ hg commit -Aqm "dest" > + $ hg up -q 2 > + $ logg > + @ (2) dest > + | affected: file > + | deleted: > + | > + | o (1) source > + |/ affected: file > + | deleted: > + | > + o (0) base > + affected: file > + deleted: > + > + > + $ hg rebase -d 2 -s 1 > + rebasing 1:b6e55a03a5dc "source" > + merging file > + warning: ([^\s]+) looks like a binary file. (re) > + warning: conflicts while merging file! (edit, then use 'hg resolve --mark') > + unresolved conflicts (see hg resolve, then hg rebase --continue) > + [1] > + $ cat -v file # The local version should be left in the working copy > + change > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "other": {"contents": "\u0000", "exists": true, "isexec": false, "issymlink": false}, "output": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] > + } > + ] > +Test case 10: Source is a file, dest is a binary file (base is still a file) > + $ cd .. > + $ reset > + $ echo "change" > file > + $ hg commit -Aqm "source" > + $ hg up -q 0 > + $ python -c 'f = open("file", "w"); f.write("\x00")' > + $ hg commit -Aqm "dest" > + $ hg up -q 2 > + $ logg > + @ (2) dest > + | affected: file > + | deleted: > + | > + | o (1) source > + |/ affected: file > + | deleted: > + | > + o (0) base > + affected: file > + deleted: > + > + > + $ hg rebase -d 2 -s 1 > + rebasing 1:ec87889f5f90 "source" > + merging file > + warning: ([^\s]+) looks like a binary file. (re) > + warning: conflicts while merging file! (edit, then use 'hg resolve --mark') > + unresolved conflicts (see hg resolve, then hg rebase --continue) > + [1] > + $ cat -v file > + ^@ (no-eol) > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "\u0000", "exists": true, "isexec": false, "issymlink": false}, "other": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "output": {"contents": "\u0000", "exists": true, "isexec": false, "issymlink": false, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] > + } > + ] > +Test case 11: Source is a symlink, dest is a file (base is still a file) > + $ cd .. > + $ reset > + $ rm file > + $ ln -s somepath file > + $ hg commit -Aqm "source" > + $ hg up -q 0 > + $ echo "change" > file > + $ hg commit -Aqm "dest" > + $ hg up -q 2 > + $ logg > + @ (2) dest > + | affected: file > + | deleted: > + | > + | o (1) source > + |/ affected: file > + | deleted: > + | > + o (0) base > + affected: file > + deleted: > + > + > + $ hg rebase -d 2 -s 1 > + rebasing 1:06aece48b59f "source" > + merging file > + warning: internal :merge cannot merge symlinks for file > + warning: conflicts while merging file! (edit, then use 'hg resolve --mark') > + unresolved conflicts (see hg resolve, then hg rebase --continue) > + [1] > + $ cat -v file > + change > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "other": {"contents": "somepath", "exists": true, "isexec": false, "issymlink": true}, "output": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] > + } > + ] > +Test case 12: Source is a file, dest is a symlink (base is still a file) > + $ cd .. > + $ reset > + $ echo "change" > file > + $ hg commit -Aqm "source" > + $ hg up -q 0 > + $ rm file > + $ ln -s somepath file > + $ hg commit -Aqm "dest" > + $ hg up -q 2 > + $ logg > + @ (2) dest > + | affected: file > + | deleted: > + | > + | o (1) source > + |/ affected: file > + | deleted: > + | > + o (0) base > + affected: file > + deleted: > + > + > + $ hg rebase -d 2 -s 1 > + rebasing 1:ec87889f5f90 "source" > + merging file > + warning: internal :merge cannot merge symlinks for file > + warning: conflicts while merging file! (edit, then use 'hg resolve --mark') > + unresolved conflicts (see hg resolve, then hg rebase --continue) > + [1] > + $ cat -v file > + cat: file: No such file or directory > + [1] > + $ ls file > + file > + $ hg resolve --tool=internal:dumpjson --all > + [ > + { > + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "somepath", "exists": true, "isexec": false, "issymlink": true}, "other": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "output": {"contents": "somepath", "exists": true, "isexec": false, "issymlink": true, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] > + } > + ] > _______________________________________________ > Mercurial-devel mailing list > Mercurial-devel@mercurial-scm.org > https://urldefense.proofpoint.com/v2/url?u=https-3A__www.mercurial-2Dscm.org_mailman_listinfo_mercurial-2Ddevel&d=DwIGaQ&c=5VD0RTtNlTh3ycd41b3MUw&r=Jw8rundaE7TbmqBYd1txIQ&m=_t4xvyJTHXGFDvTu3p6R_nIgUmMdHttb-VMlCi_eurs&s=M-_Thl2YX-gsg0mR0ai7KrBqaCpWMxi5MXB32FHMNFs&e=
Patch
diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py --- a/mercurial/filemerge.py +++ b/mercurial/filemerge.py @@ -589,6 +589,8 @@ # `up -n` or `merge -f`), "local" must reflect that, not the underlying # commit. Those contents are available in the .orig version, so we look # there and mock up the schema to look like the other contexts. + # + # Test cases affected in test-merge-conflict-cornercases.t: #0 local = { 'contents': util.readfile(origfile), 'exists': True, @@ -605,6 +607,8 @@ # pre-merge properties including any local changes), so we can reuse # that. # + # Affected test cases: #0b, #1, #6, #11, and #12. + # # Another alternative might be to use repo['.'][path] but that wouldn't # have any dirty pre-merge changes. # diff --git a/tests/test-merge-conflict-cornercases.t b/tests/test-merge-conflict-cornercases.t new file mode 100644 --- /dev/null +++ b/tests/test-merge-conflict-cornercases.t @@ -0,0 +1,575 @@ +Tests merge conflict corner cases (file-to-directory, binary-to-symlink, etc.) +"other" == source +"local" == dest + +Setup + $ cat >> $HGRCPATH <<EOF + > [extensions] + > rebase= + > EOF + + $ reset() { + > rm -rf foo + > mkdir foo + > cd foo + > hg init + > echo "base" > file + > hg commit -Aqm "base" + > } + $ logg() { + > hg log -G -T '({rev}) {desc}\naffected: {files}\ndeleted: {file_dels}\n\n' + > } + +Test case 0: A merge of just contents conflicts (not usually a corner case), +but the user had local changes and ran `merge -f`. + +tldr: Since we can premerge, the working copy is backed up to an origfile. + $ reset + $ echo "change" > file + $ hg commit -Aqm "dest" + $ hg up -q 0 + $ echo "other change" > file + $ hg commit -Aqm "source" + $ hg up -q 1 + $ logg + o (2) source + | affected: file + | deleted: + | + | @ (1) dest + |/ affected: file + | deleted: + | + o (0) base + affected: file + deleted: + + $ echo "some local changes" > file + $ hg merge 2 -f + merging file + warning: conflicts while merging file! (edit, then use 'hg resolve --mark') + 0 files updated, 0 files merged, 0 files removed, 1 files unresolved + use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon + [1] + + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "some local changes\n", "exists": true, "isexec": false, "issymlink": false}, "other": {"contents": "other change\n", "exists": true, "isexec": false, "issymlink": false}, "output": {"contents": "<<<<<<< working copy: fd7d10c36158 - test: dest\nsome local changes\n=======\nother change\n>>>>>>> merge rev: 9b65ba2922f0 - test: source\n", "exists": true, "isexec": false, "issymlink": false, "path": "$TESTTMP/foo/file"}, "path": "file"}] + } + ] + +Test case 0b: Like #0 but with a corner case: source deleted, local changed +*and* had local changes using merge -f. + +tldr: Since we couldn't premerge, the working copy is left alone. + $ reset + $ echo "change" > file + $ hg commit -Aqm "dest" + $ hg up -q 0 + $ hg rm file + $ hg commit -Aqm "source" + $ hg up -q 1 + $ logg + o (2) source + | affected: file + | deleted: file + | + | @ (1) dest + |/ affected: file + | deleted: + | + o (0) base + affected: file + deleted: + + $ echo "some local changes" > file + $ chmod +x file + $ hg merge 2 -f + local [working copy] changed file which other [merge rev] deleted + use (c)hanged version, (d)elete, or leave (u)nresolved? u + 0 files updated, 0 files merged, 0 files removed, 1 files unresolved + use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon + [1] + + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "some local changes\n", "exists": true, "isexec": true, "issymlink": false}, "other": {"exists": false}, "output": {"contents": "some local changes\n", "exists": true, "isexec": true, "issymlink": false, "path": "$TESTTMP/foo/foo/file"}, "path": "file"}] + } + ] + +Test case 1: Source deleted, dest changed + $ reset + $ echo "change" > file + $ hg commit -Aqm "dest" + $ hg up -q 0 + $ hg rm file + $ hg commit -Aqm "source" + $ hg up -q 1 + $ logg + o (2) source + | affected: file + | deleted: file + | + | @ (1) dest + |/ affected: file + | deleted: + | + o (0) base + affected: file + deleted: + + + $ hg rebase -d 1 -s 2 + rebasing 2:25c2ef28f4c7 "source" (tip) + local [dest] changed file which other [source] deleted + use (c)hanged version, (d)elete, or leave (u)nresolved? u + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "other": {"exists": false}, "output": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false, "path": "$TESTTMP/foo/foo/foo/file"}, "path": "file"}] + } + ] +Test case 1b: Like #1 but with a merge, with local changes + $ reset + $ echo "change" > file + $ hg commit -Aqm "dest" + $ hg up -q 0 + $ hg rm file + $ hg commit -Aqm "source" + $ hg up -q 1 + $ logg + o (2) source + | affected: file + | deleted: file + | + | @ (1) dest + |/ affected: file + | deleted: + | + o (0) base + affected: file + deleted: + + $ echo "some local changes" > file + $ hg merge 2 -f + local [working copy] changed file which other [merge rev] deleted + use (c)hanged version, (d)elete, or leave (u)nresolved? u + 0 files updated, 0 files merged, 0 files removed, 1 files unresolved + use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon + [1] + + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "some local changes\n", "exists": true, "isexec": false, "issymlink": false}, "other": {"exists": false}, "output": {"contents": "some local changes\n", "exists": true, "isexec": false, "issymlink": false, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] + } + ] +Test case 2: Source changed, dest deleted + $ cd .. + $ reset + $ echo "change" > file + $ hg commit -Aqm "source" + $ hg up -q 0 + $ hg rm file + $ hg commit -Aqm "dest" + $ hg up --q 2 + $ logg + @ (2) dest + | affected: file + | deleted: file + | + | o (1) source + |/ affected: file + | deleted: + | + o (0) base + affected: file + deleted: + + + $ hg rebase -d 2 -s 1 + rebasing 1:ec87889f5f90 "source" + other [source] changed file which local [dest] deleted + use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"exists": false}, "other": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "output": {"exists": false, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] + } + ] +Test case 3: Source changed, dest moved + $ cd .. + $ reset + $ echo "change" > file + $ hg commit -Aqm "source" + $ hg up -q 0 + $ hg mv file file_newloc + $ hg commit -Aqm "dest" + $ hg up -q 2 + $ logg + @ (2) dest + | affected: file file_newloc + | deleted: file + | + | o (1) source + |/ affected: file + | deleted: + | + o (0) base + affected: file + deleted: + + + $ hg rebase -d 2 -s 1 + rebasing 1:ec87889f5f90 "source" + merging file_newloc and file to file_newloc + saved backup bundle to $TESTTMP/foo/foo/foo/foo/.hg/strip-backup/ec87889f5f90-e39a76b8-backup.hg (glob) + $ hg up -q 2 # source + $ cat file_newloc # Should follow: + change + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [] + } + ] +Test case 4: Source changed, dest moved (w/o copytracing) + $ cd .. + $ reset + $ echo "change" > file + $ hg commit -Aqm "source" + $ hg up -q 0 + $ hg mv file file_newloc + $ hg commit -Aqm "dest" + $ hg up -q 2 + $ logg + @ (2) dest + | affected: file file_newloc + | deleted: file + | + | o (1) source + |/ affected: file + | deleted: + | + o (0) base + affected: file + deleted: + + + $ hg rebase -d 2 -s 1 --config experimental.disablecopytrace=True + rebasing 1:ec87889f5f90 "source" + other [source] changed file which local [dest] deleted + use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"exists": false}, "other": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "output": {"exists": false, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] + } + ] +Test case 5: Source moved, dest changed + $ cd .. + $ reset + $ hg mv file file_newloc + $ hg commit -Aqm "source" + $ hg up -q 0 + $ echo "change" > file + $ hg commit -Aqm "dest" + $ hg up -q 2 + $ logg + @ (2) dest + | affected: file + | deleted: + | + | o (1) source + |/ affected: file file_newloc + | deleted: file + | + o (0) base + affected: file + deleted: + + + $ hg rebase -d 2 -s 1 + rebasing 1:e6e7483a8950 "source" + merging file and file_newloc to file_newloc + saved backup bundle to $TESTTMP/foo/foo/foo/foo/.hg/strip-backup/e6e7483a8950-8e128ac2-backup.hg (glob) + $ hg up 2 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ cat file_newloc + change + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [] + } + ] +Test case 6: Source moved, dest changed (w/o copytracing) + $ cd .. + $ reset + $ hg mv file file_newloc + $ hg commit -Aqm "source" + $ hg up -q 0 + $ echo "change" > file + $ hg commit -Aqm "dest" + $ hg up -q 2 + $ logg + @ (2) dest + | affected: file + | deleted: + | + | o (1) source + |/ affected: file file_newloc + | deleted: file + | + o (0) base + affected: file + deleted: + + + $ hg rebase -d 2 -s 1 --config experimental.disablecopytrace=True + rebasing 1:e6e7483a8950 "source" + local [dest] changed file which other [source] deleted + use (c)hanged version, (d)elete, or leave (u)nresolved? u + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "other": {"exists": false}, "output": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] + } + ] +Test case 7: Source is a directory, dest is a file (base is still a file) + $ cd .. + $ reset + $ hg rm file + $ mkdir file # "file" is a stretch + $ echo "this will cause problems" >> file/subfile + $ hg commit -Aqm "source" + $ hg up -q 0 + $ echo "change" > file + $ hg commit -Aqm "dest" + $ hg up -q 2 + $ logg + @ (2) dest + | affected: file + | deleted: + | + | o (1) source + |/ affected: file file/subfile + | deleted: file + | + o (0) base + affected: file + deleted: + + + $ hg rebase -d 2 -s 1 + rebasing 1:ed93aeac6b3c "source" + abort: Not a directory: '$TESTTMP/foo/foo/foo/foo/file/subfile' + [255] + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [] + } + ] +Test case 8: Source is a file, dest is a directory (base is still a file) + $ cd .. + $ reset + $ echo "change" > file + $ hg commit -Aqm "source" + $ hg up -q 0 + $ hg rm file + $ mkdir file # "file" + $ echo "this will cause problems" >> file/subfile + $ hg commit -Aqm "dest" + $ hg up -q 2 + $ logg + @ (2) dest + | affected: file file/subfile + | deleted: file + | + | o (1) source + |/ affected: file + | deleted: + | + o (0) base + affected: file + deleted: + + + $ hg rebase -d 2 -s 1 + rebasing 1:ec87889f5f90 "source" + abort: Operation not permitted: '$TESTTMP/foo/foo/foo/foo/file' + [255] + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [] + } + ] +Test case 9: Source is a binary file, dest is a file (base is still a file) + $ cd .. + $ reset + $ python -c 'f = open("file", "w"); f.write("\x00")' + $ hg commit -Aqm "source" + $ hg up -q 0 + $ echo "change" > file + $ hg commit -Aqm "dest" + $ hg up -q 2 + $ logg + @ (2) dest + | affected: file + | deleted: + | + | o (1) source + |/ affected: file + | deleted: + | + o (0) base + affected: file + deleted: + + + $ hg rebase -d 2 -s 1 + rebasing 1:b6e55a03a5dc "source" + merging file + warning: ([^\s]+) looks like a binary file. (re) + warning: conflicts while merging file! (edit, then use 'hg resolve --mark') + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] + $ cat -v file # The local version should be left in the working copy + change + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "other": {"contents": "\u0000", "exists": true, "isexec": false, "issymlink": false}, "output": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] + } + ] +Test case 10: Source is a file, dest is a binary file (base is still a file) + $ cd .. + $ reset + $ echo "change" > file + $ hg commit -Aqm "source" + $ hg up -q 0 + $ python -c 'f = open("file", "w"); f.write("\x00")' + $ hg commit -Aqm "dest" + $ hg up -q 2 + $ logg + @ (2) dest + | affected: file + | deleted: + | + | o (1) source + |/ affected: file + | deleted: + | + o (0) base + affected: file + deleted: + + + $ hg rebase -d 2 -s 1 + rebasing 1:ec87889f5f90 "source" + merging file + warning: ([^\s]+) looks like a binary file. (re) + warning: conflicts while merging file! (edit, then use 'hg resolve --mark') + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] + $ cat -v file + ^@ (no-eol) + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "\u0000", "exists": true, "isexec": false, "issymlink": false}, "other": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "output": {"contents": "\u0000", "exists": true, "isexec": false, "issymlink": false, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] + } + ] +Test case 11: Source is a symlink, dest is a file (base is still a file) + $ cd .. + $ reset + $ rm file + $ ln -s somepath file + $ hg commit -Aqm "source" + $ hg up -q 0 + $ echo "change" > file + $ hg commit -Aqm "dest" + $ hg up -q 2 + $ logg + @ (2) dest + | affected: file + | deleted: + | + | o (1) source + |/ affected: file + | deleted: + | + o (0) base + affected: file + deleted: + + + $ hg rebase -d 2 -s 1 + rebasing 1:06aece48b59f "source" + merging file + warning: internal :merge cannot merge symlinks for file + warning: conflicts while merging file! (edit, then use 'hg resolve --mark') + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] + $ cat -v file + change + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "other": {"contents": "somepath", "exists": true, "isexec": false, "issymlink": true}, "output": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] + } + ] +Test case 12: Source is a file, dest is a symlink (base is still a file) + $ cd .. + $ reset + $ echo "change" > file + $ hg commit -Aqm "source" + $ hg up -q 0 + $ rm file + $ ln -s somepath file + $ hg commit -Aqm "dest" + $ hg up -q 2 + $ logg + @ (2) dest + | affected: file + | deleted: + | + | o (1) source + |/ affected: file + | deleted: + | + o (0) base + affected: file + deleted: + + + $ hg rebase -d 2 -s 1 + rebasing 1:ec87889f5f90 "source" + merging file + warning: internal :merge cannot merge symlinks for file + warning: conflicts while merging file! (edit, then use 'hg resolve --mark') + unresolved conflicts (see hg resolve, then hg rebase --continue) + [1] + $ cat -v file + cat: file: No such file or directory + [1] + $ ls file + file + $ hg resolve --tool=internal:dumpjson --all + [ + { + "conflicts": [{"base": {"contents": "base\n", "exists": true, "isexec": false, "issymlink": false}, "local": {"contents": "somepath", "exists": true, "isexec": false, "issymlink": true}, "other": {"contents": "change\n", "exists": true, "isexec": false, "issymlink": false}, "output": {"contents": "somepath", "exists": true, "isexec": false, "issymlink": true, "path": "$TESTTMP/foo/foo/foo/foo/file"}, "path": "file"}] + } + ]