[PATCH v2 0/1] rev-list: add --missing-only option to filter output
From: Siddharth Asthana <hidden>
Date: 2026-09-01 18:51:07
Hi, At GitLab, Gitaly uses rev-list --missing=print on partial clones to find missing objects. The output mixes present and missing objects and prefixes missing ones with '?', so we post-process it. --missing-only avoids that. v1 added --missing=print-only as a --missing= mode. Review preferred a separate filter flag, so v2 adds --missing-only next to --missing=print or --missing=print-info. Based on 1630431f32 (The 21st batch, 2026-08-31). Changes from v1 include: 1. Separate --missing-only flag instead of print-only mode (Phillip, Patrick, Stolee). 2. Require --missing=print or --missing=print-info. 3. Die on --count / --disk-usage (Stolee). 4. No enum comment spacing churn. 5. print-info still prints path=/type=; only '?' is dropped. 6. test_cmp-based tests (Phillip). 7. Link to v1: https://lore.kernel.org/git/20260419084840.33986-1-siddharthasthana31@gmail.com/ (local) Thanks, Siddharth --- Siddharth Asthana (1): rev-list: add --missing-only option to filter output Documentation/rev-list-options.adoc | 13 ++++++++ builtin/rev-list.c | 42 ++++++++++++++++++++++--- t/t6022-rev-list-missing.sh | 49 +++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 5 deletions(-) Range-diff versus v1: 1: 7e8f1ad997 ! 1: 3c89e6bc38 rev-list: add --missing=print-only mode @@ Metadata Author: Siddharth Asthana [off-list ref] ## Commit message ## - rev-list: add --missing=print-only mode + rev-list: add --missing-only option to filter output - When working with partial clones, it's common to want just the list of - missing objects. The current --missing=print mode does this but mixes - present and missing objects together, with missing ones prefixed by '?'. - Getting only the missing OIDs requires an extra pipe: + When working with partial clones, callers often need only the missing + object IDs. Today that means post-processing --missing=print to drop + present objects and strip the leading '?': git rev-list --objects --all --missing=print | perl -ne 'print if s/^[?]//' - Add --missing=print-only which outputs only the missing object OIDs, one - per line, without any prefix. This makes the above one-liner unnecessary - and the output directly usable by downstream tools. + Add --missing-only. Use it with --missing=print or --missing=print-info + to print only missing objects. --missing= still picks the format; + --missing-only only filters. The leading '?' is omitted. With + print-info, path= and type= are still shown. + + Require --missing=print or --missing=print-info. Reject --count and + --disk-usage. Signed-off-by: Siddharth Asthana [off-list ref] ## Documentation/rev-list-options.adoc ## -@@ Documentation/rev-list-options.adoc: Unexpected missing objects will raise an error. - The form `--missing=print` is like `allow-any`, but will also print a - list of the missing objects. Object IDs are prefixed with a ``?'' character. - + -+The form `--missing=print-only` is like `print`, but will print ONLY the -+missing objects (not the present ones), and without the ``?'' prefix. This -+is useful for scripting, as a simpler alternative to -+`--missing=print | sed -n 's/^?//p'`. +@@ Documentation/rev-list-options.adoc: If some tips passed to the traversal are missing, they will be + considered as missing too, and the traversal will ignore them. In case + we cannot get their Object ID though, an error will be raised. + ++`--missing-only`:: ++ When used together with `--missing=print` or `--missing=print-info`, ++ suppress all output for present objects and print only the missing ++ ones. The selected `--missing=` format is preserved (so ++ `--missing=print-info` still emits `path=` / `type=` fields), but the ++ leading ``?'' prefix used by the non-`-z` forms is omitted. This is ++ useful for scripting, as a simpler and faster alternative to ++ post-processing the output of `--missing=print`. ++ - The form `--missing=print-info` is like `print`, but will also print additional - information about the missing object inferred from its containing object. The - information is all printed on the same line with the missing object ID in the ++This option is incompatible with `--count` and `--disk-usage`. ++It is an error to use `--missing-only` without `--missing=print` or ++`--missing=print-info`. ++ + `--exclude-promisor-objects`:: + (For internal use only.) Prefilter object traversal at + promisor boundary. This is used with partial clone. This is ## builtin/rev-list.c ## -@@ builtin/rev-list.c: static void missing_objects_map_entry_free(void *e) - - static struct oidmap missing_objects; - enum missing_action { -- MA_ERROR = 0, /* fail if any missing objects are encountered */ -- MA_ALLOW_ANY, /* silently allow ALL missing objects */ -- MA_PRINT, /* print ALL missing objects in special section */ -- MA_PRINT_INFO, /* same as MA_PRINT but also prints missing object info */ -+ MA_ERROR = 0, /* fail if any missing objects are encountered */ -+ MA_ALLOW_ANY, /* silently allow ALL missing objects */ -+ MA_PRINT, /* print ALL missing objects in special section */ -+ MA_PRINT_INFO, /* same as MA_PRINT but also prints missing object info */ -+ MA_PRINT_ONLY, /* print ONLY missing objects, without the "?" prefix */ +@@ builtin/rev-list.c: enum missing_action { MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */ }; static enum missing_action arg_missing_action; - -+static inline int missing_action_prints(void) ++static int arg_missing_only; ++ ++static inline int should_collect_missing(void) +{ + return arg_missing_action == MA_PRINT || -+ arg_missing_action == MA_PRINT_INFO || -+ arg_missing_action == MA_PRINT_ONLY; ++ arg_missing_action == MA_PRINT_INFO; +} -+ + /* display only the oid of each object encountered */ static int arg_show_object_names = 1; - @@ builtin/rev-list.c: static void print_missing_object(struct missing_objects_map_entry *entry, { struct strbuf sb = STRBUF_INIT; - if (line_term) -+ if (arg_missing_action == MA_PRINT_ONLY) { ++ /* ++ * --missing-only filters present objects out of the walk output. ++ * It still uses the selected --missing= format for missing ones, ++ * except the human "?" prefix is omitted (script-friendly OIDs). ++ */ ++ if (arg_missing_only && line_term) + printf("%s", oid_to_hex(&entry->entry.oid)); -+ putchar(line_term); -+ return; -+ } else if (line_term) { ++ else if (line_term) printf("?%s", oid_to_hex(&entry->entry.oid)); -- else -+ } else { + else printf("%s%cmissing=yes", oid_to_hex(&entry->entry.oid), - info_term); -+ } - - if (!print_missing_info) { - putchar(line_term); -@@ builtin/rev-list.c: static inline void finish_object__ma(struct object *obj, const char *name) - - case MA_PRINT: - case MA_PRINT_INFO: -+ case MA_PRINT_ONLY: - add_missing_object_entry(&obj->oid, name, obj->type); - return; - @@ builtin/rev-list.c: static void show_commit(struct commit *commit, void *data) return; } -+ if (arg_missing_action == MA_PRINT_ONLY) { ++ if (arg_missing_only) { + finish_commit(commit); + return; + } @@ builtin/rev-list.c: static void show_object(struct object *obj, const char *name if (finish_object(obj, name, cb_data)) return; display_progress(progress, ++progress_counter); -+ if (arg_missing_action == MA_PRINT_ONLY) ++ if (arg_missing_only) + return; if (show_disk_usage) total_disk_usage += get_object_disk_usage(obj); if (info->flags & REV_LIST_QUIET) -@@ builtin/rev-list.c: static inline int parse_missing_action_value(const char *value) - return 1; +@@ builtin/rev-list.c: int cmd_rev_list(int argc, + revs.exclude_promisor_objects = 1; + } else if (skip_prefix(arg, "--missing=", &arg)) { + parse_missing_action_value(arg); ++ } else if (!strcmp(arg, "--missing-only")) { ++ arg_missing_only = 1; + } else if (!strcmp(arg, "-z")) { + line_term = '\0'; + info_term = '\0'; + } } -+ if (!strcmp(value, "print-only")) { -+ arg_missing_action = MA_PRINT_ONLY; -+ fetch_if_missing = 0; -+ return 1; -+ } ++ if (arg_missing_only && !should_collect_missing()) ++ die(_("--missing-only requires --missing=print or --missing=print-info")); ++ + die_for_incompatible_opt2(revs.exclude_promisor_objects, + "--exclude_promisor_objects", + arg_missing_action, "--missing"); +@@ builtin/rev-list.c: int cmd_rev_list(int argc, + continue; + } + ++ if (!strcmp(arg, "--missing-only")) ++ continue; ++ + usage(rev_list_usage); + + } +@@ builtin/rev-list.c: int cmd_rev_list(int argc, + (revs.left_right || revs.cherry_mark)) + die(_("marked counting and '%s' cannot be used together"), "--objects"); + ++ die_for_incompatible_opt2(arg_missing_only, "--missing-only", ++ revs.count, "--count"); ++ die_for_incompatible_opt2(arg_missing_only, "--missing-only", ++ show_disk_usage, "--disk-usage"); + - if (!strcmp(value, "allow-promisor")) { - arg_missing_action = MA_ALLOW_PROMISOR; - fetch_if_missing = 0; + save_commit_buffer = (revs.verbose_header || + revs.grep_filter.pattern_list || + revs.grep_filter.header_list); @@ builtin/rev-list.c: int cmd_rev_list(int argc, if (arg_print_omitted) oidset_init(&omitted_objects, DEFAULT_OIDSET_SIZE); - if (arg_missing_action == MA_PRINT || - arg_missing_action == MA_PRINT_INFO) { -+ if (missing_action_prints()) { ++ if (should_collect_missing()) { struct oidset_iter iter; struct object_id *oid; @@ builtin/rev-list.c: int cmd_rev_list(int argc, } - if (arg_missing_action == MA_PRINT || - arg_missing_action == MA_PRINT_INFO) { -+ if (missing_action_prints()) { ++ if (should_collect_missing()) { struct missing_objects_map_entry *entry; struct oidmap_iter iter; -@@ builtin/rev-list.c: int cmd_rev_list(int argc, - - stop_progress(&progress); - -- if (revs.count) { -+ if (revs.count && arg_missing_action != MA_PRINT_ONLY) { - if (revs.left_right && revs.cherry_mark) - printf("%d\t%d\t%d\n", revs.count_left, revs.count_right, revs.count_same); - else if (revs.left_right) -@@ builtin/rev-list.c: int cmd_rev_list(int argc, - printf("%d\n", revs.count_left + revs.count_right); - } - -- if (show_disk_usage) -+ if (show_disk_usage && arg_missing_action != MA_PRINT_ONLY) - print_disk_usage(total_disk_usage); - - cleanup: ## t/t6022-rev-list-missing.sh ## @@ t/t6022-rev-list-missing.sh: do @@ t/t6022-rev-list-missing.sh: do +for obj in "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t" +do -+ test_expect_success "rev-list --missing=print-only with missing $obj" ' ++ test_expect_success "rev-list --missing-only with missing $obj" ' + oid="$(git rev-parse $obj)" && + path=".git/objects/$(test_oid_to_path $oid)" && + -+ # Capture present OIDs before hiding anything. -+ git rev-list --objects --no-object-names HEAD ^$obj >present.raw && -+ + mv "$path" "$path.hidden" && + test_when_finished "mv $path.hidden $path" && + -+ git rev-list --missing=print-only --objects --no-object-names \ -+ HEAD >actual && -+ -+ # Only the missing OID should appear, without the "?" prefix. -+ grep "^$oid$" actual && ++ git rev-list --missing=print --missing-only --objects \ ++ --no-object-names HEAD >actual && + -+ # Present objects must NOT appear in the output. -+ while read present_oid -+ do -+ ! grep "^$present_oid$" actual || return 1 -+ done <present.raw ++ echo $oid >expect && ++ test_cmp expect actual + ' +done ++ ++test_expect_success "--missing-only requires --missing=print or --missing=print-info" ' ++ test_must_fail git rev-list --missing-only --objects HEAD 2>err && ++ test_grep "requires --missing=print" err ++' ++ ++test_expect_success "--missing-only is incompatible with --count" ' ++ test_must_fail git rev-list --missing=print --missing-only \ ++ --count --objects HEAD 2>err && ++ test_grep "cannot be used together" err ++' ++ ++test_expect_success "--missing-only is incompatible with --disk-usage" ' ++ test_must_fail git rev-list --missing=print --missing-only \ ++ --disk-usage --objects HEAD 2>err && ++ test_grep "cannot be used together" err ++' ++ ++test_expect_success "--missing-only works with --missing=print-info" ' ++ oid="$(git rev-parse HEAD:1.t)" && ++ path=".git/objects/$(test_oid_to_path $oid)" && ++ ++ mv "$path" "$path.hidden" && ++ test_when_finished "mv $path.hidden $path" && ++ ++ git rev-list --missing=print-info --missing-only --objects \ ++ --no-object-names HEAD >actual && ++ ++ # Filter keeps print-info fields; only the "?" prefix is dropped. ++ echo "$oid path=1.t type=blob" >expect && ++ test_cmp expect actual ++' + test_expect_success "-z nul-delimited --missing" ' test_when_finished rm -rf repo && base-commit: 1630431f326e15fcde608827b5ff38422528eb59