archive: load index before pathspec checks

git archive checks whether pathspec arguments match anything to avoid surprises due to typos and later loads the index to get attributes. This order was OK when these features were introduced by ba053ea96c (archive: do not read .gitattributes in working directory, 2009-04-18) and d5f53d6d6f (archive: complain about path specs that don't match anything, 2009-12-12). But when attribute matching was added to pathspec in b0db704652 (pathspec: allow querying for attributes, 2017-03-13), the pathspec checker in git archive did not support it fully, because it lacks the attributes from the index. Load the index earlier, before the pathspec check, to support attr pathspecs. Reported-by: Ronan Pigott <ronan@rjp.ie> Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

René Scharfe committed Sep 21, 2024 at 22:23 UTC 296743a7caf99f816cec4e6992690151fdfcdcd5
2 files changed +32 -19
archive.c
+20 -19
@@ -304,8 +304,6 @@ int write_archive_entries(struct archiver_args *args,
304 write_archive_entry_fn_t write_entry)
305 {
306 struct archiver_context context;
307 - struct unpack_trees_options opts;
308 - struct tree_desc t;
307 int err;
308 struct strbuf path_in_archive = STRBUF_INIT;
309 struct strbuf content = STRBUF_INIT;
@@ -331,23 +329,6 @@ int write_archive_entries(struct archiver_args *args,
329 context.args = args;
330 context.write_entry = write_entry;
331
334 - /*
335 - * Setup index and instruct attr to read index only
336 - */
337 - if (!args->worktree_attributes) {
338 - memset(&opts, 0, sizeof(opts));
339 - opts.index_only = 1;
340 - opts.head_idx = -1;
341 - opts.src_index = args->repo->index;
342 - opts.dst_index = args->repo->index;
343 - opts.fn = oneway_merge;
344 - init_tree_desc(&t, &args->tree->object.oid,
345 - args->tree->buffer, args->tree->size);
346 - if (unpack_trees(1, &t, &opts))
347 - return -1;
348 - git_attr_set_direction(GIT_ATTR_INDEX);
349 - }
350 -
332 err = read_tree(args->repo, args->tree,
333 &args->pathspec,
334 queue_or_write_archive_entry,
@@ -540,6 +521,26 @@ static void parse_treeish_arg(const char **argv,
521 if (!tree)
522 die(_("not a tree object: %s"), oid_to_hex(&oid));
523
524 + /*
525 + * Setup index and instruct attr to read index only
526 + */
527 + if (!ar_args->worktree_attributes) {
528 + struct unpack_trees_options opts;
529 + struct tree_desc t;
530 +
531 + memset(&opts, 0, sizeof(opts));
532 + opts.index_only = 1;
533 + opts.head_idx = -1;
534 + opts.src_index = ar_args->repo->index;
535 + opts.dst_index = ar_args->repo->index;
536 + opts.fn = oneway_merge;
537 + init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size);
538 + if (unpack_trees(1, &t, &opts))
539 + die(_("unable to checkout working tree"));
540 +
541 + git_attr_set_direction(GIT_ATTR_INDEX);
542 + }
543 +
544 ar_args->refname = ref;
545 ar_args->tree = tree;
546 ar_args->commit_oid = commit_oid;
t/t5000-tar-tree.sh
+12
@@ -136,6 +136,8 @@ test_expect_success 'end-of-options is correctly eaten' '
136
137 test_expect_success 'populate workdir' '
138 mkdir a &&
139 + echo "a files_named_a" >.gitattributes &&
140 + git add .gitattributes &&
141 echo simple textfile >a/a &&
142 ten=0123456789 &&
143 hundred="$ten$ten$ten$ten$ten$ten$ten$ten$ten$ten" &&
@@ -449,6 +451,16 @@ test_expect_success 'allow pathspecs that resolve to the current directory' '
451 test_cmp expect actual
452 '
453
454 +test_expect_success 'attr pathspec in bare repo' '
455 + test_expect_code 0 git --git-dir=bare.git archive -v HEAD \
456 + ":(attr:files_named_a)" >/dev/null 2>actual &&
457 + cat >expect <<-\EOF &&
458 + a/
459 + a/a
460 + EOF
461 + test_cmp expect actual
462 +'
463 +
464 # Pull the size and date of each entry in a tarfile using the system tar.
465 #
466 # We'll pull out only the year from the date; that avoids any question of