Subject: Re: [PATCH v27 0/2] status: add status.compareBranches config for multiple branch comparisons

From: Junio C Hamano <[email-masked]>
Date: Thu, 22 Jan 2026 10:53:28 -0800

"Harald Nordgren via GitGitGadget" <[email-masked]> writes:

Before these list of CC's (which does not seem to have any effect,
by the way.  The message is going only to the list with your
personal address on CC: and to nobody else), ...

> cc: Chris Torek [email-masked] cc: Yee Cheng Chin
> [email-masked] cc: "brian m. carlson" [email-masked]
> cc: Ben Knoble [email-masked] cc: "Kristoffer Haugsbakk"
> [email-masked] cc: Phillip Wood [email-masked]
> cc: Nico Williams [email-masked] cc: Patrick Steinhardt [email-masked] cc:
> Jeff King [email-masked]

... please summarize

 (1) what this series of patches are about, to help those who
     encounter these patches for the first time, and

 (2) what changed in this iteration (v27) relative to the previous
     iteration (v26).

I happen to have seen your other message so I can guess this is
about deduping when more than one comparison target is listed and
some happen to become the same branches, but others may not have
seen that other message, and it is not helpful to just dump the
range-diff and force them to read it to deduce what you did.

Thanks.

> Harald Nordgren (2):
>   refactor format_branch_comparison in preparation
>   status: add status.compareBranches config for multiple branch
>     comparisons
>
>  Documentation/config/status.adoc |  20 ++
>  remote.c                         | 192 ++++++++++++++----
>  t/t6040-tracking-info.sh         | 337 +++++++++++++++++++++++++++++++
>  3 files changed, 512 insertions(+), 37 deletions(-)
>
>
> base-commit: b5c409c40f1595e3e590760c6f14a16b6683e22c
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2138%2FHaraldNordgren%2Fahead_of_main_status-v27
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2138/HaraldNordgren/ahead_of_main_status-v27
> Pull-Request: https://github.com/git/git/pull/2138
>
> Range-diff vs v26:
>
>  1:  27a46f8d9c = 1:  27a46f8d9c refactor format_branch_comparison in preparation
>  2:  caa761f615 ! 2:  0993420fc1 status: add status.compareBranches config for multiple branch comparisons
>      @@ remote.c
>        struct counted_string {
>        	size_t len;
>        	const char *s;
>      -@@ remote.c: static void branch_release(struct branch *branch)
>      - 	free((char *)branch->refname);
>      - 	free(branch->remote_name);
>      - 	free(branch->pushremote_name);
>      -+	free((char *)branch->push_tracking_ref);
>      - 	merge_clear(branch);
>      - }
>      - 
>       @@ remote.c: int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
>        	return stat_branch_pair(branch->refname, base, num_ours, num_theirs, abf);
>        }
>      @@ remote.c: int format_tracking_info(struct branch *branch, struct strbuf *sb,
>       -	const char *full_base;
>       -	char *base;
>       -	int upstream_is_gone = 0;
>      --
>      ++	char *compare_branches = NULL;
>      ++	struct string_list branches = STRING_LIST_INIT_DUP;
>      ++	struct string_list processed_refs = STRING_LIST_INIT_DUP;
>      ++	int reported = 0;
>      ++	size_t i;
>      ++	const char *upstream_ref;
>      ++	const char *push_ref;
>      + 
>       -	cmp_fetch = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf);
>       -	if (cmp_fetch < 0) {
>       -		if (!full_base)
>       -			return 0;
>       -		upstream_is_gone = 1;
>      -+	char *compare_branches_config = NULL;
>      -+	struct string_list compare_branches = STRING_LIST_INIT_DUP;
>      -+	struct string_list_item *item;
>      -+	int reported = 0;
>      -+	size_t i;
>      -+	const char *upstream_ref;
>      -+	const char *push_ref;
>      -+
>       +	repo_config_get_string(the_repository, "status.comparebranches",
>      -+			       &compare_branches_config);
>      ++			       &compare_branches);
>       +
>      -+	if (compare_branches_config) {
>      -+		string_list_split(&compare_branches, compare_branches_config,
>      -+				  " ", -1);
>      -+		string_list_remove_empty_items(&compare_branches, 0);
>      ++	if (compare_branches) {
>      ++		string_list_split(&branches, compare_branches, " ", -1);
>      ++		string_list_remove_empty_items(&branches, 0);
>       +	} else {
>      -+		string_list_append(&compare_branches, "@{upstream}");
>      ++		string_list_append(&branches, "@{upstream}");
>        	}
>        
>       -	base = refs_shorten_unambiguous_ref(get_main_ref_store(the_repository),
>      @@ remote.c: int format_tracking_info(struct branch *branch, struct strbuf *sb,
>       -				_("  (use \"git branch --unset-upstream\" to fixup)\n"));
>       -	} else {
>       -		format_branch_comparison(sb, !cmp_fetch, ours, theirs, base, abf, show_divergence_advice);
>      -+	for (i = 0; i < compare_branches.nr; i++) {
>      ++	for (i = 0; i < branches.nr; i++) {
>       +		char *full_ref;
>       +		char *short_ref;
>       +		int ours, theirs, cmp;
>       +		int is_upstream, is_push;
>       +		unsigned flags = 0;
>       +
>      -+		item = &compare_branches.items[i];
>      -+		full_ref = resolve_compare_branch(branch, item->string);
>      ++		full_ref = resolve_compare_branch(branch,
>      ++						  branches.items[i].string);
>       +		if (!full_ref)
>       +			continue;
>       +
>      ++		if (string_list_has_string(&processed_refs, full_ref)) {
>      ++			free(full_ref);
>      ++			continue;
>      ++		}
>      ++		string_list_insert(&processed_refs, full_ref);
>      ++
>       +		short_ref = refs_shorten_unambiguous_ref(
>       +			get_main_ref_store(the_repository), full_ref, 0);
>       +
>      @@ remote.c: int format_tracking_info(struct branch *branch, struct strbuf *sb,
>        
>       -	free(base);
>       -	return 1;
>      -+	string_list_clear(&compare_branches, 0);
>      -+	free(compare_branches_config);
>      ++	string_list_clear(&branches, 0);
>      ++	string_list_clear(&processed_refs, 0);
>      ++	free(compare_branches);
>       +	return reported;
>        }
>        
>      @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
>       +	)
>       +'
>       +
>      ++test_expect_success 'status.compareBranches from upstream has no duplicates' '
>      ++	(
>      ++		cd test &&
>      ++		git checkout main &&
>      ++		git status >../actual
>      ++	) &&
>      ++	cat >expect <<-EOF &&
>      ++	On branch main
>      ++	Your branch is up to date with ${SQ}origin/main${SQ}.
>      ++
>      ++	nothing to commit, working tree clean
>      ++	EOF
>      ++	test_cmp expect actual
>      ++'
>      ++
>       +test_expect_success 'status.compareBranches shows ahead of both upstream and push branch' '
>       +	(
>       +		cd test &&