Git pushes can be surprising

I was recently working on an open source project (tryfi/hass-tryfi - A Home Assistant integration for pulling data from my dog’s collar using the TryFi API and I found out that Git pushes can behave in a surprising way after I accidentally pushed a bunch of testing commits to the wrong branch.

Background

In my workspace, I had two different remotes. One that tracked my own testing repo and one that was the official repo:

1
2
3
4
5
$ git remote -v   
official    https://github.com/tryfi/hass-tryfi.git (fetch)  
official    git@github.com:tryfi/hass-tryfi.git (push)  
origin      https://github.com/ajacques/hass-tryfi.git (fetch)  
origin      git@github.com:ajacques/hass-tryfi.git (push)

I had two branches: master (upstream to origin/master) and official-master (upstream to officialf/master). This enabled me to do testing work on my own repo and push it and when I thought it was ready, merge into the official repo. Yes these names are confusing. I’ll fix it later.

What was the surprise?

All hell broke loose when I was on my main-master branch and wanted to push some small commits to the tryfi/hass-tryfi repo:

1
git push official master

What I assumed would happen is that it would take my main-master branch commits and push them to main/master. That would make sense wouldn’t it? Turns out, that’s not what happened. It took all the juicy, barely tested commits on my master branch of the same name and pushed them to main/master.

Git Config

After cleaning up my mess, I then tried to figure out what I did wrong. Looking at the Git docs shows an interesting git config option: push.default that “Defines the action git push should take if no refspec is given (whether from the command-line, config, or elsewhere).” It defines the following options:

  • nothing - do not push anything (error out) unless a refspec is given. This is primarily meant for people who want to avoid mistakes by always being explicit.
  • current - push the current branch to update a branch with the same name on the receiving end. Works in both central and non-central workflows.
  • upstream - push the current branch back to the branch whose changes are usually integrated into the current branch (which is called @{upstream}). This mode only makes sense if you are pushing to the same repository you would normally pull from (i.e. central workflow).
  • tracking - This is a deprecated synonym for upstream.
  • simple - DEFAULT - push the current branch with the same name on the remote.
  • matching - push all branches having the same name on both ends. This makes the repository you are pushing to remember the set of branches that will be pushed out (e.g. if you always push maint and master there and no other branches, the repository you push to will have these two branches, and your local maint and master will be pushed there).

So I was using the default of simple meaning that it pushes the branch with the same name as the remote. Since I pushed to master, it pushed from my master to official/master.

In a way, I could see how some people might expect this behavior. In this situation, it was unexpected.

Instead, I want to change to use upstream to always push to the upstream branch. This can be changed with:

1
2
3
4
5
# Set for only the current repo
git config set --local push.default upstream

# Set for all repos
git config set --global push.default upstream
Copyright - All Rights Reserved

Comments

To give feedback, send an email to adam [at] this website url.

Donate

If you've found these posts helpful and would like to support this work directly, your contribution would be appreciated and enable me to dedicate more time to creating future posts. Thank you for joining me!

Donate to my blog