Undo a Push / Revert Commit

Everyone makes mistakes in Git. You committed the wrong file, pushed a broken change, or need to undo a commit that's already been shared. This guide teaches you how to safely undo commits using git revert and git reset, with special attention to the dangers of force push.

git revert (Safe) git reset (Local) Force Push (Dangerous)
When Do You Need to Undo a Commit?

There are many scenarios where you might need to undo a commit. You committed a file that contained sensitive information like an API key or password. You accidentally pushed code that breaks the build. You made a commit with the wrong message or forgot to include a file. Or you simply committed changes that you later realized were incorrect.

The approach you take depends on whether the commit has been pushed to a shared repository or not. If the commit exists only on your local machine, you have many options to undo or amend it. If the commit has been pushed and others may have pulled it, you need to be much more careful. Rewriting history that others have already seen is dangerous and should be avoided.

The golden rule of Git: Never rewrite history that has been shared with others unless you have coordinated with your team. Use git revert for shared commits, git reset for local commits only.
Git Revert: The Safe Way to Undo Shared Commits

git revert is the safest way to undo a commit that has already been pushed to a shared repository. Unlike reset, which rewrites history, revert creates a new commit that undoes the changes from a previous commit. This means the original commit remains in the history, but a new commit is added that reverses its effects. This is safe because it doesn't rewrite history—it just adds new history.

To revert a commit, you need its hash. You can find the hash using git log --oneline. Then run git revert . Git will open an editor for you to write a commit message (the default is usually fine), and then create a new commit that undoes the changes. Finally, push this new commit to the remote repository.

# View recent commits to find the hash
$ git log --oneline
a1b2c3d Fix login bug
e4f5g6h Add new feature
i7j8k9l Update documentation

# Revert the commit "Fix login bug"
$ git revert a1b2c3d

# Git creates a new commit. Push it to GitHub
$ git push origin main
When to use revert: The commit has already been pushed and other people may have pulled it. You want to undo changes without rewriting history. This is the standard for team collaboration.
Git Reset: Undoing Local Commits

git reset rewrites history by moving the branch pointer backward. This is safe only if the commits you're resetting have never been pushed to a shared repository. Reset has three modes: --soft, --mixed (the default), and --hard. Each mode determines what happens to your working directory and staging area.

git reset --soft moves the branch pointer but leaves all changes in the staging area. This is useful when you want to combine multiple commits into one or fix a commit message. Your files are unchanged, but Git thinks they are ready to be committed again.

git reset --mixed (default) moves the branch pointer and unstages changes. Your files are unchanged, but they are no longer staged for commit. This is useful when you want to keep your changes but re-commit them differently.

git reset --hard moves the branch pointer AND discards all changes in your working directory. This is dangerous because any uncommitted changes are permanently lost. Only use this when you're certain you don't need the changes.

# Undo the last commit but keep changes staged
$ git reset --soft HEAD~1

# Undo the last commit and unstage changes (keep files)
$ git reset HEAD~1

# Completely remove the last commit and all changes
$ git reset --hard HEAD~1

# Undo the last 3 commits (local only!)
$ git reset --hard HEAD~3
Warning: git reset --hard permanently deletes uncommitted changes. Always run git status before using it to see if you have any unsaved work.
Force Push: Dangerous but Sometimes Necessary

After using git reset locally, you might want to update the remote branch. However, a normal git push will be rejected because the remote branch has commits that your local branch doesn't. This is where force push comes in. Force push tells Git to overwrite the remote branch with your local version, discarding any commits that are only on the remote.

Force push is dangerous because it discards commits. If someone else has pulled those commits, their local history will diverge from the remote, causing confusion and potential data loss. Before force pushing, make sure no one else is working on the branch. Communicate with your team. And always use --force-with-lease instead of --force—it checks that no one else has pushed changes to the branch before overwriting.

# After a local reset, force push to update remote
$ git push --force-with-lease origin main

# For specific branch
$ git push --force-with-lease origin feature-branch

# NEVER use plain --force without a very good reason
$ git push --force origin main # Not recommended
DANGER: Force push permanently removes commits from the remote repository. Anyone who has pulled those commits will have a broken history. Only force push to branches you own, and always coordinate with your team first.
Git Revert vs Git Reset: When to Use Which
ScenarioRecommended CommandWhy
Commit pushed to shared branchgit revertSafe for teams, doesn't rewrite history
Commit only on local machinegit resetCleaner history, no negative impact
Need to fix last commit messagegit commit --amendSimplest for most recent commit
Forgot to add a file to last commitgit add file && git commit --amendAmends the previous commit
Multiple commits to undo (local only)git reset --hard HEAD~NRemoves N commits completely
Multiple commits to undo (pushed)git revert OLD..NEWCreates revert commits for the range
Amending the Last Commit

