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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



verify_ref_format() parses a ref-filter format string and stores
recognized items in the static array "used_atom".  For
"ahead-behind:<committish>" and "is-base:<committish>" it stores the
committish part in string_lists that are part of struct ref_format.

ref_sorting_options() also parses bare ref-filter format items and also
stores recognized ones in "used_atom".  The committish parts go to a
dummy struct ref_format in parse_sorting_atom(), though, and are leaked
and forgotten.

If verify_ref_format() is called before ref_sorting_options(), like in
git for-each-ref, then all works well if the sort key is included in the
format string.  If it isn't then sorting cannot work as the committishes
are missing.

If ref_sorting_options() is called first, like in git branch, then we
have the additional issue that if the sort key is included in the format
string then filter_ahead_behind() and filter_is_base() can't see their
committishes, will not generate any results for them and thus they will
for expanded to empty strings.

Fix those issues by making the string_lists static, like their sibling
"used_atom".  This way they can all be shared for handling both
ref-filter format strings and sorting options in the same command.
And since struct ref_format no longer contains any allocated members,
remove the now unnecessary ref_format_init() and ref_format_clear().

Reported-by: Ross Goldberg <ross.goldberg@xxxxxxxxx>
Signed-off-by: René Scharfe <l.s.r@xxxxxx>
---
 builtin/branch.c         |  3 +-
 builtin/for-each-ref.c   |  1 -
 builtin/tag.c            |  1 -
 builtin/verify-tag.c     |  1 -
 ref-filter.c             | 70 ++++++++++++++++++----------------------
 ref-filter.h             | 13 --------
 t/t3203-branch-output.sh | 28 ++++++++++++++++
 t/t6600-test-reach.sh    | 29 +++++++++++++++++
 8 files changed, 89 insertions(+), 57 deletions(-)

diff --git a/builtin/branch.c b/builtin/branch.c
index 6e7b0cfddb..9a29de5bf1 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -473,7 +473,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
 	if (verify_ref_format(format))
 		die(_("unable to parse format string"));

