Skip to content

GitLab - Squashing Commits

Squashing commits is the process of combining multiple Git commits into a single, cohesive commit. This is often done to create a cleaner, more understandable project history before merging a feature branch into the main branch (e.g., main or master). A squashed commit summarizes a series of smaller, incremental changes into one meaningful update.

Benefits of squashing include:

  • A cleaner Git history that’s easier to read and navigate.
  • Simplified rollbacks, as you only need to revert one commit for an entire feature.
  • More focused code reviews, as reviewers can see the net change of a feature in one go (though individual commits can still be reviewed before squashing).
Section titled “Method 1: Squashing Commits via GitLab Merge Request (Recommended)”

GitLab provides a convenient option to squash commits when you merge a merge request. This is often the easiest and preferred method for most users.

Steps:

  1. Push your feature branch with its multiple commits to GitLab.
  2. Create a Merge Request (MR) from your feature branch to the target branch (e.g., main).
  3. Before merging, look for a checkbox option typically labeled “Squash commits” or similar in the merge request widget. This option might be under a section like “Merge options”.
  4. Ensure this option is checked. You may also have the ability to edit the squashed commit message. By default, GitLab often uses the MR title and concatenates commit messages.
  5. Click the “Merge” button. GitLab will then squash all commits from your feature branch into a single commit on the target branch.

This method keeps the individual commits on your feature branch (if you don’t delete it) but creates a single, clean commit on the target branch.

Method 2: Squashing Commits Locally using git rebase -i (Advanced)

Section titled “Method 2: Squashing Commits Locally using git rebase -i (Advanced)”

You can also squash commits locally on your feature branch before pushing or after feedback, using an interactive rebase. This gives you more control but requires careful handling.

Caution: Never rebase commits that have already been pushed to a shared branch that other team members might be using, as it rewrites history and can cause significant conflicts. It’s generally safe to rebase your own feature branch before it’s merged.

Step 1: Create a new branch and make some commits

First, ensure you are on your feature branch. If you’re starting fresh for this example:

$ git checkout -b my-feature-branch main
# Switched to a new branch 'my-feature-branch'

Now, create some files and make a few commits. For instance:

$ echo "Initial content" > feature_file.txt
$ git add feature_file.txt
$ git commit -m "feat: Add initial feature file"
$ echo "More content" >> feature_file.txt
$ git commit -am "feat: Add more details to feature"
$ echo "Final touch" >> feature_file.txt
$ git commit -am "fix: Correct typo in feature file"

Now you have three commits on my-feature-branch that you want to squash.

Step 2: Start an interactive rebase

To squash the last 3 commits, use the git rebase -i HEAD~3 command:

$ git rebase -i HEAD~3

The HEAD~3 tells Git to rebase the last 3 commits from the current HEAD. If you wanted to squash 5 commits, you would use HEAD~5. You need at least two commits to perform a squash operation involving multiple commits.

Step 3: Choose commits to squash in the editor

This command will open your default text editor with a list of the commits you’re about to rebase, looking something like this:

pick a1b2c3d feat: Add initial feature file
pick e4f5g6h feat: Add more details to feature
pick i7j8k9l fix: Correct typo in feature file
# Rebase ... onto ...
#
# 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
# d, drop <commit> = remove commit
# ...

To squash commits, you need to change pick to squash (or s) for the commits you want to combine into the preceding commit. The first commit is usually kept as pick.

Change the second and third pick to squash (or s):

pick a1b2c3d feat: Add initial feature file
squash e4f5g6h feat: Add more details to feature
squash i7j8k9l fix: Correct typo in feature file

Save and close the editor. (e.g., press Esc, then type :wq and Enter in Vim).

Step 4: Edit the commit message for the new squashed commit

After closing the first editor, another editor window will open. This window allows you to craft the commit message for the new, single commit that results from the squash. It will contain the messages of all the commits you’re squashing.

# This is a combination of 3 commits.
# This is the 1st commit message:
feat: Add initial feature file
# This is the commit message #2:
feat: Add more details to feature
# This is the commit message #3:
fix: Correct typo in feature file
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# ...

Delete the existing messages and write a new, clear commit message that summarizes all the changes. For example:

feat: Implement complete feature X with corrections
This commit adds the initial files for feature X,
includes further details, and corrects a typo.

Save and close this editor. Git will now apply the changes, and your last three commits will be replaced by a single new commit.

Step 5: Push the squashed commit

If you had previously pushed this branch, you’ll need to force push because you’ve rewritten its history:

$ git push origin my-feature-branch --force-with-lease

--force-with-lease is safer than --force as it checks if the remote branch has been updated by someone else before force pushing.

If this is the first time pushing the branch, a normal push will suffice:

$ git push -u origin my-feature-branch

Typically, you squash commits on your feature branch right before creating a merge request, or just before merging it, to ensure the main branch history remains clean and understandable. Some teams prefer to squash during the merge (GitLab UI method), while others prefer developers to clean up their branches before proposing a merge.

Discuss with your team to establish a consistent strategy for squashing commits.