• Skip to primary navigation
  • Skip to main content
Sal Ferrarello
  • About Sal Ferrarello
  • Speaking
  • Connect
    Mastodon GitHub Twitter (deprecated)
You are here: Home / Dev Tips / Git Squash Back To

Git Squash Back To

Last updated on July 31, 2025 by Sal Ferrarello

Often in my work, I’ll create a number of commits before I craft my correct solution. Having these missteps in separate commits doesn’t provide any real value and adds noise so I’ll often squash these commits into a single commit. Historically, I’ve used a Git interactive rebase to do this but as I was reminded on Mastodon, it seems like there should be a quicker way, so I’ve created a Git alias, git squashbackto, to do this.

My Solution

This is the command I came up with to squash the last 5 commits.

git -c sequence.editor="vim --clean -c 'silent 2,\$s/^pick/squash/|:wq'" rebase --interactive --keep-base HEAD~5

Git Alias git squashbackto

I’ve turned this solution into a Git alias, git squashbackto.

Running the following from the command line will add the git squashbackto alias to your Global Git Configuration file.

git config --global alias.squashbackto '!git -c sequence.editor="vim --clean -c '\''silent 2,\$s/^pick/squash/|:wq'\'' " rebase --interactive --keep-base'

The entry in your Global Git Configuration file will look like

[alias]
    squashbackto = !git -c sequence.editor=\"vim --clean -c 'silent 2,\\$s/^pick/squash/|:wq' \" rebase --interactive --keep-base

How to Use git squashbackto

We can squash the last five commits with

git squashbackto HEAD~5

or squash all of the commits added to our branch since we branched off main (i.e. all of the commits on our branch that don’t appear on main)

git squashbackto main

How I Tested This

In the process of testing this, I created an empty directory (I called mine squashcommits/) and cded into it. Once there I ran the following:

(rm -rf ./.git || true ) && 
(rm -f ./*.md || true ) && \
git init && \
echo '# My Project' > README.md && \
git add README.md && \
git commit -m 'Initial commit: add README.md' && \
git switch -c feat/myfeature && \
echo 'Feature (attempt 1)' > myfeature.md  && \
git add myfeature.md && \
git commit -m 'Add my feature (attempt 1)' && \
echo 'Feature (attempt 2)' > myfeature.md && \
git add myfeature.md && \
git commit -m 'Add my feature (attempt 2)' && \
echo 'Feature (attempt 3)' > myfeature.md && \
git add myfeature.md && \
git commit -m 'Add my feature (attempt 3)' && \
echo 'Feature (attempt 4)' > myfeature.md && \
git add myfeature.md && \
git commit -m 'Add my feature (attempt 4)' && \
echo 'Feature (attempt 5)' > myfeature.md && \
git add myfeature.md && \
git commit -m 'Add my feature (attempt 5)' && \
git log --oneline --graph --abbrev=4

This command does the following:

  1. delete any current repo files (so I can re-run this command)
  2. setup the repo the way I want for testing and
  3. display the Git history that is created

When the command finishes the git history of the repo looks something like this:

* 87c6 (HEAD -> feat/myfeature) Add my feature (attempt 5)
* 1816 Add my feature (attempt 4)
* 4b3f Add my feature (attempt 3)
* 3282 Add my feature (attempt 2)
* 070f Add my feature (attempt 1)
* 3e62 (main) Initial commit: add README.md

Now when I run

 git -c sequence.editor="vim --clean -c 'silent 2,\$s/^pick/squash/|:wq'" rebase --interactive --keep-base HEAD~5

The git interactive rebase screen (where I would change pick to squash) is skipped and we go directly to the screen where I have all 5 commit messages and I can combine them as desired. Once that is saved, running git log --oneline --graph --abbrev=4 looks like

* 9b4a (HEAD -> feat/myfeature) Add my feature
* 3e62 (main) Initial commit: add README.md

Success!

How this Works

At the heart of this command is

git rebase --interactive --keep-base HEAD~5

which drops you into your chosen editor with content that looks like

pick 070fb05 Add my feature (attempt 1)
pick 3282a75 Add my feature (attempt 2)
pick 4b3f39c Add my feature (attempt 3)
pick 18160b0 Add my feature (attempt 4)
pick 87c6d32 Add my feature (attempt 5)

To do this manually, we would update attempts 2 through 5 from pick to squash.

--keep-base

We include –keep-base on our git rebase call, so squash commits on our current branch without pulling in any new commits from our other branch (e.g. main). Typically, a git rebase -i main would both:

  1. Add any new commits that have been added to main since we last updated from there
  2. Open the editor with the interactive rebase screen

By using --keep-base, we skip the first step and only do the second.

Automating pick to squash

Git has lots of configuration values and one of the things you can configure is what editor to use. I started by modifying the editor via the core.editor configuration value, but unfortunately an interactive rebase opens the editor twice (once to update the commits from pick to something else and once to craft the commit message for your new commit created by the squash). In this case, I want to automate the first but NOT automate the second.

The good news is Git version 2.40 (released in 2023) added a new configuration value, sequence.editor, that impacts when the editor is opened to update commits from pick to something else but NOT when crafting the commit message (see Use a Different Editor for Git Interactive Rebase).

We want to update this sequence.editor value but we only want to do this when we run this command (we don’t want to impact other times a Git interactive rebase is run). We can modify our Git config values just for the current command by run by using the Git -c option to set sequence.editor just for this run.

We’re going to set Git to use Vim as the Git interactive rebase editor (but we’re also going to pass some configuration values to Vim).

vim --clean -c 'silent 2,\$s/^pick/squash/|:wq'

--clean

The --clean argument results in “initializations from files and environment variables is skipped”.

We don’t want any of our custom Vim configuration or plugins impacting this code.

See :help --clean

-c {command}

The {command} given will run after Vim has loaded the file. Multiple commands can be given separated by a vertical bar (|). See :help -c

In this case, our command is

silent 2,$s/^pick/squash/|:wq

The 2,$s/^pick/squash/ command replaces pick with squash starting on line 2 and going to the end of the file ($) (see :help substitute) – this only applies on lines where “pick” appears at the beginning of the line (^). We prefix this command with silent to avoid the execution halting with the message

Press ENTER or type command to continue

The :wq, writes and quits the file (see :help :wq).

Peek Under the Hood

You can omit |:wq from the command to pause after the substitution has completed. If you run this command, you’ll need to enter :wq manually to get things moving again.

git -c sequence.editor="vim --clean -c 'silent 2,\$s/^pick/squash/'" rebase --interactive --keep-base HEAD~5

Adding this to My Git Configuration

Here is the PR where I add the git squashbackto alias to my Git configuration.

Other Solutions

In looking for this solution I found a number of other articles about this same idea of squashing the last so many commits but they all seem to use one of these two approaches:

  1. A Git interactive rebase (without the additional automation in this post), which has the disadvantage of requiring you update pick to squash on lines you want to squash and then save this “sequence” file
  2. A Git reset to unstage the changes and then re-adding the changes and creating a new commit, which has the disadvantage of losing your previous commit messages
Sal Ferrarello
Sal Ferrarello (@salcode)
Sal is a PHP and JavaScript developer with a focus on the WordPress platform. He is a conference speaker with a background including Piano Player, Radio DJ, Magician/Juggler, Beach Photographer, and High School Math Teacher. Sal can be found professionally at WebDevStudios, where he works as a principal engineer.

Filed Under: Computing, Dev Tips, Solution Tagged With: command line, Git, git alias, rebase, vim

Reader Interactions

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Copyright © 2026 · Bootstrap4 Genesis on Genesis Framework · WordPress · Log in