Skip to content

Commit c0599f6

Browse files
committed
Merge branch 'jk/diff-no-rename-empty'
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
2 parents a2caeb2 + 4f7cb99 commit c0599f6

File tree

9 files changed

+45
-14
lines changed

9 files changed

+45
-14
lines changed

builtin/diff.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
327327
add_head_to_pending(&rev);
328328
if (!rev.pending.nr) {
329329
struct tree *tree;
330-
tree = lookup_tree((const unsigned char*)EMPTY_TREE_SHA1_BIN);
330+
tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
331331
add_pending_object(&rev, &tree->object, "HEAD");
332332
}
333333
break;

cache.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,19 @@ static inline void hashclr(unsigned char *hash)
708708
#define EMPTY_TREE_SHA1_BIN \
709709
((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
710710

711+
#define EMPTY_BLOB_SHA1_HEX \
712+
"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
713+
#define EMPTY_BLOB_SHA1_BIN_LITERAL \
714+
"\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
715+
"\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
716+
#define EMPTY_BLOB_SHA1_BIN \
717+
((const unsigned char *) EMPTY_BLOB_SHA1_BIN_LITERAL)
718+
719+
static inline int is_empty_blob_sha1(const unsigned char *sha1)
720+
{
721+
return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
722+
}
723+
711724
int git_mkstemp(char *path, size_t n, const char *template);
712725

713726
int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);

diff.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3146,6 +3146,7 @@ void diff_setup(struct diff_options *options)
31463146
options->rename_limit = -1;
31473147
options->dirstat_permille = diff_dirstat_permille_default;
31483148
options->context = 3;
3149+
DIFF_OPT_SET(options, RENAME_EMPTY);
31493150

31503151
options->change = diff_change;
31513152
options->add_remove = diff_addremove;
@@ -3516,6 +3517,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
35163517
}
35173518
else if (!strcmp(arg, "--no-renames"))
35183519
options->detect_rename = 0;
3520+
else if (!strcmp(arg, "--rename-empty"))
3521+
DIFF_OPT_SET(options, RENAME_EMPTY);
3522+
else if (!strcmp(arg, "--no-rename-empty"))
3523+
DIFF_OPT_CLR(options, RENAME_EMPTY);
35193524
else if (!strcmp(arg, "--relative"))
35203525
DIFF_OPT_SET(options, RELATIVE_NAME);
35213526
else if (!prefixcmp(arg, "--relative=")) {

diff.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
6060
#define DIFF_OPT_SILENT_ON_REMOVE (1 << 5)
6161
#define DIFF_OPT_FIND_COPIES_HARDER (1 << 6)
6262
#define DIFF_OPT_FOLLOW_RENAMES (1 << 7)
63-
/* (1 << 8) unused */
63+
#define DIFF_OPT_RENAME_EMPTY (1 << 8)
6464
/* (1 << 9) unused */
6565
#define DIFF_OPT_HAS_CHANGES (1 << 10)
6666
#define DIFF_OPT_QUICK (1 << 11)

diffcore-rename.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,9 +512,15 @@ void diffcore_rename(struct diff_options *options)
512512
else if (options->single_follow &&
513513
strcmp(options->single_follow, p->two->path))
514514
continue; /* not interested */
515+
else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
516+
is_empty_blob_sha1(p->two->sha1))
517+
continue;
515518
else
516519
locate_rename_dst(p->two, 1);
517520
}
521+
else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
522+
is_empty_blob_sha1(p->one->sha1))
523+
continue;
518524
else if (!DIFF_PAIR_UNMERGED(p) && !DIFF_FILE_VALID(p->two)) {
519525
/*
520526
* If the source is a broken "delete", and

merge-recursive.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ static struct string_list *get_renames(struct merge_options *o,
485485
renames = xcalloc(1, sizeof(struct string_list));
486486
diff_setup(&opts);
487487
DIFF_OPT_SET(&opts, RECURSIVE);
488+
DIFF_OPT_CLR(&opts, RENAME_EMPTY);
488489
opts.detect_rename = DIFF_DETECT_RENAME;
489490
opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
490491
o->diff_rename_limit >= 0 ? o->diff_rename_limit :
@@ -1914,7 +1915,7 @@ int merge_recursive(struct merge_options *o,
19141915
/* if there is no common ancestor, use an empty tree */
19151916
struct tree *tree;
19161917

1917-
tree = lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);
1918+
tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
19181919
merged_common_ancestors = make_virtual_commit(tree, "ancestor");
19191920
}
19201921

read-cache.c

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,6 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
157157
return 0;
158158
}
159159

160-
static int is_empty_blob_sha1(const unsigned char *sha1)
161-
{
162-
static const unsigned char empty_blob_sha1[20] = {
163-
0xe6,0x9d,0xe2,0x9b,0xb2,0xd1,0xd6,0x43,0x4b,0x8b,
164-
0x29,0xae,0x77,0x5a,0xd8,0xc2,0xe4,0x8c,0x53,0x91
165-
};
166-
167-
return !hashcmp(sha1, empty_blob_sha1);
168-
}
169-
170160
static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
171161
{
172162
unsigned int changed = 0;

sequencer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ static void write_message(struct strbuf *msgbuf, const char *filename)
164164

165165
static struct tree *empty_tree(void)
166166
{
167-
return lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);
167+
return lookup_tree(EMPTY_TREE_SHA1_BIN);
168168
}
169169

170170
static int error_dirty_index(struct replay_opts *opts)

t/t6022-merge-rename.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,4 +884,20 @@ test_expect_success 'no spurious "refusing to lose untracked" message' '
884884
! grep "refusing to lose untracked file" errors.txt
885885
'
886886

887+
test_expect_success 'do not follow renames for empty files' '
888+
git checkout -f -b empty-base &&
889+
>empty1 &&
890+
git add empty1 &&
891+
git commit -m base &&
892+
echo content >empty1 &&
893+
git add empty1 &&
894+
git commit -m fill &&
895+
git checkout -b empty-topic HEAD^ &&
896+
git mv empty1 empty2 &&
897+
git commit -m rename &&
898+
test_must_fail git merge empty-base &&
899+
>expect &&
900+
test_cmp expect empty2
901+
'
902+
887903
test_done

0 commit comments

Comments
 (0)