Skip to content

Commit 850c3a2

Browse files
committed
entry: report more colliding paths
In b878579 (clone: report duplicate entries on case-insensitive filesystems, 2018-08-17) code was added to warn about index entries that resolve to the same file system entity (usually the cause is a case-insensitive filesystem). In Git for Windows, where inodes are not trusted (because of a performance trade-off, inodes are equal to 0 by default), that check does not compare inode numbers but the verbatim path. This logic works well when index entries' paths differ only in case. However, for file/directory conflicts only the file's path was reported, leaving the user puzzled with what that path collides. Let's try ot catch colliding paths even if one path is the prefix of the other. We do this also in setups where the file system is case-sensitive because the inode check would not be able to catch those collisions. While not a complete solution (for example, on macOS, Unicode normalization could also lead to file/directory conflicts but be missed by this logic), it is at least another defensive layer on top of what the previous commits added. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent e4930e8 commit 850c3a2

File tree

3 files changed

+20
-1
lines changed

3 files changed

+20
-1
lines changed

dir.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,18 @@ int fspathncmp(const char *a, const char *b, size_t count)
8888
return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
8989
}
9090

91+
int paths_collide(const char *a, const char *b)
92+
{
93+
size_t len_a = strlen(a), len_b = strlen(b);
94+
95+
if (len_a == len_b)
96+
return fspatheq(a, b);
97+
98+
if (len_a < len_b)
99+
return is_dir_sep(b[len_a]) && !fspathncmp(a, b, len_a);
100+
return is_dir_sep(a[len_b]) && !fspathncmp(a, b, len_b);
101+
}
102+
91103
unsigned int fspathhash(const char *str)
92104
{
93105
return ignore_case ? strihash(str) : strhash(str);

dir.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,13 @@ int fspatheq(const char *a, const char *b);
519519
int fspathncmp(const char *a, const char *b, size_t count);
520520
unsigned int fspathhash(const char *str);
521521

522+
/*
523+
* Reports whether paths collide. This may be because the paths differ only in
524+
* case on a case-sensitive filesystem, or that one path refers to a symlink
525+
* that collides with one of the parent directories of the other.
526+
*/
527+
int paths_collide(const char *a, const char *b);
528+
522529
/*
523530
* The prefix part of pattern must not contains wildcards.
524531
*/

entry.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ static void mark_colliding_entries(const struct checkout *state,
454454
continue;
455455

456456
if ((trust_ino && !match_stat_data(&dup->ce_stat_data, st)) ||
457-
(!trust_ino && !fspathcmp(ce->name, dup->name))) {
457+
paths_collide(ce->name, dup->name)) {
458458
dup->ce_flags |= CE_MATCHED;
459459
break;
460460
}

0 commit comments

Comments
 (0)