Undoing a Push to Remote
Pushing to a remote is the point of no return in Git — not because the data is gone, but because others might have already pulled your commits. This page walks through every approach: the safe, additive git revert path and the dangerous but sometimes necessary force-push path. Understanding the difference is essential before you touch any remote history.
Why undoing a push is different from undoing a local commit
When a commit only exists locally, you can rewrite history freely. Once it is on the remote, other developers may have run git pull and now have that commit in their local history. If you rewrite the remote and force-push, their history and yours diverge — and the next time they push they get a confusing rejection.
Safe approach —
git revert: adds a new commit that undoes the changes. History is preserved. Safe for shared branches.Unsafe approach —
git reset+ force-push: rewrites history. Breaks teammates who already pulled.
The safe approach: git revert
git revert creates a brand-new commit that is the inverse of a previous commit. The bad commit stays in history, but its effects are cancelled out. This is the recommended approach for any shared branch.
Find the commit you want to undo
git log --oneline -5 # abc1234 (HEAD -> main, origin/main) Deploy broken config # def5678 Update API endpoints # ghi9012 Add user dashboard
Revert the commit
git revert abc1234 --no-edit # [main xyz9999] Revert "Deploy broken config" # 1 file changed, 3 insertions(+), 3 deletions(-) git push origin main # Counting objects: 3, done. # To https://github.com/user/repo.git # abc1234..xyz9999 main -> main
Reverting a range of commits
Revert multiple commits in order (newest first)
# Revert the last 3 commits git revert HEAD~3..HEAD --no-edit # Creates 3 revert commits # Or revert a specific range by hash git revert def5678..abc1234 --no-edit
The unsafe approach: reset + force-push
Reset local history and force-push
# Step 1: undo the last commit locally (keep changes in working tree) git reset --soft HEAD~1 # Or to also discard the file changes: # git reset --hard HEAD~1 # Step 2: verify you are at the right point git log --oneline -3 # Step 3: force-push with --force-with-lease (safer than --force) git push --force-with-lease origin main
--force vs --force-with-lease
These two flags both rewrite remote history, but they differ in how safe they are in collaborative situations.
Flag | Behaviour | Risk level | When to use |
|---|---|---|---|
--force | Overwrites remote unconditionally, regardless of what is there | High — destroys commits pushed by others since your last fetch | Only when you are absolutely sure you are the only user of the branch |
--force-with-lease | Fails if the remote has commits you have not fetched yet | Lower — protects against accidentally overwriting a teammate's push | Preferred option when force-push is necessary |
force-with-lease in action
git push --force-with-lease origin feature-x # If someone else pushed since your last fetch: # error: failed to push some refs to 'origin' # hint: Updates were rejected because the tip of your current branch is behind # hint: its remote counterpart. # You must fetch first and decide what to do git fetch origin git log --oneline origin/feature-x # see what they added # Then rebase or merge before trying again
Decision guide: revert vs force-push
Situation | Recommended approach |
|---|---|
Shared branch (main, develop, etc.) | git revert — never rewrite shared history |
Your own feature branch, no PR yet | Either is fine; force-push is cleaner |
Feature branch with open PR, reviewer commented | Communicate first; then force-push if needed |
Sensitive data (passwords, keys) committed | Force-push AND rotate the credentials immediately |
Large file committed that breaks push size limits | Force-push with git filter-repo after removing the file |
Sensitive data committed to a public repo
Rotate/revoke the exposed credential immediately (before anything else).
Remove the file from history:
git filter-repo --path secrets.env --invert-paths.Force-push all branches:
git push --force-with-lease origin --all.Contact GitHub support to purge cached views if the repo is public.
Add the file to
.gitignoreto prevent future accidents.
Notifying teammates after a force-push
After any force-push to a shared branch, teammates need to update their local copies. Tell them exactly what to run so they do not accidentally push their diverged history back to the remote.
What teammates should run after you force-push main
# Option A: fetch and reset hard (discards any local commits on main) git fetch origin git reset --hard origin/main # Option B: if they have local commits they want to keep git fetch origin git rebase origin/main
Undoing a push to a protected branch
Most platforms (GitHub, GitLab, Bitbucket) let you protect branches so that force-pushes are blocked. If the branch is protected, you cannot force-push and must use git revert. If you need to clean history on a protected branch, an admin must temporarily remove the protection.