Try to avoid needing to do this

Broadly speaking, changing git history, especially once you’ve pushed to a remote repo is considered bad practice.

Your coworkers will probably be upset at you; your parents, disappointed; and Santa will put you on the naughty list.

Nearly all of the operations listed on this page result in new commit SHA IDs, which mean your history actually changes, not just the content!

With that warning out of the way, there are still times when you will need to change the commit history, and you will need to do it on a remote repository. You will know when this time comes.

For the basics, also see Undoing things.

Changing the last commit

If all you need to do is change the message on the last commit you made (or author, or change content), you can do this easily by amending the commit:

  • git commit --amend
    • Reloads the last commit message in an editor for editing
    • If you stage more changes before running this, those changes will be added to the commit’s

Changing multiple messages and squashing commits

If what you want to do is to change multiple commit messages at once, and/or combine a few commits (also known as “squashing”), you can use an interactive rebase:

  • git rebase -i HEAD~<n> - interactively edit the past <n> commits
    • Example: git rebase -i HEAD~10 to edit the last 10 commits you’ve made

This will open an editor with a list of commits (NOTE: This list of commits is backwards from what you’re used to seeing with i.e., git log. Older commits are at the TOP of the file; newer commits at the BOTTOM):

pick abcdef1 Initial commit
pick 123456a Second commit
pick 654321a Fix a typo in the README

# (followed by lots of comments with instructions that I am
# not going to retype here)

You can simply change the message for the commits in-place, save, and quit the editor to change messages.

If you want to squash commits, change the pick command to squash on the commits which you wish to squash into their parents. For example, to squash 654321a into its parent, change the pick on line 3 to squash. You can do this for multiple commits in a row and it “stacks”; all consecutive squash commands will be squashed down into the parent just before the first one.

Deleting commits

If you really need to delete a commit, you can do so using the rebase -i approach described earlier. Instead of squash or pick, use the drop command — and it will vanish.

Note that because of Git’s delta compression technique, this will substantially alter ALL commits which come after it. The further back in history you delete a commit, the more commits will be recreated.

Merge conflicts lie ahead. Be warned.

Complete history filtering

If you need even more than the builtin git filtering provides, you may find reposurgeon and the associated suite of tools useful. Reposurgeon effectively provides an interactive directed acyclic graph editor which operates on VCS histories.

See also

You need to understand this in order to use it

I’m not going to help you use reposurgeon. If you want to perform an operation which requires this level of editing, you must learn and understand what’s happening — or you will pull the trigger on the metaphorical gun currently aimed at your own kneecap.

All you’ll get from me is a few possible use cases. if you end up here, the reposurgeon documentation is your next step.

  • Completely removing all traces of a file from not only the current commit but all past commits as well
  • Converting between different VCS systems (not only git and svn, but also hg, darcs, rcs, and others)

git