rewinding git pull

2012/07/09

If you’re using a rebase strategy for the first time you may run git pull in a situation where Git practically tells you to do it, but you don’t actually want to do it.

The situation is described below, and the method to unwind it follows. I’ve added pictures of the branches between hashes to make it clear what the state of the world is at any given point prior to or after running a particular command, which I hope makes it easier to follow.

And then you want to push your updated topicA, with your new commits and the new commit from origin/master up to origin:

The following assumes you have git config.push default upstream set. This configuration parameter limits the branch that git will attempt to push. I highly recommend you set that value as the default is to push all branches, which you probably do not want.

First, Git tells you the push was rejected because you would have lost history. Then comes the troublesome line: “Merge the remote changes (e.g. ‘git pull’) before pushing again.”

If you were working on master and made some commits, and someone else made some commits on master as well and pushed before you did, you would end up with the same situation as above. The push would be rejected because you don’t have the commits that the other person made. Thus, if your push were to succeed their work would be lost on origin/master. This is what Git is referring to when it says “To prevent you from losing history..” The history that would be lost would be the work the other person did.

In the situation we’ve been building up, making commits and rebasing topicA, you’re the only person committing to the branch and the history you would “lose” is the set of pre-rebase commits that you pushed to the remote originally (in this case, E). You’ve overwritten E with E’ during the rebase, and Git doesn’t want you to lose the E that’s on the remote.

What you should do in this situation is git push -f to force-push the branch. You know you’re going to lose E that’s on the remote, but that’s fine because you have E’ on your local topicA. You intend to replace whatever is on the remote with whatever you have locally.

The problem is that it says to do a git pull, which will pull the remote E into your local, which you don’t want. That will give you a merge commit, I:

If the result from git log @{u} is empty or you don’t get “# Your branch is ahead of ‘origin/master’ by commit(s)” message after git status, you’re OK to git pull. In no other case do you need to, or should you, git pull.

I generally recommend always working on a topic branch and keeping master clean to avoid accidentally running git push -f on master, and to enforce the idea that after you fetch, you’re rebasing on top of origin/master directly instead of doing git pull --rebase while on master, which hides the fact that you’re rebasing on top of origin/master.