For the most recent commit that hasn't been pushed yet, git commit --amend is the simplest solution. It allows you to change the commit message or add forgotten files to the previous commit without creating a new commit.

To amend a commit message, run git commit --amend -m "New message". To add a forgotten file, stage the file with git add then run git commit --amend --no-edit (which keeps the existing message). If the commit has already been pushed, you would need to force push after amending—which is dangerous on shared branches.

# Change the last commit message
$ git commit --amend -m "Fix login bug with proper validation"

# Add a forgotten file to the last commit
$ git add forgotten-file.js
$ git commit --amend --no-edit

# If the commit was already pushed, force push after amend
$ git push --force-with-lease origin main
Pro tip: Use git commit --amend freely for commits that are only local. For pushed commits, consider if the change is worth the complexity of a force push.
Undoing Changes to a Specific File

Sometimes you don't want to undo a whole commit—just revert a specific file to a previous version. git restore (or the older git checkout --) lets you discard uncommitted changes to a file. For committed changes, you can check out a file from a previous commit.

To discard uncommitted changes to a file, use git restore filename. This reverts the file to its last committed state. To restore a file from a specific commit, use git restore --source= filename. This is useful when you want to keep other changes in the commit but revert one file.

# Discard uncommitted changes to a file
$ git restore config.js

# Restore a file from 2 commits ago
$ git restore --source=HEAD~2 app.js

# Older syntax (still works)
$ git checkout -- config.js
$ git checkout HEAD~2 -- app.js
Common Scenarios and Solutions

Scenario 1: You committed a secret (API key) and pushed it. First, revoke the secret immediately at the service provider. Then use git revert to create a commit that removes the secret. Finally, use git filter-repo or BFG to completely remove the secret from history—though this requires force pushing and team coordination.

Scenario 2: You made a commit with the wrong message, not pushed yet. Use git commit --amend -m "Correct message". Simple and safe.

Scenario 3: You committed to the wrong branch and haven't pushed. Use git reset HEAD~1 to undo the commit while keeping changes, then git stash to save them, switch to the correct branch, and git stash pop then commit.

Scenario 4: You want to undo the last 3 commits on a shared branch. Don't use reset. Use git revert HEAD~3..HEAD to create three revert commits (one for each commit being undone). This is safe and keeps history intact.

# Scenario 3: Move commit to correct branch
$ git reset HEAD~1 # Undo commit, keep changes
$ git stash # Save changes
$ git checkout correct-branch
$ git stash pop # Apply changes
$ git add . && git commit -m "Feature complete"

# Scenario 4: Revert range of commits on shared branch
$ git revert HEAD~3..HEAD
$ git push origin main
Frequently Asked Questions
What's the difference between git revert and git reset?
Revert creates a new commit that undoes changes—safe for shared branches. Reset moves the branch pointer backward, rewriting history—only safe for local branches. Use revert for commits that have been pushed.
Can I undo a push that happened a week ago?
Yes! Use git revert with the commit hash from a week ago. Since others may have built on top of that commit, revert is safer than reset. The revert commit will undo the changes from that old commit.
What's the difference between --force and --force-with-lease?
--force-with-lease checks that no one else has pushed to the branch before overwriting. It will fail if the remote branch has changed since your last fetch. This prevents you from accidentally discarding someone else's work. Always use --force-with-lease instead of --force.
How do I undo a git reset --hard?
If you haven't run any other Git commands, use git reflog to find the commit hash you were on before the reset, then git reset --hard . The reflog tracks all branch movements and can save you from accidental resets.
Can I revert a merge commit?
Yes, but it's more complex. Use git revert -m 1 . The -m 1 tells Git which parent to revert to (usually the main branch). Then push the revert commit as normal.
What happens if I force push to a protected branch?
Protected branches in GitHub block force pushes by default. This is a safety feature. If you need to force push, you'll need to temporarily remove branch protection, force push, then re-enable protection. Better to use revert instead.
How do I remove a sensitive file from Git history completely?
Use git filter-repo or BFG Repo-Cleaner. This rewrites history and requires a force push. All collaborators must re-clone. This is the nuclear option—use only for security emergencies like exposed passwords.
My commit shows as "detached HEAD" after reset. What happened?
If you reset to a commit that isn't at the tip of a branch, you enter detached HEAD state. To fix, create a new branch: git branch temp-branch, then git checkout main to return. Your changes are safe.
Previous: IaC with Terraform Next: Resolve Merge Conflicts

Mistakes in Git are fixable. The key is knowing which tool to use: revert for shared commits, reset for local work, and force push only when absolutely necessary and coordinated with your team.