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 projec
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
echo 'FETCH_HEAD' > .git/MERGE_HEAD
now commit however you normally would commit
git commit
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:
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:
http://saintgimp.org/2013/01/22/merging-two-git-repositories-into-one-repository-without-losing-file-history/
Similar to @Smar but uses file system paths, set in PRIMARY and SECONDARY:
PRIMARY=~/Code/project1
SECONDARY=~/Code/project2
cd $PRIMARY
git remote add test $SECONDARY && git fetch test
git merge test/master
Then you manually merge.
(adapted from post by Anar Manafov)
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_INDEX_FILE.new git update-index --index-info &&
mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE
' HEAD
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.
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, projectA
and projectB
. In that case, I did step (1) to B as well.
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>
Where <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 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/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.