576

I have a Git repository in a folder called XXX, and I have second Git repository called YYY.

I want to import the XXX repository into the YYY repository as a subdirectory named ZZZ and add all XXX's change history to YYY.

Folder structure before:

├── XXX
│   ├── .git
│   └── (project files)
└── YYY
    ├── .git
    └── (project files)

Folder structure after:

YYY
├── .git  <-- This now contains the change history from XXX
├──  ZZZ  <-- This was originally XXX
│    └── (project files)
└──  (project files)

Can this be done, or must I resort to using sub-modules?

Bitswazsky
  • 4,242
  • 3
  • 29
  • 58
Vijay Patel
  • 17,094
  • 6
  • 31
  • 35
  • 2
    On Github it's now possible to do this from the web interface when you create a new repo – Ben G Sep 16 '15 at 17:58
  • Possible duplicate of [How do you merge two git repositories?](http://stackoverflow.com/questions/1425892/how-do-you-merge-two-git-repositories) – BuZZ-dEE Feb 17 '16 at 09:32
  • 1
    @bgcode comment was really useful for me - thanks. You can import another repo straight from GitHub's UI and saves a ton of work – ChumKui Feb 03 '22 at 11:01

17 Answers17

502

Probably the simplest way would be to pull the XXX stuff into a branch in YYY and then merge it into master:

In YYY:

git remote add other /path/to/XXX
git fetch other
git checkout -b ZZZ other/master
mkdir ZZZ
git mv stuff ZZZ/stuff                      # repeat as necessary for each file/dir
git commit -m "Moved stuff to ZZZ"
git checkout master                
git merge ZZZ --allow-unrelated-histories   # should add ZZZ/ to master
git commit
git remote rm other
git branch -d ZZZ                           # to get rid of the extra branch before pushing
git push                                    # if you have a remote, that is

I actually just tried this with a couple of my repos and it works. Unlike Jörg's answer it won't let you continue to use the other repo, but I don't think you specified that anyway.

Note: Since this was originally written in 2009, git has added the subtree merge mentioned in the answer below. I would probably use that method today, although of course this method does still work.

Ari Seyhun
  • 11,506
  • 16
  • 62
  • 109
ebneter
  • 20,795
  • 9
  • 30
  • 30
  • 2
    Thanks. I used a slightly modified version of your technique: I created a 'staging' branch on XXX where I created the ZZZ folder, and moved the 'stuff' into it. Then I merged XXX into YYY. – Vijay Patel Nov 07 '09 at 10:45
  • 1
    This worked great for me. The only changes I made were: 1) "git branch -d ZZZ" before the push because I didn't want this temp branch hanging around. 2) "git push" was giving me the error: "No refs in common and none specified; doing nothing. Perhaps you should specify a branch such as 'master'." (The origin I was pushing to was an empty bare repository.) But "git push --all" worked like a champ. – CrazyPyro Feb 24 '11 at 20:28
  • I don’t see any reason why you can’t continue to use the other repository. It seems to be possible to merge changes from the other repository provided you use `git merge -s subtree`. – Daniel Cassidy Apr 13 '11 at 22:22
  • 1
    I wanted to end up with only the ZZZ folder plus history in the YYY repo: I wanted to delete the original XXX repo, and the ZZZ branch in the YYY repo. I found deleting the ZZZ branch as @CrazyPyro suggested removed the history — to keep it I merged the ZZZ branch into master before deleting. – Oli Studholme May 12 '12 at 05:24
  • Is it just me, or does this solution not preserve the history? I tried it with one file and after checkout of master, the file appears only as added. After the commit, I have only that one in the history. The result is the same as if I had added the file through copy paste and manual add. – Sebastian Blask Feb 26 '13 at 09:03
  • @SebastianBlask I've done this multiple times and it does preserve the history — that's the whole point of using the remotes, etc. – ebneter Feb 26 '13 at 23:01
  • 1
    @ebneter: maybe it doesn't work anymore or something is missing? I am using git version 1.7.10.4... after git mv, git status says "renamed: stuff -> ZZZ/stuff" but then after checkout of master: "new file: ZZZ/stuff" after the commit, git log only gives this this commit and git blame doesn't show anything but the last commit either. – Sebastian Blask Feb 27 '13 at 08:57
  • 4
    @SebastianBlask I just messed around with this with two of my repos, and realized that there is a missing step that no one has ever seemed to notice, despite my getting upvotes on this for years. :-) I _mentioned_ merging it into master, but didn't actually show it. Editing it now... – ebneter Feb 28 '13 at 02:26
  • Is there a way to do this without involving a git mv command? For example, so history is preserved in the "git log" command without using the --follow argument. – Danra Mar 18 '13 at 12:33
  • 1
    @Danra I don't believe so — you are changing the location of files, so there's no way around that. You could use ColinM's subtree merge solution below but I'm not sure what the history looks like in the log as I have never done it. – ebneter Mar 19 '13 at 18:56
  • 1
    Thanks for this. We've just been been through this, but we wanted to bring two branches with us, ZZZ our tracking branch, and AAA a branch of ZZZ. It took us a while. Here are my condensed notes: 1) before removing the remote, while in `./YYY` directory `git checkout -b AAA origin/AAA`, 2) switch to YYY and `git fetch`, 3) bring over other branch with `git checkout -b AAA other/AAA`, 4) merge the ported branches `git merge`. _(Watch the movie Inception to prepare your mind for the necessary Gitfoo)_ – Stu Thompson Dec 02 '14 at 01:12
  • 2
    you could add something like this, when moving files to your subfolder: `git mv $(ls|grep -v ) /` This will copy all files and folders into your new folder – serup Jan 29 '16 at 13:17
  • It works as expected, however each time I move around files in the other repository, the merge fails with `both deleted ../../*, added by us, by them`. `-Xtheirs` doesn't really change anything, anyone knows how to make the content of ZZZ folder to follow original files location and rename/move files accordingly? – Alex Nov 20 '18 at 13:22
  • Is it possible to link to the subtree merge answer? ("Below" changes over time) – Gert van den Berg May 17 '19 at 08:55
  • 1
    I wouldn't be so easily swayed towards using `git subtree`. The downside is that the merged history is unchaged (not in a subdirectory). More on subtree merges [here](https://gist.github.com/x-yuri/8ad01701db51ec2891ca431b78c58c72#file-4-2-sh). Although your solution has this very problem, that can be [avoided](https://stackoverflow.com/a/62104099/52499). Feel free to borrow from my answer. – x-yuri May 30 '20 at 14:54
  • @VijayPatel please reconsider your accepted answer, I think x-yuri's is the winner as it maintains history perfectly and doesn't require multiple `git mv` commands: https://stackoverflow.com/a/4142653/67824 – Ohad Schneider Oct 18 '22 at 19:27
