Skip to content

Commit ca4e3ca

Browse files
szedergitster
authored andcommitted
diffcore: fix iteration order of identical files during rename detection
If the two paths 'dir/A/file' and 'dir/B/file' have identical content and the parent directory is renamed, e.g. 'git mv dir other-dir', then diffcore reports the following exact renames: renamed: dir/B/file -> other-dir/A/file renamed: dir/A/file -> other-dir/B/file While technically not wrong, this is confusing not only for the user, but also for git commands that make decisions based on rename information, e.g. 'git log --follow other-dir/A/file' follows 'dir/B/file' past the rename. This behavior is a side effect of commit v2.0.0-rc4~8^2~14 (diffcore-rename.c: simplify finding exact renames, 2013-11-14): the hashmap storing sources returns entries from the same bucket, i.e. sources matching the current destination, in LIFO order. Thus the iteration first examines 'other-dir/A/file' and 'dir/B/file' and, upon finding identical content and basename, reports an exact rename. Other hashmap users are apparently happy with the current iteration order over the entries of a bucket. Changing the iteration order would risk upsetting other hashmap users and would increase the memory footprint of each bucket by a pointer to the tail element. Fill the hashmap with source entries in reverse order to restore the original exact rename detection behavior. Reported-by: Bill Okara <[email protected]> Signed-off-by: SZEDER Gábor <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 7654286 commit ca4e3ca

File tree

2 files changed

+15
-2
lines changed

2 files changed

+15
-2
lines changed

diffcore-rename.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,9 +340,11 @@ static int find_exact_renames(struct diff_options *options)
340340
int i, renames = 0;
341341
struct hashmap file_table;
342342

343-
/* Add all sources to the hash table */
343+
/* Add all sources to the hash table in reverse order, because
344+
* later on they will be retrieved in LIFO order.
345+
*/
344346
hashmap_init(&file_table, NULL, rename_src_nr);
345-
for (i = 0; i < rename_src_nr; i++)
347+
for (i = rename_src_nr-1; i >= 0; i--)
346348
insert_file_table(&file_table, i, rename_src[i].p->one);
347349

348350
/* Walk the destinations and find best source match */

t/t4001-diff-rename.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,17 @@ test_expect_success 'favour same basenames even with minor differences' '
7777
git show HEAD:path1 | sed "s/15/16/" > subdir/path1 &&
7878
git status | test_i18ngrep "renamed: .*path1 -> subdir/path1"'
7979

80+
test_expect_success 'two files with same basename and same content' '
81+
git reset --hard &&
82+
mkdir -p dir/A dir/B &&
83+
cp path1 dir/A/file &&
84+
cp path1 dir/B/file &&
85+
git add dir &&
86+
git commit -m 2 &&
87+
git mv dir other-dir &&
88+
git status | test_i18ngrep "renamed: .*dir/A/file -> other-dir/A/file"
89+
'
90+
8091
test_expect_success 'setup for many rename source candidates' '
8192
git reset --hard &&
8293
for i in 0 1 2 3 4 5 6 7 8 9;

0 commit comments

Comments
 (0)