Merge branch 'jk/diff-no-rename-empty'
authorJunio C Hamano <gitster@pobox.com>
Mon, 16 Apr 2012 19:41:49 +0000 (12:41 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 16 Apr 2012 19:41:49 +0000 (12:41 -0700)
Forbids rename detection logic from matching two empty files as renames
during merge-recursive to prevent mismerges.

By Jeff King
* jk/diff-no-rename-empty:
merge-recursive: don't detect renames of empty files
teach diffcore-rename to optionally ignore empty content
make is_empty_blob_sha1 available everywhere
drop casts from users EMPTY_TREE_SHA1_BIN

builtin/diff.c
cache.h
diff.c
diff.h
diffcore-rename.c
merge-recursive.c
read-cache.c
sequencer.c
t/t6022-merge-rename.sh
index 424c815f9bc2ca8f87eb4694d1375b949b635170..9069dc41be33a362ff04d52c00b4830eed272826 100644 (file)
@@ -327,7 +327,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                                add_head_to_pending(&rev);
                                if (!rev.pending.nr) {
                                        struct tree *tree;
-                                       tree = lookup_tree((const unsigned char*)EMPTY_TREE_SHA1_BIN);
+                                       tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
                                        add_pending_object(&rev, &tree->object, "HEAD");
                                }
                                break;
diff --git a/cache.h b/cache.h
index a8aceb5aee4fef0d7b06693921dea319690965d6..806bf2b76fbbf7b30b70bad65220d5255a67c69e 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -708,6 +708,19 @@ static inline void hashclr(unsigned char *hash)
 #define EMPTY_TREE_SHA1_BIN \
         ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
 
+#define EMPTY_BLOB_SHA1_HEX \
+       "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
+#define EMPTY_BLOB_SHA1_BIN_LITERAL \
+       "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
+       "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
+#define EMPTY_BLOB_SHA1_BIN \
+       ((const unsigned char *) EMPTY_BLOB_SHA1_BIN_LITERAL)
+
+static inline int is_empty_blob_sha1(const unsigned char *sha1)
+{
+       return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
+}
+
 int git_mkstemp(char *path, size_t n, const char *template);
 
 int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
diff --git a/diff.c b/diff.c
index b020c070cac15e7448178db67a480fc311f18ad9..5d6349feb3f8f9483c463961e526767e2f81e4c9 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -3146,6 +3146,7 @@ void diff_setup(struct diff_options *options)
        options->rename_limit = -1;
        options->dirstat_permille = diff_dirstat_permille_default;
        options->context = 3;
+       DIFF_OPT_SET(options, RENAME_EMPTY);
 
        options->change = diff_change;
        options->add_remove = diff_addremove;
@@ -3516,6 +3517,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        }
        else if (!strcmp(arg, "--no-renames"))
                options->detect_rename = 0;
+       else if (!strcmp(arg, "--rename-empty"))
+               DIFF_OPT_SET(options, RENAME_EMPTY);
+       else if (!strcmp(arg, "--no-rename-empty"))
+               DIFF_OPT_CLR(options, RENAME_EMPTY);
        else if (!strcmp(arg, "--relative"))
                DIFF_OPT_SET(options, RELATIVE_NAME);
        else if (!prefixcmp(arg, "--relative=")) {
diff --git a/diff.h b/diff.h
index e9e3d44c674a39bb1912ca366ca12512dfb4d2c7..870dc91db8fa1ff23c60135ebfc42106f4c0464e 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -60,7 +60,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
 #define DIFF_OPT_SILENT_ON_REMOVE    (1 <<  5)
 #define DIFF_OPT_FIND_COPIES_HARDER  (1 <<  6)
 #define DIFF_OPT_FOLLOW_RENAMES      (1 <<  7)
-/* (1 <<  8) unused */
+#define DIFF_OPT_RENAME_EMPTY        (1 <<  8)
 /* (1 <<  9) unused */
 #define DIFF_OPT_HAS_CHANGES         (1 << 10)
 #define DIFF_OPT_QUICK               (1 << 11)
index f639601c762ebbd12374fa739d1d63efaf265e2a..216a7a4bbcab189b5c3d1b7f58728b94b8d6aec8 100644 (file)
@@ -512,9 +512,15 @@ void diffcore_rename(struct diff_options *options)
                        else if (options->single_follow &&
                                 strcmp(options->single_follow, p->two->path))
                                continue; /* not interested */
+                       else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
+                                is_empty_blob_sha1(p->two->sha1))
+                               continue;
                        else
                                locate_rename_dst(p->two, 1);
                }
