Preventing spurious “error: failed to push some refs” messages from git
On larger Git projects, I often see the following scenario play out after someone’s done some work and is ready to push it to the remote repo:
...making and committing changes to "develop" branch...
$ git pull
Already up-to-date.
$ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 363 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:test-repo/test_repo.git
e8c1210..1a0c4d4 develop -> develop
! [rejected] new_feature -> new_feature (non-fast-forward)
error: failed to push some refs to 'git@github.com:test-repo/test_repo.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes (e.g. 'git pull') before pushing again. See the
'Note about fast-forwards' section of 'git push --help' for details.
What happened? They did the right thing and pulled before pushing, but they’re getting an error about being out of date. Running git pull
again gives Already up-to-date
again, but then pushing still gives the same error. What gives?
This isn’t really an error. By default, git push
attempts to push every local branch with a matching remote branch. The error message stems from other locally-copied branches being out of date. In the above “error” message, you can see that the develop
push actually worked correctly, only the push of the new_feature
branch failed (as it should, as it’s out of date).
There are a few options for handling this “error”:
- Checkout and pull each branch
This is a bad option. Don’t do this. I only mention it because it’s what the error message unhelpfully suggests. This is the solution when the branch you’re interested in pushing is out of date, not in this case. - Ignore it
The branch being worked on was pushed correctly, so this message can be safely ignored. However, you have to check that it was other branches causing the message and not that your copy of the branch you’re working on is out of date. - Use
git push origin develop
instead
You can fully specify which remote and which branch should be pushed, avoiding any ambiguity. A downside is that approximately 100% of git repos in the wild have only one remote and thus specifyingorigin
often becomes mere cargo-cult practice without any understanding as to why it’s necessary. Having to specify the branch also seems superfluous; when was the last time you did some work on a branch and then decided to push some other branch? - Configure git more sensibly:
git config --global push.default upstream
Instead of attempting to push all matching branches, settingpush.default
toupstream
will only push the current branch to its matching upstream branch. Why this isn’t the default is beyond me. For more information, see the man page for git config.
To my mind, git push
should mirror git pull
in only affecting the current branch. Setting it up to require more specificity or extra configuration to achieve that behavior might be useful in the rarefied world of Linux kernel development, but for everyone else using git this way makes more sense.
Postscript: On the joys of git’s documentation
Question, what does the documentation say git push
(without any arguments) does?
$ git push --help ...snip 90% of man page all the way to the EXAMPLES section... git push Works like git push <remote>, where <remote> is the current branch's remote (or origin, if no remote is configured for the current branch). # Okay, so it's equivalent to 'git push origin', what's that do? git push origin Without additional configuration, works like git push origin :. # Okay... git push origin : Push "matching" branches to origin. See <refspec> in the OPTIONS section above for a description of "matching" branches. # Sigh OPTIONS ...snip <repository> paragraph... <refspec>... ...snip 6 paragraphs... The special refspec : (or +: to allow non-fast-forward updates) directs git to push "matching" branches: for every branch that exists on the local side, the remote side is updated if a branch of the same name already exists on the remote side. # There we have it. After starting from near the end of the documentation, and traversing three levels of indirection, we've determined what the most basic form of the command does. What could be easier!
2 Comments