Subject: [PATCH 3/8] ahead-behind: implement --ignore-missing option

From: "Derrick Stolee via GitGitGadget" <[email-masked]>
Date: Mon, 06 Mar 2023 14:06:33 +0000

From: Derrick Stolee <[email-masked]>

When parsing the tip revisions from the ahead-behind inputs, it is
important to check that those tips exist before adding them to the list
for computation. The previous change caused the builtin to return with
errors if the revisions could not be resolved.

However, when running 'git ahead-behind' in an environment with
concurrent edits, such as a Git server, then the references could be
deleted from underneath the caller between reading the reference list
and starting the 'git ahead-behind' process. Avoid this race by allowing
the caller to specify '--ignore-missing' and continue using the
information that is still available.

Co-authored-by: Taylor Blau <[email-masked]>
Signed-off-by: Taylor Blau <[email-masked]>
Signed-off-by: Derrick Stolee <[email-masked]>
---
 Documentation/git-ahead-behind.txt | 6 ++++++
 builtin/ahead-behind.c             | 8 +++++++-
 t/t4218-ahead-behind.sh            | 6 ++++++
 3 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-ahead-behind.txt b/Documentation/git-ahead-behind.txt
index 0e2f989a1a0..2dd5147f6b2 100644
--- a/Documentation/git-ahead-behind.txt
+++ b/Documentation/git-ahead-behind.txt
@@ -50,6 +50,12 @@ OPTIONS
 	Read revision tips and ranges from stdin instead of from the
 	command-line.
 
+--ignore-missing::
+	When parsing tip references, ignore any references that are not
+	found. This is useful when operating in an environment where a
+	reference could be deleted between reading the reference and
+	starting the `git ahead-behind` process.
+
 
 SEE ALSO
 --------
diff --git a/builtin/ahead-behind.c b/builtin/ahead-behind.c
index c1212cc8d46..e4f65fc0548 100644
--- a/builtin/ahead-behind.c
+++ b/builtin/ahead-behind.c
@@ -8,13 +8,18 @@ static const char * const ahead_behind_usage[] = {
 	NULL
 };
 
+static int ignore_missing;
+
 static int handle_arg(struct string_list *tips, const char *arg)
 {
 	struct string_list_item *item;
 	struct commit *c = lookup_commit_reference_by_name(arg);
 
-	if (!c)
+	if (!c) {
+		if (ignore_missing)
+			return 0;
 		return error(_("could not resolve '%s'"), arg);
+	}
 
 	item = string_list_append(tips, arg);
 	item->util = c;
@@ -30,6 +35,7 @@ int cmd_ahead_behind(int argc, const char **argv, const char *prefix)
 	struct option ahead_behind_opts[] = {
 		OPT_STRING('b', "base", &base_ref, N_("base"), N_("base reference to process")),
 		OPT_BOOL(0 , "stdin", &from_stdin, N_("read rev names from stdin")),
+		OPT_BOOL(0 , "ignore-missing", &ignore_missing, N_("ignore missing tip references")),
 		OPT_END()
 	};
 
diff --git a/t/t4218-ahead-behind.sh b/t/t4218-ahead-behind.sh
index 3b8b9dc9887..56f16515896 100755
--- a/t/t4218-ahead-behind.sh
+++ b/t/t4218-ahead-behind.sh
@@ -19,6 +19,12 @@ test_expect_success 'git ahead-behind with broken tip' '
 	grep "could not resolve '\''bogus'\''" err
 '
 
+test_expect_success 'git ahead-behind with broken tip and --ignore-missing' '
+	git ahead-behind --base=HEAD --ignore-missing bogus 2>err >out &&
+	test_must_be_empty err &&
+	test_must_be_empty out
+'
+
 test_expect_success 'git ahead-behind without tips' '
 	git ahead-behind --base=HEAD 2>err &&
 	test_must_be_empty err
-- 
gitgitgadget