Git Merge Fast-Forward vs Git Rebase
Asked Answered
Y

2

28

What is the difference between a fast-forwarded git merge and a git rebase? Don't both accomplish keeping history linear and no merge commits? If so, why would one use one over the other? If not, which is what I think is true, what is the whole story that I'm not seeing?

Thanks!

Yep answered 7/1, 2022 at 22:16 Comment(5)
They are completely different.Sennit
Merging (in the general case) produces branch-and-merge history. As a special case, what Git calls a fast-forward merge (which is not a merge at all) doesn't. Since that's what you're asking about—fast-forwards—they wind up doing the same thing.Azeotrope
@torek, so fast-forward and rebase do the same thing?Yep
Not in general, but for this particular case yes.Azeotrope
Ahh, OK. Time to read up about fast-forward then.Yep
A
60

When you are ahead of main, both do the same thing.

If you're ahead and behind main, then a fast-forward merge isn't possible, since there are newer commits on master. But in that case, you can rebase, creating new commits based on the commits that are ahead of main.

When you're ahead of main:

            *E-*F-*G-*H-*I    BRANCH
           /
*A-*B-*C-*D                   MAIN

When you do a fast forward merge the main pointer is moved forward to the tip of the branch. When you rebase each commit of the branch is moved after the head of MAIN. The end result is the same:

*A-*B-*C-*D-*E-*F-*G-*H-*I MAIN | BRANCH

When you are ahead as well as behind of MAIN:

            *E-*F-*G-*H-*I    BRANCH
           /
*A-*B-*C-*D-*J-*K             MAIN

Then you can't fast-forward merge E..I into main, since J..K are in the way. So fast-forward isn't possible in this case.

But you can rebase E..I onto K:

*A-*B-*C-*D-*J-*K-*E'-*F'-*G'-*H'-*I'             MAIN | BRANCH

But what happens is that a new commit is made containing the changes of E and appended to main, then a new commit is made with the changes of F and appended to main... etc etc until all commits from the branch have been "moved"/"replayed" on the other branch. The result is again a single line of history with no branches and merges.

Because the commits had to be re-applied and conflicts potentially resolved, the actual commits will change, a new commit-id generated etc.

Ardent answered 7/1, 2022 at 22:24 Comment(0)
T
11

Yes, fast-forwarding is a way of maintaining linear history without merge commits.

What is the difference between a fast-forwarded git merge and a git rebase

When git performs a fast-forward, it is the same thing whether you are using git merge or git rebase.
However, merging 2 branches using git merge will not always be a fast-forward. In this case, you use git rebase to prepare commit history in the merging branch so that you get to achieve a fast-forward merge.

When fast-forward is possible with git merge

Let us have a scenario: You are working on your own project. Somewhere along, you want to add a feature you have thought of. It is somewhat challenging and you need to do it in a separate branch to see if you can build it.

Your project has a main branch called develop. And to build this feature, you create a branch called feature and switch to it. You work on this feature and make several commits. After some while of hacking, you have managed to build it.

At this moment, you project history is like this:

evolved history of feature branch

Actually, develop branch has not had a new commit since you shifted to building and committing in the feature branch. Therefore, there is no divergent work, i.e feature branch is directly ahead of develop. When we merge feature into develop using:

git checkout develop
git merge feature

We will get a fast-forward merge. Meaning, Git will not generate a merge commit but just point develop branch to the tip of feature branch:

fast forward merge

Using git rebase to make Git perform fast-forward merge

In the second scenario: You have more features to build for your project. And you bring in more developers and give them a feature to build in their respective feature/topic branches. Yourself, you are building yet another feature in feature-2 branch.

At some point, you have finished your feature. But at this time, other developers you assigned them features to build, have done so and already merged their work to the develop branch:

Feature branch before rebase

Right now if you merge like:

git checkout develop
git merge feature-2

You won't have a fast-forward merge because develop has evolved with newer commits since you last branched off. To make a fast-forward merge possible, you have to change the base of feature-2 branch onto the latest commit on develop branch, like:

git rebase develop feature-2 #checkout feature-2 then rebase

And this is now the history:

Feature branch after rebase

Something about git rebase is that it will change the base of a branch and reapply its commits with new IDs in the order they occured. Hence the commits illustrated as c3'...c5' in the image.

Once we have rebased feature-2 branch, we can now merge it into develop:

git checkout develop
git merge feature-2

And the result should be a fast-forward. The commits of feature-2 branch are appended to the top of everything else in develop branch:

enter image description here

If so, why would one use one over the other?

When we are certain that git will fast forward changes when merging branches, the merge command: git checkout develop; git merge feature,will yield the same project history as the rebase command: git rebase feature develop. So there is no reason to use one over the other(If the two branches can be fast-forwaded). Just your preference.

Tyler answered 18/8, 2023 at 21:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.