iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🔖

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

  1. Checkout the target branch and find the commit ID to rebase from using git log --oneline.
  2. Execute git rebase -i <commit ID>.
  3. Modify the rebase instructions in Edit mode.
  4. 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.

If not modifying commit messages
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):

If modifying commit messages
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
GitHubで編集を提案

Discussion