sparse-index: partially expand directories

The expand_to_pattern_list() method expands sparse directory entries to their list of contained files when either the pattern list is NULL or the directory is contained in the new pattern list's cone mode patterns. It is possible that the pattern list has a recursive match with a directory 'A/B/C/' and so an existing sparse directory 'A/B/' would need to be expanded. If there exists a directory 'A/B/D/', then that directory should not be expanded and instead we can create a sparse directory. To implement this, we plug into the add_path_to_index() callback for the call to read_tree_at(). Since we now need access to both the index we are writing and the pattern list we are comparing, create a 'struct modify_index_context' to use as a data transfer object. It is important that we use the given pattern list since we will use this pattern list to change the sparse-checkout patterns and cannot use istate->sparse_checkout_patterns. Signed-off-by: Derrick Stolee <derrickstolee@github.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed May 23, 2022 at 13:48 UTC 0243930af40f1ede50598c7de0965bdbe6fcbe30
1 file changed +50 -7
sparse-index.c
+50 -7
@@ -9,6 +9,11 @@
9 #include "dir.h"
10 #include "fsmonitor.h"
11
12 +struct modify_index_context {
13 + struct index_state *write;
14 + struct pattern_list *pl;
15 +};
16 +
17 static struct cache_entry *construct_sparse_dir_entry(
18 struct index_state *istate,
19 const char *sparse_dir,
@@ -231,18 +236,52 @@ static int add_path_to_index(const struct object_id *oid,
236 struct strbuf *base, const char *path,
237 unsigned int mode, void *context)
238 {
234 - struct index_state *istate = (struct index_state *)context;
239 + struct modify_index_context *ctx = (struct modify_index_context *)context;
240 struct cache_entry *ce;
241 size_t len = base->len;
242
238 - if (S_ISDIR(mode))
239 - return READ_TREE_RECURSIVE;
243 + if (S_ISDIR(mode)) {
244 + int dtype;
245 + size_t baselen = base->len;
246 + if (!ctx->pl)
247 + return READ_TREE_RECURSIVE;
248
241 - strbuf_addstr(base, path);
249 + /*
250 + * Have we expanded to a point outside of the sparse-checkout?
251 + *
252 + * Artificially pad the path name with a slash "/" to
253 + * indicate it as a directory, and add an arbitrary file
254 + * name ("-") so we can consider base->buf as a file name
255 + * to match against the cone-mode patterns.
256 + *
257 + * If we compared just "path", then we would expand more
258 + * than we should. Since every file at root is always
259 + * included, we would expand every directory at root at
260 + * least one level deep instead of using sparse directory
261 + * entries.
262 + */
263 + strbuf_addstr(base, path);
264 + strbuf_add(base, "/-", 2);
265 +
266 + if (path_matches_pattern_list(base->buf, base->len,
267 + NULL, &dtype,
268 + ctx->pl, ctx->write)) {
269 + strbuf_setlen(base, baselen);
270 + return READ_TREE_RECURSIVE;
271 + }
272
243 - ce = make_cache_entry(istate, mode, oid, base->buf, 0, 0);
273 + /*
274 + * The path "{base}{path}/" is a sparse directory. Create the correct
275 + * name for inserting the entry into the index.
276 + */
277 + strbuf_setlen(base, base->len - 1);
278 + } else {
279 + strbuf_addstr(base, path);
280 + }
281 +
282 + ce = make_cache_entry(ctx->write, mode, oid, base->buf, 0, 0);
283 ce->ce_flags |= CE_SKIP_WORKTREE | CE_EXTENDED;
245 - set_index_entry(istate, istate->cache_nr++, ce);
284 + set_index_entry(ctx->write, ctx->write->cache_nr++, ce);
285
286 strbuf_setlen(base, len);
287 return 0;
@@ -254,6 +293,7 @@ void expand_index(struct index_state *istate, struct pattern_list *pl)
293 struct index_state *full;
294 struct strbuf base = STRBUF_INIT;
295 const char *tr_region;
296 + struct modify_index_context ctx;
297
298 /*
299 * If the index is already full, then keep it full. We will convert
@@ -293,6 +333,9 @@ void expand_index(struct index_state *istate, struct pattern_list *pl)
333 full->cache_nr = 0;
334 ALLOC_ARRAY(full->cache, full->cache_alloc);
335
336 + ctx.write = full;
337 + ctx.pl = pl;
338 +
339 for (i = 0; i < istate->cache_nr; i++) {
340 struct cache_entry *ce = istate->cache[i];
341 struct tree *tree;
@@ -318,7 +361,7 @@ void expand_index(struct index_state *istate, struct pattern_list *pl)
361 strbuf_add(&base, ce->name, strlen(ce->name));
362
363 read_tree_at(istate->repo, tree, &base, &ps,
321 - add_path_to_index, full);
364 + add_path_to_index, &ctx);
365
366 /* free directory entries. full entries are re-used */
367 discard_cache_entry(ce);