diff --git a/dir.c b/dir.c index f8a11aa1ec..fd689bbe66 100644 --- a/dir.c +++ b/dir.c @@ -88,6 +88,18 @@ int fspathncmp(const char *a, const char *b, size_t count) return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count); } +int paths_collide(const char *a, const char *b) +{ + size_t len_a = strlen(a), len_b = strlen(b); + + if (len_a == len_b) + return fspatheq(a, b); + + if (len_a < len_b) + return is_dir_sep(b[len_a]) && !fspathncmp(a, b, len_a); + return is_dir_sep(a[len_b]) && !fspathncmp(a, b, len_b); +} + unsigned int fspathhash(const char *str) { return ignore_case ? strihash(str) : strhash(str); diff --git a/dir.h b/dir.h index 674747d93a..62e89a053d 100644 --- a/dir.h +++ b/dir.h @@ -519,6 +519,13 @@ int fspatheq(const char *a, const char *b); int fspathncmp(const char *a, const char *b, size_t count); unsigned int fspathhash(const char *str); +/* + * Reports whether paths collide. This may be because the paths differ only in + * case on a case-sensitive filesystem, or that one path refers to a symlink + * that collides with one of the parent directories of the other. + */ +int paths_collide(const char *a, const char *b); + /* * The prefix part of pattern must not contains wildcards. */ diff --git a/entry.c b/entry.c index 616e4f073c..a4c18c5645 100644 --- a/entry.c +++ b/entry.c @@ -454,7 +454,7 @@ static void mark_colliding_entries(const struct checkout *state, continue; if ((trust_ino && !match_stat_data(&dup->ce_stat_data, st)) || - (!trust_ino && !fspathcmp(ce->name, dup->name))) { + paths_collide(ce->name, dup->name)) { dup->ce_flags |= CE_MATCHED; break; }