405

If you want to retain the exact commit history of the second repository and therefore also retain the ability to easily merge upstream changes in the future then here is the method you want. It results in unmodified history of the subtree being imported into your repo plus one merge commit to move the merged repository to the subdirectory.

git remote add XXX_remote <path-or-url-to-XXX-repo>
git fetch XXX_remote
git merge -s ours --no-commit --allow-unrelated-histories XXX_remote/master
git read-tree --prefix=ZZZ/ -u XXX_remote/master
git commit -m "Imported XXX as a subtree."

You can track upstream changes like so:

git pull -s subtree XXX_remote master

Git figures out on its own where the roots are before doing the merge, so you don't need to specify the prefix on subsequent merges.

The downside is that in the merged history the files are unprefixed (not in a subdirectory). As a result git log ZZZ/a will show you all the changes (if any) except those in the merged history. You can do:

git log --follow -- a

but that won't show the changes other then in the merged history.

In other words, if you don't change ZZZ's files in repository XXX, then you need to specify --follow and an unprefixed path. If you change them in both repositories, then you have 2 commands, none of which shows all the changes.

Git versions before 2.9: You don’t need to pass the --allow-unrelated-histories option to git merge.

The method in the other answer that uses read-tree and skips the merge -s ours step is effectively no different than copying the files with cp and committing the result.