-	filter_ahead_behind(the_repository, format, &array);
+	filter_ahead_behind(the_repository, &array);
 	ref_array_sort(sorting, &array);

 	if (column_active(colopts)) {
@@ -884,7 +884,6 @@ int cmd_branch(int argc,
 		string_list_clear(&output, 0);
 		ref_sorting_release(sorting);
 		ref_filter_clear(&filter);
-		ref_format_clear(&format);

 		ret = 0;
 		goto out;
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 715745a262..8085ebd8fe 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -108,7 +108,6 @@ int cmd_for_each_ref(int argc,
 	filter_and_format_refs(&filter, flags, sorting, &format);

 	ref_filter_clear(&filter);
-	ref_format_clear(&format);
 	ref_sorting_release(sorting);
 	strvec_clear(&vec);
 	return 0;
diff --git a/builtin/tag.c b/builtin/tag.c
index c4bd145831..e8a344b926 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -698,7 +698,6 @@ int cmd_tag(int argc,
 cleanup:
 	ref_sorting_release(sorting);
 	ref_filter_clear(&filter);
-	ref_format_clear(&format);
 	strbuf_release(&buf);
 	strbuf_release(&ref);
 	strbuf_release(&reflog_msg);
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index a7f20618ff..f6b97048a5 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -69,6 +69,5 @@ int cmd_verify_tag(int argc,
 		if (format.format)
 			pretty_print_ref(name, &oid, &format);
 	}
-	ref_format_clear(&format);
 	return had_error;
 }
diff --git a/ref-filter.c b/ref-filter.c
index 23054694c2..aef142e105 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -242,6 +242,12 @@ 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;
+
 /*
  * Expand string, append it to strbuf *sb, then return error code ret.
  * Allow to save few lines of code.
@@ -891,7 +897,7 @@ static int rest_atom_parser(struct ref_format *format UNUSED,
 	return 0;
 }

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

-	item = string_list_append(&format->bases, arg);
+	item = string_list_append(&bases, arg);
 	item->util = lookup_commit_reference_by_name(arg);
 	if (!item->util)
 		die("failed to find '%s'", arg);
@@ -908,7 +914,7 @@ static int ahead_behind_atom_parser(struct ref_format *format,
 	return 0;
 }

-static int is_base_atom_parser(struct ref_format *format,
+static int is_base_atom_parser(struct ref_format *format UNUSED,
 			       struct used_atom *atom UNUSED,
 			       const char *arg, struct strbuf *err)
 {
@@ -917,7 +923,7 @@ static int is_base_atom_parser(struct ref_format *format,
 	if (!arg)
 		return strbuf_addf_ret(err, -1, _("expected format: %%(is-base:<committish>)"));

-	item = string_list_append(&format->is_base_tips, arg);
+	item = string_list_append(&is_base_tips, arg);
 	item->util = lookup_commit_reference_by_name(arg);
 	if (!item->util)
 		die("failed to find '%s'", arg);
@@ -3024,6 +3030,8 @@ 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) {
 		hashmap_clear_and_free(&(ref_to_worktree_map.map),
@@ -3084,22 +3092,21 @@ static void reach_filter(struct ref_array *array,
 }

 void filter_ahead_behind(struct repository *r,
-			 struct ref_format *format,
 			 struct ref_array *array)
 {
 	struct commit **commits;
-	size_t commits_nr = format->bases.nr + array->nr;
+	size_t commits_nr = bases.nr + array->nr;

-	if (!format->bases.nr || !array->nr)
+	if (!bases.nr || !array->nr)
 		return;

 	ALLOC_ARRAY(commits, commits_nr);
-	for (size_t i = 0; i < format->bases.nr; i++)
-		commits[i] = format->bases.items[i].util;
+	for (size_t i = 0; i < bases.nr; i++)
+		commits[i] = bases.items[i].util;

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

-	commits_nr = format->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;
@@ -3108,8 +3115,8 @@ void filter_ahead_behind(struct repository *r,
 		if (!commits[commits_nr])
 			continue;

-		CALLOC_ARRAY(array->items[i]->counts, format->bases.nr);
-		for (size_t j = 0; j < format->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;
@@ -3125,14 +3132,13 @@ void filter_ahead_behind(struct repository *r,
 }

 void filter_is_base(struct repository *r,
-		    struct ref_format *format,
 		    struct ref_array *array)
 {
 	struct commit **bases;
 	size_t bases_nr = 0;
 	struct ref_array_item **back_index;

-	if (!format->is_base_tips.nr || !array->nr)
+	if (!is_base_tips.nr || !array->nr)
 		return;

 	CALLOC_ARRAY(back_index, array->nr);
@@ -3142,7 +3148,7 @@ void filter_is_base(struct repository *r,
 		const char *name = array->items[i]->refname;
 		struct commit *c = lookup_commit_reference_by_name_gently(name, 1);

-		CALLOC_ARRAY(array->items[i]->is_base, format->is_base_tips.nr);
+		CALLOC_ARRAY(array->items[i]->is_base, is_base_tips.nr);

 		if (!c)
 			continue;
@@ -3152,15 +3158,15 @@ void filter_is_base(struct repository *r,
 		bases_nr++;
 	}

-	for (size_t i = 0; i < format->is_base_tips.nr; i++) {
-		struct commit *tip = format->is_base_tips.items[i].util;
+	for (size_t i = 0; i < is_base_tips.nr; i++) {
+		struct commit *tip = is_base_tips.items[i].util;
 		int base_index = get_branch_base_for_tip(r, tip, bases, bases_nr);

 		if (base_index < 0)
 			continue;

 		/* Store the string for use in output later. */
-		back_index[base_index]->is_base[i] = xstrdup(format->is_base_tips.items[i].string);
+		back_index[base_index]->is_base[i] = xstrdup(is_base_tips.items[i].string);
 	}

 	free(back_index);
@@ -3252,8 +3258,7 @@ struct ref_sorting {
 };

 static inline int can_do_iterative_format(struct ref_filter *filter,
-					  struct ref_sorting *sorting,
-					  struct ref_format *format)
+					  struct ref_sorting *sorting)
 {
 	/*
 	 * Reference backends sort patterns lexicographically by refname, so if
@@ -3279,15 +3284,15 @@ static inline int can_do_iterative_format(struct ref_filter *filter,
 	 */
 	return !(filter->reachable_from ||
 		 filter->unreachable_from ||
-		 format->bases.nr ||
-		 format->is_base_tips.nr);
+		 bases.nr ||
+		 is_base_tips.nr);
 }

 void filter_and_format_refs(struct ref_filter *filter, unsigned int type,
 			    struct ref_sorting *sorting,
 			    struct ref_format *format)
 {
-	if (can_do_iterative_format(filter, sorting, format)) {
+	if (can_do_iterative_format(filter, sorting)) {
 		int save_commit_buffer_orig;
 		struct ref_filter_and_format_cbdata ref_cbdata = {
 			.filter = filter,
@@ -3303,8 +3308,8 @@ void filter_and_format_refs(struct ref_filter *filter, unsigned int type,
 	} else {
 		struct ref_array array = { 0 };
 		filter_refs(&array, filter, type);
-		filter_ahead_behind(the_repository, format, &array);
-		filter_is_base(the_repository, format, &array);
+		filter_ahead_behind(the_repository, &array);
+		filter_is_base(the_repository, &array);
 		ref_array_sort(sorting, &array);
 		print_formatted_ref_array(&array, format);
 		ref_array_clear(&array);
@@ -3638,16 +3643,3 @@ void ref_filter_clear(struct ref_filter *filter)
 	free_commit_list(filter->unreachable_from);
 	ref_filter_init(filter);
 }
-
-void ref_format_init(struct ref_format *format)
-{
-	struct ref_format blank = REF_FORMAT_INIT;
-	memcpy(format, &blank, sizeof(blank));
-}
-
-void ref_format_clear(struct ref_format *format)
-{
-	string_list_clear(&format->bases, 0);
-	string_list_clear(&format->is_base_tips, 0);
-	ref_format_init(format);
-}
diff --git a/ref-filter.h b/ref-filter.h
index 754038ab07..013d4cfa64 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -99,12 +99,6 @@ struct ref_format {
 	/* Internal state to ref-filter */
 	int need_color_reset_at_eol;

-	/* List of bases for ahead-behind counts. */
-	struct string_list bases;
-
-	/* List of bases for is-base indicators. */
-	struct string_list is_base_tips;
-
 	struct {
 		int max_count;
 		int omit_empty;
@@ -117,8 +111,6 @@ struct ref_format {
 }
 #define REF_FORMAT_INIT {             \
 	.use_color = -1,              \
-	.bases = STRING_LIST_INIT_DUP, \
-	.is_base_tips = STRING_LIST_INIT_DUP, \
 }

 /*  Macros for checking --merged and --no-merged options */
@@ -205,7 +197,6 @@ struct ref_array_item *ref_array_push(struct ref_array *array,
  * If this is not called, then any ahead-behind atoms will be blank.
  */
 void filter_ahead_behind(struct repository *r,
-			 struct ref_format *format,
 			 struct ref_array *array);

 /*
@@ -215,13 +206,9 @@ void filter_ahead_behind(struct repository *r,
  * If this is not called, then any is-base atoms will be blank.
  */
 void filter_is_base(struct repository *r,
-		    struct ref_format *format,
 		    struct ref_array *array);

 void ref_filter_init(struct ref_filter *filter);
 void ref_filter_clear(struct ref_filter *filter);

-void ref_format_init(struct ref_format *format);
-void ref_format_clear(struct ref_format *format);
-
 #endif /*  REF_FILTER_H  */
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
index 500c9d0e72..a6bd88a58d 100755
--- a/t/t3203-branch-output.sh
+++ b/t/t3203-branch-output.sh
@@ -368,6 +368,34 @@ test_expect_success 'git branch --format with ahead-behind' '
 	test_cmp expect actual
 '

+test_expect_success 'git branch `--sort=[-]ahead-behind` option' '
+	cat >expect <<-\EOF &&
+	(HEAD detached from fromtag) 0 0
+	refs/heads/ambiguous 0 0
+	refs/heads/branch-two 0 0
+	refs/heads/branch-one 1 0
+	refs/heads/main 1 0
+	refs/heads/ref-to-branch 1 0
+	refs/heads/ref-to-remote 1 0
+	EOF
+	git branch --format="%(refname) %(ahead-behind:HEAD)" \
+		--sort=refname --sort=ahead-behind:HEAD >actual &&
+	test_cmp expect actual &&
+
+	cat >expect <<-\EOF &&
+	(HEAD detached from fromtag) 0 0
+	refs/heads/branch-one 1 0
+	refs/heads/main 1 0
+	refs/heads/ref-to-branch 1 0
+	refs/heads/ref-to-remote 1 0
+	refs/heads/ambiguous 0 0
+	refs/heads/branch-two 0 0
+	EOF
+	git branch --format="%(refname) %(ahead-behind:HEAD)" \
+		--sort=refname --sort=-ahead-behind:HEAD >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'git branch with --format=%(rest) must fail' '
 	test_must_fail git branch --format="%(rest)" >actual
 '
diff --git a/t/t6600-test-reach.sh b/t/t6600-test-reach.sh
index 2591f8b8b3..6638d1aa1d 100755
--- a/t/t6600-test-reach.sh
+++ b/t/t6600-test-reach.sh
@@ -733,4 +733,33 @@ test_expect_success 'for-each-ref is-base:multiple' '
 		--format="%(refname)[%(is-base:commit-2-3)-%(is-base:commit-6-5)]" --stdin
 '

+test_expect_success 'for-each-ref is-base: --sort' '
+	cat >input <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/commit-4-2
+	refs/heads/commit-4-4
+	refs/heads/commit-8-4
+	EOF
+
+	cat >expect <<-\EOF &&
+	refs/heads/commit-1-1
+	refs/heads/commit-4-4
+	refs/heads/commit-8-4
+	refs/heads/commit-4-2
+	EOF
+	run_all_modes git for-each-ref \
+		--format="%(refname)" --stdin \
+		--sort=refname --sort=is-base:commit-2-3 &&
+
+	cat >expect <<-\EOF &&
+	refs/heads/commit-4-2
+	refs/heads/commit-1-1
+	refs/heads/commit-4-4
+	refs/heads/commit-8-4
+	EOF
+	run_all_modes git for-each-ref \
+		--format="%(refname)" --stdin \
+		--sort=refname --sort=-is-base:commit-2-3
+'
+
 test_done
--
2.48.0





[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux