Merge branch 'sa/rev-list-missing-only' The git rev-list command has been augmented with a '--missing-only' option that filters the output to only show missing objects, stripping the leading '?' character and suppressing present objects, which is useful when used in combination with '--missing=print' or '--missing=print-info'. * sa/rev-list-missing-only: rev-list: add --missing-only option to filter output
diff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc index fd831f0..bd9f345 100644 --- a/Documentation/rev-list-options.adoc +++ b/Documentation/rev-list-options.adoc
@@ -1083,6 +1083,19 @@ 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`. ++ +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
diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 6b59623..0faa833 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c
@@ -111,6 +111,13 @@ enum missing_action { MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */ }; static enum missing_action arg_missing_action; +static int arg_missing_only; + +static inline int should_collect_missing(void) +{ + return arg_missing_action == MA_PRINT || + arg_missing_action == MA_PRINT_INFO; +} /* display only the oid of each object encountered */ static int arg_show_object_names = 1; @@ -156,7 +163,14 @@ static void print_missing_object(struct missing_objects_map_entry *entry, { struct strbuf sb = STRBUF_INIT; - if (line_term) + /* + * --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)); + else if (line_term) printf("?%s", oid_to_hex(&entry->entry.oid)); else printf("%s%cmissing=yes", oid_to_hex(&entry->entry.oid), @@ -246,6 +260,11 @@ static void show_commit(struct commit *commit, void *data) return; } + if (arg_missing_only) { + finish_commit(commit); + return; + } + if (show_disk_usage) total_disk_usage += get_object_disk_usage(&commit->object); @@ -384,6 +403,8 @@ static void show_object(struct object *obj, const char *name, void *cb_data) if (finish_object(obj, name, cb_data)) return; display_progress(progress, ++progress_counter); + if (arg_missing_only) + return; if (show_disk_usage) total_disk_usage += get_object_disk_usage(obj); if (info->flags & REV_LIST_QUIET) @@ -750,12 +771,17 @@ int cmd_rev_list(int argc, revs.exclude_promisor_objects = 1; } else if (skip_prefix(arg, "--missing=", &arg)) { parse_missing_action_value(repo, arg); + } else if (!strcmp(arg, "--missing-only")) { + arg_missing_only = 1; } else if (!strcmp(arg, "-z")) { line_term = '\0'; info_term = '\0'; } } + 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"); @@ -865,6 +891,9 @@ int cmd_rev_list(int argc, continue; } + if (!strcmp(arg, "--missing-only")) + continue; + usage(rev_list_usage); } @@ -911,6 +940,11 @@ 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"); + save_commit_buffer = (revs.verbose_header || revs.grep_filter.pattern_list || revs.grep_filter.header_list); @@ -968,8 +1002,7 @@ 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 (should_collect_missing()) { struct oidset_iter iter; struct object_id *oid; @@ -995,8 +1028,7 @@ int cmd_rev_list(int argc, printf("~%s\n", oid_to_hex(oid)); oidset_clear(&omitted_objects); } - if (arg_missing_action == MA_PRINT || - arg_missing_action == MA_PRINT_INFO) { + if (should_collect_missing()) { struct missing_objects_map_entry *entry; struct oidmap_iter iter;
diff --git a/t/t6022-rev-list-missing.sh b/t/t6022-rev-list-missing.sh index 1e472a4..1bd2c3b 100755 --- a/t/t6022-rev-list-missing.sh +++ b/t/t6022-rev-list-missing.sh
@@ -198,6 +198,55 @@ ' done +for obj in "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t" +do + test_expect_success "rev-list --missing-only with missing $obj" ' + oid="$(git rev-parse $obj)" && + path=".git/objects/$(test_oid_to_path $oid)" && + + mv "$path" "$path.hidden" && + test_when_finished "mv $path.hidden $path" && + + git rev-list --missing=print --missing-only --objects \ + --no-object-names HEAD >actual && + + 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 &&