Skip to content

Commit 8220f59

Browse files
committed
read-tree: narrow scope of index expansion for --prefix
When `unpack_trees` is provided with a prefix, the tree traversal that occurs in it requires that the base directory path given to it is a traversable directory in the index. In order to adhere to this requirement, the index is expanded if the given prefix is equal to or a subdirectory of a sparse directory in the index. Signed-off-by: Victoria Dye <[email protected]>
1 parent 23c25e8 commit 8220f59

File tree

3 files changed

+60
-3
lines changed

3 files changed

+60
-3
lines changed

builtin/read-tree.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,7 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
217217
if (opts.merge && !opts.index_only)
218218
setup_work_tree();
219219

220-
/* TODO: audit sparse index behavior in unpack_trees */
221-
if (opts.skip_sparse_checkout || opts.prefix)
220+
if (opts.skip_sparse_checkout)
222221
ensure_full_index(&the_index);
223222

224223
if (opts.merge) {

t/t1092-sparse-checkout-compatibility.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1258,7 +1258,13 @@ test_expect_success 'sparse index is not expanded: read-tree' '
12581258
do
12591259
ensure_not_expanded read-tree -mu $MERGE_TREES &&
12601260
ensure_not_expanded reset --hard HEAD || return 1
1261-
done
1261+
done &&
1262+
1263+
rm -rf sparse-index/deep/deeper2 &&
1264+
ensure_not_expanded add . &&
1265+
ensure_not_expanded commit -m "test" &&
1266+
1267+
ensure_not_expanded read-tree --prefix=deep/deeper2 -u deepest
12621268
'
12631269

12641270
# NEEDSWORK: a sparse-checkout behaves differently from a full checkout

unpack-trees.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,6 +1741,58 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
17411741
ensure_full_index(o->dst_index);
17421742
}
17431743

1744+
/*
1745+
* If the prefix is equal to or contained within a sparse directory, the
1746+
* index needs to be expanded to traverse with the specified prefix. Note
1747+
* that only the src_index is checked because the prefix is only specified
1748+
* in cases where src_index == dst_index.
1749+
*/
1750+
if (o->prefix && o->src_index->sparse_index) {
1751+
int i, ce_len;
1752+
struct cache_entry *ce;
1753+
int prefix_len = strlen(o->prefix);
1754+
1755+
if (prefix_len > 0) {
1756+
for (i = 0; i < o->src_index->cache_nr; i++) {
1757+
ce = o->src_index->cache[i];
1758+
ce_len = ce_namelen(ce);
1759+
1760+
if (!S_ISSPARSEDIR(ce->ce_mode))
1761+
continue;
1762+
1763+
/*
1764+
* Normalize comparison length for cache entry vs. prefix -
1765+
* either may have a trailing slash, which we do not want to
1766+
* compare (can assume both are directories).
1767+
*/
1768+
if (ce->name[ce_len - 1] == '/')
1769+
ce_len--;
1770+
if (o->prefix[prefix_len - 1] == '/')
1771+
prefix_len--;
1772+
1773+
/*
1774+
* If prefix length is shorter, then it is either a parent to
1775+
* this sparse directory, or a completely different path. In
1776+
* either case, we don't need to expand the index
1777+
*/
1778+
if (prefix_len < ce_len)
1779+
continue;
1780+
1781+
/*
1782+
* Avoid the case of expanding the index with a prefix
1783+
* a/beta for a sparse directory a/b.
1784+
*/
1785+
if (ce_len < prefix_len && o->prefix[ce_len] != '/')
1786+
continue;
1787+
1788+
if (!strncmp(ce->name, o->prefix, ce_len)) {
1789+
ensure_full_index(o->src_index);
1790+
break;
1791+
}
1792+
}
1793+
}
1794+
}
1795+
17441796
if (!core_apply_sparse_checkout || !o->update)
17451797
o->skip_sparse_checkout = 1;
17461798
if (!o->skip_sparse_checkout && !o->pl) {

0 commit comments

Comments
 (0)