Skip to content

Commit b45563a

Browse files
committed
rename: Break filepairs with different types.
When we consider if a path has been totally rewritten, we did not touch changes from symlinks to files or vice versa. But a change that modifies even the type of a blob surely should count as a complete rewrite. While we are at it, modernise diffcore-break to be aware of gitlinks (we do not want to touch them). Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 1c46ab1 commit b45563a

File tree

5 files changed

+104
-14
lines changed

5 files changed

+104
-14
lines changed

cache.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,13 @@ enum object_type {
192192
OBJ_MAX,
193193
};
194194

195+
static inline enum object_type object_type(unsigned int mode)
196+
{
197+
return S_ISDIR(mode) ? OBJ_TREE :
198+
S_ISGITLINK(mode) ? OBJ_COMMIT :
199+
OBJ_BLOB;
200+
}
201+
195202
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
196203
#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
197204
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"

diffcore-break.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@ static int should_break(struct diff_filespec *src,
5252
* is the default.
5353
*/
5454

55-
if (!S_ISREG(src->mode) || !S_ISREG(dst->mode))
56-
return 0; /* leave symlink rename alone */
55+
if (S_ISREG(src->mode) != S_ISREG(dst->mode)) {
56+
*merge_score_p = (int)MAX_SCORE;
57+
return 1; /* even their types are different */
58+
}
5759

5860
if (src->sha1_valid && dst->sha1_valid &&
5961
!hashcmp(src->sha1, dst->sha1))
@@ -168,11 +170,13 @@ void diffcore_break(int break_score)
168170
struct diff_filepair *p = q->queue[i];
169171
int score;
170172

171-
/* We deal only with in-place edit of non directory.
173+
/*
174+
* We deal only with in-place edit of blobs.
172175
* We do not break anything else.
173176
*/
174177
if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two) &&
175-
!S_ISDIR(p->one->mode) && !S_ISDIR(p->two->mode) &&
178+
object_type(p->one->mode) == OBJ_BLOB &&
179+
object_type(p->two->mode) == OBJ_BLOB &&
176180
!strcmp(p->one->path, p->two->path)) {
177181
if (should_break(p->one, p->two,
178182
break_score, &score)) {

t/t4008-diff-break-rewrite.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,11 @@ test_expect_success \
122122
'run diff with -B -M' \
123123
'git diff-index -B -M "$tree" >current'
124124

125-
# This should not mistake file0 as the copy source of new file1
126-
# due to type differences.
125+
# file0 changed from regular to symlink. file1 is very close to the preimage of file0.
126+
# because we break file0, file1 can become a rename of it.
127127
cat >expected <<\EOF
128128
:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T file0
129-
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100 file1
129+
:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R file0 file1
130130
EOF
131131

132132
test_expect_success \

t/t4023-diff-rename-typechange.sh

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/bin/sh
2+
3+
test_description='typechange rename detection'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success setup '
8+
9+
rm -f foo bar &&
10+
cat ../../COPYING >foo &&
11+
ln -s linklink bar &&
12+
git add foo bar &&
13+
git commit -a -m Initial &&
14+
git tag one &&
15+
16+
rm -f foo bar &&
17+
cat ../../COPYING >bar &&
18+
ln -s linklink foo &&
19+
git add foo bar &&
20+
git commit -a -m Second &&
21+
git tag two &&
22+
23+
rm -f foo bar &&
24+
cat ../../COPYING >foo &&
25+
git add foo &&
26+
git commit -a -m Third &&
27+
git tag three &&
28+
29+
mv foo bar &&
30+
ln -s linklink foo &&
31+
git add foo bar &&
32+
git commit -a -m Fourth &&
33+
git tag four &&
34+
35+
# This is purely for sanity check
36+
37+
rm -f foo bar &&
38+
cat ../../COPYING >foo &&
39+
cat ../../Makefile >bar &&
40+
git add foo bar &&
41+
git commit -a -m Fifth &&
42+
git tag five &&
43+
44+
rm -f foo bar &&
45+
cat ../../Makefile >foo &&
46+
cat ../../COPYING >bar &&
47+
git add foo bar &&
48+
git commit -a -m Sixth &&
49+
git tag six
50+
51+
'
52+
53+
test_expect_success 'cross renames to be detected for regular files' '
54+
55+
git diff-tree five six -r --name-status -B -M | sort >actual &&
56+
{
57+
echo "R100 foo bar"
58+
echo "R100 bar foo"
59+
} | sort >expect &&
60+
diff -u expect actual
61+
62+
'
63+
64+
test_expect_success 'cross renames to be detected for typechange' '
65+
66+
git diff-tree one two -r --name-status -B -M | sort >actual &&
67+
{
68+
echo "R100 foo bar"
69+
echo "R100 bar foo"
70+
} | sort >expect &&
71+
diff -u expect actual
72+
73+
'
74+
75+
test_expect_success 'moves and renames' '
76+
77+
git diff-tree three four -r --name-status -B -M | sort >actual &&
78+
{
79+
echo "R100 foo bar"
80+
echo "T100 foo"
81+
} | sort >expect &&
82+
diff -u expect actual
83+
84+
'
85+
86+
test_done

tree-walk.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,6 @@ struct name_entry {
77
unsigned int mode;
88
};
99

10-
static inline enum object_type object_type(unsigned int mode)
11-
{
12-
return S_ISDIR(mode) ? OBJ_TREE :
13-
S_ISGITLINK(mode) ? OBJ_COMMIT :
14-
OBJ_BLOB;
15-
}
16-
1710
struct tree_desc {
1811
const void *buffer;
1912
struct name_entry entry;

0 commit comments

Comments
 (0)