Try normalizing files to avoid delete/modify conflicts when merging
authorEyvind Bernhardsen <eyvind.bernhardsen@gmail.com>
Fri, 2 Jul 2010 19:20:48 +0000 (21:20 +0200)
committerJunio C Hamano <gitster@pobox.com>
Fri, 2 Jul 2010 22:44:34 +0000 (15:44 -0700)
If a file is modified due to normalization on one branch, and deleted on
another, a merge of the two branches will result in a delete/modify
conflict for that file even if it is otherwise unchanged.

Try to avoid the conflict by normalizing and comparing the "base" file
and the modified file when their sha1s differ.  If they compare equal,
the file is considered unmodified and is deleted.

Signed-off-by: Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
merge-recursive.c
t/t6038-merge-text-auto.sh

index 206c1036359ce7b1fc5a1f5734b2d0bc2a760d90..5ad8fc9e7ea7c6653e28343ccc0fa0c260e3affd 100644 (file)
@@ -1056,6 +1056,53 @@ static unsigned char *stage_sha(const unsigned char *sha, unsigned mode)
        return (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha;
 }
 
+static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst)
+{
+       void *buf;
+       enum object_type type;
+       unsigned long size;
+       buf = read_sha1_file(sha1, &type, &size);
+       if (!buf)
+               return error("cannot read object %s", sha1_to_hex(sha1));
+       if (type != OBJ_BLOB) {
+               free(buf);
+               return error("object %s is not a blob", sha1_to_hex(sha1));
+       }
+       strbuf_attach(dst, buf, size, size + 1);
+       return 0;
+}
+
+static int blob_unchanged(const unsigned char *o_sha,
+                         const unsigned char *a_sha,
+                         const char *path)
+{
+       struct strbuf o = STRBUF_INIT;
+       struct strbuf a = STRBUF_INIT;
+       int ret = 0; /* assume changed for safety */
+
+       if (sha_eq(o_sha, a_sha))
+               return 1;
+       if (!merge_renormalize)
+               return 0;
+
+       assert(o_sha && a_sha);
+       if (read_sha1_strbuf(o_sha, &o) || read_sha1_strbuf(a_sha, &a))
+               goto error_return;
+       /*
+        * Note: binary | is used so that both renormalizations are
+        * performed.  Comparison can be skipped if both files are
+        * unchanged since their sha1s have already been compared.
+        */
+       if (renormalize_buffer(path, o.buf, o.len, &o) |
+           renormalize_buffer(path, a.buf, o.len, &a))
+               ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));
+
+error_return:
+       strbuf_release(&o);
+       strbuf_release(&a);
+       return ret;
+}
+
 /* Per entry merge function */
 static int process_entry(struct merge_options *o,
                         const char *path, struct stage_data *entry)
@@ -1075,8 +1122,8 @@ static int process_entry(struct merge_options *o,
        if (o_sha && (!a_sha || !b_sha)) {
                /* Case A: Deleted in one */
                if ((!a_sha && !b_sha) ||
-                   (sha_eq(a_sha, o_sha) && !b_sha) ||
-                   (!a_sha && sha_eq(b_sha, o_sha))) {
+                   (!b_sha && blob_unchanged(o_sha, a_sha, path)) ||
+                   (!a_sha && blob_unchanged(o_sha, b_sha, path))) {
                        /* Deleted in both or deleted in one and
                         * unchanged in the other */
                        if (a_sha)
index 127baf8560b8a6c208a6afc076f6d704aaecdd1e..d1ab86ebd7c66285d922bf4c6613956afa41c9c1 100755 (executable)
@@ -51,7 +51,7 @@ test_expect_success 'Check merging addition of text=auto' '
        test_cmp file file.temp
 '
 
-test_expect_failure 'Test delete/normalize conflict' '
+test_expect_success 'Test delete/normalize conflict' '
        git checkout side &&
        git reset --hard initial &&
        git rm file &&