Skip to content

Commit 5e6154f

Browse files
committed
Merge branch 'jk/delete-modechange-conflict' into maint
Merging a branch that removes a path and another that changes the mode bits on the same path should have conflicted at the path, but it didn't and silently favoured the removal. * jk/delete-modechange-conflict: merge: detect delete/modechange conflict t6031: generalize for recursive and resolve strategies t6031: move triple-rename test to t3030
2 parents c378862 + 72fac66 commit 5e6154f

File tree

5 files changed

+144
-89
lines changed

5 files changed

+144
-89
lines changed

git-merge-one-file.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ case "${1:-.}${2:-.}${3:-.}" in
3838
# Deleted in both or deleted in one and unchanged in the other
3939
#
4040
"$1.." | "$1.$1" | "$1$1.")
41+
if { test -z "$6" && test "$5" != "$7"; } ||
42+
{ test -z "$7" && test "$5" != "$6"; }
43+
then
44+
echo "ERROR: File $4 deleted on one branch but had its" >&2
45+
echo "ERROR: permissions changed on the other." >&2
46+
exit 1
47+
fi
48+
4149
if test -n "$2"
4250
then
4351
echo "Removing $4"

merge-recursive.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,13 +1531,17 @@ static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst)
15311531
}
15321532

15331533
static int blob_unchanged(const unsigned char *o_sha,
1534+
unsigned o_mode,
15341535
const unsigned char *a_sha,
1536+
unsigned a_mode,
15351537
int renormalize, const char *path)
15361538
{
15371539
struct strbuf o = STRBUF_INIT;
15381540
struct strbuf a = STRBUF_INIT;
15391541
int ret = 0; /* assume changed for safety */
15401542

1543+
if (a_mode != o_mode)
1544+
return 0;
15411545
if (sha_eq(o_sha, a_sha))
15421546
return 1;
15431547
if (!renormalize)
@@ -1723,8 +1727,8 @@ static int process_entry(struct merge_options *o,
17231727
} else if (o_sha && (!a_sha || !b_sha)) {
17241728
/* Case A: Deleted in one */
17251729
if ((!a_sha && !b_sha) ||
1726-
(!b_sha && blob_unchanged(o_sha, a_sha, normalize, path)) ||
1727-
(!a_sha && blob_unchanged(o_sha, b_sha, normalize, path))) {
1730+
(!b_sha && blob_unchanged(o_sha, o_mode, a_sha, a_mode, normalize, path)) ||
1731+
(!a_sha && blob_unchanged(o_sha, o_mode, b_sha, b_mode, normalize, path))) {
17281732
/* Deleted in both or deleted in one and
17291733
* unchanged in the other */
17301734
if (a_sha)

t/t3030-merge-recursive.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,5 +629,35 @@ test_expect_failure 'merge-recursive rename vs. rename/symlink' '
629629
test_cmp expected actual
630630
'
631631

632+
test_expect_success 'merging with triple rename across D/F conflict' '
633+
git reset --hard HEAD &&
634+
git checkout -b main &&
635+
git rm -rf . &&
636+
637+
echo "just a file" >sub1 &&
638+
mkdir -p sub2 &&
639+
echo content1 >sub2/file1 &&
640+
echo content2 >sub2/file2 &&
641+
echo content3 >sub2/file3 &&
642+
mkdir simple &&
643+
echo base >simple/bar &&
644+
git add -A &&
645+
test_tick &&
646+
git commit -m base &&
647+
648+
git checkout -b other &&
649+
echo more >>simple/bar &&
650+
test_tick &&
651+
git commit -a -m changesimplefile &&
652+
653+
git checkout main &&
654+
git rm sub1 &&
655+
git mv sub2 sub1 &&
656+
test_tick &&
657+
git commit -m changefiletodir &&
658+
659+
test_tick &&
660+
git merge other
661+
'
632662

633663
test_done

t/t6031-merge-filemode.sh

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#!/bin/sh
2+
3+
test_description='merge: handle file mode'
4+
. ./test-lib.sh
5+
6+
test_expect_success 'set up mode change in one branch' '
7+
: >file1 &&
8+
git add file1 &&
9+
git commit -m initial &&
10+
git checkout -b a1 master &&
11+
: >dummy &&
12+
git add dummy &&
13+
git commit -m a &&
14+
git checkout -b b1 master &&
15+
test_chmod +x file1 &&
16+
git add file1 &&
17+
git commit -m b1
18+
'
19+
20+
do_one_mode () {
21+
strategy=$1
22+
us=$2
23+
them=$3
24+
test_expect_success "resolve single mode change ($strategy, $us)" '
25+
git checkout -f $us &&
26+
git merge -s $strategy $them &&
27+
git ls-files -s file1 | grep ^100755
28+
'
29+
30+
test_expect_success FILEMODE "verify executable bit on file ($strategy, $us)" '
31+
test -x file1
32+
'
33+
}
34+
35+
do_one_mode recursive a1 b1
36+
do_one_mode recursive b1 a1
37+
do_one_mode resolve a1 b1
38+
do_one_mode resolve b1 a1
39+
40+
test_expect_success 'set up mode change in both branches' '
41+
git reset --hard HEAD &&
42+
git checkout -b a2 master &&
43+
: >file2 &&
44+
H=$(git hash-object file2) &&
45+
test_chmod +x file2 &&
46+
git commit -m a2 &&
47+
git checkout -b b2 master &&
48+
: >file2 &&
49+
git add file2 &&
50+
git commit -m b2 &&
51+
{
52+
echo "100755 $H 2 file2"
53+
echo "100644 $H 3 file2"
54+
} >expect
55+
'
56+
57+
do_both_modes () {
58+
strategy=$1
59+
test_expect_success "detect conflict on double mode change ($strategy)" '
60+
git reset --hard &&
61+
git checkout -f a2 &&
62+
test_must_fail git merge -s $strategy b2 &&
63+
git ls-files -u >actual &&
64+
test_cmp actual expect &&
65+
git ls-files -s file2 | grep ^100755
66+
'
67+
68+
test_expect_success FILEMODE "verify executable bit on file ($strategy)" '
69+
test -x file2
70+
'
71+
}
72+
73+
# both sides are equivalent, so no need to run both ways
74+
do_both_modes recursive
75+
do_both_modes resolve
76+
77+
test_expect_success 'set up delete/modechange scenario' '
78+
git reset --hard &&
79+
git checkout -b deletion master &&
80+
git rm file1 &&
81+
git commit -m deletion
82+
'
83+
84+
do_delete_modechange () {
85+
strategy=$1
86+
us=$2
87+
them=$3
88+
test_expect_success "detect delete/modechange conflict ($strategy, $us)" '
89+
git reset --hard &&
90+
git checkout $us &&
91+
test_must_fail git merge -s $strategy $them
92+
'
93+
}
94+
95+
do_delete_modechange recursive b1 deletion
96+
do_delete_modechange recursive deletion b1
97+
do_delete_modechange resolve b1 deletion
98+
do_delete_modechange resolve deletion b1
99+
100+
test_done

t/t6031-merge-recursive.sh

Lines changed: 0 additions & 87 deletions
This file was deleted.

0 commit comments

Comments
 (0)