I know how to solve this using traditional tools at hand, but I want to understand the possibilities and see whether things can be done more effectively.
In this example - I want to add B as a second parent to C.
main ∙∙∙A---C---D ∙∙∙A---C---D
-> /
feat ∙∙∙--B ∙∙∙--B
- I am looking for a "permanent" history rewrite solution,
- without writing "virtual" replace refs or the likes.
C's andD's author (can be somebody else than me) & date must not change.- The solution must not rewrite unnecessary commits, so in this case it can only rewrite
CandD.
In short, to permanently write the result of something like this into repo, so that it doesn't use replace refs, just pure simple commits:
git replace --graft $commit $parent1 $parent2
I remember studying this topic on Plumbing and Porcelain in git docs and I remember I spent too much time on it, but I have no recollection of what I learned. What can I try next?
For this task we will need:
git cat-filesedgit hash-objectgit update-refLet's create a sample repository. This script sets things up so that we have repeatable commit ids and we don't run into any issues caused by weird local git configurations:
That gets us:
We can use
git cat-file -pto dump the structure of a commit. We want to add a new parent to commitC, which looks like:To make
Ba parent of this commit, we need to add a secondparentline. We need the full commit id for commitB:We can add that as a parent of C using
sed, like this:That looks right. Now we need to write that into the object database:
So now we have commit
C'with commit ida6db46299e550128fa8534dcc001f961ac4265c5. We need to editDto getD'with parentC', which is just a simple search/replace operation:Lastly, we need to update the
mainbranch to point toD'as the new HEAD:Now let's see what we have:
I think that's what you were after.
I wrote out all the steps here in detail which makes it look enormous compare to the solution from @ElpieKay, but when we distill it down to the crucial commands we get:
Fill in
$Athrough$Dwith the appropriate commit ids. If you're working with the sample repository created by the script earlier in the post, you can run (assuming you save the script to a file namedreparent-w-hash-object.sh):