File/directory conflicts are somewhat difficult to resolve; by way of
contrast, renames are much easier to handle.  For file/directory
conflicts, we already have to record the file under a different name in
the working copy due to the directory being in the way; if we also record
the file under a different name in the index then it simplifies matters
for the user, and ensures that 'git reset --hard' and 'git merge --abort'
will clean up files created by the merge operation.

Note that there are multiple ways to get a file/directory conflict:
  * add/add (one side adds a file, the other a directory)
  * modify/delete (the side that deletes puts a directory in the way)
  * rename vs add-directory (directory added on one side in way of rename)
As such, there are multiple code paths that need updating.

FIXME: Several testcases were updated to show how this affected the
  testsuite in general, but there are still 11 more tests across five
  testfiles that need fixing.  As I said, this is just WIP/RFC.

Signed-off-by: Elijah Newren <new...@gmail.com>
---
 merge-recursive.c                    | 38 ++++++++++++++++++++++------
 t/t3030-merge-recursive.sh           | 16 ++++++------
 t/t6020-merge-df.sh                  |  4 +--
 t/t6022-merge-rename.sh              |  4 +--
 t/t6036-recursive-corner-cases.sh    |  5 ++--
 t/t6042-merge-rename-corner-cases.sh |  4 +--
 t/t6043-merge-rename-directories.sh  |  4 +--
 7 files changed, 49 insertions(+), 26 deletions(-)

diff --git a/merge-recursive.c b/merge-recursive.c
index 1446e92bea..34906b0f90 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1527,6 +1527,20 @@ static int handle_change_delete(struct merge_options *o,
                 * path.  We could call update_file_flags() with update_cache=0
                 * and update_wd=0, but that's a no-op.
                 */
+               if (!o->call_depth && alt_path) {
+                       struct diff_filespec orig, new;
+                       int stage = (change_branch == o->branch1) ? 2 : 3;
+
+                       remove_file_from_cache(path);
+                       orig.mode = o_mode;
+                       oidcpy(&orig.oid, o_oid);
+                       new.mode = changed_mode;
+                       oidcpy(&new.oid, changed_oid);
+                       if (update_stages(o, alt_path, &orig,
+                                         stage == 2 ? &new : NULL,
+                                         stage == 3 ? &new : NULL))
+                               ret = -1;
+               }
                if (change_branch != o->branch1 || alt_path)
                        ret = update_file(o, 0, changed_oid, changed_mode, 
update_path);
        }
