Git Rebase IRL
This is an old blog post I wrote back in 2018. I pick it up from the wayback machine and fixed some typo & re-wording some paragraphs.
Intro
Many people might already known that there is a command call git rebase
, but most of people don't know how to use it right.
In this article, we will see some common cases of git rebase
that you can use to make your git history clearer.
P.S. the man page of
git-rebase
also provided a very clear document. if you want to learn more aboutgit rebase,
just read it :)
When will I need it?
There is some common cases:
- To split one commit to two or more.
- To rename/delete one or more commits.
- To merge some commits into one.
- To append bunch of commits into another branch.
Split one commit to two or more
Assuming we have following commits:
ad99c7e initial commit
fb89181 implement a feature and fix coding style
7c189c8 remove bugs
You might want to split your second commit into two commits, so every commit only do one thing, that also keeps git history atomic.
First, we need to start a rebase, use git rebase -I ad99c7e
. -i means interactively rebasing.
The git rebase will use ad99c7e
as a new base, think like rebase is to create a new branch. Then we works on that branch. Once we have done our works, it destroy the original branch and use the new one to replace it. As a result, the hashes starting from ad99c7e
will be changed.
$ git rebase -I ad99c7e
Then, git
will open the editor, showing up following content:
pick fb89181 implement a feature and fix coding style
pick 7c189c8 remove bugs
This file is called git-rebase-todo
. We can now edit it to tell git how we want to do during rebase.
To split a commit, we need to edit this file. Let's change the pick
command in the first line to edit
, then save the file and exit your text editor:
edit fb89181 implement a feature and fix coding style
pick 7c189c8 remove bugs
Now, git will stop at fb89181
and waiting for you. Try use git status to see what happened.
$ git status
interactive rebase in progress; onto fb89191
You are currently editing a commit while rebasing branch 'main' on 'fb89191'.
(use "git commit --amend" to amend the current commit)
(use "git rebase --continue" once you are satisfied with your changes)
To edit this commit, first we need to discard this commit by invoking git reset --soft HEAD
.
You can now use the combination of git add
, git reset
, and git commit
to create more commit.
Once you have done, use git rebase --continue
to continue.
You might notice that starting from ad99c7e
, the hashes are different now, just like I explained above:
ad99c7e initial commit
fb89181 implement a feature
4434f2c fix coding style
45fdb3a remove bugs
Merge some commits into one
Assuming we have following commits:
fb89181 implement a feature
4434f2c fix coding style
45fdb3a remove bugs
What if I want to merge 45fdb3a
into fb89181
to hide my mistake?
Let's starting rebase by git rebase -I ad99c7e
like we have done earlier:
pick fb89181 implement a feature
pick 4434f2c fix coding style
pick 45fdb3a remove bugs
This time, we need to use the squash
command. squash
means that we want to merge that commit into previous one:
pick fb89181 implement a feature
squash 45fdb3a remove bugs
pick 4434f2c change the style
By doing this, 45fdb3a
will be merged into fb89191
. You might noticed that I changed the order of the commits. That's okay to do it. You can always changing it's order when rebasing, just be careful of conflicts.
Save it and exit the editor. Then git will prompt the text editor again, to asking you the commit message the merged commit should have:
# This is a combination of 2 commits.
# This is the 1st commit message:
implement a feature
# This is the commit message #2:
remove bugs
Let's hiding the second message. save and exit the text editor. and that's all! we have successfully merged that two commits.
To rename/delete one or more commits.
Let's take following rebase todo for example:
pick fb89181 implement a feature
pick 4434f2c fix coding style
pick 45fdb3a remove bugs
To rename a commit, just use reword. To delete a commit, just delete that line ;)
Append a bunch of commits into another branch
That it a very common case if you are using some git workflow. When using gitflow workflow, for example, to fix a bug, there might be a branch bugfix/fix-some-bugs
that borned from main. Then, the main keeps adding commits, bugfix/fix-some-bugs
will have some commits too. Thus, there will be several commits ahead and also several commits behide main.
If we want to move the commits that only on bugfix/fix-some-bugs
but now on main before we merge it to main, simply do git rebase main on bugfix/fix-some-bugs
.
That's very useful if you want to keep your history graph more clear. Keeping rebase to main can also avoiding make that branch too far away from main, because it might cause some conflict.
Append bunch of commits into another branch - Part 2
At first, we made a branch branchA
after A
, then we committed B
on main
.
Continue, we made commit C
and D
on branchA
. Then, we want to do a new feature based on branchA
's work, so wo created new branch branchB
just after D
, then committed E
on that branch.
To keep sync with main, so we did a git rebase
on branchA
.
Considering following diagram:
/---\ /---\
---------| A |---| B |---
main \---/ | \---/ | /---\ /---\
| \---------| C |---| D |
| branchA \---/ \---/
|
| /----\ /----\ /---\
\--------| C' |---| D' |---| E |
branchB \----/ \----/ \---/
What if we want to keep sync branchB
with branchA
?
Since branchA
has been rebased, the hashed of C
, D
will be different on branchB
's, we marked it as C'
and D'
.
If we did git rebase branchA
on branchB
, the C'
and D'
will also be append to branchA
.
For this cases, we need to use git rebase onto:
$ # on branch `branchB`
$ git rebase --onto branchA <D''s hash> HEAD
This way, only E
will be rebase onto branchA
.