如何导入现有的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仓库foo和bar 。
foo包含:
- foo.txt的
- git的
酒吧包含:
- 跳回到bar.txt
- git的
而且我们想用包含bar历史和这些文件的foo来结束:
- foo.txt的
- git的
- foobar的/跳回到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通过sed
replace,所以确保它合并后移动到合适的path。
--allow-unrelated-histories
参数只存在于git> = 2.9之后。
我不知道有一个简单的方法来做到这一点。 你可以这样做:
- 使用git filter-branch在XXX存储库上添加一个ZZZ超级目录
- 将新分支推送到YYY存储库
- 将推送的分支合并到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)