I Pushed My API Key to GitHub: How to Undo Git Mistakes
1. The Scream at 3 AM
Accidentally committing an API Key is a well-known danger—and for good reason.
Imagine this: it's 3 AM, you're rubbing your sleepy eyes finishing up work. "Just one last push and I'll sleep," you think.
git add .
git commit -m "Deployment config done"
git push origin main
You see the To https://github.com/... message in the terminal, close your laptop with satisfaction, and lie down.
Here's the thing: there are documented cases where bots scan newly pushed public repositories almost instantly. We're talking minutes, if not less.
AWS Billing Alerts and Security Alerts flood your inbox simultaneously.
[AWS] Action Required: Your AWS account may be compromised...
The AWS Secret Access Key inside the .env file got committed and pushed to a public GitHub repository.
Forgot to add .env to .gitignore.
In a panic, you quickly delete the .env file locally and commit/push again.
rm .env
git add .
git commit -m "Remove key"
git push origin main
But that's foolish. Git remembers every history.
If you click the Commits tab and open the previous commit, the secret key is clearly visible in red text (deleted lines).
The immediate priority is to disable and rotate the AWS key—don't waste time trying to fix Git first. But understanding Git's "Time Travel" features is how you prevent or clean up these situations. Here's how they work.
2. Understanding Git's 3 Spaces
The reason Git commands are confusing is that we often don't know "where my code is."
Git manages files in three physical/logical spaces.
- Working Directory: The actual files I'm looking at in my editor (VS Code) right now.
- Staging Area (Index): Where files go when you
git add. The box where you pack things before shipping.
- Repository (HEAD): Where files are saved when you
git commit. The permanent record.
graph LR
WD[Working Directory] -- git add --> SA[Staging Area]
SA -- git commit --> Repo[Repository]
Repo -- git reset --> SA
Repo -- git reset --hard --> WD
Undoing a commit simply means taking the package from the Repository back to the Staging area or the Working Directory.
3. The Time Machine: git reset
If you haven't git pushed yet (if the mistake is only local), git reset is the perfect time machine.
This command rewinds time. The option you attach determines "how far back" you go.
3.1 git reset --soft (Safest, Recommended)
"Undo the commit, but keep the code I wrote."
# HEAD~1: Go back 1 step from current (HEAD).
git reset --soft HEAD~1
- Effect: The commit you just made disappears from history, but the files remain in the Staging Area. (In green
git added state).
- Usage: "Ah, I forgot to add one file!" or "Typo in commit message!" Use this. Add more files or fix the message, then just
git commit again.
3.2 git reset --mixed (Default)
"Undo the commit, and undo git add too."
git reset HEAD~1
# Or just
git reset HEAD~1
- Effect: The commit disappears, and files come down from the Staging Area. (
Unstaged red state). But file contents remain unchanged. Your work is safe.
- Usage: When you accidentally
git add .ed weird files (like build outputs) and want to pick files again.
3.3 git reset --hard (God of Destruction)
"Just pretend it never happened. Delete everything."
git reset --hard HEAD~1
- Effect: The commit disappears, and the code you wrote also disappears. You completely go back to that moment in time.
- Warning: Very hard to recover. Use only when absolutely certain. I once used this in anger ("Screw it!") and lost 3 hours of code. I almost cried.
4. The "Pause" Button: git stash
Not an undo, but a lifesaver.
Scenario: "I'm working on Feature A, but Boss wants bug B fixed NOW."
You can't commit messy code, and you can't switch branches with dirty files.
Solution: git stash (Temporary Shelf)
# 1. Save current work to a temporary shelf
git stash save "WIP: Feature A halfway done"
# 2. Now your workspace is clean. Switch branch.
git checkout hotfix/bug-b
# ... fix bug ...
# 3. Come back
git checkout feature-a
# 4. Bring back your work
git stash pop
I use this daily. It keeps my commit history clean from "WIP" garbage.
5. What If I Already Pushed? : git revert
The problem is when you've already git pushed to the Remote repository. (Like my AWS key incident)
If you use git reset locally to erase the past and try to push again, Git will reject it.
Error: failed to push some refs to '...'. Updates were rejected because the tip of your current branch is behind its remote counterpart.
(Your local history is behind!)
Sure, you can force overwrite with git push --force. But if it's not a solo branch?
The moment your colleagues git pull, hell breaks loose. In a team project, --force is a declaration of war.
In this case, you must use git revert.
git revert HEAD
This isn't "rewinding time," but "creating a new commit that cancels out the mistake."
If commit A added code, a new commit A' creates a change that deletes that code. (+1 canceled by -1)
The result is the code is gone, but the record "I made a mistake, and I undid it" remains.
It's embarrassing, but it's the safest and most polite way for collaboration. You aren't manipulating history.
6. Polishing History: git rebase -i
Sometimes you want to hide your messy process.
Example: 5 commits just to fix one typo.
Commit 5: Fix typo again
Commit 4: Fix typo
Commit 3: Fix bug
Commit 2: Oops missed one file
Commit 1: Add new feature
To squash these into one beautiful commit:
# Interactive rebase of last 5 commits
git rebase -i HEAD~5
An editor opens. Change pick to squash (or s) for the commits you want to merge into the previous one.
pick a1b2c Add new feature
squash d3e4f Oops missed one file
squash g5h6i Fix bug
This merges them into "Add new feature". Do this before pushing!
7. The Eraser of Shame: git reflog
What if you accidentally used git reset --hard and wiped your working code? Is your life over?
No. Git has a black box called reflog.
git reflog
Typing this shows every command you executed (commit, reset, checkout, etc.) in order. Even IDs of deleted commits appear here.
a1b2c3d HEAD@{0}: reset: moving to HEAD~1
d4e5f6g HEAD@{1}: commit: Deployment config done (Includes AWS Key)
Here, d4e5f6g is the commit I just deleted (undid with reset).
In Git, commits aren't deleted immediately after a reset. They remain as 'Dangling Objects' for a while.
# Travel back to that specific point in time!
git reset --hard d4e5f6g
Doing this brings back the lost code like magic. Git is a much safer and stickier tool than I thought. I just didn't know how to use it properly.
6. Pro Tip: Fixing Small Mistakes without Reset
If you just made a typo in the commit message or forgot to add one file, you don't need a full git reset.
Just use --amend.
git add forgotten_file.js
git commit --amend -m "Fixed typo and added file"
This modifies the most recent commit in place. It's cleaner than creating a "Fix typo" commit.
Warning: This changes the Commit ID (SHA), so never use it if you have already pushed to a shared branch.
8. Cherry-pick: Take Only What You Need
"I just want that one specific commit from another branch, not the whole thing."
When a full merge is too much, use Cherry-pick.
# 1. Find the commit ID you want (e.g., a1b2c3d)
# 2. Bring it to your current branch
git cherry-pick a1b2c3d
Just like picking the best cherries off a cake, you can apply specific changes without merging unrelated code.
9. The Bug Hunter: git bisect
"It worked yesterday, but it's broken today. Which commit broke it?"
Checking out commits one by one is torture.
git bisect uses Binary Search to find the culprit.
git bisect start
git bisect bad # Current version is broken
git bisect good HEAD~20 # It was definitely working 20 commits ago
Git will check out the middle commit for you. run your test.
If it works, type git bisect good. If it fails, type git bisect bad.
Git narrows down the range and quickly tells you: "The first bad commit is [id]."
10. So What About My API Key? (Permanent History Deletion)
If an API key is left in history like in my case, reset or revert isn't enough. Hackers scan the entire history.
You must manipulate the history itself to wipe the traces of that specific file from the face of the earth.
There are commands like git filter-branch, but they are slow and complex. Using a tool called BFG Repo-Cleaner is easiest.
# 1. Delete all history involving .env file with BFG (Locally)
bfg --delete-files .env
# 2. Cleanup debris (Garbage Collection)
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# 3. Force overwrite (Must use force here, MUST notify team!)
git push --force
However, the most realistic and safe solution is different.
-
Go to AWS Console, Deactivate and Delete the leaked key immediately.
- Issue a new key.
- Don't waste time fixing Git. You are being billed every second.
Once a key is leaked, it's "public property." Even if you wipe Git history, someone (a bot) already has it. Changing the key is the only real answer.
8. One-Line Summary
If you mistake locally, quietly fix with git reset --soft; if pushed, apologize politely with git revert; and if you posted a password, stop praying and delete the AWS key first.