I needed to edit a commit in my Git history recently and I couldn’t find a clear explanation of how it all worked. Once I figured it out, I thought I’d share what I learned.
Let’s say you accidentally committed a sensitive password into your git history, several commits back (all hypothetical, of course). All your commits are fine except for the one with the password.
We can use the “edit” option in an interactive rebase to fix this. I’m going to assume you’re familiar with interactive rebases but you can check out this article if you need a refresher.
The interactive rebase
We’ll start our interactive rebase at
15b337e, a commit ID that occurs before the bad commit on our branch:
git rebase -i 15b337e
Here’s what we see:
Now let’s tell git that we want to edit
e081013 (the bad commit) by replacing its “pick” with “edit”:
We save the changes and the rebase begins.
Now here’s the important part: when the rebase gets to the “edit” commit it will leave the commit unchanged and then pause before the next commit:
We are in-between commits now. We can tell because a
git status shows nothing staged and no changes in our working directory:
$ git status interactive rebase in progress; onto 15b337e ⋮ nothing to commit, working tree clean
What about the commit we wanted to edit? It’s in the past. If we do a
git log, our commit appears as the most recent commit in the history:
$ git log commit e081013caa58d6aef84dcde1050fabcaf81a82fc (HEAD) Author: Bryan Braun <[email protected]> Date: Thu Jan 3 14:22:21 2019 -0500 feat: make changes to deploy scripts commit 32054c3c53264d5ac8032ca4e158c4faa217b4b2 ⋮
This is a little weird… we expected to edit this commit and we flew right past it. 😐
So now we need to find a way to edit the previous commit. Here are two options I like:
Option 1: Amend the commit
When we amend commits, we make new changes and tell git to smush them into the last commit. It looks like this:
# (remove our password) git add -A git commit --amend
Instead of creating a new commit, our change gets applied to the past commit (
e081013) and we get a chance to change its commit message. The result: that commit should no longer have our password in it.
Option 2: Soft reset
In this option we step back into the previous commit to make the changes we want. It looks like this:
git reset --soft HEAD~
Now if we do a
git status, we can see where we are:
$ git status interactive rebase in progress; onto 15b337e ⋮ Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: Rakefile modified: lib/tasks/deploy.rake modified: lib/tasks/database.rake modified: config/database.yml
Great! We see all the changes that were made in that commit, staged but not yet committed. Now we can just delete our password, stage the change, and re-commit it as if it never happened:
# (remove our password) git add -A git commit
Wrapping up loose ends
Once our commit is fixed, we run
git rebase --continue and our rebase should be done.
We can check our work by inspecting the new commit to ensure the password change is no longer there:
# (given that our commit now has an ID of 89353a4...) git show 89353a4 # Scroll through commit changes to confirm all looks good.
It’s like our password was never committed in the first place. 🙌