From cc910442560e606546a328375c1394a982dfe8a6 Mon Sep 17 00:00:00 2001 From: Derrick Stolee Date: Tue, 22 Mar 2022 17:28:35 +0000 Subject: [PATCH 1/6] list-objects-filter: remove CL_ARG__FILTER We have established the command-line interface for the --[no-]filter options for a while now, so we do not need a helper to make this editable in the future. Signed-off-by: Derrick Stolee Signed-off-by: Junio C Hamano --- builtin/fetch-pack.c | 4 ++-- list-objects-filter-options.h | 5 +---- revision.c | 4 ++-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index c2d96f4c89..c4b9104f9b 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -153,11 +153,11 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) args.from_promisor = 1; continue; } - if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) { + if (skip_prefix(arg, ("--filter="), &arg)) { parse_list_objects_filter(&args.filter_options, arg); continue; } - if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) { + if (!strcmp(arg, ("--no-filter"))) { list_objects_filter_set_no_filter(&args.filter_options); continue; } diff --git a/list-objects-filter-options.h b/list-objects-filter-options.h index 2eb6c98394..90e4bc9625 100644 --- a/list-objects-filter-options.h +++ b/list-objects-filter-options.h @@ -69,9 +69,6 @@ struct list_objects_filter_options { */ }; -/* Normalized command line arguments */ -#define CL_ARG__FILTER "filter" - /* * Parse value of the argument to the "filter" keyword. * On the command line this looks like: @@ -111,7 +108,7 @@ int opt_parse_list_objects_filter(const struct option *opt, const char *arg, int unset); #define OPT_PARSE_LIST_OBJECTS_FILTER(fo) \ - OPT_CALLBACK(0, CL_ARG__FILTER, fo, N_("args"), \ + OPT_CALLBACK(0, "filter", fo, N_("args"), \ N_("object filtering"), \ opt_parse_list_objects_filter) diff --git a/revision.c b/revision.c index 2646b78990..7d435f8048 100644 --- a/revision.c +++ b/revision.c @@ -2691,9 +2691,9 @@ static int handle_revision_pseudo_opt(struct rev_info *revs, revs->no_walk = 0; } else if (!strcmp(arg, "--single-worktree")) { revs->single_worktree = 1; - } else if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) { + } else if (skip_prefix(arg, ("--filter="), &arg)) { parse_list_objects_filter(&revs->filter, arg); - } else if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) { + } else if (!strcmp(arg, ("--no-filter"))) { list_objects_filter_set_no_filter(&revs->filter); } else { return 0; From 80f6de4f5bd43631a9dabc9112bf80c987763035 Mon Sep 17 00:00:00 2001 From: Derrick Stolee Date: Tue, 22 Mar 2022 17:28:36 +0000 Subject: [PATCH 2/6] pack-objects: move revs out of get_object_list() We intend to parse the --filter option directly into revs.filter, but we first need to move the 'revs' variable out of get_object_list() and pass it as a pointer instead. This change only deals with the issues of making that work. Signed-off-by: Derrick Stolee Signed-off-by: Junio C Hamano --- builtin/pack-objects.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 829ca359cf..f29bef9d0b 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -3714,9 +3714,8 @@ static void mark_bitmap_preferred_tips(void) } } -static void get_object_list(int ac, const char **av) +static void get_object_list(struct rev_info *revs, int ac, const char **av) { - struct rev_info revs; struct setup_revision_opt s_r_opt = { .allow_exclude_promisor_objects = 1, }; @@ -3724,10 +3723,10 @@ static void get_object_list(int ac, const char **av) int flags = 0; int save_warning; - repo_init_revisions(the_repository, &revs, NULL); + repo_init_revisions(the_repository, revs, NULL); save_commit_buffer = 0; - setup_revisions(ac, av, &revs, &s_r_opt); - list_objects_filter_copy(&revs.filter, &filter_options); + setup_revisions(ac, av, revs, &s_r_opt); + list_objects_filter_copy(&revs->filter, &filter_options); /* make sure shallows are read */ is_repository_shallow(the_repository); @@ -3757,13 +3756,13 @@ static void get_object_list(int ac, const char **av) } die(_("not a rev '%s'"), line); } - if (handle_revision_arg(line, &revs, flags, REVARG_CANNOT_BE_FILENAME)) + if (handle_revision_arg(line, revs, flags, REVARG_CANNOT_BE_FILENAME)) die(_("bad revision '%s'"), line); } warn_on_object_refname_ambiguity = save_warning; - if (use_bitmap_index && !get_object_list_from_bitmap(&revs)) + if (use_bitmap_index && !get_object_list_from_bitmap(revs)) return; if (use_delta_islands) @@ -3772,24 +3771,24 @@ static void get_object_list(int ac, const char **av) if (write_bitmap_index) mark_bitmap_preferred_tips(); - if (prepare_revision_walk(&revs)) + if (prepare_revision_walk(revs)) die(_("revision walk setup failed")); - mark_edges_uninteresting(&revs, show_edge, sparse); + mark_edges_uninteresting(revs, show_edge, sparse); if (!fn_show_object) fn_show_object = show_object; - traverse_commit_list(&revs, + traverse_commit_list(revs, show_commit, fn_show_object, NULL); if (unpack_unreachable_expiration) { - revs.ignore_missing_links = 1; - if (add_unseen_recent_objects_to_traversal(&revs, + revs->ignore_missing_links = 1; + if (add_unseen_recent_objects_to_traversal(revs, unpack_unreachable_expiration)) die(_("unable to add recent objects")); - if (prepare_revision_walk(&revs)) + if (prepare_revision_walk(revs)) die(_("revision walk setup failed")); - traverse_commit_list(&revs, record_recent_commit, + traverse_commit_list(revs, record_recent_commit, record_recent_object, NULL); } @@ -3872,6 +3871,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) int rev_list_index = 0; int stdin_packs = 0; struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; + struct rev_info revs; + struct option pack_objects_options[] = { OPT_SET_INT('q', "quiet", &progress, N_("do not show progress meter"), 0), @@ -3976,6 +3977,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) read_replace_refs = 0; + repo_init_revisions(the_repository, &revs, NULL); + sparse = git_env_bool("GIT_TEST_PACK_SPARSE", -1); if (the_repository->gitdir) { prepare_repo_settings(the_repository); @@ -4154,7 +4157,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) } else if (!use_internal_rev_list) { read_object_list_from_stdin(); } else { - get_object_list(rp.nr, rp.v); + get_object_list(&revs, rp.nr, rp.v); } cleanup_preferred_base(); if (include_tag && nr_result) From 831ee253b7e07dbca3daafcc9cf9143e53220df7 Mon Sep 17 00:00:00 2001 From: Derrick Stolee Date: Tue, 22 Mar 2022 17:28:37 +0000 Subject: [PATCH 3/6] pack-objects: parse --filter directly into revs.filter The previous change moved the 'revs' variable into cmd_pack_objects() and now we can remove the global filter_options in favor of revs.filter. Signed-off-by: Derrick Stolee Signed-off-by: Junio C Hamano --- builtin/pack-objects.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index f29bef9d0b..d39f668ad5 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -237,8 +237,6 @@ static unsigned long cache_max_small_delta_size = 1000; static unsigned long window_memory_limit = 0; -static struct list_objects_filter_options filter_options; - static struct string_list uri_protocols = STRING_LIST_INIT_NODUP; enum missing_action { @@ -3723,10 +3721,8 @@ static void get_object_list(struct rev_info *revs, int ac, const char **av) int flags = 0; int save_warning; - repo_init_revisions(the_repository, revs, NULL); save_commit_buffer = 0; setup_revisions(ac, av, revs, &s_r_opt); - list_objects_filter_copy(&revs->filter, &filter_options); /* make sure shallows are read */ is_repository_shallow(the_repository); @@ -3958,7 +3954,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) &write_bitmap_index, N_("write a bitmap index if possible"), WRITE_BITMAP_QUIET, PARSE_OPT_HIDDEN), - OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), + OPT_PARSE_LIST_OBJECTS_FILTER(&revs.filter), OPT_CALLBACK_F(0, "missing", NULL, N_("action"), N_("handling for missing objects"), PARSE_OPT_NONEG, option_parse_missing_action), @@ -4080,7 +4076,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (!rev_list_all || !rev_list_reflog || !rev_list_index) unpack_unreachable_expiration = 0; - if (filter_options.choice) { + if (revs.filter.choice) { if (!pack_to_stdout) die(_("cannot use --filter without --stdout")); if (stdin_packs) From 017303eb483c48515095abcabf024101951f82ae Mon Sep 17 00:00:00 2001 From: Derrick Stolee Date: Tue, 22 Mar 2022 17:28:38 +0000 Subject: [PATCH 4/6] bundle: move capabilities to end of 'verify' The 'filter' capability was added in 105c6f14a (bundle: parse filter capability, 2022-03-09), but was added in a strange place in the 'git bundle verify' output. The tests for this show output like the following: The bundle contains these 2 refs: The bundle uses this filter: blob:none The bundle records a complete history. This looks very odd if we have a thin bundle that contains boundary commits instead of a complete history: The bundle contains these 2 refs: The bundle uses this filter: blob:none The bundle requires these 2 refs: This separation between tip refs and boundary refs is unfortunate. Move the filter capability output to the end of the output. Update the documentation to match. Signed-off-by: Derrick Stolee Signed-off-by: Junio C Hamano --- Documentation/git-bundle.txt | 10 +++++----- bundle.c | 9 ++++----- t/t6020-bundle-misc.sh | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt index ac4c4352aa..7685b57045 100644 --- a/Documentation/git-bundle.txt +++ b/Documentation/git-bundle.txt @@ -75,11 +75,11 @@ verify :: cleanly to the current repository. This includes checks on the bundle format itself as well as checking that the prerequisite commits exist and are fully linked in the current repository. - Information about additional capabilities, such as "object filter", - is printed. See "Capabilities" in link:technical/bundle-format.html - for more information. Finally, 'git bundle' prints a list of - missing commits, if any. The exit code is zero for success, but - will be nonzero if the bundle file is invalid. + Then, 'git bundle' prints a list of missing commits, if any. + Finally, information about additional capabilities, such as "object + filter", is printed. See "Capabilities" in link:technical/bundle-format.html + for more information. The exit code is zero for success, but will + be nonzero if the bundle file is invalid. list-heads :: Lists the references defined in the bundle. If followed by a diff --git a/bundle.c b/bundle.c index e359370cfc..276b55f8ce 100644 --- a/bundle.c +++ b/bundle.c @@ -267,11 +267,6 @@ int verify_bundle(struct repository *r, (uintmax_t)r->nr); list_refs(r, 0, NULL); - if (header->filter.choice) { - printf_ln("The bundle uses this filter: %s", - list_objects_filter_spec(&header->filter)); - } - r = &header->prerequisites; if (!r->nr) { printf_ln(_("The bundle records a complete history.")); @@ -282,6 +277,10 @@ int verify_bundle(struct repository *r, (uintmax_t)r->nr); list_refs(r, 0, NULL); } + + if (header->filter.choice) + printf_ln("The bundle uses this filter: %s", + list_objects_filter_spec(&header->filter)); } return ret; } diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh index ed95d19542..c4ab1367af 100755 --- a/t/t6020-bundle-misc.sh +++ b/t/t6020-bundle-misc.sh @@ -510,8 +510,8 @@ do refs/tags/v2 refs/tags/v3 HEAD - The bundle uses this filter: $filter The bundle records a complete history. + The bundle uses this filter: $filter EOF test_cmp expect actual && From 8ba221e2453658f8843176cc00fc5cfe36e07b41 Mon Sep 17 00:00:00 2001 From: Derrick Stolee Date: Tue, 22 Mar 2022 17:28:39 +0000 Subject: [PATCH 5/6] bundle: output hash information in 'verify' The previous change moved the 'filter' capability to the end of the 'git bundle verify' output. Now, add the 'object-format' capability to the output, when it exists. This change makes 'git bundle verify' output the hash used in all cases, even if the capability is not in the bundle. This means that v2 bundles will always output that they use "sha1". This might look noisy to some users, but it does simplify the implementation and the test strategy for this feature. Since 'verify' ends early when a prerequisite commit is missing, we need to insert this hash message carefully into our expected test output throughout t6020. Signed-off-by: Derrick Stolee Signed-off-by: Junio C Hamano --- bundle.c | 2 ++ t/t6020-bundle-misc.sh | 24 +++++++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/bundle.c b/bundle.c index 276b55f8ce..d50cfb5aa7 100644 --- a/bundle.c +++ b/bundle.c @@ -278,6 +278,8 @@ int verify_bundle(struct repository *r, list_refs(r, 0, NULL); } + printf_ln("The bundle uses this hash algorithm: %s", + header->hash_algo->name); if (header->filter.choice) printf_ln("The bundle uses this filter: %s", list_objects_filter_spec(&header->filter)); diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh index c4ab1367af..833205125a 100755 --- a/t/t6020-bundle-misc.sh +++ b/t/t6020-bundle-misc.sh @@ -122,6 +122,8 @@ format_and_save_expect () { sed -e 's/Z$//' >expect } +HASH_MESSAGE="The bundle uses this hash algorithm: $GIT_DEFAULT_HASH" + # (C) (D, pull/1/head, topic/1) # o --- o # / \ (L) @@ -194,11 +196,12 @@ test_expect_success 'create bundle from special rev: main^!' ' git bundle verify special-rev.bdl | make_user_friendly_and_stable_output >actual && - format_and_save_expect <<-\EOF && + format_and_save_expect <<-EOF && The bundle contains this ref: refs/heads/main The bundle requires this ref: Z + $HASH_MESSAGE EOF test_cmp expect actual && @@ -215,12 +218,13 @@ test_expect_success 'create bundle with --max-count option' ' git bundle verify max-count.bdl | make_user_friendly_and_stable_output >actual && - format_and_save_expect <<-\EOF && + format_and_save_expect <<-EOF && The bundle contains these 2 refs: refs/heads/main refs/tags/v1 The bundle requires this ref: Z + $HASH_MESSAGE EOF test_cmp expect actual && @@ -240,7 +244,7 @@ test_expect_success 'create bundle with --since option' ' git bundle verify since.bdl | make_user_friendly_and_stable_output >actual && - format_and_save_expect <<-\EOF && + format_and_save_expect <<-EOF && The bundle contains these 5 refs: refs/heads/main refs/heads/release @@ -250,6 +254,7 @@ test_expect_success 'create bundle with --since option' ' The bundle requires these 2 refs: Z Z + $HASH_MESSAGE EOF test_cmp expect actual && @@ -267,11 +272,12 @@ test_expect_success 'create bundle 1 - no prerequisites' ' EOF git bundle create stdin-1.bdl --stdin expect <<-\EOF && + format_and_save_expect <<-EOF && The bundle contains these 2 refs: refs/heads/topic/1 refs/heads/topic/2 The bundle records a complete history. + $HASH_MESSAGE EOF # verify bundle, which has no prerequisites @@ -308,13 +314,14 @@ test_expect_success 'create bundle 2 - has prerequisites' ' --stdin \ release refs/heads/release The bundle requires these 3 refs: Z Z Z + $HASH_MESSAGE EOF git bundle verify 2.bdl | @@ -367,13 +374,14 @@ test_expect_success 'create bundle 3 - two refs, same object' ' --stdin \ main HEAD refs/heads/main HEAD The bundle requires these 2 refs: Z Z + $HASH_MESSAGE EOF git bundle verify 3.bdl | @@ -409,12 +417,13 @@ test_expect_success 'create bundle 4 - with tags' ' --stdin \ --all expect <<-\EOF && + cat >expect <<-EOF && The bundle contains these 3 refs: refs/tags/v1 refs/tags/v2 refs/tags/v3 The bundle records a complete history. + $HASH_MESSAGE EOF git bundle verify 4.bdl | @@ -511,6 +520,7 @@ do refs/tags/v3 HEAD The bundle records a complete history. + $HASH_MESSAGE The bundle uses this filter: $filter EOF test_cmp expect actual && From 5cb28270a1ff94a0a23e67b479bbbec3bc993518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Mon, 28 Mar 2022 17:43:18 +0200 Subject: [PATCH 6/6] pack-objects: lazily set up "struct rev_info", don't leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the preceding [1] (pack-objects: move revs out of get_object_list(), 2022-03-22) the "repo_init_revisions()" was moved to cmd_pack_objects() so that it unconditionally took place for all invocations of "git pack-objects". We'd thus start leaking memory, which is easily reproduced in e.g. git.git by feeding e83c5163316 (Initial revision of "git", the information manager from hell, 2005-04-07) to "git pack-objects"; $ echo e83c5163316f89bfbde7d9ab23ca2e25604af290 | ./git pack-objects initial [...] ==19130==ERROR: LeakSanitizer: detected memory leaks Direct leak of 7120 byte(s) in 1 object(s) allocated from: #0 0x455308 in __interceptor_malloc (/home/avar/g/git/git+0x455308) #1 0x75b399 in do_xmalloc /home/avar/g/git/wrapper.c:41:8 #2 0x75b356 in xmalloc /home/avar/g/git/wrapper.c:62:9 #3 0x5d7609 in prep_parse_options /home/avar/g/git/diff.c:5647:2 #4 0x5d415a in repo_diff_setup /home/avar/g/git/diff.c:4621:2 #5 0x6dffbb in repo_init_revisions /home/avar/g/git/revision.c:1853:2 #6 0x4f599d in cmd_pack_objects /home/avar/g/git/builtin/pack-objects.c:3980:2 #7 0x4592ca in run_builtin /home/avar/g/git/git.c:465:11 #8 0x457d81 in handle_builtin /home/avar/g/git/git.c:718:3 #9 0x458ca5 in run_argv /home/avar/g/git/git.c:785:4 #10 0x457b40 in cmd_main /home/avar/g/git/git.c:916:19 #11 0x562259 in main /home/avar/g/git/common-main.c:56:11 #12 0x7fce792ac7ec in __libc_start_main csu/../csu/libc-start.c:332:16 #13 0x4300f9 in _start (/home/avar/g/git/git+0x4300f9) SUMMARY: LeakSanitizer: 7120 byte(s) leaked in 1 allocation(s). Aborted Narrowly fixing that commit would have been easy, just add call repo_init_revisions() right before get_object_list(), which is effectively what was done before that commit. But an unstated constraint when setting it up early is that it was needed for the subsequent [2] (pack-objects: parse --filter directly into revs.filter, 2022-03-22), i.e. we might have a --filter command-line option, and need to either have the "struct rev_info" setup when we encounter that option, or later. Let's just change the control flow so that we'll instead set up the "struct rev_info" only when we need it. Doing so leads to a bit more verbosity, but it's a lot clearer what we're doing and why. An earlier version of this commit[3] went behind opt_parse_list_objects_filter()'s back by faking up a "struct option" before calling it. Let's avoid that and instead create a blessed API for this pattern. We could furthermore combine the two get_object_list() invocations here by having repo_init_revisions() invoked on &pfd.revs, but I think clearly separating the two makes the flow clearer. Likewise redundantly but explicitly (i.e. redundant v.s. a "{ 0 }") "0" to "have_revs" early in cmd_pack_objects(). While we're at it add parentheses around the arguments to the OPT_* macros in in list-objects-filter-options.h, as we need to change those lines anyway. It doesn't matter in this case, but is good general practice. 1. https://lore.kernel.org/git/619b757d98465dbc4995bdc11a5282fbfcbd3daa.1647970119.git.gitgitgadget@gmail.com 2. https://lore.kernel.org/git/97de926904988b89b5663bd4c59c011a1723a8f5.1647970119.git.gitgitgadget@gmail.com 3. https://lore.kernel.org/git/patch-1.1-193534b0f07-20220325T121715Z-avarab@gmail.com/ Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/pack-objects.c | 28 +++++++++++++++++++++++----- list-objects-filter-options.c | 4 ++++ list-objects-filter-options.h | 24 +++++++++++++++++++++--- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index d39f668ad5..09680fb6a8 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -3857,6 +3857,21 @@ static int option_parse_unpack_unreachable(const struct option *opt, return 0; } +struct po_filter_data { + unsigned have_revs:1; + struct rev_info revs; +}; + +static struct list_objects_filter_options *po_filter_revs_init(void *value) +{ + struct po_filter_data *data = value; + + repo_init_revisions(the_repository, &data->revs, NULL); + data->have_revs = 1; + + return &data->revs.filter; +} + int cmd_pack_objects(int argc, const char **argv, const char *prefix) { int use_internal_rev_list = 0; @@ -3867,7 +3882,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) int rev_list_index = 0; int stdin_packs = 0; struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; - struct rev_info revs; + struct po_filter_data pfd = { .have_revs = 0 }; struct option pack_objects_options[] = { OPT_SET_INT('q', "quiet", &progress, @@ -3954,7 +3969,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) &write_bitmap_index, N_("write a bitmap index if possible"), WRITE_BITMAP_QUIET, PARSE_OPT_HIDDEN), - OPT_PARSE_LIST_OBJECTS_FILTER(&revs.filter), + OPT_PARSE_LIST_OBJECTS_FILTER_INIT(&pfd, po_filter_revs_init), OPT_CALLBACK_F(0, "missing", NULL, N_("action"), N_("handling for missing objects"), PARSE_OPT_NONEG, option_parse_missing_action), @@ -3973,8 +3988,6 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) read_replace_refs = 0; - repo_init_revisions(the_repository, &revs, NULL); - sparse = git_env_bool("GIT_TEST_PACK_SPARSE", -1); if (the_repository->gitdir) { prepare_repo_settings(the_repository); @@ -4076,7 +4089,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (!rev_list_all || !rev_list_reflog || !rev_list_index) unpack_unreachable_expiration = 0; - if (revs.filter.choice) { + if (pfd.have_revs && pfd.revs.filter.choice) { if (!pack_to_stdout) die(_("cannot use --filter without --stdout")); if (stdin_packs) @@ -4152,7 +4165,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) add_unreachable_loose_objects(); } else if (!use_internal_rev_list) { read_object_list_from_stdin(); + } else if (pfd.have_revs) { + get_object_list(&pfd.revs, rp.nr, rp.v); } else { + struct rev_info revs; + + repo_init_revisions(the_repository, &revs, NULL); get_object_list(&revs, rp.nr, rp.v); } cleanup_preferred_base(); diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c index f02d8df142..4b25287886 100644 --- a/list-objects-filter-options.c +++ b/list-objects-filter-options.c @@ -285,6 +285,10 @@ int opt_parse_list_objects_filter(const struct option *opt, const char *arg, int unset) { struct list_objects_filter_options *filter_options = opt->value; + opt_lof_init init = (opt_lof_init)opt->defval; + + if (init) + filter_options = init(opt->value); if (unset || !arg) list_objects_filter_set_no_filter(filter_options); diff --git a/list-objects-filter-options.h b/list-objects-filter-options.h index 90e4bc9625..ffc02d77e7 100644 --- a/list-objects-filter-options.h +++ b/list-objects-filter-options.h @@ -104,13 +104,31 @@ void parse_list_objects_filter( struct list_objects_filter_options *filter_options, const char *arg); +/** + * The opt->value to opt_parse_list_objects_filter() is either a + * "struct list_objects_filter_option *" when using + * OPT_PARSE_LIST_OBJECTS_FILTER(). + * + * Or, if using no "struct option" field is used by the callback, + * except the "defval" which is expected to be an "opt_lof_init" + * function, which is called with the "opt->value" and must return a + * pointer to the ""struct list_objects_filter_option *" to be used. + * + * The OPT_PARSE_LIST_OBJECTS_FILTER_INIT() can be used e.g. the + * "struct list_objects_filter_option" is embedded in a "struct + * rev_info", which the "defval" could be tasked with lazily + * initializing. See cmd_pack_objects() for an example. + */ int opt_parse_list_objects_filter(const struct option *opt, const char *arg, int unset); +typedef struct list_objects_filter_options *(*opt_lof_init)(void *); +#define OPT_PARSE_LIST_OBJECTS_FILTER_INIT(fo, init) \ + { OPTION_CALLBACK, 0, "filter", (fo), N_("args"), \ + N_("object filtering"), 0, opt_parse_list_objects_filter, \ + (intptr_t)(init) } #define OPT_PARSE_LIST_OBJECTS_FILTER(fo) \ - OPT_CALLBACK(0, "filter", fo, N_("args"), \ - N_("object filtering"), \ - opt_parse_list_objects_filter) + OPT_PARSE_LIST_OBJECTS_FILTER_INIT((fo), NULL) /* * Translates abbreviated numbers in the filter's filter_spec into their