Merging two Git repositories together in not a common task. In my own consulting work, this is a task that does come up every so often when the code I’m working on needs to be moved from an initial Git repository, whether it’s GitHub or Azure DevOps, to another Git repository that will be the final destination for the source code. Sure, it’s easy to “move” the source code to another repository by copying the files and making a commit, but it can be much more useful to preserve the commit history for tracking purposes. Luckily, Git does support this with the git merge
command when setting up multiple remotes using git remote
. Also, the git-filter-repo
tool can be used to move files and their history to a new folder location within the same repository before making the move, in cases where one of the repositories needs to be moved while preserving history to a subfolder of the target / destination repository.
Let’s take a look at the bash or CLI commands to merge Git repositories while maintaining commit history!
The bash and CLI commands shown in this article for merging two Git repositories while maintaining commit history with with Git distributed version control. This means it will work with GitHub repositories, Azure DevOps repositories, or any other Git repositories!
Merge Two Git Repositories into One
To merge two Git repositories, you’ll need to perform the following steps:
- Determine which Git repositories are going to be the Source and Target. Target being the repository that will have the Source merged into.
- Create a working directory, such as
/workspace
- Clone the Source repository into the
/workspace/source
folder - Check out the branch of Source that you want to merge, via
git checkout
- Clone the Target repository into the
/workspace/target
folder - Create a new branch to receive the merged files and history in the Target repository, via
git branch
andgit checkout
commands - Within the Target repository in the
/workspace/target
folder, add a new Git remote pointed towards the/workspace/source
folder and the branch of the Source repository to be merged into Target, for example usinggit remote add -f local ../source
- Within Target, use the
git merge command to merge in the local/main
remote branch, using the--allow-unrelated-histories
flag to make sure it all merges - Use
git push
to push up the new merged branch to the Target repo - Celebrate that your two Git repositories are now merged into one!
Here’s an example script that performs all these steps with comments to help you walk through the process of merging two Git repositories into one:
# Create a working directory
mkdir workspace
cd workspace
# Clone the 'source' repo getting merged into 'target' repo
git clone https://github.com/build5nines/source-repo.git source
cd source
# Checkout the 'main' branch of the 'source' repo
git checkout main
# Clone the 'target' repo that's the destination for the merge
cd ..
git clone https://github.com/build5nines/target-repo.git target
# Create new branch in 'target' repo for the merge
cd target
git branch merged-repos
git checkout merged-repos
# Add Git 'remote' to 'target' repo that pulls from 'source' repo folder
# Name the 'source' remote 'local'
git remote add -f local ../source
# Merge files and history from 'source' repo (via 'main' branch of 'local' remote) into 'target'
git merge --allow-unrelated-histories -m 'merge repos' local/main
# Push the finished 'target' repo merge branch to remote / GitHub
git push --set-upstream origin merged-repos
Merge Git Repository into Subfolder of another Git Repository
When merging two Git repositories that have files and/or folders with the names, then those files and folders will need to be either moved or renamed before merging to avoid conflicts. Thankfully, the git-filter-repo
tool exists that allows you to easily move the contents of a Git repository to a new subfolder within the same repository while preserving history.
The git-filter-repo
tool requires Python to run. You may need to perform a couple steps depending on if you’re installing on Windows, Linux or macOS. The git-filter-repo
tool Install documentation will help you with this.
In general, you can use your favorite package manager to install it:
$ <fav-package-manager> install git-filter-repo
If you’re using macOS, then git-filter-repo
can be installed easily using Homebrew:
// Homebrew on macOS
brew install git-filter-repo
Once, you have git-filter-repo
installed, then it’s a single command that can be run with Git to perform the file with history move. Here’s the git-filter-repo
command to move the entire root (/
) contents of the Git repository to a subfolder named /Foo
:
git filter-repo -f --to-subdirectory-filter Foo --tag-rename :foo-
Now that you have git-filter-repo
installed, you will be able to merge the Source repository into a subfolder of the Target repository while preserving all commit history.
Here’s the above Git merge example with the use of git filter-repo
added in the appropriate place to move the contents of the Source repository into a subfolder named /Foo
within the repository before then merging it into the Target repository:
'# Create a working directory
mkdir workspace
cd workspace
# Clone the 'source' repo getting merged into 'target' repo
git clone https://github.com/build5nines/source-repo.git source
cd source
# Checkout the 'main' branch of the 'source' repo
git checkout main
# Use 'git-filter-repo' to move 'source' repo with history to subfolder named 'Foo'
git filter-repo -f --to-subdirectory-filter Foo --tag-rename :foo-
# After running the root '/' contents of 'source' repo will be located in '/Foo/' subfolder
# Clone the 'target' repo that's the destination for the merge
cd ..
git clone https://github.com/build5nines/target-repo.git target
# Create new branch in 'target' repo for the merge
cd target
git branch merged-repos
git checkout merged-repos
# Add Git 'remote' to 'target' repo that pulls from 'source' repo folder
# Name the 'source' remote 'local'
git remote add -f local ../source
# Merge files and history from 'source' repo (via 'main' branch of 'local' remote) into 'target'
git merge --allow-unrelated-histories -m 'merge repos' local/main
# Push the finished 'target' repo merge branch to remote / GitHub
git push --set-upstream origin merged-repos
I hope these examples help clarify what is necessary to perform both a simple merge of two Git repositories, as well as merging the contents of one repository into a subfolder of another Git repository.
Happy Git merging!