When developing a feature, it is common to generate several incremental commits: minor fixes, formatting adjustments, or interim progress markers. While useful during development, these commits often create unnecessary noise in the project’s history. Before opening a pull request or merging a branch, it is usually preferable to consolidate these changes into a single, clean commit.

This article provides a concise guide for merging multiple commits into one using Git’s interactive rebase feature. The example below uses a simple test repository, but the steps are identical for real-world projects.

Sample Scenario#

Imagine you created a repo and made three commits:

echo "# dummy project" >> README.md
git add README.md
git commit -m "#1 issue fix by async"

echo "some issue fixes" >> README.md
git add README.md
git commit -m "#2 fix sonar rules"

echo "some junit test case fixes" >> README.md
git add README.md
git commit -m "improve junit test cases"

Check the commit history:

git log --oneline

produces:

8372c3f (HEAD -> master, origin/master) improve junit test cases
9f3f7d5 #2 fix sonar rules
acda08b #1 issue fix by async

Our goal is to combine all three commits into a single, well-described commit.

Step 1 — Start an Interactive Rebase#

To rewrite the last three commits, run:

git rebase -i HEAD~3

This opens a temporary editor containing a list of the commits in reverse chronological order:

pick acda08b #1 issue fix by async
pick 9f3f7d5 #2 fix sonar rules
pick 8372c3f improve junit test cases
...

If your first commit is the root commit (i.e., the very first commit in the repository), rebasing it will fail with an error similar to:

fatal: invalid upstream 'HEAD~3'

This occurs because Git cannot rebase before the root commit.

How to fix it: Instead of rebasing from HEAD~N, use:

git rebase -i --root

This instructs Git to include the root commit in the rebase operation.

Step 2 — Mark Commits to Squash#

The first commit in the list will serve as the base commit. Update the remaining commits to use the squash (or s) command:

pick acda08b #1 issue fix by async
squash 9f3f7d5 #2 fix sonar rules
squash 8372c3f improve junit test cases

This instructs Git to merge all selected commits into the first commit.

Save and close the editor.

Step 3 — Provide the Final Commit Message#

Git then opens a second editor session where you can specify the message for the consolidated commit. Provide a clear and descriptive message, for example:

Fix various issues and improve test cases

Save and exit. Git will rewrite your commit history accordingly.

Step 4 — Verify the Result#

Check the new commit history:

git log --oneline

You should now see a single commit representing the previously separate changes:

e61ca32 (HEAD -> master) Fix various issues and improve test cases

Notes on Usage#

  • This operation rewrites Git history. Use it only on branches that have not been pushed, or ensure that force-pushing is acceptable for your workflow.
  • If the branch has already been pushed, you will need to run:
git push --force
  • Squashing commits helps create a cleaner and more maintainable project history, especially when preparing work for review.

Summary#

Merging multiple commits into a single commit is a simple but powerful technique for keeping your Git history organized. With git rebase -i, you can consolidate development-phase commits into a single, meaningful change that better reflects the intent of your work. This not only improves readability but also makes code reviews significantly smoother.

A quick workflow reminder:

  1. git rebase -i HEAD~N (or git rebase -i --root if the first commit is the root commit)
  2. Change extra commits to squash
  3. Write the new commit message
  4. Force-push if necessary

A clean commit history leads to cleaner code reviews and a more maintainable project.