The risk and return of git rebase
Rebase is a formidable tool, that changes the quality of your work. Yet, it carries substantial risks. In this article, I’ll weave together my moments of inspiration with rebase and the lessons learned from my most significant missteps, showcasing the compelling allure and inherent dangers of this command.
Merge
and Rebase
Difference between Merge
The merge command combines two branches by creating a new "merge commit" that ties together the changes from both branches. It takes the changes from the source branch and applies them to the target branch while maintaining the full history of both branches.
// from develop branch
git checkout -b feature
// after develop code and push changes
// update develop branch and merge
git pull origin develop:develop
git merge develop
Characteristics of Merge:
- Preserves History: Merge keeps the entire history intact, reflecting the full timeline of when and how changes were introduced.
- New Merge Commit: It results in an additional commit, known as the "merge commit," that represents the joining of the two branches.
Use Case:
Merge is ideal when you want to maintain a complete and accurate history of the development process, especially for collaborative projects with multiple contributors.
Rebase
The rebase command integrates changes by reapplying commits from one branch on top of another. Instead of creating a merge commit, rebase rewrites the commit history to make it appear as if the changes from the feature branch were made directly on top of the latest commits in the target branch.
// from develop branch
git checkout -b feature
// after develop code and push changes
// update develop branch and merge
git pull origin develop:develop
git rebase develop
Characteristics of Rebase:
- Rewrites History: Rebase rewrites the commit history so that it appears as though the feature branch was created from the latest state of the main branch.
- No Merge Commit: Rebase does not create an extra merge commit, making the commit history linear and cleaner.
Use Case:
Rebase is useful when you want to keep the project history clean and linear, especially when working solo or in projects with strict commit policies.
Pros and Cons
Merge | Rebase | |
---|---|---|
pros | Preserves context | Linear history |
cons | Commit clutter | History rewriting |
rebase
The reason I choose - Once used only
merge
. That was enough for my work. - Many saids
rebase
is risky and felt lazy to learn. - One day, couldn't resolve the judge of
This branch is out-of-date
after multiple times ofmerge
-
rebase
resolved it by cleaning oldmerge
commits - Now believe in
rebase
.
Powerfulness of rebase
Interactive mode
One of the standout features of git rebase is its interactive mode (git rebase -i), which allows for precise control over commit history.
git rebase -i HEAD~n // amend n commits from latest
or
git rebase -i <commit_id> // start ammend from latest to <commit_id>
- Pop up text editor in your terminali (like vim).
You can also reorder the commits by sorting the lists.
// change "pick" to something you wanna do.
pick 1234567 Commit message for the first commit
pick 89abcde Commit message for the second commit
pick fghijkl Commit message for the third commit
# Rebase 1234567..fghijkl onto 1234567 (3 commands)
#
# Commands:
# pick = use commit
# reword = use commit, but edit the commit message
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
# fixup = like "squash", but discard this commit's message
# exec = run command (the rest of the line is a shell command)
# drop = remove commit
- Close the editor and force-push to the remote branch
// need -f option to push
git push -f
Double-edged sword
Felt like invincible with rebase
, and believed it omnipotent and reliable. But I learned it could counter-attack under some conditions and throw the fruit your labor into void.
Tried to remove the old merge commits but some time merge commits looks like enormous like below:
// change "pick" to something you wanna do.
pick 1234567 someone's commit 1
pick 89abcde someone's commit 2
pick fghijkl someone's commit 3
pick 1234567 someone's commit 4
pick 81abcde someone's commit 5
pick fghllil someone's commit 6
pick 1pi4567 someone's commit 7
pick 80mkcde someone's commit 8
pick m07ijkl someone's commit 9
.
.
.
pick sda3d6f someone's commit 199
pick jklfghi someone's commit 200
pick linpjkl my commit // I didn't notice it...
# Rebase 1234567..fghijkl onto 1234567 (3 commands)
#
# Commands:
# pick = use commit
# reword = use commit, but edit the commit message
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
# fixup = like "squash", but discard this commit's message
# exec = run command (the rest of the line is a shell command)
# drop = remove commit
I thought it only include the merge commits and replaced all to "drop" and carelessly force-pushed to remote branch.
I lost everything, and in that instant, the depth of my own ignorance hit me like a thunderbolt.
Look before leap
In this way, rebase possesses the power to alter history and plunge everything into oblivion. Before being bitten by the claws of rebase, keep the following points in mind:
- Exercise caution akin to when using
rm -rf
. - Perform rebases only on personal feature branches. Avoid doing so lightly on develop or main.
- Familiarize yourself with the recovery methods that will be introduced later.
Recovery
If your rebase goes awry, take a deep breath, make yourself a nice cup of tea, and relax for a moment. Don’t panic—if you haven’t gone on a wild commit spree and tangled up your branch beyond recognition, there’s still hope. Once you've settled down, try the following steps. You've got this, as long as you avoid making things worse in a fit of confusion!
PlanA: Backup branch
The most secure way would be utilizing backup branch. You can reset your devastiated branch if you are careful and prepared beforehand.
// create backup_branch
git checkout <feature_branch>
git checkout -b backup_<feature_branch>
// work on feature_branch
git checkout <feature_branch>
// failture something like rebase or reset --hard
// recovery from backup_branch
git reset --hard backup_<feature_branch>
git push -f
PlanB: Reflog
Totally had a mishap and forgot to back up? No need to throw in the towel just yet! You can always turn to git reflog, your trusty time machine for HEAD. With a little ingenuity, you can rewind the clock and rescue your lost commits. So, don’t despair—adventure awaits in your command line!
git reflog
then it shows like:
e5f4a3c HEAD@{0}: rebase finished: returning to refs/heads/feature-branch
e5f4a3c HEAD@{1}: rebase: squash 1a2b3c4 Fix typo in README
1a2b3c4 HEAD@{2}: commit: Add new feature implementation // revert to this commit
c3d2e1f HEAD@{3}: merge main: Merge made by the 'recursive' strategy.
b2c1a0f HEAD@{4}: commit: Update documentation
a3f2e1b HEAD@{5}: commit: Initial commit
pick the target commit id and reset to it.
// get commit id to roll back
git reset --hard 1a2b3c4
Conlution
Rebase is an incredibly versatile and powerful command, Just like katana. However, it also harbors a terrifying ability to erase everything in an instant. It’s essential to respect its power without abusing it, nor should we fear it blindly. By mastering its applications and recovery techniques, we can become proficient engineers — true samurai in the realm of coding.
Discussion