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/
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 <siddharthasthana31@gmail.com>
## 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 <siddharthasthana31@gmail.com>
## 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