Skip to content

remote: resolve "simple" @{push} when pushing to another remote - #2466

Open
HaraldNordgren wants to merge 2 commits into
git:masterfrom
HaraldNordgren:at-push-simple-triangular
Open

HaraldNordgren wants to merge 2 commits into
git:masterfrom
HaraldNordgren:at-push-simple-triangular

Conversation

@HaraldNordgren

@HaraldNordgren HaraldNordgren commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

With push.default=simple and remote.pushDefault pointing at a fork, git push pushes the branch to the fork, but @{push} fails with "cannot resolve 'simple' push to a single destination".

So git status cannot compare the branch with where it was pushed. Make @{push} resolve to where git push goes.

Changes in v2:

  • Moved the "simple" @{push} code out of branch_get_push_1() into its own function in a new preparatory commit.

cc: "D. Ben Knoble" ben.knoble@gmail.com

@HaraldNordgren

Copy link
Copy Markdown
Contributor Author

/submit

@gitgitgadget-git

Copy link
Copy Markdown

Submitted as pull.2466.git.git.1793105039624.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-git-2466/HaraldNordgren/at-push-simple-triangular-v1

To fetch this version to local tag pr-git-2466/HaraldNordgren/at-push-simple-triangular-v1:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-git-2466/HaraldNordgren/at-push-simple-triangular-v1

@gitgitgadget-git

Copy link
Copy Markdown

"D. Ben Knoble" wrote on the Git mailing list (how to reply to this email):

On Tue, Oct 27, 2026 at 8:48 AM Harald Nordgren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> From: Harald Nordgren <haraldnordgren@gmail.com>
>
> With push.default left at "simple", a branch whose upstream is on one
> remote and whose push remote is another gets pushed by "git push" to a
> branch of the same name on the push remote. But @{push} for that
> branch fails with "cannot resolve 'simple' push to a single
> destination".

At first I was a bit confused about the need for a different push
remote, but then I realized: simple's documentation says that if the
push and pull remote are the *same* you need the upstream to have the
same name! So in the differing case, it might behave quite differently
than I'm used to.

> "git push" learned to handle "simple" this way in ed2b18292b (push:
> change `simple` to accommodate triangular workflows, 2013-06-19). When
> @{push} was added in e291c75a95 (remote.c: add branch_get_push,
> 2015-05-21), its "simple" case only covered pushing back to the remote
> the branch is fetched from, where the upstream must have the same name.
>
> Make @{push} follow "git push". When the push remote is not the remote
> of the branch, resolve to the branch of the same name on the push
> remote, as "current" does.

Whatever else we do, making @{push} work in more scenarios seems good
to me so we can be consistent.

