Subject: Re: [PATCH] ref-filter: share bases and is_base_tips between formatting and sorting

From: René Scharfe <[email-masked]>
Date: Sun, 19 Jan 2025 10:11:04 +0100

Am 18.01.25 um 18:11 schrieb René Scharfe:
> I think moving to the right place in one step requires less churn.  Let's
> compare; v2 coming up.

The version building on top of v1 would look like this:

---
 ref-filter.c | 48 ++++++++++++++++++++++++++++--------------------
 1 file changed, 28 insertions(+), 20 deletions(-)

diff --git a/ref-filter.c b/ref-filter.c
index 89f44936f9..caf28466ab 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -235,6 +235,9 @@ static struct used_atom {
 			enum { S_BARE, S_GRADE, S_SIGNER, S_KEY,
 			       S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL } option;
 		} signature;
+		struct {
+			struct commit *commit;
+		} base;
 		struct strvec describe_args;
 		struct refname_atom refname;
 		char *head;
@@ -242,9 +245,6 @@ static struct used_atom {
 } *used_atom;
 static int used_atom_cnt, need_tagged, need_symref;

-/* List of bases for ahead-behind counts. */
-static struct string_list bases = STRING_LIST_INIT_DUP;
-
 /* List of bases for is-base indicators. */
 static struct string_list is_base_tips = STRING_LIST_INIT_DUP;

@@ -898,17 +898,14 @@ static int rest_atom_parser(struct ref_format *format UNUSED,
 }

 static int ahead_behind_atom_parser(struct ref_format *format UNUSED,
-				    struct used_atom *atom UNUSED,
+				    struct used_atom *atom,
 				    const char *arg, struct strbuf *err)
 {
-	struct string_list_item *item;
-
 	if (!arg)
 		return strbuf_addf_ret(err, -1, _("expected format: %%(ahead-behind:<committish>)"));

-	item = string_list_append(&bases, arg);
-	item->util = lookup_commit_reference_by_name(arg);
-	if (!item->util)
+	atom->u.base.commit = lookup_commit_reference_by_name(arg);
+	if (!atom->u.base.commit)
 		die("failed to find '%s'", arg);

 	return 0;
@@ -3030,7 +3027,6 @@ void ref_array_clear(struct ref_array *array)
 	}
 	FREE_AND_NULL(used_atom);
 	used_atom_cnt = 0;
-	string_list_clear(&bases, 0);
 	string_list_clear(&is_base_tips, 0);

 	if (ref_to_worktree_map.worktrees) {
@@ -3095,18 +3091,27 @@ void filter_ahead_behind(struct repository *r,
 			 struct ref_array *array)
 {
 	struct commit **commits;
-	size_t commits_nr = bases.nr + array->nr;
+	size_t bases_nr, commits_nr;
+
+	if (!array->nr)
+		return;

-	if (!bases.nr || !array->nr)
+	for (size_t i = bases_nr = 0; i < used_atom_cnt; i++) {
+		if (used_atom[i].atom_type == ATOM_AHEADBEHIND)
+			bases_nr++;
+	}
+	if (!bases_nr)
 		return;

-	ALLOC_ARRAY(commits, commits_nr);
-	for (size_t i = 0; i < bases.nr; i++)
-		commits[i] = bases.items[i].util;
+	ALLOC_ARRAY(commits, st_add(bases_nr, array->nr));
+	for (size_t i = 0, j = 0; i < used_atom_cnt; i++) {
+		if (used_atom[i].atom_type == ATOM_AHEADBEHIND)
+			commits[j++] = used_atom[i].u.base.commit;
+	}

-	ALLOC_ARRAY(array->counts, st_mult(bases.nr, array->nr));
+	ALLOC_ARRAY(array->counts, st_mult(bases_nr, array->nr));

-	commits_nr = bases.nr;
+	commits_nr = bases_nr;
 	array->counts_nr = 0;
 	for (size_t i = 0; i < array->nr; i++) {
 		const char *name = array->items[i]->refname;
@@ -3115,8 +3120,8 @@ void filter_ahead_behind(struct repository *r,
 		if (!commits[commits_nr])
 			continue;

-		CALLOC_ARRAY(array->items[i]->counts, bases.nr);
-		for (size_t j = 0; j < bases.nr; j++) {
+		CALLOC_ARRAY(array->items[i]->counts, bases_nr);
+		for (size_t j = 0; j < bases_nr; j++) {
 			struct ahead_behind_count *count;
 			count = &array->counts[array->counts_nr++];
 			count->tip_index = commits_nr;
@@ -3282,9 +3287,12 @@ static inline int can_do_iterative_format(struct ref_filter *filter,
 	 * - filtering on reachability
 	 * - including ahead-behind information in the formatted output
 	 */
+	for (size_t i = 0; i < used_atom_cnt; i++) {
+		if (used_atom[i].atom_type == ATOM_AHEADBEHIND)
+			return 0;
+	}
 	return !(filter->reachable_from ||
 		 filter->unreachable_from ||
-		 bases.nr ||
 		 is_base_tips.nr);
 }

--
2.48.1