All the answers so far don't address the trailing concern:
Is there an efficient method when there are hundreds of revisions
after the one to be deleted?
The steps follow, but for reference, let's assume the following history:
[master] -> [hundreds-of-commits-including-merges] -> [C] -> [R] -> [B]
C:
commit just following the commit to be removed (clean)
R:
The commit to be removed
B:
commit just preceding the commit to be removed (base)
Because of the "hundreds of revisions" constraint, I'm assuming the following pre-conditions:
- there is some embarrassing commit that you wish never existed
- there are ZERO subsequent commits that actually depend on that embarassing commit (zero conflicts on revert)
- you don't care that you will be listed as the 'Committer' of the hundreds of intervening commits ('Author' will be preserved)
- you have never shared the repository
- or you actually have enough influence over all the people who have ever cloned history with that commit in it to convince them to use your new history
- and you don't care about rewriting history
This is a pretty restrictive set of constraints, but there is an interesting answer that actually works in this corner case.
Here are the steps:
git branch base B
git branch remove-me R
git branch save
git rebase --preserve-merges --onto base remove-me
If there are truly no conflicts, then this should proceed with no further interruptions. If there are conflicts, you can resolve them and rebase --continue
or decide to just live with the embarrassment and rebase --abort
.
Now you should be on master
that no longer has commit R in it. The save
branch points to where you were before, in case you want to reconcile.
How you want to arrange everyone else's transfer over to your new history is up to you. You will need to be acquainted with stash
, reset --hard
, and cherry-pick
. And you can delete the base
, remove-me
, and save
branches