+               else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
+                        is_empty_blob_sha1(p->one->sha1))
+                       continue;
                else if (!DIFF_PAIR_UNMERGED(p) && !DIFF_FILE_VALID(p->two)) {
                        /*
                         * If the source is a broken "delete", and
index 857f03dc6112ed5bce6b77b7da9535330ea29015..680937c39e2dacb7aaa008ee77b34eb9f7c208cb 100644 (file)
@@ -485,6 +485,7 @@ static struct string_list *get_renames(struct merge_options *o,
        renames = xcalloc(1, sizeof(struct string_list));
        diff_setup(&opts);
        DIFF_OPT_SET(&opts, RECURSIVE);
+       DIFF_OPT_CLR(&opts, RENAME_EMPTY);
        opts.detect_rename = DIFF_DETECT_RENAME;
        opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
                            o->diff_rename_limit >= 0 ? o->diff_rename_limit :
@@ -1914,7 +1915,7 @@ int merge_recursive(struct merge_options *o,
                /* if there is no common ancestor, use an empty tree */
                struct tree *tree;
 
-               tree = lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);
+               tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
                merged_common_ancestors = make_virtual_commit(tree, "ancestor");
        }
 
index 274e54b4f31da69bf7c0721d4c8ba8e264db5dde..6c8f3958369b0a2afd0ad85aea5ab23a44678f70 100644 (file)
@@ -157,16 +157,6 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
        return 0;
 }
 
-static int is_empty_blob_sha1(const unsigned char *sha1)
-{
-       static const unsigned char empty_blob_sha1[20] = {
-               0xe6,0x9d,0xe2,0x9b,0xb2,0xd1,0xd6,0x43,0x4b,0x8b,
-               0x29,0xae,0x77,0x5a,0xd8,0xc2,0xe4,0x8c,0x53,0x91
-       };
-
-       return !hashcmp(sha1, empty_blob_sha1);
-}
-
 static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
 {
        unsigned int changed = 0;
index a37846a594f5a2d6acfb075ece1b5c30dda2329f..4307364b261bcbe7a2e97116aa631aa7aa35613c 100644 (file)
@@ -164,7 +164,7 @@ static void write_message(struct strbuf *msgbuf, const char *filename)
 
 static struct tree *empty_tree(void)
 {
-       return lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);
+       return lookup_tree(EMPTY_TREE_SHA1_BIN);
 }
 
 static int error_dirty_index(struct replay_opts *opts)
index 9d8584e957a26cadda2f04d38d27fd0c4b97ae29..1104249182074c1a987905e1661356ff8da7580c 100755 (executable)
@@ -884,4 +884,20 @@ test_expect_success 'no spurious "refusing to lose untracked" message' '
        ! grep "refusing to lose untracked file" errors.txt
 '
 
+test_expect_success 'do not follow renames for empty files' '
+       git checkout -f -b empty-base &&
+       >empty1 &&
+       git add empty1 &&
+       git commit -m base &&
+       echo content >empty1 &&
+       git add empty1 &&
+       git commit -m fill &&
+       git checkout -b empty-topic HEAD^ &&
+       git mv empty1 empty2 &&
+       git commit -m rename &&
+       test_must_fail git merge empty-base &&
+       >expect &&
+       test_cmp expect empty2
+'
+
 test_done