gitmergerebasegit-rewrite-history

Intermingling merge-basing two history tails in Git


I have two Git repositories with separate histories. After much researching and a lot of tinkering, I wound up with a history that looks like this (using YYYY-DD as pseudo commit-hashes):

HEAD---2016-09---2016-08---2016-07---2016-06
                     \-----2015-10---2015-09

Note that this history appears to have two "roots", as the 2015-10 sequence was merged/grafted into the middle. The important thing is that the files in the 2016-07 history and the 2015-10 history contain completely different files with nothing in common.

I would like to combine this history into one complete line of commits, in original commit order from oldest to newest, with no skips (i.e. disappearing content that reappears later). In other words, here is what I want to end up with:

HEAD---2016-09---(2016-08)---2016-07---2016-06---2015-10---2015-09

I put (2016-08) in parentheses, because as a merge commit I don't even think I need it if all goes correctly.

At first I thought do a git replace --edit 2016-08 to remove its 2015-10 parent, and then a git replace --edit 2016-06 to put the 2015-10 as its parent (see https://stackoverflow.com/a/37001417/421049), and sure enough, after doing a git filter-branch --tag-name-filter cat -- --all I got a single sequence that looked like what I wanted.

But then I started thinking: because the content was completely different in the two "tails", won't the content in 2015-10 "disappear" in 2016-06 and then "come back" in 2016-08? (I confess I can't immediately verify whether it did or not right now; I erased that attempt and I'm too tired to do it again tonight.) How can I effectively "merge" (in the everyday sense) the two tails, as if the content in 2016-06 was added onto the content in 2015-10? That is, whatever commit that replaces 2016-06 will have all the content in 2015-10 along with the content in the original 2016-06? However I want the new 2016-06 to have its original commit date, log message, etc.

Then, after that's done, how do I remove the "merge" commit 2016-08 from the history, as it's not needed any more? I assume I can use git rebase --onto 2016-08~ 2016-08 HEAD from https://stackoverflow.com/a/3705310/421049. I tried it in the initial attempt, but it didn't work --- I assume it had something to do with the content that "disappeared" in 2016-07 and 2016-06 because I just restitched the tails in a different sequence, but I'm not sure.


Solution

  • On a simplified example, I was able to

    git checkout -b middle 2016-07
    git rebase 2015-10
    

    This replays your commits 2016-07 and 2016-06 on top of 2015-10.

    git checkout master
    

    Assuming your master points to 2016-09 or a descendant

    git rebase middle
    

    which replays 2016-09 (or more, depending on your master) on top of the new middle we just created. This rebase will also discard the merge commit.

    If, as you say, "the files in the 2016-07 history and the 2015-10 history contain completely different files with nothing in common" this should give you a nice history without conflicts.