提交 3f231e23 编写于 作者: J Junio C Hamano

Merge branch 'jk/diff-no-rename-empty' into maint

Rename detection logic used to match two empty files as renames during
merge-recursive, leading unnatural 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
...@@ -327,7 +327,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) ...@@ -327,7 +327,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
add_head_to_pending(&rev); add_head_to_pending(&rev);
if (!rev.pending.nr) { if (!rev.pending.nr) {
struct tree *tree; 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"); add_pending_object(&rev, &tree->object, "HEAD");
} }
break; break;
......
...@@ -708,6 +708,19 @@ static inline void hashclr(unsigned char *hash) ...@@ -708,6 +708,19 @@ static inline void hashclr(unsigned char *hash)
#define EMPTY_TREE_SHA1_BIN \ #define EMPTY_TREE_SHA1_BIN \
((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL) ((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_mkstemp(char *path, size_t n, const char *template);
int git_mkstemps(char *path, size_t n, const char *template, int suffix_len); int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
......
...@@ -3146,6 +3146,7 @@ void diff_setup(struct diff_options *options) ...@@ -3146,6 +3146,7 @@ void diff_setup(struct diff_options *options)
options->rename_limit = -1; options->rename_limit = -1;
options->dirstat_permille = diff_dirstat_permille_default; options->dirstat_permille = diff_dirstat_permille_default;
options->context = 3; options->context = 3;
DIFF_OPT_SET(options, RENAME_EMPTY);
options->change = diff_change; options->change = diff_change;
options->add_remove = diff_addremove; options->add_remove = diff_addremove;
...@@ -3516,6 +3517,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) ...@@ -3516,6 +3517,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
} }
else if (!strcmp(arg, "--no-renames")) else if (!strcmp(arg, "--no-renames"))
options->detect_rename = 0; 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")) else if (!strcmp(arg, "--relative"))
DIFF_OPT_SET(options, RELATIVE_NAME); DIFF_OPT_SET(options, RELATIVE_NAME);
else if (!prefixcmp(arg, "--relative=")) { else if (!prefixcmp(arg, "--relative=")) {
......
...@@ -60,7 +60,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data) ...@@ -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_SILENT_ON_REMOVE (1 << 5)
#define DIFF_OPT_FIND_COPIES_HARDER (1 << 6) #define DIFF_OPT_FIND_COPIES_HARDER (1 << 6)
#define DIFF_OPT_FOLLOW_RENAMES (1 << 7) #define DIFF_OPT_FOLLOW_RENAMES (1 << 7)
/* (1 << 8) unused */ #define DIFF_OPT_RENAME_EMPTY (1 << 8)
/* (1 << 9) unused */ /* (1 << 9) unused */
#define DIFF_OPT_HAS_CHANGES (1 << 10) #define DIFF_OPT_HAS_CHANGES (1 << 10)
#define DIFF_OPT_QUICK (1 << 11) #define DIFF_OPT_QUICK (1 << 11)
......
...@@ -512,9 +512,15 @@ void diffcore_rename(struct diff_options *options) ...@@ -512,9 +512,15 @@ void diffcore_rename(struct diff_options *options)
else if (options->single_follow && else if (options->single_follow &&
strcmp(options->single_follow, p->two->path)) strcmp(options->single_follow, p->two->path))
continue; /* not interested */ continue; /* not interested */
else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
is_empty_blob_sha1(p->two->sha1))
continue;
else else
locate_rename_dst(p->two, 1); 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)) { else if (!DIFF_PAIR_UNMERGED(p) && !DIFF_FILE_VALID(p->two)) {
/* /*
* If the source is a broken "delete", and * If the source is a broken "delete", and
......
...@@ -485,6 +485,7 @@ static struct string_list *get_renames(struct merge_options *o, ...@@ -485,6 +485,7 @@ static struct string_list *get_renames(struct merge_options *o,
renames = xcalloc(1, sizeof(struct string_list)); renames = xcalloc(1, sizeof(struct string_list));
diff_setup(&opts); diff_setup(&opts);
DIFF_OPT_SET(&opts, RECURSIVE); DIFF_OPT_SET(&opts, RECURSIVE);
DIFF_OPT_CLR(&opts, RENAME_EMPTY);
opts.detect_rename = DIFF_DETECT_RENAME; opts.detect_rename = DIFF_DETECT_RENAME;
opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit : opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
o->diff_rename_limit >= 0 ? o->diff_rename_limit : o->diff_rename_limit >= 0 ? o->diff_rename_limit :
...@@ -1914,7 +1915,7 @@ int merge_recursive(struct merge_options *o, ...@@ -1914,7 +1915,7 @@ int merge_recursive(struct merge_options *o,
/* if there is no common ancestor, use an empty tree */ /* if there is no common ancestor, use an empty tree */
struct tree *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"); merged_common_ancestors = make_virtual_commit(tree, "ancestor");
} }
......
...@@ -157,16 +157,6 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st) ...@@ -157,16 +157,6 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
return 0; 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) static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
{ {
unsigned int changed = 0; unsigned int changed = 0;
......
...@@ -164,7 +164,7 @@ static void write_message(struct strbuf *msgbuf, const char *filename) ...@@ -164,7 +164,7 @@ static void write_message(struct strbuf *msgbuf, const char *filename)
static struct tree *empty_tree(void) 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) static int error_dirty_index(struct replay_opts *opts)
......
...@@ -884,4 +884,20 @@ test_expect_success 'no spurious "refusing to lose untracked" message' ' ...@@ -884,4 +884,20 @@ test_expect_success 'no spurious "refusing to lose untracked" message' '
! grep "refusing to lose untracked file" errors.txt ! 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 test_done
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册