How to delete a commit in git is a common question for developers who need to clean up their history, remove accidental changes, or rewrite a branch before sharing it with others. Knowing the right technique ensures you keep the repository healthy without losing valuable work.
Introduction
Git’s distributed nature gives you powerful control over commits, but that same power can lead to mistakes—like committing sensitive data, leaving a typo‑filled message, or creating a noisy history. Deleting a commit isn’t just about erasing a line; it’s about choosing the safest method that preserves collaboration integrity. In this guide we’ll explore when and how to delete a commit in git, walk through step‑by‑step commands, and highlight best practices to avoid common pitfalls Surprisingly effective..
Understanding Git Commits
A commit in Git is a snapshot of your project at a specific point in time, identified by a unique SHA‑1 hash. Commits are linked together in a directed acyclic graph (DAG), forming the commit history you see with git log. Because each commit references its parent, simply removing a node can affect downstream commits unless you adjust the graph accordingly Most people skip this — try not to..
When to Delete a Commit
- Accidental inclusion of secrets (passwords, API keys).
- Wrong files added (large binaries, build artifacts).
- Incorrect commit message that needs fixing.
- Noise reduction before a pull request or release.
- Experiment cleanup after testing a feature branch.
If the commit is already pushed and others may have based work on it, prefer reverting over deletion to avoid rewriting shared history It's one of those things that adds up. Which is the point..
Methods to Delete a Commit in Git
Git offers several approaches, each suited to different scenarios And that's really what it comes down to..
Using git reset
git reset moves the branch pointer to a previous commit, optionally discarding changes in the working directory or staging area And that's really what it comes down to..
--soft– Keeps changes staged; useful when you want to re‑commit after editing.--mixed(default) – Unstages changes but leaves them in the working directory.--hard– Discards all changes; use only when you are certain you don’t need any of the modifications.
Using git revert
git revert creates a new commit that undoes the changes introduced by a target commit, preserving history. This is the safest option for public branches.
Using Interactive Rebase (git rebase -i)
An interactive rebase lets you edit, squash, drop, or reorder commits. To delete a commit, you mark it with drop (or simply delete its line) in the todo list.
Using git filter-branch or git filter-repo
These tools rewrite history across many commits (e.g., removing a file from the entire repo). They are powerful but risky; prefer git filter-repo for modern workflows and always back up first.
Step‑by‑Step Guide
Deleting the Last Commit
If the commit you want to remove is the most recent one (HEAD), the process is straightforward Easy to understand, harder to ignore..
- Check the current state
git log --oneline -5 - Choose a reset mode
- To keep changes for a new commit:
git reset --soft HEAD~1 - To unstage changes but keep them in the working directory:
git reset --mixed HEAD~1 # or just git reset HEAD~1 - To completely discard the commit and its changes:
git reset --hard HEAD~1
- To keep changes for a new commit:
- If the commit was already pushed, you must force‑push the updated branch:
Never use plaingit push origin--force-with-lease --forceon shared branches;--force-with-leasehelps avoid overwriting others’ work.
Deleting an Older Commit
When the target commit is not at the tip, you have two main options: revert or interactive rebase Simple, but easy to overlook..
Option A: Revert (history‑preserving)
- Identify the commit hash:
git log --oneline - Create a revert commit:
git revert - Resolve any conflicts, then commit the revert.
- Push as usual:
git push origin
Option B: Interactive Rebase (history‑rewriting)
- Start an interactive rebase for the range that includes the commit:
Take this: to edit the last three commits:git rebase -i~1 git rebase -i HEAD~3. - In the editor that opens, change the word
picktodrop(or delete the line) for the commit you want to remove. - Save and close the editor. Git will replay the remaining commits, skipping the dropped one.
- If conflicts arise, resolve them, then run
git rebase --continue. - Force‑push if the branch is public:
git push origin--force-with-lease
Deleting Multiple Consecutive Commits
You can extend the techniques above:
- Reset:
git reset --hard HEAD~nwhere n is the number of commits to drop. - Revert: Run
git revert <oldest-hash>..<newest-hash>(Git will create a revert commit for each). - Interactive rebase: Mark each unwanted commit with
dropin the todo list
Deleting a Commit That Is a Merge Commit
Merge commits have two parents, so removing them requires extra care.
- Identify the merge commit hash and decide which parent line of history you want to keep (usually the first parent, i.e., the branch you merged into).
- Reset to the desired parent:
Usegit reset --hard^1 ^1for the first parent (the base branch) or^2for the second parent (the feature branch). - Force-push the rewritten history:
Note: If you usedgit push origin--force-with-lease reverton a merge commit previously, you must specify the mainline parent:git revert -m 1 <merge-commit-hash>.
Purging a File (or Sensitive Data) from All History
If you accidentally committed a password, key, or large binary file, deleting a single commit isn't enough—you must purge the file from every commit Simple as that..
Do not use git filter-branch (it is slow and error-prone). Use the modern, faster git filter-repo (install via pip install git-filter-repo or your package manager).
- Backup your repository (mirror clone is safest):
git clone --mirror git@github.com:user/repo.git cd repo.git - Run the purge:
# Remove a specific file from all history git filter-repo --path path/to/secret.txt --invert-paths # Remove a folder entirely git filter-repo --path folder/to/remove --invert-paths - Force-push all refs to the remote:
git push origin --force --all git push origin --force --tags - Notify collaborators: They must re-clone the repository. Expire the reflog and garbage collect to reclaim disk space immediately:
git reflog expire --expire=now --all git gc --prune=now --aggressive - Existing local clones cannot be safely "fixed" with a simple pull.
Some disagree here. Fair enough Which is the point..
Recovering From a Mistake: The Reflog Safety Net
If you accidentally reset --hard the wrong commit or botched a rebase, the commits aren't gone—they are just "unreachable." Git’s reflog records every movement of HEAD.
- View the reflog:
Output example:git refloga1b2c3d HEAD@{0}: reset: moving to HEAD~1 <-- The mistake e4f5g6h HEAD@{1}: commit: "Add feature X" <-- The commit you lost - Reset back to the previous state:
Or create a branch to inspect the lost commits first:git reset --hard HEAD@{1}git branch recovery-branch HEAD@{1}
Summary Cheat Sheet
| Scenario | Command | History Rewritten? | Safe for Shared Branches? |
|---|---|---|---|
| Undo last commit, keep changes | git reset --soft HEAD~1 |
Yes | Only with --force-with-lease |
| Undo last commit, discard changes | git reset --hard HEAD~1 |
Yes | Only with --force-with-lease |
| Undo public commit (safe) | git revert <hash> |
No | Yes (Standard push) |
| Remove middle commit(s) | git rebase -i <parent>~1 (mark drop) |
Yes | Only with --force-with-lease |
| Remove merge commit | git reset --hard <hash>^1 |
Yes | Only with --force-with-lease |
| Purge file from all history | git filter-repo --path <file> --invert-paths |
Yes | Requires full team re-clone |
Conclusion
Deleting commits in Git is fundamentally about choosing between preserving truth (revert) and curating history (reset/rebase/filter-repo).
- For shared, public branches: Default to
git revert. It protects your collaborators from the chaos of rewritten history and forces a clear audit trail of why a change was undone. - For local/private branches: Use
git resetor interactive rebase freely to craft a clean, logical narrative before sharing your work. - For security incidents (secrets/large files): Treat
git filter-repoas a nuclear option. It requires coordinated team action (re-cloning) but is the only way to truly excise data from the object database.
Golden Rule: Before any history-rewriting command (`reset --