How do you merge two Git repositories?

static_rtti Source

Consider the following scenario:

I have developed a small experimental project A in its own Git repo. It has now matured, and I'd like A to be part of larger project B, which has its own big repository. I'd now like to add A as a subdirectory of B.

How do I merge A into B, without losing history on any side?



answered 9 years ago Greg Hewgill #1

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 foo.c, bar.html, you would have projb/foo.c and projb/bar.html.

Then, you should be able to do something like the following:

git remote add projb [wherever]
git pull projb

The 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 projb/ directory.

Further searching indicates that something similar was done to merge gitk into git. Junio C Hamano writes about it here:[email protected]/msg03395.html

answered 9 years ago Jakub Narębski #2

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 development is a separate stand-alone development in A. See also SubmoduleSupport and GitSubmoduleTutorial pages on Git Wiki.

Subtree merge

You can merge repository A into a subdirectory of a 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

(Option --allow-unrelated-histories is needed for Git >= 2.9.0.)

Or you can use git subtree tool (repository on GitHub) by apenwarr (Avery Pennarun), announced for example in his blog post A new alternative to Git submodules: git subtree.

I think in your case (A is to be part of larger project B) the correct solution would be to use subtree merge.

answered 7 years ago Smar #3

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 --allow-unrelated-histories.

After this, conflicts may occur. You can resolve them for example with git mergetool. kdiff3 can be used solely with keyboard, so 5 conflict file takes when reading the code just few minutes.

Remember to finish the merge:

git commit

answered 7 years ago Turadg #4

Similar to @Smar but uses file system paths, set in PRIMARY and SECONDARY:

git remote add test $SECONDARY && git fetch test
git merge test/master

Then you manually merge.

(adapted from post by Anar Manafov)

answered 7 years ago David Lemphers #5

I had a similar challenge, but in my case, we had developed one version of the codebase in repo A, then cloned that into a new repo, repo B, for the new version of the product. After fixing some bugs in repo A, we needed to FI the changes into repo B. Ended up doing the following:

  1. Adding a remote to repo B that pointed to repo A (git remote add...)
  2. Pulling the current branch (we were not using master for bug fixes) (git pull remoteForRepoA bugFixBranch)
  3. Pushing merges to github

Worked a treat :)

answered 6 years ago jettero #6

I know it's long after the fact, but I wasn't happy with the other answers I found here, so I wrote this:

me=$(basename $0)

TMP=$(mktemp -d /tmp/$me.XXXXXXXX)
echo "building new repo in $TMP"
sleep 1

set -e

cd $TMP
mkdir new-repo
cd new-repo
    git init
    cd ..

while [ -n "$1" ]; do
    repo="$1"; shift
    git clone "$repo"
    dirname=$(basename $repo | sed -e 's/\s/-/g')
    if [[ $dirname =~ ^git:.*\.git$ ]]; then
        dirname=$(echo $dirname | sed s/.git$//)

    cd $dirname
        git remote rm origin
        git filter-branch --tree-filter \
            "(mkdir -p $dirname; find . -maxdepth 1 ! -name . ! -name .git ! -name $dirname -exec mv {} $dirname/ \;)"
        cd ..

    cd new-repo
        git pull --no-commit ../$dirname
        [ $x -gt 0 ] && git commit -m "merge made by $me"
        cd ..

    x=$(( x + 1 ))

answered 6 years ago Andresch Serj #7

If you want to merge project-a into project-b:

cd path/to/project-b
git remote add project-a path/to/project-a
git fetch project-a
git merge --allow-unrelated-histories project-a/master # or whichever branch you want to merge
git remote remove project-a

Taken from: git merge different repositories?

This method worked pretty well for me, it's shorter and in my opinion a lot cleaner.

Note: The --allow-unrelated-histories parameter only exists since git >= 2.9. See Git - git merge Documentation / --allow-unrelated-histories

answered 6 years ago Eric Lee #8

If you're trying to simply glue two repositories together, submodules and subtree merges are the wrong tool to use because they don't preserve all of the file history (as people have noted on other answers). See this answer here for the simple and correct way to do this.

answered 6 years ago Simon Perepelitsa #9

A single branch of another repository can be easily placed under a subdirectory retaining its history. For example:

git subtree add --prefix=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>

Where <rev> is a SHA-1 commit hash. You can still see the history, blame some changes.

git log <rev>
git blame <rev> --

Note that you can't see the directory prefix from here since this is an actual old branch left intact. You should treat this like 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/

# then continue from original tree
git log <rev> --

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.

answered 5 years ago koppor #10

When you want to merge three or more projects in a single commit, do the steps as described in the other answers (remote add -f, merge). Then, (soft) reset the index to old head (where no merge happened). Add all files (git add -A) and commit them (message "Merging projects A, B, C, and D into one project). This is now the commit-id of master.

Now, create .git/info/grafts with following content:

<commit-id of master> <list of commit ids of all parents>

Run git filter-branch -- head^..head head^2..head head^3..head. If you have more than three branches, just add as much head^n..head as you have branches. To update tags, append --tag-name-filter cat. Do not always add that, because this might cause a rewrite of some commits. For details see man page of filter-branch, search for "grafts".

Now, your last commit has the right parents associated.

answered 5 years ago Paul Draper #11

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 subtree,

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 update-index --index-info &&
    mv $ $GIT_INDEX_FILE

Note: This rewrites history, so if you intend to continue using this repo A, you may want to clone (copy) a throwaway copy of it first.

(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, projectA and projectB. In that case, I did step (1) to B as well.

answered 5 years ago Calahad #12

I kept losing history when using merge, so I ended up using rebase since in my case the two repositories are different enough not to end up merging at every commit:

git clone [email protected]/projA.git projA
git clone [email protected]/projB.git projB

cd projB
git remote add projA ../projA/
git fetch projA 
git rebase projA/master HEAD

=> resolve conflicts, then continue, as many times as needed...

git rebase --continue

Doing this leads to one project having all commits from projA followed by commits from projB

answered 3 years ago eitch #13

I have gathered a lot of information here on Stack OverFlow, etc., and have manage to put a script together which solves the problem for me.

The caveat is that it only takes into account the 'develop' branch of each repository and merges it into a separate directory in a completely new repository.

Tags and other branches are ignored - this might not be what you want.

The script even handles feature branches and tags - renaming them in the new project so you know where they came from.

## Script to merge multiple git repositories into a new repository
## - The new repository will contain a folder for every merged repository
## - The script adds remotes for every project and then merges in every branch
##   and tag. These are renamed to have the origin project name as a prefix
## Usage: <new_project> <my_repo_urls.lst>
## - where <new_project> is the name of the new project to create
## - and <my_repo_urls.lst> is a file containing the URLs to the repositories
##   which are to be merged on separate lines.
## Author: Robert von Burg
##            [email protected]
## Version: 0.2.0
## Created: 2015-06-17

# Disallow using undefined variables
shopt -s -o nounset

# Script variables
declare SCRIPT_NAME="${0##*/}"
declare SCRIPT_DIR="$(cd ${0%/*} ; pwd)"
declare ROOT_DIR="$PWD"

# Detect proper usage
if [ "$#" -ne "2" ] ; then
  echo -e "ERROR: Usage: $0 <new_project> <my_repo_urls.lst>"
  exit 1

# Script functions
function failed() {
  echo -e "ERROR: Merging of projects failed:"
  echo -e "$1"
  exit 1

function commit_merge() {
  current_branch="$(git symbolic-ref HEAD 2>/dev/null)"
  CHANGES=$(git status | grep "working directory clean")
  MERGING=$(git status | grep "merging")
  if [[ "$CHANGES" != "" ]] && [[ "$MERGING" == "" ]] ; then
    echo -e "INFO:   No commit required."
    echo -e "INFO:   Committing ${sub_project}..."
    if ! git commit --quiet -m "[Project] Merged branch '$1' of ${sub_project}" ; then
      failed "Failed to commit merge of branch '$1' of ${sub_project} into ${current_branch}"

## Script variables

# Make sure the REPO_URL_FILE exists
if [ ! -e "${REPO_URL_FILE}" ] ; then
  echo -e "ERROR: Repo file ${REPO_URL_FILE} does not exist!"
  exit 1

# Make sure the required directories don't exist
if [ -e "${PROJECT_PATH}" ] ; then
  echo -e "ERROR: Project ${PROJECT_NAME} already exists!"
  exit 1

# Create the new project
echo -e "INFO: Creating new git repository ${PROJECT_NAME}..."
echo -e "===================================================="
cd ${ROOT_DIR}
git init
echo "Initial Commit" > initial_commit

# Since this is a new repository we need to have at least one commit
# thus were we create temporary file, but we delete it again.
# Deleting it guarantees we don't have conflicts later when merging
git add initial_commit
git commit --quiet -m "[Project] Initial Master Repo Commit"
git rm --quiet initial_commit
git commit --quiet -m "[Project] Initial Master Repo Commit"

# Merge all projects into th branches of this project
echo -e "INFO: Merging projects into new repository..."
echo -e "===================================================="
for url in $(cat ${REPO_URL_FILE}) ; do

  # Extract the name of this project
  export sub_project=${url##*/}

  echo -e "INFO: Project ${sub_project}"
  echo -e "----------------------------------------------------"

  # Fetch the project
  echo -e "INFO:   Fetching ${sub_project}..."
  git remote add "${sub_project}" "${url}"
  if ! git fetch --no-tags --quiet ${sub_project} 2>/dev/null ; then
    failed "Failed to fetch project ${sub_project}"

  # Add remote branches
  echo -e "INFO:   Creating local branches for ${sub_project}..."
  while read branch ; do 
    branch_ref=$(echo $branch | tr " " "\t" | cut -f 1)
    branch_name=$(echo $branch | tr " " "\t" | cut -f 2 | cut -d / -f 3-)

    echo -e "INFO:   Creating branch ${branch_name}..."

    # Create and checkout new merge branch off of master
    git checkout --quiet -b "${sub_project}/${branch_name}" master
    git reset --hard --quiet
    git clean -d --force --quiet

    # Merge the project
    echo -e "INFO:   Merging ${sub_project}..."
    if ! git merge --quiet --no-commit "remotes/${sub_project}/${branch_name}" 2>/dev/null ; then
      failed "Failed to merge branch 'remotes/${sub_project}/${branch_name}' from ${sub_project}"

    # And now see if we need to commit (maybe there was a merge)
    commit_merge "${sub_project}/${branch_name}"

    # Relocate projects files into own directory
    if [ "$(ls)" == "${sub_project}" ] ; then
      echo -e "WARN:   Not moving files in branch ${branch_name} of ${sub_project} as already only one root level."
      echo -e "INFO:   Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..."
      mkdir ${sub_project}
      for f in $(ls -a) ; do
        if  [[ "$f" == "${sub_project}" ]] || 
            [[ "$f" == "." ]] || 
            [[ "$f" == ".." ]] ; then 
        git mv -k "$f" "${sub_project}/"

      # Commit the moving
      if ! git commit --quiet -m  "[Project] Move ${sub_project} files into sub directory" ; then
        failed "Failed to commit moving of ${sub_project} files into sub directory"
  done < <(git ls-remote --heads ${sub_project})

  # Checkout master of sub probject
  if ! git checkout "${sub_project}/master" 2>/dev/null ; then
    failed "sub_project ${sub_project} is missing master branch!"

  # Copy remote tags
  echo -e "INFO:   Copying tags for ${sub_project}..."
  while read tag ; do 
    tag_ref=$(echo $tag | tr " " "\t" | cut -f 1)
    tag_name=$(echo $tag | tr " " "\t" | cut -f 2 | cut -d / -f 3)

    # hack for broken tag names where they are like 1.2.0^{} instead of just 1.2.0

    echo -e "INFO:     Copying tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}..."
    if ! git tag "${tag_new_name}" "${tag_ref}" 2>/dev/null ; then
      echo -e "WARN:     Could not copy tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}"
  done < <(git ls-remote --tags ${sub_project})

  # Remove the remote to the old project
  echo -e "INFO:   Removing remote ${sub_project}..."
  git remote rm ${sub_project}


# Now merge all project master branches into new master
git checkout --quiet master
echo -e "INFO: Merging projects master branches into new repository..."
echo -e "===================================================="
for url in $(cat ${REPO_URL_FILE}) ; do

  # extract the name of this project
  export sub_project=${url##*/}

  echo -e "INFO:   Merging ${sub_project}..."
  if ! git merge --quiet --no-commit "${sub_project}/master" 2>/dev/null ; then
    failed "Failed to merge branch ${sub_project}/master into master"

  # And now see if we need to commit (maybe there was a merge)
  commit_merge "${sub_project}/master"


# Done
cd ${ROOT_DIR}
echo -e "INFO: Done."

exit 0

You can also get it from

First create a file with the URL to each repository, e.g.:

[email protected]:eitchnet/ch.eitchnet.parent.git
[email protected]:eitchnet/ch.eitchnet.utils.git
[email protected]:eitchnet/ch.eitchnet.privilege.git

Then call the script giving a name of the project and the path to the script:

./ eitchnet_test eitchnet.lst

The script itself has a lot of comments which should explain what it does.

answered 3 years ago user123568943685 #14

To merge a A within B:

1) In the project A

git fast-export --all --date-order > /tmp/ProjectAExport

2) In the project B

git checkout -b projectA
git fast-import --force < /tmp/ProjectAExport

In this branch do all operations you need to do and commit them.

C) Then back to the master and a classical merge between the two branches:

git checkout master
git merge projectA

answered 3 years ago Praveen Kumar #15

Given command is the best possible solution I suggest.

git subtree add --prefix=MY_PROJECT git:// master

answered 3 years ago Rian #16

I've been trying to do the same thing for days, I am using git 2.7.2. Subtree does not preserve the history.

You can use this method if you will not be using the old project again.

I would suggest that you branch B first and work in the branch.

Here are the steps without branching:

cd B

# You are going to merge A into B, so first move all of B's files into a sub dir
mkdir B

# Move all files to B, till there is nothing in the dir but .git and B
git mv <files> B

git add .

git commit -m "Moving content of project B in preparation for merge from A"

# Now merge A into B
git remote add -f A <A repo url>

git merge A/<branch>

mkdir A

# move all the files into subdir A, excluding .git
git mv <files> A

git commit -m "Moved A into subdir"

# Move B's files back to root    
git mv B/* ./

rm -rf B

git commit -m "Reset B to original state"

git push

If you now log any of the files in subdir A you will get the full history

git log --follow A/<file>

This was the post that help me do this:

answered 2 years ago Radon Rosborough #17

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 main-project.

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|my-plugin) 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

Long version

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 my-plugin and 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|my-plugin) 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 filter-branch.

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 and plugins/my-plugin at the same time.

Finally, I use the zsh "negative glob" feature ^(.git|my-plugin) 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.

The --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 filter-branch itself.

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 main-project with:

$ 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 main-project.

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.


answered 2 years ago Andrey Izman #18

This function will clone remote repo into local repo dir, after merging all commits will be saved, git log will be show the original commits and proper paths:

function git-add-repo
    dir="$(echo "$2" | sed 's/\/$//')"

    tmp="$(mktemp -d)"
    remote="$(echo "$tmp" | sed 's/\///g'| sed 's/\./_/g')"

    git clone "$repo" "$tmp"
    cd "$tmp"

    git filter-branch --index-filter '
        git ls-files -s |
        sed "s,\t,&'"$dir"'/," |
        GIT_INDEX_FILE="$" git update-index --index-info &&
        mv "$" "$GIT_INDEX_FILE"
    ' HEAD

    cd "$path"
    git remote add -f "$remote" "file://$tmp/.git"
    git pull "$remote/master"
    git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
    git remote remove "$remote"
    rm -rf "$tmp"

How to use:

cd current/package
git-add-repo dir/to/save

If make a little changes you can even move files/dirs of merged repo into different paths, for example:


tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed 's/\///g' | sed 's/\./_/g')"

git clone "$repo" "$tmp"
cd "$tmp"


function git-mv-store
    from="$(echo "$1" | sed 's/\./\\./')"
    to="$(echo "$2" | sed 's/\./\\./')"


# NOTICE! This paths used for example! Use yours instead!
git-mv-store 'public/index.php' 'public/admin.php'
git-mv-store 'public/data' 'public/x/_data'
git-mv-store 'public/.htaccess' '.htaccess'
git-mv-store 'core/config' 'config/config'
git-mv-store 'core/defines.php' 'defines/defines.php'
git-mv-store '' 'doc/'
git-mv-store '.gitignore' 'unneeded/.gitignore'

git filter-branch --index-filter '
    git ls-files -s |
    sed "'"$GIT_ADD_STORED"'" |
    GIT_INDEX_FILE="$" git update-index --index-info &&
    mv "$" "$GIT_INDEX_FILE"


cd "$path"
git remote add -f "$remote" "file://$tmp/.git"
git pull "$remote/master"
git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
git remote remove "$remote"
rm -rf "$tmp"

Paths replaces via sed, so make sure it moved in proper paths after merging.
The --allow-unrelated-histories parameter only exists since git >= 2.9.

answered 1 year ago RahulMohan Kolakandy #19

Merging 2 repos

git clone ssh://<project-repo> project1
cd project1
git remote add -f project2 project2
git merge --allow-unrelated-histories project2/master
git remote rm project2

delete the ref to avoid errors
git update-ref -d refs/remotes/project2/master

answered 8 months ago Collin Anderson #20

I merge projects slightly manually, which allows me to avoid needing to deal with merge conflicts.

first, copy in the files from the other project however you want them.

cp -R myotherproject newdirectory
git add newdirectory

next pull in the history

git fetch path_or_url_to_other_repo

tell git to merge in the history of last fetched thing


now commit however you normally would commit

git commit

answered 6 months ago Finn Haakansson #21

If you want to put the files from a branch in repo B in a subtree of repo A and also preserve the history, keep reading. (In the example below, I am assuming that we want repo B's master branch merged into repo A's master branch.)

In repo A, first do the following to make repo B available:

git remote add B ../B # Add repo B as a new remote.
git fetch B

Now we create a brand new branch (with only one commit) in repo A that we call new_b_root. The resulting commit will have the files that were committed in the first commit of repo B's master branch but put in a subdirectory called path/to/b-files/.

git checkout --orphan new_b_root master
git rm -rf . # Remove all files.
git cherry-pick -n `git rev-list --max-parents=0 B/master`
mkdir -p path/to/b-files
git mv README path/to/b-files/
git commit --date="$(git log --format='%ai' $(git rev-list --max-parents=0 B/master))"

Explanation: The --orphan option to the checkout command checks out the files from A's master branch but doesn't create any commit. We could have selected any commit because next we clear out all the files anyway. Then, without committing yet (-n), we cherry-pick the first commit from B's master branch. (The cherry-pick preserves the original commit message which a straight checkout doesn't seem to do.) Then we create the subtree where we want to put all files from repo B. We then have to move all files that were introduced in the cherry-pick to the subtree. In the example above, there's only a README file to move. Then we commit our B-repo root commit, and, at the same time, we also preserve the timestamp of the original commit.

Now, we'll create a new B/master branch on top of the newly created new_b_root. We call the new branch b:

git checkout -b b B/master
git rebase -s recursive -Xsubtree=path/to/b-files/ new_b_root

Now, we merge our b branch into A/master:

git checkout master
git merge --allow-unrelated-histories --no-commit b
git commit -m 'Merge repo B into repo A.'

Finally, you can remove the B remote and temporary branches:

git remote remove B
git branch -D new_b_root b

The final graph will have a structure like this:

enter image description here

comments powered by Disqus