unpack-trees: compare sparse directories correctly

As we further integrate the sparse-index into unpack-trees, we need to ensure that we compare sparse directory entries correctly with other entries. This affects searching for an exact path as well as sorting index entries. Sparse directory entries contain the trailing directory separator. This is important for the sorting, in particular. Thus, within do_compare_entry() we stop using S_IFREG in all cases, since sparse directories should use S_IFDIR to indicate that the comparison should treat the entry name as a dirctory. Within compare_entry(), it first calls do_compare_entry() to check the leading portion of the name. When the input path is a directory name, we could match exactly already. Thus, we should return 0 if we have an exact string match on a sparse directory entry. The final check is a length comparison between the strings. Reviewed-by: Elijah Newren <newren@gmail.com> Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed Jul 14, 2021 at 13:12 UTC cd807a5cdabe5720071d91801dbc76faa8a643ba
1 file changed +13 -1
unpack-trees.c
+13 -1
@@ -983,6 +983,7 @@ static int do_compare_entry(const struct cache_entry *ce,
983 int pathlen, ce_len;
984 const char *ce_name;
985 int cmp;
986 + unsigned ce_mode;
987
988 /*
989 * If we have not precomputed the traverse path, it is quicker
@@ -1005,7 +1006,8 @@ static int do_compare_entry(const struct cache_entry *ce,
1006 ce_len -= pathlen;
1007 ce_name = ce->name + pathlen;
1008
1008 - return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);
1009 + ce_mode = S_ISSPARSEDIR(ce->ce_mode) ? S_IFDIR : S_IFREG;
1010 + return df_name_compare(ce_name, ce_len, ce_mode, name, namelen, mode);
1011 }
1012
1013 static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
@@ -1014,6 +1016,16 @@ static int compare_entry(const struct cache_entry *ce, const struct traverse_inf
1016 if (cmp)
1017 return cmp;
1018
1019 + /*
1020 + * At this point, we know that we have a prefix match. If ce
1021 + * is a sparse directory, then allow an exact match. This only
1022 + * works when the input name is a directory, since ce->name
1023 + * ends in a directory separator.
1024 + */
1025 + if (S_ISSPARSEDIR(ce->ce_mode) &&
1026 + ce->ce_namelen == traverse_path_len(info, tree_entry_len(n)) + 1)
1027 + return 0;
1028 +
1029 /*
1030 * Even if the beginning compared identically, the ce should
1031 * compare as bigger than a directory leading up to it!