iTranslated by AI
How to Squash Commits Using git rebase -i
The Problem
Currently, you have derived a feature/hogehoge branch from the develop branch at commit_ID_1. You have then committed changes in the order of commit_ID_2 -> commit_ID_3 -> commit_ID_4, and pushed the feature/hogehoge branch to the remote repository.
In this state, how can you combine (squash) the commits from commit_ID_2 to commit_ID_4?
In other words, you want to change this:
commit1 (develop branch) -> commit2 (feature/hogehoge branch) -> commit3 (feature/hogehoge branch) -> commit4 (feature/hogehoge branch)
To this:
commit1 (develop branch) -> commit2-4 (feature/hogehoge branch)
The Solution
- Checkout the target branch and find the commit ID to rebase from using
git log --oneline. - Execute
git rebase -i <commit ID>. - Modify the rebase instructions in Edit mode.
- Force push using
git push --force-with-lease origin HEAD.
1. Find the commit ID to rebase from using git log --oneline
$ git checkout feature/hogehoge
$ git log --oneline
commit_ID_4 (HEAD -> feature/hogehoge, origin/feature/hogehoge) commit_message_4
commit_ID_3 commit_message_3
commit_ID_2 commit_message_2
commit_ID_1 (origin/develop, origin/HEAD, develop) commit_message_1
2. Execute git rebase -i <commit ID>
Specify the commit ID immediately preceding the commits you want to combine, then run git rebase -i.
In this case, since we want to combine commit_ID_2 through commit_ID_4, we specify commit_ID_1.
$ git rebase -i commit_ID_1
3. Modify the rebase instructions in Edit mode
When you run git rebase -i, an Edit mode interface will appear like the one below.
The commits are listed from oldest to newest based on commit_ID_1 (this should be the reverse order of git log --oneline).
pick commit_ID_2 commit_message_2
pick commit_ID_3 commit_message_3
pick commit_ID_4 commit_message_4
# Rebase xxxxx..yyyyy onto zzzzzz
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
In this example, we will choose to squash commit_ID_3 and commit_ID_4 into commit_ID_2. (Note: There is no way to squash into commit_ID_4 from the previous commits).
You will often use the abbreviated forms like p, r, or f.
p commit_ID_2 commit_message_2
f commit_ID_3 commit_message_3
f commit_ID_4 commit_message_4
Save and exit Edit mode with :wq. If the rebase is successful, you will see the following output:
Successfully rebased and updated refs/heads/feature/hogehoge.
This completes the rebase process.
If you want to modify the commit message
In many cases, you will want to update the commit message, so configure your rebase instructions as follows (the r is the key):
r commit_ID_2 commit_message_2
f commit_ID_3 commit_message_3
f commit_ID_4 commit_message_4
After saving and exiting with :wq, you will be prompted to edit the commit message.
Once you save and exit that with :wq as well, you will see a prompt like the one below. Continue the rebase process by running git rebase --continue.
Aborting commit due to empty commit message.
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
4. Force push using git push --force-with-lease origin HEAD
Perform a force push.
It is recommended to verify that you are on the correct branch before doing this.
$ git push --force-with-lease origin HEAD
(Appendix) If you make a mistake during rebase
Running the following command should revert the state to what it was before executing git rebase -i.
$ git rebase --abort
Discussion