I think some confusion is raised with this question, because you phrase it as wanting to "revert" the mistake - and "revert" means something specific to git. I know what you mean isn't what git means by that word, because with git's meaning a phrase like "revert history" isn't a thing.
Because you want to undo the change entirely, a history rewrite is the first step. AnoE's answer shows one way to do this, assuming that there is just one ref from which the bad merge is reachable, and that there are no merge commits "between" that ref and the bad merge.
In the event there are multiple refs, you'd need to do something more. For example if you had
x -- x -- x --- ML -- x -- A -- x <--(master)
/ \
(linux history) o <--(branch_one)
completing the rebase would give you
x' -- A' -- x' <--(master)
/
x -- x -- x --- ML -- x -- A -- o <--(branch_one)
/
(linux history)
You'd then need to transplant the o
commit, with something like
git rebase --onto A' A branch_one
(replacing A
and A'
with either the commit ID or some other expression that names the appropriate commit).
If there are merges that need to be rewritten, then you have a bigger issue. The rebase
command will try to write a linear history by default. You can tell it you want to keep the merge topology with the --preserve-merges
option, but it may not work properly. If a merge commit had conflicts, you'll have to re-resolve it. Worse, if a merge commit doesn't have conflicts, but was not originally completed using the default merge result, then rebase
will not recreate the merge (or any children of it) correctly.
So the only safe way to rebase, then, is in segments, manually reproducing merges as you encounter them.
Another option might be to use git filter-branch
instead of rebase
; but this is tricky, too. It's only workable if you can script the removal of anything the merge introduced. For example, if the linux history is in different paths than your own work, so that you could clean up a given instance of the content by rm
ing certain paths, then you could use filter-branch
.
(Since this is an option that may or may not be viable for you, for now I won't spell out the detailed steps. The filter-branch
documentation can fill in the blanks. Basically you'd use a parent-filter
to bypass the merge commit (by re-parenting the following commit onto the first parent commit), plus an index-filter
or tree-filter
to remove the linux files from the subsequent commits.)
One way or another, once you have the history cleaned up you would still have all that history in your repo's database. At a minimum you need to make sure nothing references that history. Then it would eventually get cleared out by gc
(or you could force that to happen sooner).
Mostly that means you have to find any refs that can reach the linux history. Since the rewrite moved "your" refs, this would likely comprise any refs (branches or tags) pulled in with the linux history itself. So you'd just want to delete those.
There also will be reflogs that can (indirectly) reach the linux history, and gc can't remove history that's reachable in this way. Honestly at this point the easiest thing to do is probably to re-clone the repo (as a new clone should only get the current refs and their history) and replace origin with the result.
If you want to repair an existing repo instead of re-cloning for whatever reason, the next step would be to wipe out reflogs (I usually just rm -r .git/logs
) and then run an aggressive gc
(see the gc
docs)