If you want to merge
cd path/to/project-b git remote add project-a /path/to/project-a git fetch project-a --tags git merge --allow-unrelated-histories project-a/master # or whichever branch you want to merge git remote remove project-a
This method worked pretty well for me, it’s shorter and in my opinion a lot cleaner.
In case you want to put
project-a into a subdirectory, you can use
filter-branch is discouraged). Run the following commands before the commands above:
cd path/to/project-a git filter-repo --to-subdirectory-filter project-a
How to merge two Git repositories?
Here are two possible solutions:
Either copy repository A into a separate directory in larger project B, or (perhaps better) clone repository A into a subdirectory in project B. Then use git submodule to make this repository a submodule of a repository B.
This is a good solution for loosely-coupled repositories, where development in repository A continues, and the major portion of the development is a separate stand-alone development in A.
You can merge repository A into a subdirectory of project B using the subtree merge strategy. This is described in Subtree Merging and You by Markus Prinz.
git remote add -f Bproject /path/to/B git merge -s ours --allow-unrelated-histories --no-commit Bproject/master git read-tree --prefix=dir-B/ -u Bproject/master git commit -m "Merge B project as our subdirectory" git pull -s subtree Bproject master
--allow-unrelated-histories is needed for Git >= 2.9.0.)
How to git merge two repositories with different history?
A single branch of another repository can be easily placed under a subdirectory retaining its history. For example:
git subtree add --prefix=rails git://github.com/rails/rails.git master
This will appear as a single commit where all files of Rails master branch are added into “rails” directory. However the commit’s title contains a reference to the old history tree:
Add ‘rails/’ from commit
<rev> is a SHA-1 commit hash. You can still see the history, blame some changes.
git log <rev> git blame <rev> -- README.md
Note that you can’t see the directory prefix from here since this is an actual old branch left intact. You should treat this as a usual file move commit: you will need an extra jump when reaching it.
# finishes with all files added at once commit git log rails/README.md # then continue from original tree git log <rev> -- README.md
There are more complex solutions like doing this manually or rewriting the history as described in other answers.
The git-subtree command is a part of official git-contrib, some packet managers install it by default (OS X Homebrew). But you might have to install it by yourself in addition to git.
The submodule approach is good if you want to maintain the project separately. However, if you really want to merge both projects into the same repository, then you have a bit more work to do.
The first thing would be to use
git filter-branch to rewrite the names of everything in the second repository to be in the subdirectory where you would like them to end up. So instead of
bar.html, you would have
Then, you should be able to do something like the following:
git remote add projb [wherever] git pull projb
git pull will do a
git fetch followed by a
git merge. There should be no conflicts, if the repository you’re pulling to does not yet have a
Further searching indicates that something similar was done to merge
How to merge two Git repositories? Answer #5:
git-subtree is nice, but it is probably not the one you want.
For example, if
projectA is the directory created in B, after
git log projectA
lists only one commit: the merge. The commits from the merged project are for different paths, so they don’t show up.
Greg Hewgill’s answer comes closest, although it doesn’t actually say how to rewrite the paths.
The solution is surprisingly simple.
(1) In A,
PREFIX=projectA #adjust this git filter-branch --index-filter ' git ls-files -s | sed "s,\t,&'"$PREFIX"'/," | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE ' HEAD
Note: This rewrites history; you may want to first make a backup of A.
Note Bene: You have to modify the substitute script inside the sed command in the case that you use non-ascii characters (or white characters) in file names or path. In that case the file location inside a record produced by “ls-files -s” begins with quotation mark.
(2) Then in B, run
git pull path/to/A
Voila! You have a
projectA directory in B. If you run
git log projectA, you will see all commits from A.
In my case, I wanted two subdirectories,
projectB. In that case, I did step (1) to B as well.
If both repositories have same kind of files (like two Rails repositories for different projects), you can fetch data of the secondary repository to your current repository:
git fetch git://repository.url/repo.git master:branch_name
and then merge it to current repository:
git merge --allow-unrelated-histories branch_name
If your Git version is smaller than 2.9, remove
After this, conflicts may occur. You can resolve them for example with
kdiff3 can be used solely with keyboard, so 5 conflict file takes when reading the code just few minutes.
Remember to finish the merge:
How do you merge two Git repositories with different commit history?
In my case, I had a
my-plugin repository and a
main-project repository and I wanted to pretend that
my-plugin had always been developed in the
plugins subdirectory of
Basically, I rewrote the history of the
my-plugin repository so that it appeared all development took place in the
plugins/my-plugin subdirectory. Then, I added the development history of
my-plugin into the
main-project history, and merged the two trees together. Since there was no
plugins/my-plugin directory already present in the
main-project repository, this was a trivial no-conflicts merge. The resulting repository contained all history from both original projects, and had two roots.
$ cp -R my-plugin my-plugin-dirty $ cd my-plugin-dirty $ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all $ cd ../main-project $ git checkout master $ git remote add --fetch my-plugin ../my-plugin-dirty $ git merge my-plugin/master --allow-unrelated-histories $ cd .. $ rm -rf my-plugin-dirty
First, create a copy of the
my-plugin repository, because we’re going to be rewriting the history of this repository.
Now, navigate to the root of the
my-plugin repository, check out your main branch (probably
master), and run the following command. Of course, you should substitute for
plugins whatever your actual names are.
$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all
Now for an explanation.
git filter-branch --tree-filter (...) HEAD runs the
(...) command on every commit that is reachable from
HEAD. Note that this operates directly on the data stored for each commit, so we don’t have to worry about notions of “working directory”, “index”, “staging”, and so on.
If you run a
filter-branch command that fails, it will leave behind some files in the
.git directory and the next time you try
filter-branch it will complain about this, unless you supply the
-f option to
As for the actual command, I didn’t have much luck getting
bash to do what I wanted, so instead I use
zsh -c to make
zsh execute a command. First I set the
extended_glob option, which is what enables the
^(...) syntax in the
mv command, as well as the
glob_dots option, which allows me to select dotfiles (such as
.gitignore) with a glob (
Next, I use the
mkdir -p command to create both
plugins/my-plugin at the same time.
Finally, I use the
zsh “negative glob” feature
^(.git|plugins) to match all files in the root directory of the repository except for
.git and the newly created
my-plugin folder. (Excluding
.git might not be necessary here, but trying to move a directory into itself is an error.)
In my repository, the initial commit did not include any files, so the
mv command returned an error on the initial commit (since nothing was available to move). Therefore, I added a
|| true so that
git filter-branch would not abort.
--all option tells
filter-branch to rewrite the history for all branches in the repository, and the extra
-- is necessary to tell
git to interpret it as a part of the option list for branches to rewrite, instead of as an option to
Now, navigate to your
main-project repository and check out whatever branch you want to merge into. Add your local copy of the
my-plugin repository (with its history modified) as a remote of
$ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY
You will now have two unrelated trees in your commit history, which you can visualize nicely using:
$ git log --color --graph --decorate --all
To merge them, use:
$ git merge my-plugin/master --allow-unrelated-histories
Note that in pre-2.9.0 Git, the
--allow-unrelated-histories option does not exist. If you are using one of these versions, just omit the option: the error message that
--allow-unrelated-histories prevents was also added in 2.9.0.
You should not have any merge conflicts. If you do, it probably means that either the
filter-branch command did not work correctly or there was already a
plugins/my-plugin directory in
Make sure to enter an explanatory commit message for any future contributors wondering what hackery was going on to make a repository with two roots.
You can visualize the new commit graph, which should have two root commits, using the above
git log command. Note that only the
master branch will be merged. This means that if you have important work on other
my-plugin branches that you want to merge into the
main-project tree, you should refrain from deleting the
my-plugin remote until you have done these merges. If you don’t, then the commits from those branches will still be in the
main-project repository, but some will be unreachable and susceptible to eventual garbage collection. (Also, you will have to refer to them by SHA, because deleting a remote removes its remote-tracking branches.)
Optionally, after you have merged everything you want to keep from
my-plugin, you can remove the
my-plugin remote using:
$ git remote remove my-plugin
You can now safely delete the copy of the
my-plugin repository whose history you changed. In my case, I also added a deprecation notice to the real
my-plugin repository after the merge was complete and pushed.
Tested on Mac OS X El Capitan with
git --version 2.9.0 and
zsh --version 5.2. Your mileage may vary.
Hope you learned something from this post.
Follow Programming Articles for more!