Skip to content

Commit

Permalink
rev-list: allow missing tips with --missing=[print|allow*]
Browse files Browse the repository at this point in the history
In 9830926 (rev-list: add commit object support in `--missing`
option, 2023-10-27) we fixed the `--missing` option in `git rev-list`
so that it works with with missing commits, not just blobs/trees.

Unfortunately, such a command would still fail with a "fatal: bad
object <oid>" if it is passed a missing commit, blob or tree as an
argument (before the rev walking even begins).

When such a command is used to find the dependencies of some objects,
for example the dependencies of quarantined objects (see the
"QUARANTINE ENVIRONMENT" section in the git-receive-pack(1)
documentation), it would be better if the command would instead
consider such missing objects, especially commits, in the same way as
other missing objects.

If, for example `--missing=print` is used, it would be nice for some
use cases if the missing tips passed as arguments were reported in
the same way as other missing objects instead of the command just
failing.

We could introduce a new option to make it work like this, but most
users are likely to prefer the command to have this behavior as the
default one. Introducing a new option would require another dumb loop
to look for that option early, which isn't nice.

Also we made `git rev-list` work with missing commits very recently
and the command is most often passed commits as arguments. So let's
consider this as a bug fix related to these recent changes.

While at it let's add a NEEDSWORK comment to say that we should get
rid of the existing ugly dumb loops that parse the
`--exclude-promisor-objects` and `--missing=...` options early.

Helped-by: Linus Arver <linusa@google.com>
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
chriscool authored and gitster committed Feb 14, 2024
1 parent 686101f commit 7b644c8
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 4 deletions.
4 changes: 4 additions & 0 deletions Documentation/rev-list-options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,10 @@ 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.
+
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.

--exclude-promisor-objects::
(For internal use only.) Prefilter object traversal at
Expand Down
18 changes: 17 additions & 1 deletion builtin/rev-list.c
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,18 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
*
* Let "--missing" to conditionally set fetch_if_missing.
*/
/*
* NEEDSWORK: These loops that attempt to find presence of
* options without understanding that the options they are
* skipping are broken (e.g., it would not know "--grep
* --exclude-promisor-objects" is not triggering
* "--exclude-promisor-objects" option). We really need
* setup_revisions() to have a mechanism to allow and disallow
* some sets of options for different commands (like rev-list,
* replay, etc). Such a mechanism should do an early parsing
* of options and be able to manage the `--missing=...` and
* `--exclude-promisor-objects` options below.
*/
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "--exclude-promisor-objects")) {
Expand Down Expand Up @@ -753,8 +765,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)

if (arg_print_omitted)
oidset_init(&omitted_objects, DEFAULT_OIDSET_SIZE);
if (arg_missing_action == MA_PRINT)
if (arg_missing_action == MA_PRINT) {
oidset_init(&missing_objects, DEFAULT_OIDSET_SIZE);
/* Add missing tips */
oidset_insert_from_set(&missing_objects, &revs.missing_commits);
oidset_clear(&revs.missing_commits);
}

traverse_commit_list_filtered(
&revs, show_commit, show_object, &info,
Expand Down
14 changes: 11 additions & 3 deletions revision.c
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,10 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
return NULL;
if (revs->exclude_promisor_objects && is_promisor_object(oid))
return NULL;
if (revs->do_not_die_on_missing_objects) {
oidset_insert(&revs->missing_commits, oid);
return NULL;
}
die("bad object %s", name);
}
object->flags |= flags;
Expand Down Expand Up @@ -1947,6 +1951,7 @@ void repo_init_revisions(struct repository *r,
init_display_notes(&revs->notes_opt);
list_objects_filter_init(&revs->filter);
init_ref_exclusions(&revs->ref_excludes);
oidset_init(&revs->missing_commits, 0);
}

static void add_pending_commit_list(struct rev_info *revs,
Expand Down Expand Up @@ -2178,13 +2183,18 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
if (revarg_opt & REVARG_COMMITTISH)
get_sha1_flags |= GET_OID_COMMITTISH;

/*
* Even if revs->do_not_die_on_missing_objects is set, we
* should error out if we can't even get an oid, as
* `--missing=print` should be able to report missing oids.
*/
if (get_oid_with_context(revs->repo, arg, get_sha1_flags, &oid, &oc))
return revs->ignore_missing ? 0 : -1;
if (!cant_be_filename)
verify_non_filename(revs->prefix, arg);
object = get_reference(revs, arg, &oid, flags ^ local_flags);
if (!object)
return revs->ignore_missing ? 0 : -1;
return (revs->ignore_missing || revs->do_not_die_on_missing_objects) ? 0 : -1;
add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
add_pending_object_with_path(revs, object, arg, oc.mode, oc.path);
free(oc.path);
Expand Down Expand Up @@ -3830,8 +3840,6 @@ int prepare_revision_walk(struct rev_info *revs)
FOR_EACH_OBJECT_PROMISOR_ONLY);
}

oidset_init(&revs->missing_commits, 0);

if (!revs->reflog_info)
prepare_to_use_bloom_filter(revs);
if (!revs->unsorted_input)
Expand Down
56 changes: 56 additions & 0 deletions t/t6022-rev-list-missing.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,60 @@ do
done
done

for missing_tip in "HEAD~1" "HEAD~1^{tree}" "HEAD:1.t"
do
# We want to check that things work when both
# - all the tips passed are missing (case existing_tip = ""), and
# - there is one missing tip and one existing tip (case existing_tip = "HEAD")
for existing_tip in "" "HEAD"
do
for action in "allow-any" "print"
do
test_expect_success "--missing=$action with tip '$missing_tip' missing and tip '$existing_tip'" '
oid="$(git rev-parse $missing_tip)" &&
path=".git/objects/$(test_oid_to_path $oid)" &&
# Before the object is made missing, we use rev-list to
# get the expected oids.
if test "$existing_tip" = "HEAD"
then
git rev-list --objects --no-object-names \
HEAD ^$missing_tip >expect.raw
else
>expect.raw
fi &&
# Blobs are shared by all commits, so even though a commit/tree
# might be skipped, its blob must be accounted for.
if test "$existing_tip" = "HEAD" && test $missing_tip != "HEAD:1.t"
then
echo $(git rev-parse HEAD:1.t) >>expect.raw &&
echo $(git rev-parse HEAD:2.t) >>expect.raw
fi &&
mv "$path" "$path.hidden" &&
test_when_finished "mv $path.hidden $path" &&
git rev-list --missing=$action --objects --no-object-names \
$oid $existing_tip >actual.raw &&
# When the action is to print, we should also add the missing
# oid to the expect list.
case $action in
allow-any)
;;
print)
grep ?$oid actual.raw &&
echo ?$oid >>expect.raw
;;
esac &&
sort actual.raw >actual &&
sort expect.raw >expect &&
test_cmp expect actual
'
done
done
done

test_done

0 comments on commit 7b644c8

Please sign in to comment.