> The test added in 9bf9eed093 (remote: fix leak in branch_get_push_1()
> with invalid "simple" config, 2026-01-19) relied on a different push
> remote being invalid for "simple". Use an upstream with a different
> name on the same remote instead, so that it still exercises the error
> path.
>
> Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
> ---
>     remote: resolve "simple" @{push} when pushing to another remote
>
>     With push.default=simple and remote.pushDefault pointing at a fork, git
>     push pushes the branch to the fork, but @{push} fails with "cannot
>     resolve 'simple' push to a single destination".
>
>     So git status cannot compare the branch with where it was pushed. Make
>     @{push} resolve to where git push goes.
>
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2466%2FHaraldNordgren%2Fat-push-simple-triangular-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2466/HaraldNordgren/at-push-simple-triangular-v1
> Pull-Request: https://github.com/git/git/pull/2466
>
>  remote.c                  |  4 ++++
>  t/for-each-ref-tests.sh   |  4 +++-
>  t/t1514-rev-parse-push.sh | 11 +++++++++++
>  3 files changed, 18 insertions(+), 1 deletion(-)
>
> diff --git a/remote.c b/remote.c
> index 49ee2ea318..fd434a3a10 100644
> --- a/remote.c
> +++ b/remote.c
> @@ -2016,6 +2016,10 @@ static char *branch_get_push_1(struct repository *repo,
>                         const char *up;
>                         char *cur;
>
> +                       if (strcmp(remote->name,
> +                                  remotes_remote_for_branch(remote_state, branch, NULL)))
> +                               return tracking_for_push_dest(repo, remote, branch->refname, err);

This certainly matches the logic described in the log message, and I
think it's probably right based on that description. One thing I can't
decide is whether it should come before or after the current set of
cases, and I'm undecided. On one hand, it's a new case, so I would
think it should come after. OTOH, it's a fairly straightforward case,
and I usually like discharging those early so I can focus on the
complex stuff.

So, I would say this is probably fine as-is?

[Skipping the tests for now]

@gitgitgadget-git

Copy link
Copy Markdown

User "D. Ben Knoble" <ben.knoble@gmail.com> has been added to the cc: list.

@gitgitgadget-git

Copy link
Copy Markdown

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

> So, I would say this is probably fine as-is?

Thanks!


Harald

@gitgitgadget-git

Copy link
Copy Markdown

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

"D. Ben Knoble" <ben.knoble@gmail.com> writes:

>> diff --git a/remote.c b/remote.c
>> index 49ee2ea318..fd434a3a10 100644
>> --- a/remote.c
>> +++ b/remote.c
>> @@ -2016,6 +2016,10 @@ static char *branch_get_push_1(struct repository *repo,
>>                         const char *up;
>>                         char *cur;
>>
>> +                       if (strcmp(remote->name,
>> +                                  remotes_remote_for_branch(remote_state, branch, NULL)))
>> +                               return tracking_for_push_dest(repo, remote, branch->refname, err);
>
> This certainly matches the logic described in the log message, and I
> think it's probably right based on that description. One thing I can't
> decide is whether it should come before or after the current set of
> cases, and I'm undecided. On one hand, it's a new case, so I would
> think it should come after. OTOH, it's a fairly straightforward case,
> and I usually like discharging those early so I can focus on the
> complex stuff.

I actually wondered if the existing case for "simple" becomes a
subset of this new code?  After all, tracking_for_push_dest() asks
where the thing gets pushed to, no?

In any case, all the case arms in this function is getting a bit
unmanageably long and this block is, with an extra artificial block,
unnecessarily deeply nested by one extra level.  I wonder if we can
move the code for "case PUSH_DEFAULT_SIMPLE" case to a separate
function and make it a bit more readable?

I agree that the end result the commit wants to see is sensible and
consistent with the earlier "what should happen with 'simple' when
triangular?" topic.

Thanks.

The "simple" arm is the longest in the switch of branch_get_push_1()
and needs an extra block to declare its variables. Give it its own
function so that it reads at one level of indentation and the next
commit can teach it about pushing to another remote without nesting
it further.

No functional change.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
@HaraldNordgren
HaraldNordgren force-pushed the at-push-simple-triangular branch 2 times, most recently from 90b2052 to 1c3809c Compare October 29, 2026 22:06
With push.default left at "simple", a branch whose upstream is on one
remote and whose push remote is another gets pushed by "git push" to a
branch of the same name on the push remote. But @{push} for that
branch fails with "cannot resolve 'simple' push to a single
destination". As a result "git status" shows the "git push" hint under
the comparison with the upstream, even though "git push" goes
elsewhere, and leaves out the comparison with the push branch when
status.compareBranches asks for it.

"git push" learned to handle "simple" this way in ed2b182 (push:
change `simple` to accommodate triangular workflows, 2013-06-19). When
@{push} was added in e291c75 (remote.c: add branch_get_push,
2015-05-21), its "simple" case only covered pushing back to the remote
the branch is fetched from, where the upstream must have the same name.

Make @{push} follow "git push". When the push remote is not the remote
of the branch, resolve to the branch of the same name on the push
remote, as "current" does.

The test added in 9bf9eed (remote: fix leak in branch_get_push_1()
with invalid "simple" config, 2026-01-19) relied on a different push
remote being invalid for "simple". Use an upstream with a different
name on the same remote instead, so that it still exercises the error
path.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
@HaraldNordgren
HaraldNordgren force-pushed the at-push-simple-triangular branch from 1c3809c to c58afcc Compare October 29, 2026 22:45
@gitgitgadget-git

Copy link
Copy Markdown

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

> >> +                       if (strcmp(remote->name,
> >> +                                  remotes_remote_for_branch(remote_state, branch, NULL)))
> >> +                               return tracking_for_push_dest(repo, remote, branch->refname, err);
> >
> > This certainly matches the logic described in the log message, and I
> > think it's probably right based on that description. One thing I can't
> > decide is whether it should come before or after the current set of
> > cases, and I'm undecided. On one hand, it's a new case, so I would
> > think it should come after. OTOH, it's a fairly straightforward case,
> > and I usually like discharging those early so I can focus on the
> > complex stuff.
>
> I actually wondered if the existing case for "simple" becomes a
> subset of this new code?  After all, tracking_for_push_dest() asks
> where the thing gets pushed to, no?

Yes, both resolve to the same branch. The same remote case only adds a
check that the upstream has the same name, because "git push" refuses
otherwise.

> In any case, all the case arms in this function is getting a bit
> unmanageably long and this block is, with an extra artificial block,
> unnecessarily deeply nested by one extra level.  I wonder if we can
> move the code for "case PUSH_DEFAULT_SIMPLE" case to a separate
> function and make it a bit more readable?

Good point.


Harald

@HaraldNordgren

Copy link
Copy Markdown
Contributor Author

/submit

@gitgitgadget-git

Copy link
Copy Markdown

Submitted as pull.2466.v2.git.git.1793433841.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-git-2466/HaraldNordgren/at-push-simple-triangular-v2

To fetch this version to local tag pr-git-2466/HaraldNordgren/at-push-simple-triangular-v2:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-git-2466/HaraldNordgren/at-push-simple-triangular-v2

@gitgitgadget-git

Copy link
Copy Markdown

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

"Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com> writes:

> With push.default=simple and remote.pushDefault pointing at a fork, git push
> pushes the branch to the fork, but @{push} fails with "cannot resolve
> 'simple' push to a single destination".
>
> So git status cannot compare the branch with where it was pushed. Make
> @{push} resolve to where git push goes.
>
> Changes in v2:
>
>  * Moved the "simple" @{push} code out of branch_get_push_1() into its own
>    function in a new preparatory commit.

Looking good.  Will queue.

Thanks.

@gitgitgadget-git

Copy link
Copy Markdown

This branch is now known as hn/remote-simple-push-triangular.

@gitgitgadget-git

Copy link
Copy Markdown

This patch series was integrated into seen via e8421d3.

@gitgitgadget-git gitgitgadget-git Bot added the seen label Nov 2, 2026
@gitgitgadget-git

Copy link
Copy Markdown

There was a status update in the "New Topics" section about the branch hn/remote-simple-push-triangular on the Git mailing list:

The 'simple' push resolution in 'git rev-parse @{push}' has been
updated to mirror the behavior of 'git push'. When the configured push
remote differs from the fetch remote, it now resolves to the branch of
the same name on the push remote instead of failing.

Will merge to 'next'?
cf. <xmqq5wyh2w8m.fsf@gitster.g>
source: <pull.2466.v2.git.git.1793433841.gitgitgadget@gmail.com>

@gitgitgadget-git

Copy link
Copy Markdown

This patch series was integrated into next via 3d87058.

@gitgitgadget-git gitgitgadget-git Bot added the next label Nov 4, 2026
@gitgitgadget-git

Copy link
Copy Markdown

There was a status update in the "Cooking" section about the branch hn/remote-simple-push-triangular on the Git mailing list:

The 'simple' push resolution in 'git rev-parse @{push}' has been
updated to mirror the behavior of 'git push'. When the configured push
remote differs from the fetch remote, it now resolves to the branch of
the same name on the push remote instead of failing.

Will merge to 'master'.
cf. <xmqq5wyh2w8m.fsf@gitster.g>
source: <pull.2466.v2.git.git.1793433841.gitgitgadget@gmail.com>

@gitgitgadget-git

Copy link
Copy Markdown

There was a status update in the "Cooking" section about the branch hn/remote-simple-push-triangular on the Git mailing list:

The 'simple' push resolution in 'git rev-parse @{push}' has been
updated to mirror the behavior of 'git push'. When the configured push
remote differs from the fetch remote, it now resolves to the branch of
the same name on the push remote instead of failing.

Will merge to 'master'.
cf. <xmqq5wyh2w8m.fsf@gitster.g>
source: <pull.2466.v2.git.git.1793433841.gitgitgadget@gmail.com>

@gitgitgadget-git

Copy link
Copy Markdown

There was a status update in the "Cooking" section about the branch hn/remote-simple-push-triangular on the Git mailing list:

The 'simple' push resolution in 'git rev-parse @{push}' has been
updated to mirror the behavior of 'git push'. When the configured push
remote differs from the fetch remote, it now resolves to the branch of
the same name on the push remote instead of failing.

Will merge to 'master'.
cf. <xmqq5wyh2w8m.fsf@gitster.g>
source: <pull.2466.v2.git.git.1793433841.gitgitgadget@gmail.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant