Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why git rebase replay the feature branch commit one by one?

Tags:

git

github

rebase

I am new to git commands and recently came over the git rebase command. I got a doubt regarding why the git rebase command replays the commits of feature branch one by one instead of just replaying the latest commit on the feature branch.

This was my scenario on a project where I was working:

M1 --- M2 --- M3 --- M4
       |
       M2 --- F1 --- F2 --- F3

We were 4 developers working on the project and I forked the project from upstream/master at point M2.

I started working on my feature and started adding commits. The commit F1 was with some basic code. Then the commit F2 was some bug fixes for the commit F1 and similarly, the commit F3 was the bug fix for code at F2 and F3 was my final feature ready code.

In the meantime, the other developers completed their feature and their code got merged into the upstream/master. Now I have to put my code on top of their feature that is M4. Now the below part is my understanding about the git rebase and I may be wrong:

I checked out the feature branch and did a git rebase with the upstream/master, which kind of put my feature branch on top of the M4 and started replaying my commits (F1, F2, and F3) one by one. I got conflicts on F1 replay and resolved them. As F2 was a bug fix on F1, I again got similar conflicts on the same place and again resolved them. Finally, I again got a similar sort of conflicts in the same place when F3 was replayed. I resolved the conflicts and everything was fine.

But instead of the replaying the commits F1, F2, F3, if git would have tried to put only the last commit of my feature branch on top of M4 then I would have to resolve the conflicts only one. [As my code in F3 would have the code for F1 and F2 as well]. So why these extra replays.

I may be missing some basic concepts here, please correct me where I am doing the wrong thinking. Thanks!

like image 823
Pankaj Avatar asked Apr 17 '19 07:04

Pankaj


2 Answers

You must tell Git to squash all commits except your last commit to achieve what you expect of having to resolve conflicts only once.

To do this, initiate an interactive rebase on top of M4 while your feature branch is checked out:

git rebase -i M4

You'll be presented with an editable snippet resembling the following:

pick 111111 F1
pick 222222 F2
pick 333333 F3

You'd then edit this snippet to tell Git to squash F2 and F3 into F1, resulting in a single commit which represents all of your changes ever since you branched off:

pick 111111 F1
squash 222222 F2
squash 222222 F3

F1 is nominated as the commit to squash into because that's how the command works (i.e. it melds the changes into the previous commit).

Save those edits, exit the snippet and you should be presented with only one conflict resolution step.

Please keep in mind that in squashing F2 and F3, you've lost those explicit change sets. If you wish to keep them, there's no alternative to resolving conflicts on each replay step.

like image 184
miqh Avatar answered Oct 19 '22 11:10

miqh


Rebase is all about applying commits on top of another one one at a time.

From the documentation:

The commits [...] are then reapplied to the current branch, one by one, in order.

So, rebase is effectively merging one commit at time on top of the previous one. Now, compare and contrast with the other kind of merging you can do in Git:

git checkout master
git merge feature

The merge command does exactly what you're asking for: it applies the cumulative changes associated to the latest commit in the feature branch on top of the latest commit in master in a single operation. This means you'll have to solve all conflicts just once.

A merge operation usually result in the creation of a merge commit on the target branch; the purpose of a merge commit is to join two (or more!) lines of history. So, while a regular commit only has one parent, merge commits naturally reference multiple parents.

M1 --- M2 --- M3 --- M4 --- MF
       |                    |
       M2 --- F1 --- F2 --- F3

Now, a merge operation doesn't necessarily have to result in a merge commit. If you want (and this is probably what you're looking for) you can merge the changes that occurred in different branches while still creating one single commit.

You can do that creating a so called squash merge:

git checkout master
git merge feature --squash

The difference here is that the combined changes from master and feature are recorded in a single commit on top of master, resulting in a history that looks like this:

M1 --- M2 --- M3 --- M4 --- MF
       |                    
       M2 --- F1 --- F2 --- F3

Where MF contains the combined changes from M4 and F3 with all the conflicts resolved.

like image 37
Enrico Campidoglio Avatar answered Oct 19 '22 12:10

Enrico Campidoglio