Original source was from github's "Subtree Merge" help article. And another useful link.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
ColinM
  • 13,367
  • 3
  • 42
  • 49
  • 11
    this doesn't seem to have preserved history... if I do a `git log` on any of the files I pulled in I just see the single merge commit and nothing from its previous life in the other repo? Git 1.8.0 – Anentropic Jan 21 '13 at 13:14
  • hmm, on the other hand I do see commits from the imported repo if I just `git log` at the root level, so its a limitation of how I was trying to view the history of imported files - is there a way? – Anentropic Jan 21 '13 at 13:27
  • 9
    aha! if I use the old path of the imported file, i.e. omit the subdir it's been imported into, then git log will give me the commit history, eg `git log -- myfile` instead of `git log -- rack/myfile` – Anentropic Jan 21 '13 at 17:28
  • Using git pull as you said, has imported the logs in my master. What can I do? – Francesco Frassinelli Mar 25 '13 at 19:24
  • 2
    @FrancescoFrassinelli, isn't that desirable? Bringing the history in is a *feature* of this method. – pattivacek Sep 09 '13 at 14:34
  • @patrickvacek It became difficult to browse the history and I'm not interested in my project dependencies history. – Francesco Frassinelli Sep 09 '13 at 20:09
  • 5
    @FrancescoFrassinelli, if you don't want history, why not just do a regular copy? I'm trying to figure out what would draw you to this method if not for the history -- that's the only reason I used this method! – pattivacek Sep 09 '13 at 20:57
  • @patrickvacek I was looking for a clean way to have dependencies. Maybe there are better solution for my kind of problem (I'm not git expert). – Francesco Frassinelli Sep 09 '13 at 21:20
  • 2
    @FrancescoFrassinelli Have you tried the "--follow" option of "git log"? – ColinM Dec 11 '13 at 17:29
  • 1
    An alternative link, referenced from the Github link: https://www.kernel.org/pub/software/scm/git/docs/howto/using-merge-subtree.html It contains some more explanation and reasoning, and is a little easier to follow than Github's example. – CoupleWavyLines Feb 24 '14 at 20:17
  • You can find this information in here https://help.github.com/articles/working-with-subtree-merge – lastboy May 13 '14 at 12:25
  • 1
    Great solution, it works great. I would just add another comment if you wish to merge two repositories but the second will be merge into the first (no new parent repository) replace the read-tree line with: git read-tree -mu rack_remote/master- it means that the second repository will be merge to the first. – lastboy May 13 '14 at 12:28
  • If this solution is used, is it safe to delete the rack repository after the merge is completed? – Alex Brooks Jan 29 '16 at 03:09
  • @AlexBrooks I assume you mean the "remote" since there is only one repo involved. It is safe, although you probably won't be saving much space by doing so since the commits that were merged will remain in your repo. That is, all of your own commits and the merged commits live in the same repo after this operation. – ColinM Feb 03 '16 at 16:01
  • @ColinM So I mean if I merge repo `A` into repo `B` with this method, is it then safe to completely delete repo `A`? – Alex Brooks Feb 04 '16 at 18:09
  • If you omit the explicit merge, this should replace submodules forever. – dcow Mar 04 '16 at 06:06
  • 8
    Since Git 2.9, you need the option `--allow-unrelated-histories` when doing the merge. – stuXnet Jul 28 '16 at 14:43
  • This works great, thanks. Any chance of not importing the tags from the other repository? – Sebastian Bergmann Aug 21 '16 at 07:43
  • @Anentropic Regarding the history, this link seems to be provide some way of rewriting the history so that the paths point to the new subdirectory. Haven't yet tried this, so I'm not sure if it works: https://gofore.com/merge-multiple-git-repositories-one-retaining-history/ – Juha Palomäki Sep 21 '16 at 09:34
  • I love this; the only change I'd suggest is to flag arbitrary option values as, well, arbitrary. I assumed "rack" was a keyword and so on. – AbuNassar Sep 30 '16 at 15:43
  • 1
    @FrancescoFrassinelli Should this keep the history tab in bitbucket up-to-date? Or is the most important concern preserving the git log history? I see the git log showing all past commits and bitbucket also shows the history via the "Blame" button but when I actually go into the history tab in the repo on bitbucket itself I only see my recent merge..I'm thinking the history tab is related to that file only?? – bschmitty Nov 17 '16 at 21:31
  • Can I add patches on top of XXX/ZZZ? What happens to git pull if I had patches in this subfolder? – vvassilev Jan 18 '17 at 10:25
  • 1
    @vvassilev You can both modify your imported subtree and merge updates from the upstream. When you merge the upstream changes the conflicts will be resolved like any other merge. – ColinM Jan 19 '17 at 16:41
175

git-subtree is a script designed for exactly this use case of merging multiple repositories into one while preserving history (and/or splitting history of subtrees, though that seems to be irrelevant to this question). It is distributed as part of the git tree since release 1.7.11.

To merge a repository <repo> at revision <rev> as subdirectory <prefix>, use git subtree add as follows:

git subtree add -P <prefix> <repo> <rev>

git-subtree implements the subtree merge strategy in a more user friendly manner.

For your case, inside repository YYY, you would run:

git subtree add -P ZZZ /path/to/XXX.git master

The downside is that in the merged history the files are unprefixed (not in a subdirectory). As a result git log ZZZ/a will show you all the changes (if any) except those in the merged history. You can do:

git log --follow -- a

but that won't show the changes other then in the merged history.

In other words, if you don't change ZZZ's files in repository XXX, then you need to specify --follow and an unprefixed path. If you change them in both repositories, then you have 2 commands, none of which shows all the changes.

More on it here.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
kynan
  • 13,235
  • 6
  • 79
  • 81
  • 4
    If you have a directory to merge instead of a bare repository or remote, `git subtree add -P name-of-desired-prefix ~/location/of/git/repo-without-.git branch-name` – Tatsh Nov 22 '15 at 10:32
  • 2
    Noob experience: git (version 2.9.0.windows.1) responds "fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree" when I tried this in a freshly initialised, local, non-bare repository, But it worked fine after I _really_ got the new repository going, i.e. after adding a plain file and committing the regular way. – Stein Dec 01 '16 at 19:49
52

There is a well-known instance of this in the Git repository itself, which is collectively known in the Git community as "the coolest merge ever" (after the subject line Linus Torvalds used in the e-mail to the Git mailinglist which describes this merge). In this case, the gitk Git GUI which now is part of Git proper, actually used to be a separate project. Linus managed to merge that repository into the Git repository in a way that

  • it appears in the Git repository as if it had always been developed as part of Git,
  • all the history is kept intact and
  • it can still be developed independently in its old repository, with changes simply being git pulled.

The e-mail contains the steps needed to reproduce, but it is not for the faint of heart: first, Linus wrote Git, so he probably knows a bit more about it than you or me, and second, this was almost 5 years ago and Git has improved considerably since then, so maybe it is now much easier.

In particular, I guess nowadays one would use a gitk submodule, in that specific case.

Community
  • 1
  • 1
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 3
    BTW. the strategy used for subsequent merges (if there are any) is called **subtree** merge, and there is third party `git-subtree` tool which can help you with this: http://github.com/apenwarr/git-subtree – Jakub Narębski Nov 06 '09 at 22:48
  • Thanks, I forgot about that. The `subtree` merge strategy, especially in conjunction with the `git-subtree` tool is a nice, maybe even superior alternative to submodules. – Jörg W Mittag Nov 07 '09 at 01:21
  • That link was broken for me; this one works (for now): https://marc.info/?l=git&m=111947722514210&w=2 – BallpointBen Jun 16 '22 at 01:01
17

Let me use names a (in place of XXX and ZZZ) and b (in place of YYY), since that makes the description a bit easier to read.

Say you want to merge repository a into b (I'm assuming they're located alongside one another):

cd a
git filter-repo --to-subdirectory-filter a
cd ..
cd b
git remote add a ../a
git fetch a
git merge --allow-unrelated-histories a/master
git remote remove a

For this you need git-filter-repo installed (filter-branch is discouraged).

An example of merging 2 big repositories, putting one of them into a subdirectory: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731

More on it here.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
  • 2
    Excellent. History appears in `git log` without issues, unlike the solution with `git subtree add -P ...`. – Martin Jambon Oct 12 '20 at 19:47
  • 1
    The one thing the original requester wanted was XXX to be in a folder ZZZ. Hence the 'git mv stuff ZZZ/stuff` command. i don't see how your solution addresses that requirement. – Jim May 05 '21 at 20:45
  • 1
    @Jim the subfolder part is taken care of by `filter-repo --to-subdirectory-filter`, see https://htmlpreview.github.io/?https://github.com/newren/git-filter-repo/blob/docs/html/git-filter-repo.html. Just used this method myself, worked perfectly (since I'm on Windows, had to fix the PATH per https://htmlpreview.github.io/?https://github.com/newren/git-filter-repo/blob/docs/html/git-filter-repo.html) – Ohad Schneider Oct 18 '22 at 19:23
14

The simple way to do that is to use git format-patch.

Assume we have 2 git repositories foo and bar.

foo contains:

  • foo.txt
  • .git

bar contains:

  • bar.txt
  • .git

and we want to end-up with foo containing the bar history and these files:

  • foo.txt
  • .git
  • foobar/bar.txt

So to do that:

 1. create a temporary directory eg PATH_YOU_WANT/patch-bar
 2. go in bar directory
 3. git format-patch --root HEAD --no-stat -o PATH_YOU_WANT/patch-bar --src-prefix=a/foobar/ --dst-prefix=b/foobar/
 4. go in foo directory
 5. git am PATH_YOU_WANT/patch-bar/*

And if we want to rewrite all message commits from bar we can do, eg on Linux:

git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD

This will add "[bar] " at the beginning of each commit message.

Damien R.
  • 191
  • 2
  • 4
  • If the original repository contained branches and merges, `git am` will likely fail. – Adam Monsen Sep 12 '12 at 14:57
  • 1
    Minor gotcha: git am strips anything in `[ ]` from the commit message. So you should use a different marker than `[bar]` – HRJ Jun 03 '13 at 16:55
  • Did not work for me. Got "error: foobar/mySubDir/test_host1: does not exist in index. The copy of the patch that failed is found in: /home/myuser/src/proj/.git/rebase-apply/patch When you have resolved this problem, run "git am --continue". This was after applying 11 patches (out of 60). – oligofren Mar 20 '14 at 12:32
  • 1
    [This blog](http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/) has a similar answer to a somewhat different question (moving only selected files). – Jesse Glick Aug 11 '14 at 14:11
  • I see one disadvantage, all commits are added to the HEAD of the target repository. – CSchulz Sep 23 '14 at 16:59
10

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
{
    repo="$1"
    dir="$(echo "$2" | sed 's/\/$//')"
    path="$(pwd)"

    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_INDEX_FILE.new" git update-index --index-info &&
        mv "$GIT_INDEX_FILE.new" "$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 https://github.com/example/example dir/to/save

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

repo="https://github.com/example/example"
path="$(pwd)"

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

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

GIT_ADD_STORED=""

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

    GIT_ADD_STORED+='s,\t'"$from"',\t'"$to"',;'
}

# 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 'README.md' 'doc/README.md'
git-mv-store '.gitignore' 'unneeded/.gitignore'

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

GIT_ADD_STORED=""

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"

Notices
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.

Andrey Izman
  • 1,807
  • 1
  • 24
  • 27
  • 2
    For OS X folks out there, install `gnu-sed` to get the `git-add-repo` function working. Thanks again Andrey! – ptaylor Sep 08 '17 at 09:59
7

Based on this article, using subtree is what worked for me and only applicable history was transferred. Posting here in case anyone needs the steps (make sure to replace the placeholders with values applicable to you):

in your source repository split subfolder into a new branch

git subtree split --prefix=<source-path-to-merge> -b subtree-split-result

in your destination repo merge in the split result branch

git remote add merge-source-repo <path-to-your-source-repository>
git fetch merge-source-repo
git merge -s ours --no-commit merge-source-repo/subtree-split-result
git read-tree --prefix=<destination-path-to-merge-into> -u merge-source-repo/subtree-split-result

verify your changes and commit

git status
git commit

Don't forget to

Clean up by deleting the subtree-split-result branch

git branch -D subtree-split-result

Remove the remote you added to fetch the data from source repo

git remote rm merge-source-repo

Alex
  • 9,250
  • 11
  • 70
  • 81
3

Adding another answer as I think this is a bit simpler. A pull of repo_dest is done into repo_to_import and then a push --set-upstream url:repo_dest master is done.

This method has worked for me importing several smaller repos into a bigger one.

How to import: repo1_to_import to repo_dest

# checkout your repo1_to_import if you don't have it already 
git clone url:repo1_to_import repo1_to_import
cd repo1_to_import

# now. pull all of repo_dest
git pull url:repo_dest
ls 
git status # shows Your branch is ahead of 'origin/master' by xx commits.
# now push to repo_dest
git push --set-upstream url:repo_dest master

# repeat for other repositories you want to import

Rename or move files and dirs into desired position in original repo before you do the import. e.g.

cd repo1_to_import
mkdir topDir
git add topDir
git mv this that and the other topDir/
git commit -m"move things into topDir in preparation for exporting into new repo"
# now do the pull and push to import

The method described at the following link inspired this answer. I liked it as it seemed more simple. BUT Beware! There be dragons! https://help.github.com/articles/importing-an-external-git-repository git push --mirror url:repo_dest pushes your local repo history and state to remote (url:repo_dest). BUT it deletes the old history and state of the remote. Fun ensues! :-E

gaoithe
  • 4,218
  • 3
  • 30
  • 38
2

Here is the script that will work right off the bat.

#!/bin/bash -xe
# script name: merge-repo.sh
# To merge repositories into the current.
# To see the log of the new repo use 'git log --follow -- unprefixed-filename'
# So if the file is repo/test.cpp use 'git log --follow -- test.cpp'
# I'm not sure how this will work when two files have the same name.
#
# `git branch -a` will show newly created branches.
# You can delete them if you want.
merge_another() {
    repo="$1" # url of the remote repo
    rn="$2"   # new name of the repo, you can keep the same name as well.
    git remote add ${rn} ${repo}
    git fetch ${rn}
    git merge -s ours --no-commit --allow-unrelated-histories ${rn}/master
    git read-tree --prefix=${rn}/ -u ${rn}/master
    git commit -m "Imported ${rn} as a subtree."
    git pull -s subtree ${rn} master
}

merge_another $1 $2

To run the script. Go to the repo where you want the other repo to be merged, and run the script.

cd base-repo
./merge-repo.sh git@github.com:username/repo-to-be-merged.git repo-to-be-merged-new-name

Now push the changes on the master branch to remote/origin. This step may not be required depending on what you are trying to do.

git push origin master
A. K.
  • 34,395
  • 15
  • 52
  • 89
1

I wanted to import only some files from the other repository (XXX) in my case. The subtree was too complicated for me and the other solutions didn't work. This is what I did:

ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr '\n' ' ')

This gives you a space-separated list of all the commits that affect the files I wanted to import (ZZZ) in reverse order (you might have to add --follow to capture renames as well). I then went into the target repository (YYY), added the other repository (XXX) as remote, did a fetch from it and finally:

git cherry-pick $ALL_COMMITS

which adds all the commits to your branch, you'll thus have all the files with their history and can do whatever you want with them as if they've always been in this repository.

Sebastian Blask
  • 2,870
  • 1
  • 16
  • 29
1

See Basic example in this article and consider such mapping on repositories:

  • A <-> YYY,
  • B <-> XXX

After all activity described in this chapter (after merging), remove branch B-master:

$ git branch -d B-master

Then, push changes.

It works for me.

VeLKerr
  • 2,995
  • 3
  • 24
  • 47
0

I was in a situation where I was looking for -s theirs but of course, this strategy doesn't exist. My history was that I had forked a project on GitHub, and now for some reason, my local master could not be merged with upstream/master although I had made no local changes to this branch. (Really don't know what happened there -- I guess upstream had done some dirty pushes behind the scenes, maybe?)

What I ended up doing was

# as per https://help.github.com/articles/syncing-a-fork/
git fetch upstream
git checkout master
git merge upstream/master
....
# Lots of conflicts, ended up just abandonging this approach
git reset --hard   # Ditch failed merge
git checkout upstream/master
# Now in detached state
git branch -d master # !
git checkout -b master   # create new master from upstream/master

So now my master is again in sync with upstream/master (and you could repeat the above for any other branch you also want to sync similarly).

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • 1
    A `git reset --hard upstream/master` on your local `master` branch would do the job. This way you don’t lose local branch conflg – things like the default upstream. – tomekwi Nov 08 '17 at 17:51
0

I can suggest another solution (alternative to git-submodules) for your problem - gil (git links) tool

It allows to describe and manage complex git repositories dependencies.

Also it provides a solution to the git recursive submodules dependency problem.

Consider you have the following project dependencies: sample git repository dependency graph

Then you can define .gitlinks file with repositories relation description:

# Projects
CppBenchmark CppBenchmark https://github.com/chronoxor/CppBenchmark.git master
CppCommon CppCommon https://github.com/chronoxor/CppCommon.git master
CppLogging CppLogging https://github.com/chronoxor/CppLogging.git master

# Modules
Catch2 modules/Catch2 https://github.com/catchorg/Catch2.git master
cpp-optparse modules/cpp-optparse https://github.com/weisslj/cpp-optparse.git master
fmt modules/fmt https://github.com/fmtlib/fmt.git master
HdrHistogram modules/HdrHistogram https://github.com/HdrHistogram/HdrHistogram_c.git master
zlib modules/zlib https://github.com/madler/zlib.git master

# Scripts
build scripts/build https://github.com/chronoxor/CppBuildScripts.git master
cmake scripts/cmake https://github.com/chronoxor/CppCMakeScripts.git master

Each line describe git link in the following format:

  1. Unique name of the repository
  2. Relative path of the repository (started from the path of .gitlinks file)
  3. Git repository which will be used in git clone command Repository branch to checkout
  4. Empty line or line started with # are not parsed (treated as comment).

Finally you have to update your root sample repository:

# Clone and link all git links dependencies from .gitlinks file
gil clone
gil link

# The same result with a single command
gil update

As the result you'll clone all required projects and link them to each other in a proper way.

If you want to commit all changes in some repository with all changes in child linked repositories you can do it with a single command:

gil commit -a -m "Some big update"

Pull, push commands works in a similar way:

gil pull
gil push

Gil (git links) tool supports the following commands:

usage: gil command arguments
Supported commands:
    help - show this help
    context - command will show the current git link context of the current directory
    clone - clone all repositories that are missed in the current context
    link - link all repositories that are missed in the current context
    update - clone and link in a single operation
    pull - pull all repositories in the current directory
    push - push all repositories in the current directory
    commit - commit all repositories in the current directory

More about git recursive submodules dependency problem.

chronoxor
  • 3,159
  • 3
  • 20
  • 31
0

Don't have enough rep to add a comment to x-yuri's answer, but it works beautifully and preserves history. I was working with two working local repo's and received this error:

Aborting: Refusing to destructively overwrite repo history since this does not look like a fresh clone. (expected freshly packed repo) Please operate on a fresh clone instead. If you want to proceed anyway, use --force.

Rather than worry about the implications of the --force flag, I cloned the repo locally first with:

cd tempDir
git clone <location of repo to be merged> --no-local

and used this freshly cloned copy for the series of commands that x-yuri laid out. Lastly, in: git filter-repo --to-subdirectory-filter a, a is the name you are giving to the root folder for the repo that you will be importing.

-2

I don't know of an easy way to do that. You COULD do this:

  1. Use git filter-branch to add a ZZZ super-directory on the XXX repository
  2. Push the new branch to the YYY repository
  3. Merge the pushed branch with YYY's trunk.

I can edit with details if that sounds appealing.

Walter Mundt
  • 24,753
  • 5
  • 53
  • 61
-3

I think you can do this using 'git mv' and 'git pull'.

I'm a fair git noob - so be careful with your main repository - but I just tried this in a temp dir and it seems to work.

First - rename the structure of XXX to match how you want it to look when it's within YYY:

cd XXX
mkdir tmp
git mv ZZZ tmp/ZZZ
git mv tmp ZZZ

Now XXX looks like this:

XXX
 |- ZZZ
     |- ZZZ

Now use 'git pull' to fetch the changes across:

cd ../YYY
git pull ../XXX

Now YYY looks like this:

YYY
 |- ZZZ
     |- ZZZ
 |- (other folders that already were in YYY)
Aaron
  • 9,123
  • 5
  • 40
  • 38