Preventing spurious “error: failed to push some refs” messages from git

by

Kitten in a shoe

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).

Kitten in a shoe

This kitten changed her default git configuration. (Kitten in a Shoe from Eran Finkle's photostream)

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 specifying origin 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, setting push.default to upstream 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

  1. In Git 1.7.1, ‘upstream’ is no longer a valid option for push.default. Try ‘tracking’ instead … git config –global push.default tracking

Leave a Reply

Your email address will not be published. Required fields are marked *