9

I have a my-feature branch that is pushed up to origin for code review. It is not shared. Eventually it will get merged into my develop branch which is shared amongst my team. I'd like to rebase my develop branch into my-feature to keep the history cleaner and then merge my feature branch into develop. This is what I have been doing:

$ git checkout my-feature
// do some work. make commits.

$ git rebase develop
// fix some conflicts

$ git add .

$ git rebase --continue

After I have rebased successfully, I check the status:

$ git status
On branch my-feature
Your branch and 'origin/my-feature' have diverged,
and have 155 and 1 different commit each, respectively.
  (use "git pull" to merge the remote branch into yours)

$ git what do I do here?

I have to idea what to do here. If I git pull, then I've noticed that I will get some conflicts which doesn't make sense. Some people say to force push but I'm nervous about that. Is it normal to force push my topic branch to origin? So long as no one else uses that branch?

Jeff
  • 2,293
  • 4
  • 26
  • 43

3 Answers3

17

First, you can do a git push --force at this stage without any worries: you have said your branch is not shared.
So changing its history on the server side won't affect any other user.

Second, when you rebase a branch, you change its common ancestor from with its remote tracking branch (origin/xxx)

Before git rebase develop, you have

d--d--d (develop)
    \
     x--x--x--x--x--X--y--y--y (my-feature)
                    |
       (origin/my-feature)   

X is the common ancestor between origin/feature and feature, which simply means you have added a few commits in your my-feature branch).
A git status would return something like:

On branch my-feature
Your branch is ahead of 'origin/my-feature' by 3 commit.

But when you rebase that branch on top of develop, you replay all the commits of my-feature on top of develop HEAD

        X--X--X--X--X--X--Y--Y--Y  (my-feature)
       /
d--D--d (develop)
    \
     x--x--x--x--x--X
                    |
       (origin/my-feature)   

This time, the common ancestor between my-feature and origin/my-feature no longer is a few added commit, but the full history of my-feature, plus some commits of develop! (D here)

Hence the status

Your branch and 'origin/my-feature' have diverged,
and have 155 and 1 different commit each, respectively.

Since your branch isn't shared, again, a simple git push --force origin my-feature will complete the operation:

        X--X--X--X--X--X--Y--Y--Y  (my-feature, origin/my-feature)
       /
d--D--d (develop)

(If my-feature has an upstream branch, a git push --force is enough, provided the default push policy push.default is set to simple.
Check that with git rev-parse --abbrev-ref --symbolic-full-name @{u})

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • great explanation. And then I can merge my ``feature`` into ``develop`` with no consequences? – Jeff Dec 01 '15 at 16:08
  • @Jeff yes the merge will then be a trivial fast-forward merge. – VonC Dec 01 '15 at 16:10
  • 1
    @VonC Depending on the version of git someone uses a git push --force is a bit risky. Before git 1.9.5 the git default behaviour for pushing was to push every branch from your local repository. So it is still better to define the branch explicit: git push --force origin – Eruvanos Dec 02 '15 at 09:34
  • @Eruvanos I agree. I have edited the answer to make that more visible, with a link back to http://stackoverflow.com/a/21772695/6309 – VonC Dec 02 '15 at 10:04
  • And what happens if my non-shared my-feature branch (which I've rebased off of develop and force pushed) suddenly becomes shared (someone pulls it down and begins making commits)? Am I screwed? Should I throw away the branch? – Jeff Dec 08 '15 at 14:22
  • @Jeff No need to throw away anything. Once the branch is shared, you would integrate `develop` evolution by merging `develop` to `my-feature` (instead of rebasing `my-feature` on top of `develop`): that will allow you to push `my-feature` *without* the --force. – VonC Dec 08 '15 at 14:27
  • 1
    A great explanation by using `git push --force`, it solves my problem. – Sojimaxi Oct 21 '20 at 05:03
2

What you're seeing is that my-feature is indeed different than origin/my-feature (what my-feature looked like on origin the last time you checked) because you just changed my-feature (the my-feature you're working with) when you did the rebase.

When you do a rebase, you're changing the history of a branch so you'll almost always need to --force push afterwards. It's fine if it's your branch and there aren't any collaborators using that branch.

If this feature branch does have collaborators, then you shouldn't have rebased to begin with. General rule of thumb is to not alter the history of shared branches. If this is the case, you'll need to undo the rebase, which you can refer to this post for help with.

Community
  • 1
  • 1
jaredready
  • 2,398
  • 4
  • 23
  • 50
  • Thanks. That makes sense. Sometimes I don't know if a branch will have collaborators or not so I was wondering if there was a best practice for sharing a feature branch that has been rebased. I suppose I could start a new branch and cherry pick the commits I want onto the new branch. – Jeff Nov 20 '15 at 00:34
  • 1
    If it's already been shared, then don't rebase. If you rebase before anyone takes it, then you're fine. Generally you should know if rebasing is safe or not for your situation. – jaredready Nov 20 '15 at 20:08
1

Assume you have following git-commit-structure:

A--B--C--D--E(origin/my-feature)
 \
  F--G--H--I(someotherbranch)

if you now rebase origin/my-feature on someotherbranch then you have following situation:

A--B--C--D--E(origin/my-feature)
 \
  F--G--H--I(someotherbranch)
            \
             B'--C'--D'--E'(my-feature)

that is the diverging situation. my-feature in on another branch than origin/my-feature and you are not able to fast-forward any of both branches. Now If origin/my-feature is not pulled by any person you can safly push -f. But if someone has pulled it you may have problems if that person continues working on that branch. Check that any person that has pulled your wrong origin/my-feature whether he has changed the head of the branch to your newly forced commit.

Always follow the rule: Do not rebase any pushed commit.

Bad situation:

              J--K--G(origin/otherperson)
             /
A--B--C--D--E(origin/my-feature)
 \
  F--G--H--I(someotherbranch)
            \
             B'--C'--D'--E'(my-feature)

This should then be changed into:

A--B--C--D--E(origin/my-feature) (orphaned if push-f)
 \
  F--G--H--I(someotherbranch)
            \
             \               J'--K'--G'(origin/otherperson)
              \             /
               B'--C'--D'--E'(my-feature)
Michael Mairegger
  • 6,833
  • 28
  • 41