git fixup

When using git sometimes you want to make a change and include it with the previous commit. The easiest way to do this is:

$ git add <files>
$ git commit --amend --no-edit

Sometimes you want to include your change in a commit before the previous one. To do this takes a few steps.

fixup! and --autosquash

The general way to edit history in git is with git rebase -i. This does an interactive rebase, which allows you to re-order, remove, edit, and squash commits.

When you do git rebase --help, you find this documentation about --autosquash:

--autosquash
    When the commit log message begins with "squash! ..." (or "fixup! ..."), and there is already a commit in the todo list that matches the same ..., automatically modify the todo list of rebase -i so that the commit marked for squashing comes right after the commit to be modified, and change the action of the moved commit from pick to squash (or fixup). A commit matches the ...  if the commit subject matches, or if the ...  refers to the commit’s hash. As a fall-back, partial matches of the commit subject work, too. The recommended way to create fixup/squash commits is by using the --fixup/--squash options of git-commit(1).

    This option is only valid when the --interactive option is used.

So if we have a commit with the message fixup! <commit-hash> where <commit-hash> is the hash of the commit you want your changes to be included in, we can do git rebase -i --autosquash to get the rebase command to automatically re-order the commits and mark them for being squashed.

$ git add <files>
$ git commit -m "fixup! <commit-hash>"
$ git rebase -i --autosquash

Then the interactive rebase editor opens and you have to save and exit. This isn’t as nice of a workflow as git commit --amend --no-edit because you need to make a new commit, and open a text editor during the interactive rebase.

git fixup <commit-hash>

We can add a new git command git fixup by adding an executable called git-fixup on our path.

Here is the contents of my git-fixup script:

#!/bin/bash
if [ "$#" -ne 1 ]; then
    echo "usage: git fixup <commit>"
else
    COMMIT=$(git rev-parse "$1")
    numstaged=$(git diff --cached --numstat | wc -l)
    if [ $numstaged -gt 0 ]
    then
        git commit -m "fixup! $COMMIT"
    fi
    GIT_SEQUENCE_EDITOR=true git rebase -i --autosquash --autostash $COMMIT^
fi

To use this script, save it with the name git-fixup somewhere in you PATH. Make sure you make it executable. Then you simply find the commit hash of the commit you want to modify and type:

$ git add <files>
$ git fixup <commit-hash>

This is very similar to the git commit --amend --no-edit workflow.

Remember, git rebase --autosquash only works for interactive rebases. In order to skip the interactive portion of the rebase we set the environment variable GIT_SEQUENCE_EDITOR=true during the git rebase -i command, which will automatically accept the suggested rebase actions.