It happens to everyone: the PR is up, CI is running, and then you see it. debug-temp.js. A half-finished experiment. A commit that was meant for a different branch entirely.
There are two ways out, and they differ in one thing: whether you're willing to rewrite the PR's history.
Way 1: interactive rebase + force push (the classic)
git rebase -i HEAD~6
Find the offender in the list, change its pick to drop (or just delete the line), save. Git replays the remaining commits without it. Then:
git push --force-with-lease
The PR updates with the commit gone, like it never happened.
The costs are real, though. You rewrote commits the reviewer may have already read; GitHub marks existing review comments as outdated and unthreads them from the code they were about. If later commits touched the same lines as the dropped one, you're resolving conflicts mid-rebase. And --force on the wrong day becomes a recovery exercise.
On a solo PR with no review yet, this is perfectly fine. On an active review, think twice.
Way 2: revert the commit inside the PR (no rewrite)
git revert <sha-of-the-bad-commit>
git push
This adds a new commit that undoes exactly the bad one's changes. The PR now contains the mistake and its cancellation — slightly noisier history, but nothing is rewritten, review threads stay anchored, and there's zero force-push risk.
If the PR gets squash-merged anyway (many teams do), the add-and-remove pair collapses into the single squashed commit and the noise disappears entirely. Clean result, no rewrite at any point.
Remove it without rewriting anything
Point git.tools at the commit. It computes the exact inverse of that commit's changes and opens a pull request against your branch — the PR updates with clean history the moment you merge, and nothing ever gets force-pushed.
Open Remove Commit →The exception: secrets
If what you committed is a credential, removal is step two, not step one. Anything pushed to GitHub should be treated as public: rotate the key or password now, before you touch history. Removing the commit afterwards is hygiene; rotating is the actual fix.
Picking between them
- Solo PR, no reviews yet, commit is recent → rebase + drop, force push, move on
- Active review, shared branch, or commit buried deep → inverse-commit removal, no rewrite
- Secret in the commit → rotate first, then either of the above