Search code examples
gitvisual-studiobitbucketatlassian-sourcetree

Git - merge two repositories with common ancestor


Let's say I used to have a repository Z and created two repositories (A and B) based on it.

I made them mutually exclusive. What I mean is files in A should not be in B and viceversa. So, I deleted the some files in A and also files in B.

These two repositories kept growing over time.

Now, due to some management decision, I need them back as a single repository. I tried doing a merge as indicated in How to use the subtree merge strategy, but it gives me too many conflicts and the merge itself was very dirty. I understand this is an uncommon and difficult task.

Have any of you faced the same problem, how did you fix it?


Solution

  • I have my doubts about this being a good idea, but it sounds like it doesn't really matter whether it is or not.

    So you had a single repo with, I'll assume, two projects in it. They shared a history, and it seems you need to get back to that state - one master branch with all the files.

    You've apparently gotten as far as bringing all the objects into one repo. For example, perhaps you added one repo as a remote of the other, and fetched its master ref; so let's say you have

    A -- B -- C -- X1 -- X2 -- X3 <--(master)
               \
                Y1 -- Y2 -- Y3 <--(repoB/master)
    

    You try

    git checkout master
    git merge repoB/master
    

    and you get conflicts and other messiness, and probably understanding that messiness will help understand what to do instead. The conflicts could be as simple as "file was deleted here and modified there", which is pretty easy to resolve, but other things can be going on as well.

    For example, if you were splitting up two projects, then you might have moved a bunch of stuff from what was a project sub-directory up to the work tree root. Or you might have created a file in one repo, with the same path/name as an existing file in the other repo. Or whatever.

    So the first thing I would do is to make sure each repo's worktree is laid out as a subset of the combined worktree you want going forward. If it's already the case - i.e. you didn't move the files around after splitting the repos, there no path/filename that exists in both repos, and the desired worktree is just what you'd get by cding to one repo's worktree and cp -Ring the other repo's worktree - then you can skip ahead past this step.

    But otherwise, you could

    git checkout master
    

    and then mv files as needed so that you have a subset of the desired worktree, and commit. Then

    git checkout repoB/master
    git checkout -b repoB
    

    and again mv things around as needed and commit.

    A -- B -- C -- X1 -- X2 -- X3 -- X <--(master)
               \
                Y1 -- Y2 -- Y3 -- Y <--(repoB)
    

    You could get by without this, but it gives us simpler options for the next step than what we would have otherwise.

    Next we probably want to start a merge. We don't want the default merge result, which means we're creating what might be called an "evil" merge; but in my view it's not as bad since the merge would, by default, conflict - so in a sense, there is no default merge result.

    Anyway, we want to put git in a "merge-in-progress" state, but its default merge algorithm isn't much use here; so

    git checkout master
    git merge -s ours --no-commit repob
    

    Now we need to add the files from repob, which will work smoothly because none of them are at the same path as a file we already have.

    git checkout repob -- .
    

    Verify that the worktree now looks right. Then

    git commit