Commit 479a38bf153f13966a423124994e4ed91561e595

Colin Stolley 2021-09-09T15:21:48

merge: Check file mode when resolving renames. When determining if ours or theirs changed, we check the oids but not their respective file modes. This can lead to merges introducing incorrect file mode changes (eg., in a revert). A simple linear example might be: commit A - introduces file `foo` with chmod 0755 commit B - updates some unrelated file commit C - renames `foo` to `bar` and chmod 0644 If B is reverted, `bar` will unexpectedly acquire mode 0755.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
diff --git a/src/merge.c b/src/merge.c
index 191fb98..1c841bd 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -816,8 +816,11 @@ static int merge_conflict_resolve_one_renamed(
 		conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
 		return 0;
 
-	ours_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->our_entry.id) != 0);
-	theirs_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->their_entry.id) != 0);
+	ours_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->our_entry.id) != 0) ||
+		(conflict->ancestor_entry.mode != conflict->our_entry.mode);
+
+	theirs_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->their_entry.id) != 0) ||
+		(conflict->ancestor_entry.mode != conflict->their_entry.mode);
 
 	/* if both are modified (and not to a common target) require a merge */
 	if (ours_changed && theirs_changed &&