@@ -3089,11 +3103,11 @@ static int merge_content(struct merge_options *o,
 
        if (df_conflict_remains || is_dirty) {
                char *new_path;
-               if (o->call_depth) {
-                       remove_file_from_cache(path);
-               } else {
+               new_path = unique_path(o, path, rename_conflict_info->branch1);
+               remove_file_from_cache(path);
+               if (!o->call_depth) {
                        if (!mfi.clean) {
-                               if (update_stages(o, path, &one, &a, &b))
+                               if (update_stages(o, new_path, &one, &a, &b))
                                        return -1;
                        } else {
                                int file_from_stage2 = was_tracked(o, path);
@@ -3101,14 +3115,13 @@ static int merge_content(struct merge_options *o,
                                oidcpy(&merged.oid, &mfi.oid);
                                merged.mode = mfi.mode;
 
-                               if (update_stages(o, path, NULL,
+                               if (update_stages(o, new_path, NULL,
                                                  file_from_stage2 ? &merged : 
NULL,
                                                  file_from_stage2 ? NULL : 
&merged))
                                        return -1;
                        }
 
                }
-               new_path = unique_path(o, path, rename_conflict_info->branch1);
                if (is_dirty) {
                        output(o, 1, _("Refusing to lose dirty file at %s"),
                               path);
@@ -3244,10 +3257,19 @@ static int process_entry(struct merge_options *o,
                        output(o, 1, _("CONFLICT (%s): There is a directory 
with name %s in %s. "
                               "Adding %s as %s"),
                               conf, path, other_branch, path, new_path);
+                       remove_file_from_cache(path);
+                       if (!o->call_depth) {
+                               struct diff_filespec dfs;
+
+                               dfs.mode = mode;
+                               oidcpy(&dfs.oid, oid);
+                               if (update_stages(o, new_path, NULL,
+                                                 a_oid ? &dfs : NULL,
+                                                 a_oid ? NULL : &dfs))
+                                       clean_merge = -1;
+                       }
                        if (update_file(o, 0, oid, mode, new_path))
                                clean_merge = -1;
-                       else if (o->call_depth)
-                               remove_file_from_cache(path);
                        free(new_path);
                } else {
                        output(o, 2, _("Adding %s"), path);
diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh
index ff641b348a..6d456da001 100755
--- a/t/t3030-merge-recursive.sh
+++ b/t/t3030-merge-recursive.sh
@@ -369,9 +369,9 @@ test_expect_success 'merge-recursive d/f conflict result' '
 
        git ls-files -s >actual &&
        (
-               echo "100644 $o0 1      a" &&
-               echo "100644 $o1 2      a" &&
                echo "100644 $o4 0      a/c" &&
+               echo "100644 $o0 1      a~$c1" &&
+               echo "100644 $o1 2      a~$c1" &&
                echo "100644 $o0 0      b" &&
                echo "100644 $o0 0      c" &&
                echo "100644 $o1 0      d/e"
@@ -393,9 +393,9 @@ test_expect_success 'merge-recursive d/f conflict result 
the other way' '
 
        git ls-files -s >actual &&
        (
-               echo "100644 $o0 1      a" &&
-               echo "100644 $o1 3      a" &&
                echo "100644 $o4 0      a/c" &&
+               echo "100644 $o0 1      a~$c1" &&
+               echo "100644 $o1 3      a~$c1" &&
                echo "100644 $o0 0      b" &&
                echo "100644 $o0 0      c" &&
                echo "100644 $o1 0      d/e"
@@ -420,9 +420,9 @@ test_expect_success 'merge-recursive d/f conflict result' '
                echo "100644 $o1 0      a" &&
                echo "100644 $o0 0      b" &&
                echo "100644 $o0 0      c" &&
-               echo "100644 $o6 3      d" &&
                echo "100644 $o0 1      d/e" &&
-               echo "100644 $o1 2      d/e"
+               echo "100644 $o1 2      d/e" &&
+               echo "100644 $o6 3      d~$c6"
        ) >expected &&
        test_cmp expected actual
 
@@ -444,9 +444,9 @@ test_expect_success 'merge-recursive d/f conflict result' '
                echo "100644 $o1 0      a" &&
                echo "100644 $o0 0      b" &&
                echo "100644 $o0 0      c" &&
-               echo "100644 $o6 2      d" &&
                echo "100644 $o0 1      d/e" &&
-               echo "100644 $o1 3      d/e"
+               echo "100644 $o1 3      d/e" &&
+               echo "100644 $o6 2      d~$c6"
        ) >expected &&
        test_cmp expected actual
 
diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh
index 2af1beec5f..f4ea318ec8 100755
--- a/t/t6020-merge-df.sh
+++ b/t/t6020-merge-df.sh
@@ -81,7 +81,7 @@ test_expect_success 'modify/delete + directory/file conflict' 
'
 
        test 5 -eq $(git ls-files -s | wc -l) &&
        test 4 -eq $(git ls-files -u | wc -l) &&
-       test 1 -eq $(git ls-files -o | wc -l) &&
+       test 0 -eq $(git ls-files -o | wc -l) &&
 
        test -f letters/file &&
        test -f letters.txt &&
@@ -100,7 +100,7 @@ test_expect_success 'modify/delete + directory/file 
conflict; other way' '
 
        test 5 -eq $(git ls-files -s | wc -l) &&
        test 4 -eq $(git ls-files -u | wc -l) &&
-       test 1 -eq $(git ls-files -o | wc -l) &&
+       test 0 -eq $(git ls-files -o | wc -l) &&
 
        test -f letters/file &&
        test -f letters.txt &&
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
index b760c223c6..0ea3760265 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6022-merge-rename.sh
@@ -385,7 +385,7 @@ test_expect_success 'Rename+D/F conflict; renamed file 
cannot merge and dir in t
        test_must_fail git merge --strategy=recursive dir-in-way &&
 
        test 5 -eq "$(git ls-files -u | wc -l)" &&
-       test 3 -eq "$(git ls-files -u dir | grep -v file-in-the-way | wc -l)" &&
+       test 3 -eq "$(git ls-files -u dir~HEAD | wc -l)" &&
        test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
 
        test_must_fail git diff --quiet &&
@@ -421,7 +421,7 @@ test_expect_success 'Same as previous, but merged other 
way' '
        test_must_fail git merge --strategy=recursive 
renamed-file-has-conflicts &&
 
        test 5 -eq "$(git ls-files -u | wc -l)" &&
-       test 3 -eq "$(git ls-files -u dir | grep -v file-in-the-way | wc -l)" &&
+       test 3 -eq "$(git ls-files -u dir~renamed-file-has-conflicts | wc -l)" 
&&
        test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
 
        test_must_fail git diff --quiet &&
diff --git a/t/t6036-recursive-corner-cases.sh 
b/t/t6036-recursive-corner-cases.sh
index 59e52c5a09..f8790e6975 100755
--- a/t/t6036-recursive-corner-cases.sh
+++ b/t/t6036-recursive-corner-cases.sh
@@ -471,7 +471,7 @@ test_expect_success 'setup differently handled merges of 
directory/file conflict
                test_must_fail git merge C^0 &&
                git clean -fd &&
                git rm -rf a/ &&
-               git rm a &&
+               git rm a~HEAD &&
                git cat-file -p B:a >a2 &&
                git add a2 &&
                git commit -m D2 &&
@@ -492,6 +492,7 @@ test_expect_success 'setup differently handled merges of 
directory/file conflict
                test_must_fail git merge B^0 &&
                git clean -fd &&
                git rm -rf a/ &&
+               git rm a~B^0 &&
                test_write_lines 1 2 3 4 5 6 7 8 >a &&
                git add a &&
                git commit -m E3 &&
@@ -501,7 +502,7 @@ test_expect_success 'setup differently handled merges of 
directory/file conflict
                test_must_fail git merge B^0 &&
                git clean -fd &&
                git rm -rf a/ &&
-               git rm a &&
+               git rm a~B^0 &&
                test_write_lines 1 2 3 4 5 6 7 8 >a2 &&
                git add a2 &&
                git commit -m E4 &&
diff --git a/t/t6042-merge-rename-corner-cases.sh 
b/t/t6042-merge-rename-corner-cases.sh
index 07dd09d985..25cb50478c 100755
--- a/t/t6042-merge-rename-corner-cases.sh
+++ b/t/t6042-merge-rename-corner-cases.sh
@@ -305,14 +305,14 @@ test_expect_success 'rename/directory conflict + clean 
content merge' '
                git ls-files -u >out &&
                test_line_count = 1 out &&
                git ls-files -o >out &&
-               test_line_count = 2 out &&
+               test_line_count = 1 out &&
 
                echo 0 >expect &&
                git cat-file -p base:file >>expect &&
                echo 7 >>expect &&
                test_cmp expect newfile~HEAD &&
 
-               test $(git rev-parse :2:newfile) = $(git hash-object expect) &&
+               test $(git rev-parse :2:newfile~HEAD) = $(git hash-object 
expect) &&
 
                test_path_is_file newfile/realfile &&
                test_path_is_file newfile~HEAD
diff --git a/t/t6043-merge-rename-directories.sh 
b/t/t6043-merge-rename-directories.sh
index 4a71f17edd..12047a3309 100755
--- a/t/t6043-merge-rename-directories.sh
+++ b/t/t6043-merge-rename-directories.sh
@@ -1160,10 +1160,10 @@ test_expect_success '5d-check: Directory/file/file 
conflict due to directory ren
                git ls-files -u >out &&
                test_line_count = 1 out &&
                git ls-files -o >out &&
-               test_line_count = 2 out &&
+               test_line_count = 1 out &&
 
                git rev-parse >actual \
-                       :0:y/b :0:y/c :0:z/d :0:y/f :2:y/d :0:y/d/e &&
+                       :0:y/b :0:y/c :0:z/d :0:y/f :2:y/d~HEAD :0:y/d/e &&
                git rev-parse >expect \
                         O:z/b  O:z/c  B:z/d  B:z/f  A:y/d  B:y/d/e &&
                test_cmp expect actual &&
-- 
2.18.0.550.g44d6daf40a.dirty

Reply via email to