Merge branch 'cb/maint-merge-recursive-fix' into maint
authorJunio C Hamano <gitster@pobox.com>
Tue, 13 Jan 2009 09:13:56 +0000 (01:13 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 13 Jan 2009 09:13:56 +0000 (01:13 -0800)
* cb/maint-merge-recursive-fix:
merge-recursive: do not clobber untracked working tree garbage
modify/delete conflict resolution overwrites untracked file

Conflicts:
builtin-merge-recursive.c

merge-recursive.c
t/t7607-merge-overwrite.sh [new file with mode: 0755]
index a0c804c8171d021780680f11901680d4c7e89e0b..2da4333439d01d29365d150aed3156aceb0cfbd9 100644 (file)
@@ -447,6 +447,30 @@ static void flush_buffer(int fd, const char *buf, unsigned long size)
        }
 }
 
+static int would_lose_untracked(const char *path)
+{
+       int pos = cache_name_pos(path, strlen(path));
+
+       if (pos < 0)
+               pos = -1 - pos;
+       while (pos < active_nr &&
+              !strcmp(path, active_cache[pos]->name)) {
+               /*
+                * If stage #0, it is definitely tracked.
+                * If it has stage #2 then it was tracked
+                * before this merge started.  All other
+                * cases the path was not tracked.
+                */
+               switch (ce_stage(active_cache[pos])) {
+               case 0:
+               case 2:
+                       return 0;
+               }
+               pos++;
+       }
+       return file_exists(path);
+}
+
 static int make_room_for_path(const char *path)
 {
        int status;
@@ -462,6 +486,14 @@ static int make_room_for_path(const char *path)
                die(msg, path, "");
        }
 
+       /*
+        * Do not unlink a file in the work tree if we are not
+        * tracking it.
+        */
+       if (would_lose_untracked(path))
+               return error("refusing to lose untracked file at '%s'",
+                            path);
+
        /* Successful unlink is good.. */
        if (!unlink(path))
                return 0;
diff --git a/t/t7607-merge-overwrite.sh b/t/t7607-merge-overwrite.sh
new file mode 100755 (executable)
index 0000000..49f4e15
--- /dev/null
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='git-merge
+
+Do not overwrite changes.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       echo c0 > c0.c &&
+       git add c0.c &&
+       git commit -m c0 &&
+       git tag c0 &&
+       echo c1 > c1.c &&
+       git add c1.c &&
+       git commit -m c1 &&
+       git tag c1 &&
+       git reset --hard c0 &&
+       echo c2 > c2.c &&
+       git add c2.c &&
+       git commit -m c2 &&
+       git tag c2 &&
+       git reset --hard c1 &&
+       echo "c1 a" > c1.c &&
+       git add c1.c &&
+       git commit -m "c1 a" &&
+       git tag c1a &&
+       echo "VERY IMPORTANT CHANGES" > important
+'
+
+test_expect_success 'will not overwrite untracked file' '
+       git reset --hard c1 &&
+       cat important > c2.c &&
+       ! git merge c2 &&
+       test_cmp important c2.c
+'
+
+test_expect_success 'will not overwrite new file' '
+       git reset --hard c1 &&
+       cat important > c2.c &&
+       git add c2.c &&
+       ! git merge c2 &&
+       test_cmp important c2.c
+'
+
+test_expect_success 'will not overwrite staged changes' '
+       git reset --hard c1 &&
+       cat important > c2.c &&
+       git add c2.c &&
+       rm c2.c &&
+       ! git merge c2 &&
+       git checkout c2.c &&
+       test_cmp important c2.c
+'
+
+test_expect_success 'will not overwrite removed file' '
+       git reset --hard c1 &&
+       git rm c1.c &&
+       git commit -m "rm c1.c" &&
+       cat important > c1.c &&
+       ! git merge c1a &&
+       test_cmp important c1.c
+'
+
+test_expect_success 'will not overwrite re-added file' '
+       git reset --hard c1 &&
+       git rm c1.c &&
+       git commit -m "rm c1.c" &&
+       cat important > c1.c &&
+       git add c1.c &&
+       ! git merge c1a &&
+       test_cmp important c1.c
+'
+
+test_expect_success 'will not overwrite removed file with staged changes' '
+       git reset --hard c1 &&
+       git rm c1.c &&
+       git commit -m "rm c1.c" &&
+       cat important > c1.c &&
+       git add c1.c &&
+       rm c1.c &&
+       ! git merge c1a &&
+       git checkout c1.c &&
+       test_cmp important c1.c
+'
+
+test_done