Subject: Re: [PATCH v11] status: show comparison with push remote tracking branch

From: Phillip Wood <[email-masked]>
Date: Fri, 2 Jan 2026 15:18:43 +0000

Hi Harald

On 02/01/2026 11:17, Harald Nordgren via GitGitGadget wrote:
> From: Harald Nordgren <[email-masked]>
> 
> "git status" on a branch that follows a remote branch compares
> commits on the current branch and the remote-tracking branch it
> builds upon, to show "ahead", "behind", or "diverged" status.
> 
> When working on a feature branch that tracks a remote feature branch,
> but you also want to track progress relative to the push remote's
> tracking branch (which may differ from the upstream branch), git status
> now shows an additional comparison.

This is great and it is really good that it is now using the default 
push destination rather than a custom config key.

> When a push remote is configured (via branch.<name>.pushRemote or
> remote.pushDefault) git status shows both the comparison with the
> upstream tracking branch (as before) and an additional comparison with
> the push remote's tracking branch, if it differs from the upstream
> tracking branch.

Looking at the tests, it seems that the extra information is shown 
whenever the upstream branch differs from the default push destination 
even if they are on the same remote. I think that is sensible but this 
paragraph should be updated to reflect the fact that one does not need 
to set either of the "pushDefault" config settings to benefit from this.

> The push branch comparison appears on a separate
> line after the upstream branch status, using the same format:
> - "Ahead of 'origin/feature' by N commits" when purely ahead
> - "Behind 'origin/feature' by N commits" when purely behind
> - "Diverged from 'origin/feature' by N commits" when diverged

I'm wondering why we don't reuse the existing messages - I don't see how 
using a different wording for the push destination compared to the 
upstream branch benefits the user. We should adjust the hints that are 
shown so that we only recommend pulling from the upstream branch and 
only recommend pushing to the default push destination but the 
comparisons should be the same. Also I don't find the message "Diverged 
from 'origin/feature' by N commits' very helpful, I'd find it more 
useful if it gave the ahead/behind count like we do for the upstream branch.

> Example output when tracking upstream/main with pushRemote set to origin:
>      On branch feature
>      Your branch is ahead of 'upstream/main' by 2 commits.
>        (use "git pull" if you want to integrate the remote branch with yours)

Is this a typo? - why are we recommending "git pull" when our branch is 
ahead of the upstream branch?

>      Ahead of 'origin/feature' by 5 commits.

It would be nice to have a hint suggesting the user runs "git push" here.

> The comparison is only shown when a push remote is configured and the
> push remote's tracking branch differs from the upstream tracking branch.

Looking at the tests, only the second half of that sentence appears to 
be true.

