The DeLorean time machine, an automobile-based time travel vehicle.If you are the type of programmer who writes perfect, error-free code in every commit without fail, this tip is not for you. If you're like me, small mistakes sometimes occur – a debugger breakpoint left buried in an obscure area of the app, or maybe I missed resolving a conflict during a hairy merge. With discipline these things shouldn't happen frequently, but when there's an inevitable lapse it's nice to know that git's got your back.

To begin with I want to temper this advice with a word of caution. By changing history you are creating a new timeline of events. Just like in time travel, which I'm sure you're all familiar with, it's important to avoid overlapping different timelines. The following rule will help you avoid running into trouble: changing history that is contained entirely in your local history (i.e. a feature branch that you have not published to a shared repository) is generally safe. Nobody has to know that an alternate timeline ever existed by the time you push your branch.

I realized my mistake immediately after committing it

(putting on my philosopher hat) As in many things, the sooner you realize your mistake the easier it is to fix. If you have not yet made another commit, git commit has a flag which allows you to easily tack on to the most recent revision.

  1. Rectify your error in the code
  2. git add <fixed file(s)> to stage the desired changes
  3. git commit --amend will modify the previous commit to include the staged changes.

I messed up and didn't realize it until several commits later

Even when your mistake didn't happen in the most recent revision, fear not, there is still a way to git* in there and fix the broken commit. First, you may need to identify which commit actually introduced the problem. Usually you'll have a pretty good sense of where it came from, but to make absolutely sure you can look at the recent history.

  1. git log -10 --oneline will list the last 10 commits
  2. git show displays the changes represented by a specific commit
  3. If you have no idea where it may have come from and there’s a lot of history to look through you may benefit from reading up on git bisect to do a binary search for the offending commit. That is outside the scope of this blog post, however.

So now you know where the problem came from, strap on your goggles – it’s time to change history!

  1. Once again rectify the problem you discovered in your code.
  2. git add <fixed file(s)> to stage the desired changes
  3. git commit -m "facepalm"
  4. git rebase -i <revision> will begin an interactive rebase… the mechanism which lets us change history! The revision you specify is the starting point for the window of history made available for you to change. Because I'm lazy, rather than using a precise revision I often use the name of the branch from which I cut the feature branch (e.g. master)Rebasing a feature branch on top of master.
  5. An editor window will open displaying each revision from the specified revision until the current revision, each revision on a line containing the command "pick", the short hash of the revision, and the short description of the commit. There will be some comments underneath the commits which describe some of the different commands available in the rebase.
  6. You may begin to experience a tingling in your fingertips; this is the power of git you are feeling. Forge ahead…
  7. At the bottom of the list you will see your most recent commit labeled "facepalm" - move this line to be directly below the commit which introduced the problem. Yes, you can actually re-order commits.
  8. Change the command on the "facepalm" commit from "pick" to "f" (which is just the short version of "fixup")Rebasing a feature branch on top of master.
  9. Save and exit

That’s all there is to it. git then re-applies each commit in the order you specified, and when it reaches the "fixup" commit it will merge it into the previous commit. The "facepalm" commit comment is discarded and your feature branch's timeline no longer includes a reference to a commit that introduced a silly mistake. Collaborators will be amazed at how each revision of your code is perfectly crafted.

* pun intended

Want access to more articles like this?  Subscribe now to stay up to date on the latest from Table XI