Skip to content

Commit 331a183

Browse files
eyvindgitster
authored andcommitted
Try normalizing files to avoid delete/modify conflicts when merging
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f217f0e commit 331a183

File tree

2 files changed

+50
-3
lines changed

2 files changed

+50
-3
lines changed

merge-recursive.c

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,53 @@ static unsigned char *stage_sha(const unsigned char *sha, unsigned mode)
10561056
return (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha;
10571057
}
10581058

1059+
static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst)
1060+
{
1061+
void *buf;
1062+
enum object_type type;
1063+
unsigned long size;
1064+
buf = read_sha1_file(sha1, &type, &size);
1065+
if (!buf)
1066+
return error("cannot read object %s", sha1_to_hex(sha1));
1067+
if (type != OBJ_BLOB) {
1068+
free(buf);
1069+
return error("object %s is not a blob", sha1_to_hex(sha1));
1070+
}
1071+
strbuf_attach(dst, buf, size, size + 1);
1072+
return 0;
1073+
}
1074+
1075+
static int blob_unchanged(const unsigned char *o_sha,
1076+
const unsigned char *a_sha,
1077+
const char *path)
1078+
{
1079+
struct strbuf o = STRBUF_INIT;
1080+
struct strbuf a = STRBUF_INIT;
1081+
int ret = 0; /* assume changed for safety */
1082+
1083+
if (sha_eq(o_sha, a_sha))
1084+
return 1;
1085+
if (!merge_renormalize)
1086+
return 0;
1087+
1088+
assert(o_sha && a_sha);
1089+
if (read_sha1_strbuf(o_sha, &o) || read_sha1_strbuf(a_sha, &a))
1090+
goto error_return;
1091+
/*
1092+
* Note: binary | is used so that both renormalizations are
1093+
* performed. Comparison can be skipped if both files are
1094+
* unchanged since their sha1s have already been compared.
1095+
*/
1096+
if (renormalize_buffer(path, o.buf, o.len, &o) |
1097+
renormalize_buffer(path, a.buf, o.len, &a))
1098+
ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));
1099+
1100+
error_return:
1101+
strbuf_release(&o);
1102+
strbuf_release(&a);
1103+
return ret;
1104+
}
1105+
10591106
/* Per entry merge function */
10601107
static int process_entry(struct merge_options *o,
10611108
const char *path, struct stage_data *entry)
@@ -1075,8 +1122,8 @@ static int process_entry(struct merge_options *o,
10751122
if (o_sha && (!a_sha || !b_sha)) {
10761123
/* Case A: Deleted in one */
10771124
if ((!a_sha && !b_sha) ||
1078-
(sha_eq(a_sha, o_sha) && !b_sha) ||
1079-
(!a_sha && sha_eq(b_sha, o_sha))) {
1125+
(!b_sha && blob_unchanged(o_sha, a_sha, path)) ||
1126+
(!a_sha && blob_unchanged(o_sha, b_sha, path))) {
10801127
/* Deleted in both or deleted in one and
10811128
* unchanged in the other */
10821129
if (a_sha)

t/t6038-merge-text-auto.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ test_expect_success 'Check merging addition of text=auto' '
5151
test_cmp file file.temp
5252
'
5353

54-
test_expect_failure 'Test delete/normalize conflict' '
54+
test_expect_success 'Test delete/normalize conflict' '
5555
git checkout side &&
5656
git reset --hard initial &&
5757
git rm file &&

0 commit comments

Comments
 (0)