Let's consider the first blue node in the history where the ugly branching begins to be the start point. Let us call its hash S
.
First, let's gather a list of the commits we would like to linearize:
git log --oneline --reverse --full-history --simplify-merges S..HEAD > commits
Now, let us throw away all that history and reset the branch back to S
:
git reset --hard S
Finally, let us use git rebase --interactive
to pick those commits into the branch:
# Null rebase with no commits:
git rebase -i HEAD
Now we are sitting in a text editor, with the buffer containing the word noop
: we have initiated a blank rebase of nothing. Delete the noop
line and in its place, read the commits
file into the edit buffer. In front of each line, add the prefix p
to pick that commit. Re-arrange the order as you like. Then save and quit. The interactive rebase script will happily oblige and cherry pick all the commits we added into the buffer.
You now have to deal with the interactive rebase workflow to resolve any conflicts arising from the order in which these are picked.
It could be that this is doable in one step, perhaps with git rebase -i S
. I'm not sure how rebase deals with convoluted histories. The above approach makes it concrete; we tweak the git log
options to extract the exact list of commits that we want, and if we are happy with what's in the commits
file, we (ab)use interactive rebase to cherry pick it for us.
You should triple-check that commits
contains what you want from the graph. When the dust settles, do a git diff <original-HEAD-SHA>
to see what is different. Ideally there should be no difference at all, or only some trivial diffs based on doing some merge resolution slightly differently (e.g. order of declaration of variables in some function and such).