> diff --git a/remote.c b/remote.c
> index 59b3715120..2317725f7d 100644
> --- a/remote.c
> +++ b/remote.c
> @@ -2237,6 +2237,81 @@ 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);
>   }
>   
> +static char *get_remote_push_branch(struct branch *branch, char **full_ref_out)
> +{
> +	const char *push_remote;
> +	const char *resolved;
> +	int flag;
> +	struct strbuf ref_buf = STRBUF_INIT;
> +	char *ret = NULL;
> +
> +	if (!branch)
> +		return NULL;
> +
> +	push_remote = pushremote_for_branch(branch, NULL);
> +	if (!push_remote)
> +		return NULL;
> +
> +	strbuf_addf(&ref_buf, "refs/remotes/%s/%s", push_remote, branch->name);

Shouldn't we be taking account of the push and fetch refspecs here? 
There is no guarantee that $branch maps to refs/remotes/$remote/$branch. 
To take a silly example, if we have

     remote.$remote.fetch = 
refs/heads/$branch:refs/remotes/$remote/abc-$branch
     remote.$remote.pull = refs/heads/$branch:refs/heads/xyz-$branch

Then we should be using "refs/remotes/$remote/abc-xyz-$branch" in the 
message above as "$branch" will be pushed to "xyz-$branch" on the remote 
which is fetched to "$remote/abc-xyz-$branch"

> +
> +	resolved = refs_resolve_ref_unsafe(
> +		get_main_ref_store(the_repository),
> +		ref_buf.buf,
> +		RESOLVE_REF_READING,
> +		NULL, &flag);

As we don't use flag we can pass NULL - see the documentation for this 
function in refs.h

> +static void format_push_branch_comparison(struct strbuf *sb,
> +					     const char *branch_refname,
> +					     const char *push_full,
> +					     const char *push_short,
> +					     enum ahead_behind_flags abf)
> +{
> +	int push_ahead = 0, push_behind = 0;
> +	int stat_result;
> +
> +	stat_result = stat_branch_pair(branch_refname, push_full,
> +				       &push_ahead, &push_behind, abf);
> +	if (stat_result < 0)
> +		return;
> +
> +	strbuf_addstr(sb, "\n");

As I said above it would be nice if we could use the existing messages 
here. Can we have a separate preparatory patch that refactors 
fromat_tracking_info() so that we can use the same messages for the 
upstream branch and the default push destination?

> @@ -2311,6 +2387,19 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb,
>   			strbuf_addstr(sb,
>   				_("  (use \"git pull\" if you want to integrate the remote branch with yours)\n"));
>   	}
> +
> +	if (!upstream_is_gone && sti >= 0 && abf != AHEAD_BEHIND_QUICK) {

Why do we handle AHEAD_BEHIND_QUICK differently to the upstream branch? 
Surely it would be useful to tell the user whether the branch is up to 
date with the default push destination or not?

> diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
> index 0b719bbae6..f27ae719ad 100755
> --- a/t/t6040-tracking-info.sh
> +++ b/t/t6040-tracking-info.sh
> [...]
> +test_expect_success 'status shows ahead of both origin/main and feature branch' '
> +	(
> +		cd test &&
> +		git checkout -b feature2 origin/main &&
> +		git push origin HEAD &&
> +		advance work &&
> +		git status >../actual
> +	) &&
> +	cat >expect <<-EOF &&
> +	On branch feature2
> +	Your branch is ahead of ${SQ}origin/main${SQ} by 1 commit.
> +	  (use "git push" to publish your local commits)

Why are we still suggesting pushing to the upstream branch when we know 
the user is pushing to a different remote branch?

> +	Ahead of ${SQ}origin/feature2${SQ} by 1 commit.

Why aren't we suggesting to use "git push" here?

Thanks for working on this. With a few tweaks it will be a really useful 
improvement to "git status"

Phillip

> +	nothing to commit, working tree clean
> +	EOF
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'checkout shows ahead of both origin/main and feature branch' '
> +	(
> +		cd test &&
> +		git checkout feature2 >../actual
> +	) &&
> +	cat >expect <<-EOF &&
> +	Your branch is ahead of ${SQ}origin/main${SQ} by 1 commit.
> +	  (use "git push" to publish your local commits)
> +
> +	Ahead of ${SQ}origin/feature2${SQ} by 1 commit.
> +	EOF
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'setup for ahead of tracked but diverged from main' '
> +	(
> +		cd test &&
> +		git checkout -b feature4 origin/main &&
> +		advance work1 &&
> +		git checkout origin/main &&
> +		advance work2 &&
> +		git push origin HEAD:main &&
> +		git checkout feature4 &&
> +		advance work3
> +	)
> +'
> +
> +test_expect_success 'status shows diverged from origin/main and ahead of feature branch' '
> +	(
> +		cd test &&
> +		git checkout feature4 &&
> +		git branch --set-upstream-to origin/main &&
> +		git push origin HEAD &&
> +		advance work &&
> +		git status >../actual
> +	) &&
> +	cat >expect <<-EOF &&
> +	On branch feature4
> +	Your branch and ${SQ}origin/main${SQ} have diverged,
> +	and have 3 and 1 different commits each, respectively.
> +	  (use "git pull" if you want to integrate the remote branch with yours)
> +
> +	Ahead of ${SQ}origin/feature4${SQ} by 1 commit.
> +
> +	nothing to commit, working tree clean
> +	EOF
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'setup upstream remote' '
> +	(
> +		cd test &&
> +		git remote add upstream ../. &&
> +		git fetch upstream &&
> +		git config remote.pushDefault origin
> +	)
> +'
> +
> +test_expect_success 'status with upstream remote and push.default set to origin' '
> +	(
> +		cd test &&
> +		git checkout -b feature5 upstream/main &&
> +		git push origin &&
> +		advance work &&
> +		git status >../actual
> +	) &&
> +	cat >expect <<-EOF &&
> +	On branch feature5
> +	Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
> +	  (use "git push" to publish your local commits)
> +
> +	Ahead of ${SQ}origin/feature5${SQ} by 1 commit.
> +
> +	nothing to commit, working tree clean
> +	EOF
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'status with upstream remote and push.default set to origin and diverged' '
> +	(
> +		cd test &&
> +		git checkout -b feature6 upstream/main &&
> +		advance work &&
> +		git push origin &&
> +		git reset --hard upstream/main &&
> +		advance work &&
> +		git status >../actual
> +	) &&
> +	cat >expect <<-EOF &&
> +	On branch feature6
> +	Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
> +	  (use "git push" to publish your local commits)
> +
> +	Diverged from ${SQ}origin/feature6${SQ} by 2 commits.
> +
> +	nothing to commit, working tree clean
> +	EOF
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'status with upstream remote and push branch up to date' '
> +	(
> +		cd test &&
> +		git checkout -b feature7 upstream/main &&
> +		git push origin &&
> +		git status >../actual
> +	) &&
> +	cat >expect <<-EOF &&
> +	On branch feature7
> +	Your branch is up to date with ${SQ}upstream/main${SQ}.
> +
> +	Your branch is up to date with ${SQ}origin/feature7${SQ}.
> +
> +	nothing to commit, working tree clean
> +	EOF
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'checkout shows push branch up to date' '
> +	(
> +		cd test &&
> +		git checkout feature7 >../actual
> +	) &&
> +	cat >expect <<-EOF &&
> +	Your branch is up to date with ${SQ}upstream/main${SQ}.
> +
> +	Your branch is up to date with ${SQ}origin/feature7${SQ}.
> +	EOF
> +	test_cmp expect actual
> +'
> +
>   test_done
> 
> base-commit: 68cb7f9e92a5d8e9824f5b52ac3d0a9d8f653dbe