如何导入现有的Git仓库到另一个?

我有一个名为XXX的文件夹中的Git仓库,我有第二个Git仓库,名为YYY

我想将XXX存储库作为名为ZZZ的子目录导入YYY存储库,并将所有XXX的更改历史logging添加到YYY

文件夹结构之前:

XXX |- .git |- (project files) YYY |- .git |- (project files) 

文件夹结构后:

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

可以这样做,还是我必须诉诸使用子模块?

也许最简单的方法就是把XXX的东西放到YYY的分支中,然后把它合并到master中:

在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 

我其实只是尝试了几个我的回购,它的作品。 不像Jörg的回答,它不会让你继续使用其他回购,但我不认为你指定了这一点。

注:由于这是最初编写在2009年,git已经添加了在下面的答案中提到的子树合并。 我今天可能会使用这种方法,当然这个方法仍然有效。

如果您想保留第二个存储库的确切提交历史logging,并且因此还保留了将来可以轻松合并上游更改的function,那么以下是您想要的方法。 它会导致子树的未修改历史logging被导入到您的repo中,另外还有一个合并提交将合并的存储库移动到子目录中。

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

您可以跟踪上游更改,如下所示:

 git pull -s subtree XXX_remote master 

Git根据自己的位置计算合并之前的位置,所以在后续合并时不需要指定前缀。

GIT 2.9+:合并命令将需要选项: --allow-unrelated-histories allow-unrelated --allow-unrelated-histories 。 谢谢@stuXnet!

在另一个使用read-tree并跳过merge -s ours步骤的答案中的方法实际上没有用cp复制文件并提交结果不同。

原始的来源是从github的“Subtree Merge”帮助文章 。

在Git仓库本身中有一个众所周知的例子,它在Git社区中被统称为“ 有史以来最酷的合并 ”(在Linus Torvalds的主题行中用于电子邮件中的Git邮件列表描述了这个合并)。 在这种情况下,现在是Git的一部分的gitk Git GUI实际上曾经是一个单独的项目。 Linus以这种方式设法将该存储库合并到Git存储库中

  • 它出现在Git仓库中,好像它一直是作为Git的一部分开发的,
  • 所有的历史都保持完整
  • 它仍然可以在其旧的版本库中独立开发,只需要修改即可。

这封电子邮件包含了复制所需的步骤,但这并不是因为内心的淡淡:首先,Linus 写了 Git,所以他可能比你或我更了解它,其次,这已经是差不多五年了从那以后Git已经有了很大的改进,所以现在可能更容易一些。

特别是,我想现在有人会使用一个gitk子模块。

git-subtree是一个脚本,专门用于将多个存储库合并为一个,同时保留历史logging(和/或分割子树的历史,尽pipe这似乎与此问题无关)。 它是自1.7.11版以来作为git树的一部分发布的 。

要将版本<rev>的存储库<repo>合并为子目录<prefix> ,请使用git subtree add ,如下所示:

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

git-subtree以更友好的方式实现了子树合并策略 。

对于你的情况,在仓库YYY,你会运行:

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

简单的方法是使用git format-patch。

假设我们有2个git仓库foobar

foo包含:

  • foo.txt的
  • git的

酒吧包含:

  • 跳回到bar.txt
  • git的

而且我们想用包含bar历史和这些文件的foo来结束:

  • foo.txt的
  • git的
  • foob​​ar的/跳回到bar.txt

所以要这样做:

  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/* 

如果我们想重写所有的消息提交,我们可以做,例如在Linux上:

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

这将在每个提交消息的开头添加“[bar]”。

根据这篇文章 ,使用子树是我的工作,只有适用的历史转移。 如果任何人需要这些步骤,请在这里发帖(确保用适用于您的值replace占位符):

在您的源存储库拆分子文件夹到一个新的分支

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

在您的目的地回购合并拆分结果分支

 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 

validation您的更改并提交

 git status git commit 

别忘了

通过删除subtree-split-result分支来清理

git branch -D subtree-split-result

删除您添加的远程从源回购获取数据

git remote rm merge-source-repo

加上另一个答案,因为我认为这有点简单。 将repo_dest拉到repo_to_import中,然后push -set-upstream url:repo_dest master完成。

这种方法已经为我导入了几个较小的回购更大的一个。

如何导入:repo1_to_import到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 

在导入之前,将文件和目录重命名或移动到原始回购中的所需位置。 例如

 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 

在下面的链接描述的方法启发了这个答案。 我喜欢它,因为它看起来更简单。 但要小心! 有龙! https://help.github.com/articles/importing-an-external-git-repository git push --mirror url:repo_dest将你本地的回购历史和状态推到远程(url:repo_dest)。 但它删除了旧的历史和远程的状态。 乐趣随之而来! :-E

我想在我的情况下只从其他库(XXX)导入一些文件。 子树对我来说太复杂了,其他的解决scheme都不起作用。 这就是我所做的:

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

这将为您提供一个空格分隔的列表,列出所有影响我想导入的文件(ZZZ)的提交(您可能需要添加 – 关注捕获重命名)。 然后我进入目标存储库(YYY),将另一个存储库(XXX)添加为远程,从中进行获取,最后:

 git cherry-pick $ALL_COMMITS 

它将所有提交添加到您的分支,您将因此拥有所有文件与他们的历史,并可以做任何你想要他们,就像他们一直在这个存储库。

当时我正在寻找-s theirs ,当然,这个策略是不存在的。 我的历史是我在GitHub上分了一个项目,现在出于某种原因,我的本地master不能与upstream/master合并,尽pipe我没有对这个部门进行本地更改。 (真的不知道那里发生了什么 – 我想上游在幕后做了一些肮脏的推动,也许?)

我最终做的是

 # 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 

所以现在我的master再次与upstream/master同步(你可以重复上面的任何其他分支,你也想同步同步)。

请参阅本文中的 基本示例 ,并考虑在存储库上的这种映射:

  • YYY
  • B < – > XXX

在本章描述的所有活动之后(合并之后),移除分支B-master

 $ git branch -d B-master 

然后,推送更改。

它适用于我。

这个函数会把远程仓库克隆到本地仓库,合并后所有的提交都会被保存, git log会显示原始的提交和正确的path:

 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" } 

如何使用:

 cd current/package git-add-repo https://github.com/example/example dir/to/save 

如果稍作修改,甚至可以将合并回购的文件/目录移动到不同的path中,例如:

 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" 

通告
path通过sedreplace,所以确保它合并后移动到合适的path。
--allow-unrelated-histories参数只存在于git> = 2.9之后。

我不知道有一个简单的方法来做到这一点。 你可以这样做:

  1. 使用git filter-branch在XXX存储库上添加一个ZZZ超级目录
  2. 将新分支推送到YYY存储库
  3. 将推送的分支合并到YYY的主干。

如果听起来很吸引人,我可以编辑细节。

我想你可以用'git mv'和'git pull'来做到这一点。

我是一个公平的混帐noob – 所以要小心你的主要存储库 – 但我只是尝试在临时目录中,这似乎工作。

首先 – 重命名XXX的结构,使其与YYY内的外观相匹配:

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

现在XXX看起来像这样:

 XXX |- ZZZ |- ZZZ 

现在使用'git pull'来获取跨越的变化:

 cd ../YYY git pull ../XXX 

现在YYY看起来像这样:

 YYY |- ZZZ |- ZZZ |- (other folders that already were in YYY)