Skip to content

Commit 0ce3964

Browse files
dschogitster
authored andcommitted
diffcore-rename: favour identical basenames
When there are several candidates for a rename source, and one of them has an identical basename to the rename target, take that one. Noticed by Govind Salinas, posted by Shawn O. Pearce, partial patch by Linus Torvalds. Signed-off-by: Johannes Schindelin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 37cd4f7 commit 0ce3964

File tree

2 files changed

+45
-1
lines changed

2 files changed

+45
-1
lines changed

diffcore-rename.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,21 @@ static int is_exact_match(struct diff_filespec *src,
119119
return 0;
120120
}
121121

122+
static int basename_same(struct diff_filespec *src, struct diff_filespec *dst)
123+
{
124+
int src_len = strlen(src->path), dst_len = strlen(dst->path);
125+
while (src_len && dst_len) {
126+
char c1 = src->path[--src_len];
127+
char c2 = dst->path[--dst_len];
128+
if (c1 != c2)
129+
return 0;
130+
if (c1 == '/')
131+
return 1;
132+
}
133+
return (!src_len || src->path[src_len - 1] == '/') &&
134+
(!dst_len || dst->path[dst_len - 1] == '/');
135+
}
136+
122137
struct diff_score {
123138
int src; /* index in rename_src */
124139
int dst; /* index in rename_dst */
@@ -186,8 +201,11 @@ static int estimate_similarity(struct diff_filespec *src,
186201
*/
187202
if (!dst->size)
188203
score = 0; /* should not happen */
189-
else
204+
else {
190205
score = (int)(src_copied * MAX_SCORE / max_size);
206+
if (basename_same(src, dst))
207+
score++;
208+
}
191209
return score;
192210
}
193211

@@ -295,9 +313,22 @@ void diffcore_rename(struct diff_options *options)
295313
if (rename_dst[i].pair)
296314
continue; /* dealt with an earlier round */
297315
for (j = 0; j < rename_src_nr; j++) {
316+
int k;
298317
struct diff_filespec *one = rename_src[j].one;
299318
if (!is_exact_match(one, two, contents_too))
300319
continue;
320+
321+
/* see if there is a basename match, too */
322+
for (k = j; k < rename_src_nr; k++) {
323+
one = rename_src[k].one;
324+
if (basename_same(one, two) &&
325+
is_exact_match(one, two,
326+
contents_too)) {
327+
j = k;
328+
break;
329+
}
330+
}
331+
301332
record_rename_pair(i, j, (int)MAX_SCORE);
302333
rename_count++;
303334
break; /* we are done with this entry */

t/t4001-diff-rename.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,17 @@ test_expect_success \
6464
'validate the output.' \
6565
'compare_diff_patch current expected'
6666

67+
test_expect_success 'favour same basenames over different ones' '
68+
cp path1 another-path &&
69+
git add another-path &&
70+
git commit -m 1 &&
71+
git rm path1 &&
72+
mkdir subdir &&
73+
git mv another-path subdir/path1 &&
74+
git runstatus | grep "renamed: .*path1 -> subdir/path1"'
75+
76+
test_expect_success 'favour same basenames even with minor differences' '
77+
git show HEAD:path1 | sed "s/15/16/" > subdir/path1 &&
78+
git runstatus | grep "renamed: .*path1 -> subdir/path1"'
79+
6780
test_done

0 commit comments

Comments
 (0)