GitUndoing a Push

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 approachgit revert: adds a new commit that undoes the changes. History is preserved. Safe for shared branches.

  • Unsafe approachgit 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

Bash
git log --oneline -5
# abc1234 (HEAD -> main, origin/main) Deploy broken config
# def5678 Update API endpoints
# ghi9012 Add user dashboard

Revert the commit

Bash
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
What --no-edit does
`--no-edit` accepts the auto-generated revert message without opening an editor. Omit it if you want to write a custom message explaining why you reverted.
Reverting a range of commits

Revert multiple commits in order (newest first)

Bash
# 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
Warning
Force-pushing rewrites the remote branch history. If anyone has pulled since your last push, their local history will diverge from the remote. They will need to run `git pull --rebase` or `git reset --hard origin/main` to recover — and they may lose work if they are not careful. Always communicate before force-pushing a shared branch.

Reset local history and force-push

Bash
# 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

Bash
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
Warning
If you accidentally pushed a password, API key, or private key to a **public** repository, assume it is already compromised — bots scan GitHub in real time. Rotate or revoke the credential immediately, then clean the history.
  1. Rotate/revoke the exposed credential immediately (before anything else).

  2. Remove the file from history: git filter-repo --path secrets.env --invert-paths.

  3. Force-push all branches: git push --force-with-lease origin --all.

  4. Contact GitHub support to purge cached views if the repo is public.

  5. Add the file to .gitignore to 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

Bash
# 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
Tip
Post a message in your team chat explaining what happened, why you force-pushed, and which commands teammates need to run. Include the exact commands above.
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.