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

From: "Harald Nordgren via GitGitGadget" <[email-masked]>
Date: Thu, 26 Feb 2026 10:33:40 +0000

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]

Harald Nordgren (2):
  refactor format_branch_comparison in preparation
  status: add status.compareBranches config for multiple branch
    comparisons

 Documentation/config/status.adoc |  19 ++
 remote.c                         | 178 ++++++++++++++----
 t/t6040-tracking-info.sh         | 310 +++++++++++++++++++++++++++++++
 3 files changed, 470 insertions(+), 37 deletions(-)

base-commit: 7b2bccb0d58d4f24705bf985de1f4612e4cf06e5
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2138%2FHaraldNordgren%2Fahead_of_main_status-v30
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2138/HaraldNordgren/ahead_of_main_status-v30
Pull-Request: https://github.com/git/git/pull/2138

Range-diff vs v29:

 1:  48db1f4847 = 1:  7f517b8c7f refactor format_branch_comparison in preparation
 2:  6a88f41fa5 ! 2:  501bd40294 status: add status.compareBranches config for multiple branch comparisons
     @@ remote.c: int stat_tracking_info(struct branch *branch, int *num_ours, int *num_
      +	if (!branch || !name)
      +		return NULL;
      +
     -+	if (!strcasecmp(name, "@{upstream}"))
     ++	if (!strcasecmp(name, "@{upstream}")) {
      +		resolved = branch_get_upstream(branch, NULL);
     -+	else if (!strcasecmp(name, "@{push}"))
     ++	} else if (!strcasecmp(name, "@{push}")) {
      +		resolved = branch_get_push(branch, NULL);
     -+	else {
     -+		warning(_("ignoring value '%s' for status.compareBranches; only @{upstream} and @{push} are supported"),
     ++	} else {
     ++		warning(_("ignoring value '%s' for status.compareBranches, "
     ++			  "only @{upstream} and @{push} are supported"),
      +			name);
      +		return NULL;
      +	}
     @@ remote.c: int stat_tracking_info(struct branch *branch, int *num_ours, int *num_
      -				     bool show_divergence_advice)
      +				     unsigned flags)
       {
     -+	bool enable_push_advice = (flags & ENABLE_ADVICE_PUSH) &&
     -+		advice_enabled(ADVICE_STATUS_HINTS);
     -+	bool enable_pull_advice = (flags & ENABLE_ADVICE_PULL) &&
     -+		advice_enabled(ADVICE_STATUS_HINTS);
     -+	bool enable_divergence_advice = (flags & ENABLE_ADVICE_DIVERGENCE) &&
     -+		advice_enabled(ADVICE_STATUS_HINTS);
     ++	bool use_push_advice = (flags & ENABLE_ADVICE_PUSH);
     ++	bool use_pull_advice = (flags & ENABLE_ADVICE_PULL);
     ++	bool use_divergence_advice = (flags & ENABLE_ADVICE_DIVERGENCE);
      +
       	if (up_to_date) {
       		strbuf_addf(sb,
     @@ remote.c: static void format_branch_comparison(struct strbuf *sb,
       			    _("Your branch and '%s' refer to different commits.\n"),
       			    branch_name);
      -		if (advice_enabled(ADVICE_STATUS_HINTS))
     -+		if (enable_push_advice)
     ++		if (use_push_advice && advice_enabled(ADVICE_STATUS_HINTS))
       			strbuf_addf(sb, _("  (use \"%s\" for details)\n"),
       				    "git status --ahead-behind");
       	} else if (!theirs) {
     @@ remote.c: static void format_branch_comparison(struct strbuf *sb,
       			   ours),
       			branch_name, ours);
      -		if (advice_enabled(ADVICE_STATUS_HINTS))
     -+		if (enable_push_advice)
     ++		if (use_push_advice && advice_enabled(ADVICE_STATUS_HINTS))
       			strbuf_addstr(sb,
       				_("  (use \"git push\" to publish your local commits)\n"));
       	} else if (!ours) {
     @@ remote.c: static void format_branch_comparison(struct strbuf *sb,
       			   theirs),
       			branch_name, theirs);
      -		if (advice_enabled(ADVICE_STATUS_HINTS))
     -+		if (enable_pull_advice)
     ++		if (use_pull_advice && advice_enabled(ADVICE_STATUS_HINTS))
       			strbuf_addstr(sb,
       				_("  (use \"git pull\" to update your local branch)\n"));
       	} else {
     @@ remote.c: static void format_branch_comparison(struct strbuf *sb,
       			branch_name, ours, theirs);
      -		if (show_divergence_advice &&
      -		    advice_enabled(ADVICE_STATUS_HINTS))
     -+		if (enable_divergence_advice)
     ++		if (use_divergence_advice && advice_enabled(ADVICE_STATUS_HINTS))
       			strbuf_addstr(sb,
       				_("  (use \"git pull\" if you want to integrate the remote branch with yours)\n"));
       	}
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +	test_cmp expect actual
      +'
      +
     -+test_expect_success 'setup for compareBranches tests' '
     -+	(
     -+		cd test &&
     -+		git config push.default current &&
     -+		git config status.compareBranches "@{upstream} @{push}"
     -+	)
     -+'
     -+
      +test_expect_success 'status.compareBranches from upstream has no duplicates' '
     -+	(
     -+		cd test &&
     -+		git checkout main &&
     -+		git status >../actual
     -+	) &&
     ++	test_config -C test status.compareBranches "@{upstream} @{push}" &&
     ++	git -C test checkout main &&
     ++	git -C test status >actual &&
      +	cat >expect <<-EOF &&
      +	On branch main
      +	Your branch is up to date with ${SQ}origin/main${SQ}.
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +'
      +
      +test_expect_success 'status.compareBranches shows ahead of both upstream and push branch' '
     -+	(
     -+		cd test &&
     -+		git checkout -b feature2 origin/main &&
     -+		git push origin HEAD &&
     -+		advance work &&
     -+		git status >../actual
     -+	) &&
     ++	test_config -C test push.default current &&
     ++	test_config -C test status.compareBranches "@{upstream} @{push}" &&
     ++	git -C test checkout -b feature2 origin/main &&
     ++	git -C test push origin HEAD &&
     ++	(cd test && advance work) &&
     ++	git -C test status >actual &&
      +	cat >expect <<-EOF &&
      +	On branch feature2
      +	Your branch is ahead of ${SQ}origin/main${SQ} by 1 commit.
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +'
      +
      +test_expect_success 'checkout with status.compareBranches shows both branches' '
     -+	(
     -+		cd test &&
     -+		git checkout feature2 >../actual
     -+	) &&
     ++	test_config -C test push.default current &&
     ++	test_config -C test status.compareBranches "@{upstream} @{push}" &&
     ++	git -C test checkout feature2 >actual &&
      +	cat >expect <<-EOF &&
      +	Your branch is ahead of ${SQ}origin/main${SQ} by 1 commit.
      +
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +'
      +
      +test_expect_success 'status.compareBranches shows diverged and ahead' '
     -+	(
     -+		cd test &&
     -+		git checkout feature4 &&
     -+		git branch --set-upstream-to origin/main &&
     -+		git push origin HEAD &&
     -+		advance work &&
     -+		git status >../actual
     -+	) &&
     ++	test_config -C test push.default current &&
     ++	test_config -C test status.compareBranches "@{upstream} @{push}" &&
     ++	git -C test checkout feature4 &&
     ++	git -C test branch --set-upstream-to origin/main &&
     ++	git -C test push origin HEAD &&
     ++	(cd test && advance work) &&
     ++	git -C test status >actual &&
      +	cat >expect <<-EOF &&
      +	On branch feature4
      +	Your branch and ${SQ}origin/main${SQ} have diverged,
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +'
      +
      +test_expect_success 'status --no-ahead-behind with status.compareBranches' '
     -+	(
     -+		cd test &&
     -+		git checkout feature4 &&
     -+		git status --no-ahead-behind >../actual
     -+	) &&
     ++	test_config -C test push.default current &&
     ++	test_config -C test status.compareBranches "@{upstream} @{push}" &&
     ++	git -C test checkout feature4 &&
     ++	git -C test status --no-ahead-behind >actual &&
      +	cat >expect <<-EOF &&
      +	On branch feature4
      +	Your branch and ${SQ}origin/main${SQ} refer to different commits.
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +	(
      +		cd test &&
      +		git remote add upstream ../. &&
     -+		git fetch upstream &&
     -+		git config remote.pushDefault origin
     ++		git fetch upstream
      +	)
      +'
      +
      +test_expect_success 'status.compareBranches with upstream and origin remotes' '
     -+	(
     -+		cd test &&
     -+		git checkout -b feature5 upstream/main &&
     -+		git push origin &&
     -+		advance work &&
     -+		git status >../actual
     -+	) &&
     ++	test_config -C test push.default current &&
     ++	test_config -C test remote.pushDefault origin &&
     ++	test_config -C test status.compareBranches "@{upstream} @{push}" &&
     ++	git -C test checkout -b feature5 upstream/main &&
     ++	git -C test push origin &&
     ++	(cd test && advance work) &&
     ++	git -C test status >actual &&
      +	cat >expect <<-EOF &&
      +	On branch feature5
      +	Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +'
      +
      +test_expect_success 'status.compareBranches supports ordered upstream/push entries' '
     -+	(
     -+		cd test &&
     -+		git checkout -b feature6 upstream/main &&
     -+		git push origin &&
     -+		advance work &&
     -+		git -c status.compareBranches="@{push} @{upstream}" status >../actual
     -+	) &&
     ++	test_config -C test push.default current &&
     ++	test_config -C test remote.pushDefault origin &&
     ++	test_config -C test status.compareBranches "@{push} @{upstream}" &&
     ++	git -C test checkout -b feature6 upstream/main &&
     ++	git -C test push origin &&
     ++	(cd test && advance work) &&
     ++	git -C test status >actual &&
      +	cat >expect <<-EOF &&
      +	On branch feature6
      +	Your branch is ahead of ${SQ}origin/feature6${SQ} by 1 commit.
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +'
      +
      +test_expect_success 'status.compareBranches with diverged push branch' '
     -+	(
     -+		cd test &&
     -+		git checkout -b feature7 upstream/main &&
     -+		advance work &&
     -+		git push origin &&
     -+		git reset --hard upstream/main &&
     -+		advance work &&
     -+		git status >../actual
     -+	) &&
     ++	test_config -C test push.default current &&
     ++	test_config -C test remote.pushDefault origin &&
     ++	test_config -C test status.compareBranches "@{upstream} @{push}" &&
     ++	git -C test checkout -b feature7 upstream/main &&
     ++	(cd test && advance work71) &&
     ++	git -C test push origin &&
     ++	git -C test reset --hard upstream/main &&
     ++	(cd test && advance work72) &&
     ++	git -C test status >actual &&
      +	cat >expect <<-EOF &&
      +	On branch feature7
      +	Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +'
      +
      +test_expect_success 'status.compareBranches shows up to date branches' '
     -+	(
     -+		cd test &&
     -+		git checkout -b feature8 upstream/main &&
     -+		git push origin &&
     -+		git status >../actual
     -+	) &&
     ++	test_config -C test push.default current &&
     ++	test_config -C test remote.pushDefault origin &&
     ++	test_config -C test status.compareBranches "@{upstream} @{push}" &&
     ++	git -C test checkout -b feature8 upstream/main &&
     ++	git -C test push origin &&
     ++	git -C test status >actual &&
      +	cat >expect <<-EOF &&
      +	On branch feature8
      +	Your branch is up to date with ${SQ}upstream/main${SQ}.
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +'
      +
      +test_expect_success 'status --no-ahead-behind with status.compareBranches up to date' '
     -+	(
     -+		cd test &&
     -+		git checkout feature8 &&
     -+		git push origin &&
     -+		git status --no-ahead-behind >../actual
     -+	) &&
     ++	test_config -C test push.default current &&
     ++	test_config -C test remote.pushDefault origin &&
     ++	test_config -C test status.compareBranches "@{upstream} @{push}" &&
     ++	git -C test checkout feature8 >actual &&
     ++	git -C test push origin &&
     ++	git -C test status --no-ahead-behind >actual &&
      +	cat >expect <<-EOF &&
      +	On branch feature8
      +	Your branch is up to date with ${SQ}upstream/main${SQ}.
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +'
      +
      +test_expect_success 'checkout with status.compareBranches shows up to date' '
     -+	(
     -+		cd test &&
     -+		git checkout feature8 >../actual
     -+	) &&
     ++	test_config -C test push.default current &&
     ++	test_config -C test remote.pushDefault origin &&
     ++	test_config -C test status.compareBranches "@{upstream} @{push}" &&
     ++	git -C test checkout feature8 >actual &&
      +	cat >expect <<-EOF &&
      +	Your branch is up to date with ${SQ}upstream/main${SQ}.
      +
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +'
      +
      +test_expect_success 'status.compareBranches with upstream behind and push up to date' '
     -+	(
     -+		cd test &&
     -+		git checkout -b ahead upstream/main &&
     -+		advance work &&
     -+		git push upstream HEAD &&
     -+		git checkout -b feature9 upstream/main &&
     -+		git push origin &&
     -+		git branch --set-upstream-to upstream/ahead &&
     -+		git status >../actual
     -+	) &&
     ++	test_config -C test push.default current &&
     ++	test_config -C test remote.pushDefault origin &&
     ++	test_config -C test status.compareBranches "@{upstream} @{push}" &&
     ++	git -C test checkout -b ahead upstream/main &&
     ++	(cd test && advance work) &&
     ++	git -C test push upstream HEAD &&
     ++	git -C test checkout -b feature9 upstream/main &&
     ++	git -C test push origin &&
     ++	git -C test branch --set-upstream-to upstream/ahead &&
     ++	git -C test status >actual &&
      +	cat >expect <<-EOF &&
      +	On branch feature9
      +	Your branch is behind ${SQ}upstream/ahead${SQ} by 1 commit, and can be fast-forwarded.
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +'
      +
      +test_expect_success 'status.compareBranches with remapped push refspec' '
     -+	(
     -+		cd test &&
     -+		git checkout -b feature10 origin/main &&
     -+		git config remote.origin.push refs/heads/feature10:refs/heads/remapped &&
     -+		git push &&
     -+		advance work &&
     -+		git status >../actual
     -+	) &&
     ++	test_config -C test remote.origin.push refs/heads/feature10:refs/heads/remapped &&
     ++	test_config -C test status.compareBranches "@{upstream} @{push}" &&
     ++	git -C test checkout -b feature10 origin/main &&
     ++	git -C test push &&
     ++	(cd test && advance work) &&
     ++	git -C test status >actual &&
      +	cat >expect <<-EOF &&
      +	On branch feature10
      +	Your branch is ahead of ${SQ}origin/main${SQ} by 1 commit.
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +'
      +
      +test_expect_success 'status.compareBranches with remapped push and upstream remote' '
     -+	(
     -+		cd test &&
     -+		git checkout -b feature11 upstream/main &&
     -+		git config remote.origin.push refs/heads/feature11:refs/heads/remapped &&
     -+		git push origin &&
     -+		advance work &&
     -+		git status >../actual
     -+	) &&
     ++	test_config -C test remote.pushDefault origin &&
     ++	test_config -C test remote.origin.push refs/heads/feature11:refs/heads/remapped &&
     ++	test_config -C test status.compareBranches "@{upstream} @{push}" &&
     ++	git -C test checkout -b feature11 upstream/main &&
     ++	git -C test push origin &&
     ++	(cd test && advance work) &&
     ++	git -C test status >actual &&
      +	cat >expect <<-EOF &&
      +	On branch feature11
      +	Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
     @@ t/t6040-tracking-info.sh: test_expect_success '--set-upstream-to @{-1}' '
      +	EOF
      +	test_cmp expect actual
      +'
     -+
     -+test_expect_success 'clean up after compareBranches tests' '
     -+	(
     -+		cd test &&
     -+		git config --unset status.compareBranches
     -+	)
     -+'
      +
       test_done

-- 
gitgitgadget