threads / discuss / 62645

remote.<name>.push without dst doesn't behave as documented

Subject: remote.<name>.push without dst doesn't behave as documented

## tl;dr

4 messages between Dec 16, 2024 and Dec 19, 2024.

replies: 3people: 2as markdown or json

Yuri Kanivetsky· Dec 16, 2024, 16:47 UTC · lore
Hi,
> If git push [<repository>] without any <refspec> argument is set to update some ref at the destination with <src> with remote.<repository>.push configuration variable, :<dst> part can be omitted—such a push will update a ref that <src> normally updates without any <refspec> on the command line. Otherwise, missing :<dst> means to update the same ref as the <src>.
https://git-scm.com/docs/git-push#Documentation/git-push.txt-ltrefspecgt82308203

The only case I can think of where refspec is not passed via command line and it updates a non-matching branch is when push.default = upstream. But with remote.<repository>.push without dst it starts updating a matching branch. Either I don't understand what's meant, or it doesn't behave as documented.

The second test fails:
@test "normally a non-matching ref updates" {
    start_cloned_repo
    git config push.default upstream
    git checkout -b bb
    git branch -u origin/ba
    git commit --allow-empty -m b
    git push
    assert_equal_refs origin/ba bb
}
@test "with remote.<name>.push without dst happens what happens normally" {
    start_cloned_repo
    git config push.default upstream
    git config remote.origin.push 'refs/heads/*'
    git checkout -b bb
    git branch -u origin/ba
    git commit --allow-empty -m b
    git push
    assert_equal_refs origin/ba bb
}
start_cloned_repo() {
    (mkrepo)
    cd "$BATS_TEST_TMPDIR"
    git clone --bare a a.git
    git clone a.git b
    cd b
    git config push.default upstream
    git config user.email you@example.com
    git config user.name "Your Name"
}
mkrepo() {
    cd "$BATS_TEST_TMPDIR"
    mkdir a
    (cd a
    git init
    git branch -m ba
    git config user.email you@example.com
    git config user.name "Your Name"
    git commit --allow-empty -m a)
}

More details in a gist: https://gist.github.com/x-yuri/943fd13704b38551da36c8363d7852e1

Regards, Yuri

Junio C Hamano· Dec 16, 2024, 18:12 UTC · re: Yuri Kanivetsky · lore

Re: remote.<name>.push without dst doesn't behave as documented

Yuri Kanivetsky <yuri.kanivetsky@gmail.com> writes:
Show 6 quoted lines
>> If git push [<repository>] without any <refspec> argument is set
>> to update some ref at the destination with <src> with
>> remote.<repository>.push configuration variable, :<dst> part can
>> be omitted—such a push will update a ref that <src> normally
>> updates without any <refspec> on the command line. Otherwise,
>> missing :<dst> means to update the same ref as the <src>.

This excerpt is for <refspec>... arguments that are given on the command line, e.g., the command line would look like

    $ git push origin smart
However, if we look at the command sequnce you gave,
Show 14 quoted lines
> ...
> The second test fails:
>
> @test "normally a non-matching ref updates" {
> ...
>     git push
> ...
> }
>
> @test "with remote.<name>.push without dst happens what happens normally" {
> ...
>     git push
> ...
> }

neither of the above "git push" invocations have <refspec>... on the command line. So the rules you quoted would not apply to the above two "git push" invocations, wouldn't it?

In
    $ git push origin smart

a <refspec> "smart" is given, but "smart" lacks ":<dst>" part and only has <src> that is "smart", typically local branch "smart", iow, "refs/heads/smart".

The paragraph you quoted describes what happens with that command is designed to be similar to what "git push origin" without any <refspec> arguments does wrt the given <src> ref.

For example, if
    $ git push origin

