182

I have rebased a branch locally which was already pushed.

Git is advising that my branch and remote have diverged and that:

"and have 109 and 73 different commits each, respectively"

Will pushing my branch resolve this - i.e. is this to be expected after a rebase?

Marty Wallace
  • 34,046
  • 53
  • 137
  • 200

4 Answers4

228

When you rebase a branch, you have to rewrite the commits for any commit which is above the commits in the branch onto which you are rebasing. This is because one of the properties of a commit is its parent (or parents). When you rebase, you're changing the parent of the oldest local commit on your branch - and thus changing the commit hashes of all of your local commits, since this change bubbles up through the commits transitively.

Since you'd already pushed the branch, you should have merged in the source branch, rather than rebasing against it. It is possible to "force push" your new branch (using the -f flag), but a normal push won't work, because the integrity of the branches history will be disturbed. If you are collaborating with others on this branch, force pushing is a bad idea, as it will cause other collaborators to become very confused when their history suddenly doesn't match.

TL;DR - If you're not collaborating, push the branch using push -f. If you are, reset the branch to the previous state, and merge in the source branch, instead.

Jason LeBrun
  • 13,037
  • 3
  • 46
  • 42
  • 5
    "Since you'd already pushed the branch, you should have merged in the source branch" - why is that? – hammett Mar 24 '14 at 16:13
  • 2
    @HamiltonVerissimo Jason's first sentence: "When you rebase a branch, you have to rewrite the commits for any commit which is above the commits in the branch onto which you are rebasing." Even though the changes captured in the "above" commits have the same logical content, they are being applied to a different base and therefore are different commits with different hashes. If other developers are working from the pre-rebased branch then doing a git push -f would be extremely disruptive to their workflow. Thus, since this branch has been pushed to a public source he should have merged. – awolf May 13 '14 at 20:44
  • 9
    you can aswell use push --force-with-lease if you are not sure if anybody else did already push something. – quadroid Dec 16 '15 at 11:23
  • 1
    @jason-lebrun Isn't the downside to merging the upstream changes that you can overwrite your own work without warning? Are merge conflicts detected in the same way as when rebasing? For instance, if I remove a section from a file in my branch because it's no longer needed, but someone else made a trivial change to the same section on the upstream, like removing some whitespace or some global find/replace action, wouldn't merging that on top of my branch replace my deletion with their trivially-changed version? – Chris Bloom Mar 10 '16 at 14:20
  • If the same section of a file is changed in your branch and the upstream branch, a merge conflict will be noted, and you'll have a chance to resolve it. – Jason LeBrun Mar 10 '16 at 19:57
  • If your manager gets all antsy if you push -f, create a new branch, push that and delete the old one. – Rich Aug 24 '17 at 23:33
  • 1
    Is merging the other branch to yours not a bad idea? E.g. Merging master into a feature branch - will that not create a new commit for that? Will that not result in an extra commit once this feature branch if merge to master later on? – Web Dev May 15 '18 at 22:54
79

All your commits have changed ids, so the diversion is not truly a diverge.

To get your problem resolved you have to overwrite your remote branch:

git push -f origin experiment

http://git-scm.com/book/ch3-6.html

Explanation:

See how in this image C3 is not put as C3 after the rebase, but as C3'. This is because it is not exactly C3, but it has all of its code changes.

Rebase

On this other image you get the picture of what a rebase is seen when a remote is involved, and why there is a diversion.

diverge and git push

In any case, after you do the forced push, it will tell you that it did a (force update), you should be fine at that point.

Checkout the link at the top, and search for "git push --force". You will see a more detailed explanation.

mimoralea
  • 9,590
  • 7
  • 58
  • 59
  • 5
    Yup. I guess this solves the problem. But, force push may not work when you are working with in a team when your push may overwrite any recent work pushed by others. Am I right? – Hari Krishna Ganji Nov 23 '14 at 13:56
  • 1
    It might not have the desired effects. Make sure you merge your changes 'fast forward' before doing the rebase. – mimoralea Nov 23 '14 at 20:34
7

I had success with the rebase diverge for a push by doing the following:

git checkout mybranch
git pull
git push origin mybranch

The pull resolved the diverge.

BEFORE the pull

Your branch and 'origin/mybranch' have diverged,
and have 2 and 1 different commit(s) each, respectively.

PULL output

Merge made by recursive. mypath/myfile.py | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-)

AFTER pull

Your branch is ahead of 'origin/mybranch' by 3 commits.

AFTER PUSH

mybranch is 3 ahead of the branch the still has an open pull request merge message added to the commit history Merge branch mybranch of remote into mybranch

I am assuming this is probably what the force push does, and I have not verified.

As the others have said, avoid a rebase if you already have an open pull request. I am providing this example as something that worked for me.

zerocog
  • 1,703
  • 21
  • 32
6

This can be fixed without a force push by rebasing the target branch into your current local branch, switching to your target branch, and then rebasing your local branch into the target. This doesn't diverge since the commit(s) that may be missing are added and don't need to be created anymore. Example for easier explanation:

  1. main branch is develop
  2. You checkout a new branch feature/doing_stuff
  3. A team member pushes a new commit to develop

If you have NOT updated your develop branch then a "git checkout develop" && "git rebase feature/doing_stuff" will work correctly since no commits have been added since your checkout. However, if you have checked out develop and pulled down the new commit then you will see this divergence if you try to rebase due to a new commit being seen. An easy fix without force pushing (usually not a good idea in a team environment) is to:

  1. git checkout feature/doing_stuff
  2. git rebase develop
  3. git checkout develop
  4. git rebase feature/doing_stuff

The rebase from step 2 brings the missing commit into the feature/doing_stuff so when step 4 comes along it is up to date and does not need to create a new commit for the change.

This is a solution that I know works because I just ran into this and did the steps above to successfully push develop without forcing. I work in a team of over 50 developers so it is forbidden to force push anything other than my own testing branches so I had to find a resolution.

Chris
  • 81
  • 1
  • 2