Merge branch 'mz/rerere-remaining'
authorJunio C Hamano <gitster@pobox.com>
Mon, 28 Feb 2011 05:58:30 +0000 (21:58 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 28 Feb 2011 05:58:30 +0000 (21:58 -0800)
* mz/rerere-remaining:
mergetool: don't skip modify/remove conflicts
rerere "remaining"

builtin/rerere.c
git-mergetool.sh
rerere.c
rerere.h
t/t7610-mergetool.sh
index 642bf35587ed994948d3eeac0a189ae29e39bf11..67cbfeb152386a1c918a103a2073d83138bfdff0 100644 (file)
@@ -8,7 +8,7 @@
 #include "xdiff-interface.h"
 
 static const char * const rerere_usage[] = {
-       "git rerere [clear | status | diff | gc]",
+       "git rerere [clear | status | remaining | diff | gc]",
        NULL,
 };
 
@@ -156,7 +156,17 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
        else if (!strcmp(argv[0], "status"))
                for (i = 0; i < merge_rr.nr; i++)
                        printf("%s\n", merge_rr.items[i].string);
-       else if (!strcmp(argv[0], "diff"))
+       else if (!strcmp(argv[0], "remaining")) {
+               rerere_remaining(&merge_rr);
+               for (i = 0; i < merge_rr.nr; i++) {
+                       if (merge_rr.items[i].util != RERERE_RESOLVED)
+                               printf("%s\n", merge_rr.items[i].string);
+                       else
+                               /* prepare for later call to
+                                * string_list_clear() */
+                               merge_rr.items[i].util = NULL;
+               }
+       } else if (!strcmp(argv[0], "diff"))
                for (i = 0; i < merge_rr.nr; i++) {
                        const char *path = merge_rr.items[i].string;
                        const char *name = (const char *)merge_rr.items[i].util;
index 2f8dc441c6d575323f2fad920ecdb7b20e1445e6..bacbda2bb75854235b912903c7e45fc94d38e719 100755 (executable)
@@ -269,7 +269,7 @@ rerere=false
 files_to_merge() {
     if test "$rerere" = true
     then
-       git rerere status
+       git rerere remaining
     else
        git ls-files -u | sed -e 's/^[^ ]*      //' | sort -u
     fi
index d2608434750c336c3f3881efda8373b7c67d4b11..22996bd08b532fb8016f24391dd0e63010706385 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -7,6 +7,11 @@
 #include "ll-merge.h"
 #include "attr.h"
 
+#define RESOLVED 0
+#define PUNTED 1
+#define THREE_STAGED 2
+void *RERERE_RESOLVED = &RERERE_RESOLVED;
+
 /* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
 static int rerere_enabled = -1;
 
@@ -345,21 +350,74 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
        return hunk_no;
 }
 
-static int find_conflict(struct string_list *conflict)
+static int check_one_conflict(int i, int *type)
 {
-       int i;
-       if (read_cache() < 0)
-               return error("Could not read index");
-       for (i = 0; i+1 < active_nr; i++) {
+       struct cache_entry *e = active_cache[i];
+
+       if (!ce_stage(e)) {
+               *type = RESOLVED;
+               return i + 1;
+       }
+
+       *type = PUNTED;
+       if (ce_stage(e) == 1) {
+               if (active_nr <= ++i)
+                       return i + 1;
+       }
+
+       /* Only handle regular files with both stages #2 and #3 */
+       if (i + 1 < active_nr) {
                struct cache_entry *e2 = active_cache[i];
-               struct cache_entry *e3 = active_cache[i+1];
+               struct cache_entry *e3 = active_cache[i + 1];
                if (ce_stage(e2) == 2 &&
                    ce_stage(e3) == 3 &&
-                   ce_same_name(e2, e3) &&
+                   ce_same_name(e, e3) &&
                    S_ISREG(e2->ce_mode) &&
-                   S_ISREG(e3->ce_mode)) {
-                       string_list_insert(conflict, (const char *)e2->name);
-                       i++; /* skip over both #2 and #3 */
+                   S_ISREG(e3->ce_mode))
+                       *type = THREE_STAGED;
+       }
+
+       /* Skip the entries with the same name */
+       while (i < active_nr && ce_same_name(e, active_cache[i]))
+               i++;
+       return i;
+}
+
+static int find_conflict(struct string_list *conflict)
+{
+       int i;
+       if (read_cache() < 0)
+               return error("Could not read index");
+
+       for (i = 0; i < active_nr;) {
+               int conflict_type;
+               struct cache_entry *e = active_cache[i];
+               i = check_one_conflict(i, &conflict_type);
+               if (conflict_type == THREE_STAGED)
+                       string_list_insert(conflict, (const char *)e->name);
+       }
+       return 0;
+}
+
+int rerere_remaining(struct string_list *merge_rr)
+{
+       int i;
+       if (read_cache() < 0)
+               return error("Could not read index");
+
+       for (i = 0; i < active_nr;) {
+               int conflict_type;
+               struct cache_entry *e = active_cache[i];
+               i = check_one_conflict(i, &conflict_type);
+               if (conflict_type == PUNTED)
+                       string_list_insert(merge_rr, (const char *)e->name);
+               else if (conflict_type == RESOLVED) {
+                       struct string_list_item *it;
+                       it = string_list_lookup(merge_rr, (const char *)e->name);
+                       if (it != NULL) {
+                               free(it->util);
+                               it->util = RERERE_RESOLVED;
+                       }
                }
        }
        return 0;
index eaa9004dcdbb2f0dfd446e1ac68237c6dff18530..595f49f701dc1003927679085ca6eea8ad1083ae 100644 (file)
--- a/rerere.h
+++ b/rerere.h
@@ -6,11 +6,19 @@
 #define RERERE_AUTOUPDATE   01
 #define RERERE_NOAUTOUPDATE 02
 
+/*
+ * Marks paths that have been hand-resolved and added to the
+ * index. Set in the util field of such paths after calling
+ * rerere_remaining.
+ */
+extern void *RERERE_RESOLVED;
+
 extern int setup_rerere(struct string_list *, int);
 extern int rerere(int);
 extern const char *rerere_path(const char *hex, const char *file);
 extern int has_rerere_resolution(const char *hex);
 extern int rerere_forget(const char **);
+extern int rerere_remaining(struct string_list *);
 
 #define OPT_RERERE_AUTOUPDATE(v) OPT_UYN(0, "rerere-autoupdate", (v), \
        "update the index with reused conflict resolution if possible")
index d78bdec330cd8b9505aa92ff2e81e0f716b9a741..dc838c93bcb00a80ad56c4d1233009fe9220b90e 100755 (executable)
@@ -16,23 +16,33 @@ Testing basic merge tool invocation'
 test_expect_success 'setup' '
     git config rerere.enabled true &&
     echo master >file1 &&
+    echo master file11 >file11 &&
+    echo master file12 >file12 &&
+    echo master file13 >file13 &&
+    echo master file14 >file14 &&
     mkdir subdir &&
     echo master sub >subdir/file3 &&
-    git add file1 subdir/file3 &&
-    git commit -m "added file1" &&
+    git add file1 file1[1-4] subdir/file3 &&
+    git commit -m "add initial versions" &&
 
     git checkout -b branch1 master &&
     echo branch1 change >file1 &&
     echo branch1 newfile >file2 &&
+    echo branch1 change file11 >file11 &&
+    echo branch1 change file13 >file13 &&
     echo branch1 sub >subdir/file3 &&
-    git add file1 file2 subdir/file3 &&
+    git add file1 file11 file13 file2 subdir/file3 &&
+    git rm file12 &&
     git commit -m "branch1 changes" &&
 
     git checkout master &&
     echo master updated >file1 &&
     echo master new >file2 &&
+    echo master updated file12 >file12 &&
+    echo master updated file14 >file14 &&
     echo master new sub >subdir/file3 &&
-    git add file1 file2 subdir/file3 &&
+    git add file1 file12 file14 file2 subdir/file3 &&
+    git rm file11 &&
     git commit -m "master updates" &&
 
     git config merge.tool mytool &&
@@ -46,6 +56,8 @@ test_expect_success 'custom mergetool' '
     ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
     ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
     ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
     test "$(cat file1)" = "master updated" &&
     test "$(cat file2)" = "master new" &&
     test "$(cat subdir/file3)" = "master new sub" &&
@@ -59,6 +71,8 @@ test_expect_success 'mergetool crlf' '
     ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
     ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
     ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
     test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
     test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
     test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
@@ -82,6 +96,8 @@ test_expect_success 'mergetool on file in parent dir' '
        cd subdir &&
        ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
        ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
+       ( yes "d" | git mergetool ../file11 >/dev/null 2>&1 ) &&
+       ( yes "d" | git mergetool ../file12 >/dev/null 2>&1 ) &&
        test "$(cat ../file1)" = "master updated" &&
        test "$(cat ../file2)" = "master new" &&
        git commit -m "branch1 resolved with mergetool - subdir"
@@ -92,6 +108,8 @@ test_expect_success 'mergetool skips autoresolved' '
     git checkout -b test4 branch1 &&
     test_must_fail git merge master &&
     test -n "$(git ls-files -u)" &&
+    ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
     output="$(git mergetool --no-prompt)" &&
     test "$output" = "No files need merging" &&
     git reset --hard
@@ -102,13 +120,23 @@ test_expect_success 'mergetool merges all from subdir' '
        cd subdir &&
        git config rerere.enabled false &&
        test_must_fail git merge master &&
-       git mergetool --no-prompt &&
+       ( yes "d" "d" | git mergetool --no-prompt ) &&
        test "$(cat ../file1)" = "master updated" &&
        test "$(cat ../file2)" = "master new" &&
        test "$(cat file3)" = "master new sub" &&
-       git add ../file1 ../file2 file3 &&
        git commit -m "branch2 resolved by mergetool from subdir"
     )
 '
 
+test_expect_success 'mergetool skips resolved paths when rerere is active' '
+    git config rerere.enabled true &&
+    rm -rf .git/rr-cache &&
+    git checkout -b test5 branch1
+    test_must_fail git merge master >/dev/null 2>&1 &&
+    ( yes "d" "d" | git mergetool --no-prompt >/dev/null 2>&1 ) &&
+    output="$(yes "n" | git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging" &&
+    git reset --hard
+'
+
 test_done