is set to update some ref on the other side with "smart", "git push origin smart" would update the same ref on the other side. e.g., if you have

    [remote "origin"]
	push = refs/heads/*:refs/remotes/satellite/*

which would normally cause "git push origin" to use refs/heads/smart to update refs/remotes/satellite/smart, then

    $ git push origin smart

would do the same thing, i.e. send "refs/heads/smart" to "refs/remotes/satellite/smart" on the other side. If your "git push origin" is configured to do the matching push, "git push origin master" would update their "refs/heads/master" with ours, because that is what "git push origin" would do to our "master". That is what the paragraph you quoted describes, I think.

Now, the command may behave differently from how we described in the documentation when you did give <refspec> from the command line, and in that case you may have found a bug. But I do not think the @test things you had in your report triggers the paragraph you quoted from the documentation, so would not demonstrate a bug in there.

Thanks.
Yuri Kanivetsky· Dec 17, 2024, 08:12 UTC · re: Junio C Hamano · lore

Re: remote.<name>.push without dst doesn't behave as documented

So it was your doing :) (no offense meant)
https://github.com/git/git/commit/ca02465b41311fe7634acb9bb5b5c61975ef5f38
> If git push [<repository>] without any <refspec> argument is set to update some ref at the destination with <src> with remote.<repository>.push configuration variable, :<dst> part can be omitted—such a push will update a ref that <src> normally updates without any <refspec> on the command line. Otherwise, missing :<dst> means to update the same ref as the <src>.
My understanding of this was (the only phrasing that made some sense to me):
> If git push [<repository>] without any <refspec> argument __is invoked__ to update some ref at the destination with <src> __and remote.<repository>.push is set__, :<dst> part can be omitted __in remote.<repository>.push__ -- such a push will update a ref that <src> normally updates without any <refspec> on the command line. Otherwise, missing :<dst> means to update the same ref as the <src>.

As such I chose `push.default = upstream` because that's the only mode which allows non-matching refs. I expected it to allow me to discriminate between having and not having :dst in remote.<repository>.push. But without :dst in remote.<repository>.push it updated the matching ref (the second test failed).

Now I understand that what was really meant:
> If remote.<repository>.push is set and the refspec on the command line doesn't have the :dst part, then the former (remote.<repository>.push) is used as a refmap. Otherwise (if remote.<repository>.push is not set), then missing :dst makes it push to the matching branch.
I believe the phrasing in the documentation calls for being changed.

But also I can't confirm the "otherwise" part. Below the last test fails. Either you have to (unless I'm missing something) say:

> If remote.<repository>.push is not set, then it falls back to the `push.default` behavior.

Or make it really push to the matching branch even if `push.default = upstream` and the upstream is a non-matching branch.

Regards, Yuri

--

The whole file is in a gist: https://gist.github.com/x-yuri/943fd13704b38551da36c8363d7852e1#file-b-md

# the state of the cloned repository (1 commit, 1 local branch ba) # a (HEAD -> ba, origin/ba)

@test "normally it acts according to push.default" {
    start_cloned_repo
    git commit --allow-empty -m b
    git push
    assert_equal_refs origin/ba ba
}
@test "but remote.<repository>.push changes the destination when the
refspec on the command line doesn't have :dst" {
    start_cloned_repo
    git config remote.origin.push 'refs/heads/ba:refs/heads/bb'
    git commit --allow-empty -m b
    git push origin ba
    assert_equal_refs origin/bb ba
}
@test "specifying :dst overrides remote.<repository>.push" {
    start_cloned_repo
    git config remote.origin.push 'refs/heads/*:refs/heads/*'
    git commit --allow-empty -m b
    git push origin ba:bb
    assert_equal_refs origin/bb ba
}
@test "if remote.<repository>.push is not set, it pushes to the
matching branch" {
    start_cloned_repo
    git config push.default upstream
    git push origin ba:bb
    git branch -u origin/bb
    git commit --allow-empty -m b
    git push origin ba
    assert_equal_refs origin/ba ba
}
start_cloned_repo() {
    (mkrepo)
    cd "$BATS_TEST_TMPDIR"
    git clone --bare a a.git
    git clone a.git b
    cd b
    git config user.email you@example.com
    git config user.name "Your Name"
}
mkrepo() {
    cd "$BATS_TEST_TMPDIR"
    mkdir a
    (cd a
    git init
    git branch -m ba
    git config user.email you@example.com
    git config user.name "Your Name"
    git commit --allow-empty -m a)
}
Yuri Kanivetsky· Dec 19, 2024, 03:48 UTC · re: Yuri Kanivetsky · lore

Re: remote.<name>.push without dst doesn't behave as documented

I guess I need to add, that I hoped that the beginning would be taken as a joke. If it wasn't the case, then sorry :( I didn't mean to offend anyone.

Regards, Yuri

← back to recent threads