Merging two, three or more git repositories keeping the log history

 TAGS:undefinedIf you ask anyone, merging several git repositories into a single one would usually be considered a bad strategy: as a rule of thumb, your code base should have several components isolated and then use a dependency manager (e.g: Composer) to bring them together.

Nevertheless, there are always many different scenarios and with them come different use cases, weird situations or just plain normal activities that would justify this procedure. In any case if you ever need to merge several repos into one, and still want to keep the git history this is an easy way to do it.

The following technique is not rocket science and it's based in the simple addition of remotes and branch merging. If you expected something fancier I am sorry to tell you you'll find this boring.


Preparing the repositories

As we said, we are going to put every repo in a different folder inside the new one. This is what you need to do (you can copy sequentially the commands writing the right paths):

Create a new directory and clone everything inside. The final repo and the existing ones. E.g:

mkdir ~/all_repos_merged
cd all_repos_merged
git clone
git clone
git clone
git clone

Now go to the first repository you want to import and create a new folder, and git-move all the content inside. The folder structure you create now is the folder you'll see in the merged repo. In the following example this first repo would appear under the path/to/repository folder.

cd repository/
mkdir -p path/to/repository
git mv -k * path/to/repository/
git commit -m "Merging repositories like a boss"

Note that the -k flag ignores errors such as moving the newly created folder to itself. Before moving on with the next operation you might want to check that you don't have in each repos conflicting files, such as the .gitignore. You can delete those files and restore them later or you just fix the conflicts when they arise (edit the file, git add and then git commit: conflict solved).

Now we prepare the second, third, forth... repository. Repeat the same N more times:

    cd ../repository2/
    mkdir -p path/to/repository2
    git mv -k * path/to/repository2/
    git commit -m "Merging repositories like a boss"

    cd ../repository3/
    mkdir -p path/to/repository3
    git mv -k * path/to/repository3/
    git commit -m "Merging repositories like a boss"

CAUTION: You should never PUSH this changes to origin.

Commits are local in your computer and remember you want a new repo, not mess around with the existing ones.

Now the three repos are "ready". Meaning they are all inside a new folder and content is not mixed.

Merging all the repos together in the new one

Next steps are having the just prepared repositories available for importing code in the final repository. With git you can reference any other external repository with the "remotes".

Go to the final repo:

cd ../repository-all-merged/

Add all the remotes for merging named repository, repository2, repository3... Note that although being a "remote" this repos are the ones you cloned in your local computer:

    git remote add repository ~/all_repos_merged/repository
    git remote add repository2 ~/all_repos_merged/repository2/
    git remote add repository3 ~/all_repos_merged/repository3/

And this is the moment when we actually bring the changes to the final repo:

    git checkout master
    git pull repository master

Now we have in the master branch all the changes of repository/master branch. Conflicts might arise at this point (specially if you didn't do anything with the gitignore), but fixing them is as easy as:

    vi .gitignore
    git add .gitignore
    git commit

And repeat for every other repo:

    git pull repository2 master
    git pull repository3 master

Check that everything is in place:

git log --oneline --graph --decorate --all

The repository-all-merged contains now all the code with all the history of the master branches. If you for instance need to import more branches it would be as easy as:

git checkout another_branch
git pull repositoryN another_branch

If this merged repo has a remote, you can clean and push it now:

git remote rm repository
git remote rm repository2
git remote rm repository3
git push

The rest of the repositories can be deleted, so you don't push by mistake later on.