git: squash/fixup earlier commit

If you're just looking for the easy solution for fixing up earlier commits, read the question! It explains it all. But since Elmarco was asking for a slick way, here we go:

As of Git 1.7.0, there is an --autosquash option for rebase, which does what you want. There is also the --fixup and --squash options for commit to make things easier. With some aliasing you can probably even get the whole thing into a single command.

I'd suggest upgrading to the newest Git for maximum awesomeness:

git/Documentation/RelNotes $ grep -i -A1 autosquash\\\|fixup *
1.7.0.txt: * "git rebase -i" learned new action "fixup" that squashes the change
1.7.0.txt-   but does not affect existing log message.
--
1.7.0.txt: * "git rebase -i" also learned --autosquash option that is useful
1.7.0.txt:   together with the new "fixup" action.
1.7.0.txt-
--
1.7.3.txt: * "git rebase -i" peeks into rebase.autosquash configuration and acts as
1.7.3.txt:   if you gave --autosquash from the command line.
1.7.3.txt-
--
1.7.4.txt: * "git commit" learned --fixup and --squash options to help later invocation
1.7.4.txt-   of the interactive rebase.
--
1.7.4.txt: * "git rebase --autosquash" can use SHA-1 object names to name which
1.7.4.txt:   commit to fix up (e.g. "fixup! e83c5163").
1.7.4.txt-

I created some aliases to make it easier to use the git commit --fixup and git commit --squash commands added in git 1.7. Add these to your ~/.gitconfig:

[alias]
  fixup = !sh -c 'REV=$(git rev-parse $1) && git commit --fixup $@ && git rebase -i --autosquash $REV^' -
  squash = !sh -c 'REV=$(git rev-parse $1) && git commit --squash $@ && git rebase -i --autosquash $REV^' -

Usage:

$ git commit -am 'bad commit'
$ git commit -am 'good commit'

$ git add .          # Stage changes to correct the bad commit
$ git fixup HEAD^    # HEAD^ can be replaced by the SHA of the bad commit

The bad commit can be several commits back.


My current git work flow is so --fixup/--squash intensive, that I wrote a new git-fixup command that handles most of the annoying bits automatically:

  • git fixup shows the modified files grouped under that latest commits that touch the same files
  • git fixup -a commits all those changes as --fixup changes with their corresponding "parent" commits
  • git fixup -r does an automatic git rebase --autosquash for all the fixup commits

A lot of changes are such that just the three commands above are enough to get the job done, no copy-pasting of commit-id's or reading thru the git log to find the right --fixup targets.

Source: https://github.com/ohmu/git-crust