Git Squash at the Command Line with a Bash Script

Useful if the commits on your branch are sloppy, and you want to clean them up, and you don’t mind that your new commits will be file based instead of time based.

What does that mean exactly? Let’s say you worked two days on 3 files. (One of the files was a class, another a config, and the third a unit test.) Over the course of two days you made 13 commits. Sometimes a commit was to one file, sometimes a commit was to both, sometimes all three… 13 times! (Sloppy! You should be ashamed.) After running the script below you will lose 13 commits but can restage and create either a single commit message, or up to 3 new commit messages: One for each file. (Clean! Dick Grune approved.)

Useful if you’re too lazy to use rebase with fixup. Instead, just run one cheap and barely good enough bash script and you’re done. So lazy in fact you’re scrolling through all these words not even reading them, get to the script already.

Useful in scenarios where GitHub’s Squash & Merge interface is unavailable.

Prerequisites before running the script below, where feature/foo is your branch:

  • The script is saved somewhere in your path.
  • You are working on branch feature/foo. You want to create a single squashed commit, or restage in batches, up to as many commits as you have files.
  • You have merged “destination” into feature/foo and resolved conflicts.
  • You are the author of all the commits on feature/foo (Or want to be the author? You will be rewriting…)
  • You are currently on feature/foo.
#!/bin/bash
set -e
destination='master'
branch=$(git rev-parse --abbrev-ref HEAD)
git checkout ${destination}
git pull
git checkout ${branch}
git checkout -b ${branch}-backup
git checkout ${destination}
git branch -D ${branch}
git checkout -b ${branch}
git merge --squash ${branch}-backup
_done="
Everything seems fine. Next steps:
    git status
    git commit # Or re-stage and do multiple commits
    git push --force-with-lease --set-upstream origin ${branch}

To undo:
    git reset HEAD .
    git checkout -- .
    git checkout ${destination}
    git branch -d ${branch}
    # If branch exists on GiHub:
    git branch -D ${branch}-backup
    git fetch
    git checkout ${branch}
    # Else:
    git branch -m ${branch}-backup ${branch}
"
echo "$_done"

Source code.

After running this script your files will be staged. You can either rewrite a nice single commit message for all the files, restage and commit in batches, or undo.

Inspired by this Gist.

Leave a Reply

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