Subject: 'simple' push check that branch name matches does not work if push.default is unset (and hence implicitly simple)

From: Adam Williamson <[email-masked]>
Date: Wed, 26 Nov 2014 14:29:28 -0800

Hi, folks. Ran into an unfortunate issue with git which helped me mess
up a Fedora package repo today :/

The problem can be reproduced thus:

1. Create an empty repo, clone it
2. Push its master branch with something in it (just to get started)
3. git branch --track moo origin/master
4. git checkout moo
5. echo moo >> moo && git commit -a -m "create moo"
6. git push
** BUG HAPPENS - CHANGES ARE PUSHED TO origin/master **
7. git config --local push.default simple
8. echo moo2 >> moo && git commit -a -m "update moo"
9. git push
** PUSH IS CORRECTLY REJECTED **

In both those cases, the push behaviour is supposed to be 'simple' - at
step 6 it's *implicitly* set to 'simple' (according to the
documentation), while at step 9 it's *explicitly* set to 'simple'. At
step 6, a warning is printed to the console:

=============

warning: push.default is unset; its implicit value has changed in
Git 2.0 from 'matching' to 'simple'. To squelch this message
and maintain the traditional behavior, use:

  git config --global push.default matching

To squelch this message and adopt the new behavior now, use:

  git config --global push.default simple

When push.default is set to 'matching', git will push local branches
to the remote branches that already exist with the same name.

Since Git 2.0, Git defaults to the more conservative 'simple'
behavior, which only pushes the current branch to the corresponding
remote branch that 'git pull' uses to update the current branch.

See 'git help config' and search for 'push.default' for further information.
(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode
'current' instead of 'simple' if you sometimes use older versions of Git)

==============

If you follow the trail there and look at 'git help config', you find
this:

============

           ·   simple - in centralized workflow, work like upstream with an
               added safety to refuse to push if the upstream branch’s name is
               different from the local one.

===========

However, at step 6, the changes from branch 'moo' are pushed to 'master'
- even though that text clearly says they shouldn't be, as the names
don't match.

After step 7 - *explicitly* setting push.default to simple, rather than
relying on it being set to simple *implicitly* - another git push is
correctly rejected, with this message:

============

fatal: The upstream branch of your current branch does not match
the name of your current branch.  To push to the upstream branch
on the remote, use

    git push origin HEAD:master

To push to the branch of the same name on the remote, use

    git push origin moo

=============

I believe the 'implicit' case was broken by the commit "push: change
`simple` to accommodate triangular workflows":

https://github.com/git/git/commit/ed2b18292bfeedc98c9e2b6bd8a35d8001dab2fc

It changes the condition for running the 'does the branch name match'
test from "if (simple)" to "if (push_default == PUSH_DEFAULT_SIMPLE)".
AFAICS, in the 'implicit' case, push_default ==
PUSH_DEFAULT_UNSPECIFIED, not PUSH_DEFAULT_SIMPLE, so the 'does the
branch name match' check is not run, even though the behaviour is
supposed (according to the documentation) to be the same as if the
default were explicitly set to 'simple'.

Thanks to this, when I accidentally did 'git push' on a branch of the
Fedora kernel package git repo which only exists in my downstream
checkout, all my changes got pushed to the upstream master branch :( So
it's a bit dangerous.
-- 
Adam Williamson
Fedora QA Community Monkey
IRC: adamw | Twitter: AdamW_Fedora | XMPP: adamw AT happyassassin . net
http://www.happyassassin.net