How do I delete unpushed git commits?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Use git reset --hard HEAD~N to delete the last N unpushed commits and discard all changes, or git reset --soft HEAD~N to undo the commits while keeping your changes staged for a new commit. Since these commits have not been pushed, you can safely rewrite local history without affecting anyone else.
Understanding What "Unpushed" Means
An unpushed commit exists in your local repository but has not been sent to the remote (e.g., GitHub, GitLab). You can see how many unpushed commits you have:
Because these commits only exist on your machine, you can modify or delete them freely without affecting collaborators.
Method 1: git reset (Rewrite History)
git reset moves the branch pointer backward, effectively removing commits from the branch history. The three modes control what happens to the file changes from those commits:
Soft Reset: Keep Changes Staged
After this, your working directory is unchanged and all modifications from the removed commits are staged. You can create a new, single commit with all those changes:
This is useful for squashing multiple small commits into one clean commit before pushing.
Mixed Reset (Default): Keep Changes Unstaged
The changes are preserved in your working directory, but they are not staged. You can review, selectively stage, and recommit:
Hard Reset: Discard Everything
This is destructive. The commits are removed, and all file modifications from those commits are permanently deleted from your working directory. Use this when you want to completely abandon the work in those commits.
Method 2: git revert (Preserve History)
If you want to undo changes from specific commits without rewriting history, git revert creates new commits that reverse the effect:
git revert is primarily designed for undoing pushed commits, but it works for unpushed commits too. The key difference from git reset is that the original commits remain in the history.
Method 3: Reset to a Specific Commit
Instead of counting backward with HEAD~N, you can reset to a specific commit hash:
This removes every commit after c3b2a1d, regardless of how many there are.
Method 4: Interactive Rebase (Selective Removal)
When you need to remove specific commits from the middle of your unpushed history while keeping others, use interactive rebase:
This opens an editor showing:
Delete the line for the commit you want to remove (or change pick to drop), save and close the editor. Git replays the remaining commits without the removed one.
Comparison of Methods
| Method | Changes to Files | History | Best For |
git reset --soft HEAD~N | Kept (staged) | Commits removed | Squashing commits before push |
git reset --mixed HEAD~N | Kept (unstaged) | Commits removed | Re-organizing changes into new commits |
git reset --hard HEAD~N | Discarded | Commits removed | Completely abandoning work |
git revert HEAD | Reversed by new commit | Preserved + new commit | Undoing pushed commits (or audit trail) |
git rebase -i HEAD~N | Depends on action | Rewritten | Removing specific commits from middle |
Recovering from an Accidental Hard Reset
If you run git reset --hard and immediately realize you lost work you needed, Git's reflog saves you:
The reflog keeps entries for about 90 days by default, so you have a window to recover.
Resetting to Match the Remote Exactly
If you want to throw away all local commits and make your branch identical to the remote:
This discards all unpushed commits and any uncommitted changes. Your local branch becomes an exact copy of the remote branch.
Common Pitfalls
Using git reset --hard on pushed commits. If commits have already been pushed to a shared branch, git reset --hard followed by git push --force rewrites public history. This breaks the repository for everyone who has pulled those commits. Use git revert for pushed commits instead.
Forgetting that git reset without a flag defaults to --mixed. Running git reset HEAD~2 keeps your changes but unstages them. If you expected either a soft or hard reset, the result will surprise you. Always specify the flag explicitly.
Counting commits wrong with HEAD~N. HEAD~1 means one commit before HEAD (removing only the latest commit). HEAD~2 means two commits before HEAD (removing the last two). Off-by-one errors are common. Use git log --oneline to verify commit hashes before resetting.
Losing untracked files. git reset --hard does not delete untracked files (files that were never added to Git). If you also need to clean up untracked files, run git clean -fd separately. But be careful since this deletion is permanent and not recoverable via reflog.
Interactive rebase conflicts. When you remove a commit via git rebase -i that other commits depend on, Git will stop with a merge conflict. You will need to resolve the conflict manually and run git rebase --continue.
Summary
For unpushed commits, git reset is the most direct tool. Use --soft to keep changes staged for recommitting, --mixed (default) to keep changes unstaged for review, or --hard to discard everything. For selective removal from the middle of your history, use interactive rebase. Reserve git revert for pushed commits where history preservation matters. Always check git reflog if you accidentally discard work you need, as Git keeps a recovery log for about 90 days.

