diff --git a/builtin/diff.c b/builtin/diff.c index 424c815f9bc2ca8f87eb4694d1375b949b635170..9069dc41be33a362ff04d52c00b4830eed272826 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -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 --- 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 7ebc7394e16cb3ddcbdf329df2d7963f7df59bb8..0dea484ee8c8bd2f2b356c8a8eb1ef15d1be07f4 100644 --- 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 cb687436a0ddb9a08fc1a9e9cec569233284e01f..dd48eca71ddf1a8709135f158520c8bb7cd5f8d1 100644 --- 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) diff --git a/diffcore-rename.c b/diffcore-rename.c index f639601c762ebbd12374fa739d1d63efaf265e2a..216a7a4bbcab189b5c3d1b7f58728b94b8d6aec8 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -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 diff --git a/merge-recursive.c b/merge-recursive.c index 6479a60cd112c5b06b354b1a251c60bb4bce972a..0fb174359ac12ec9dc02078f870f750915557725 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -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"); } diff --git a/read-cache.c b/read-cache.c index 274e54b4f31da69bf7c0721d4c8ba8e264db5dde..6c8f3958369b0a2afd0ad85aea5ab23a44678f70 100644 --- a/read-cache.c +++ b/read-cache.c @@ -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; diff --git a/sequencer.c b/sequencer.c index a37846a594f5a2d6acfb075ece1b5c30dda2329f..4307364b261bcbe7a2e97116aa631aa7aa35613c 100644 --- a/sequencer.c +++ b/sequencer.c @@ -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) diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh index 9d8584e957a26cadda2f04d38d27fd0c4b97ae29..1104249182074c1a987905e1661356ff8da7580c 100755 --- a/t/t6022-merge-rename.sh +++ b/t/t6022-merge-rename.sh @@ -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