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!
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With