Editing Commits With Git

As developers who use GitHub, we use and love pull requests. But what happens when you only want part of a pull request?

Let’s say, for example, that we have a pull request with three commits. Unfortunately, the pull request’s author accidentally used spaces instead of tabs in the second commit, so we want to fix this up before we pull it in.

The main tool we’re going to use here is interactive rebasing. Rebasing is a way to rewrite your git repository’s history, so you should never use it on code that you’ve pushed up. However, since we’re bringing in new code, it’s perfectly fine to do this. The PR’s author may need to reset their repository, but their changes should be on separate branches to avoid this.

So, let’s get started. First, follow GitHub’s first two instructions to merge (click the little i next to the Merge Pull Request button):

git checkout -b otheruser-master master
git pull git@github.com:otheruser/myproject.git master

Now that we have our repository up-to-date with theirs, it’s time to rewrite history. We want to rebase (rewrite) the last three commits, so we specify the range as HEAD~3:

git rebase -i HEAD~3

This puts us into the editor to change the rebase history. It should look like this:

pick c6ffde3 Point out how obvious it is
pick 9686795 We're proud of our little file!
pick a712c2c Add another file.

There are a number of commands we can pick here. For us, we want to leave the first and last unchanged, so we’ll keep those as pick. However, we want to edit the second commit, so we’ll change pick to edit there. Saving the file then spits us back out to the terminal. It’ll also tell us that when we’re done, we should run git commit --amend and git rebase --continue.

Internally, what this does is replay all the commits up until the one with edit. After it replays that commit, it pauses and waits for us to continue it.

Now, we can go and edit the file. Make the changes you need here, then as it told us, it’s time to make amends:

git commit --amend

This merges the changes you just made with the previous commit (the one with the misspelling) into a new commit. Once we’ve done that, we need to continue the rebase, as all the commits after this one have to be rewritten to point to our new one in the history.

git rebase --continue

Our history rewriting is now done! It’s now time to merge it back in to master, push up our changes and close the pull request. First we’ll need to switch back to master, then we can merge and push.

git checkout master
git merge otheruser-master master
git push

Congratulations, you just (re)wrote history!


For those of you who want to try this, I’ve made a test repository for you to work on. Try it out and see if you can fix it yourself!

Thanks to Japh for asking the question that inspired this post!

3 thoughts on “Editing Commits With Git

  1. If you are working in a shared development project you have to be careful with rewriting history.

    Problems with rewriting history

    The primary problem with rewriting the history of a branch has to do with merging. Suppose somebody fetches your branch and merges it into their branch, with a result something like this:

    o–o–O–o–o–o <– origin

    t–t–t–m <– their branch:

    Then suppose you modify the last three commits:

    o–o–o <– new head of origin

    /

    o–o–O–o–o–o <– old head of origin

    If we examined all this history together in one repository, it will look like:

    o–o–o <– new head of origin

    /

    o–o–O–o–o–o <– old head of origin

    t–t–t–m <– their branch:

    Git has no way of knowing that the new head is an updated version of the old head; it treats this situation exactly the same as it would if two developers had independently done the work on the old and new heads in parallel. At this point, if someone attempts to merge the new head in to their branch, git will attempt to merge together the two (old and new) lines of development, instead of trying to replace the old by the new. The results are likely to be unexpected.

    You may still choose to publish branches whose history is rewritten, and it may be useful for others to be able to fetch those branches in order to examine or test them, but they should not attempt to pull such branches into their own work.

    For true distributed development that supports proper merging, published branches should never be rewritten.

Leave a Reply