How to import existing Git repository into another?
我在一个名为xxx的文件夹中有一个Git存储库,还有第二个名为yyy的Git存储库。
我想将XXX存储库作为名为zzz的子目录导入到YYY存储库中,并将所有XXX的更改历史添加到YYY中。
之前的文件夹结构:
1 2 3 4 5 6 | XXX |- .git |- (project files) YYY |- .git |- (project files) |
之后的文件夹结构:
1 2 3 4 5 | YYY |- .git <-- This now contains the change history from XXX |- ZZZ <-- This was originally XXX |- (project files) |- (project files) |
可以这样做吗,还是必须使用子模块?
最简单的方法可能是将xxx文件拉入YYY中的一个分支,然后将其合并到master中:
在YYY:
1 2 3 4 5 6 7 8 9 10 11 12 | 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添加了下面答案中提到的子树合并。我今天可能会使用那个方法,当然这个方法仍然有效。
如果您希望保留第二个存储库的确切提交历史记录,因此也可以保留将来轻松合并上游更改的能力,那么这里是您想要的方法。它会导致导入到您的repo中的子树的未修改历史记录加上一个合并提交,以将合并的存储库移动到子目录。
1 2 3 4 5 | 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." |
您可以像这样跟踪上游更改:
1 | git pull -s subtree XXX_remote master |
在进行合并之前,Git会自己计算出根在哪里,所以您不需要在随后的合并中指定前缀。
2.9之前的Git版本:您不需要将
另一个答案中使用
源代码来自Github的"子树合并"帮助文章。
将
git-subtree implements the subtree merge strategy in a more user friendly manner.
For your case, inside repository YYY, you would run:
ZZU1〔8〕
在Git存储库中有一个众所周知的例子,在Git社区中,它被称为"有史以来最酷的合并"(在描述此合并的Git邮件列表的电子邮件中使用的主题行Linus Torvalds之后)。在这种情况下,现在是git-proper的一部分的
- 它出现在Git存储库中,就好像它一直是作为Git的一部分开发的一样,
- 所有的历史都完好无损
- 它仍然可以在旧的存储库中独立开发,只需修改
git pull ed。
电子邮件包含了复制所需的步骤,但这并不是为了发自内心的懦夫:首先,Linus写了Git,所以他可能比你或我对它了解得更多;其次,这是近5年前的事了,此后Git有了很大的改进,所以现在可能更容易了。
特别是,我想现在有人会使用Gitk子模块,在那个特定的情况下。
实现这一点的简单方法是使用git格式补丁。
假设我们有2个Git存储库foo和bar。
FO包含:
- 英尺·txt
- Git
酒吧包含:
- TXT
- Git
最后我们要看到foo,其中包含了酒吧历史和这些文件:
- 英尺·txt
- Git
- 福巴尔/巴特
所以这样做:
1 2 3 4 5 | 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/* |
如果我们想重写BAR的所有消息提交,我们可以这样做,例如在Linux上:
1 | git filter-branch --msg-filter 'sed"1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD |
这将在每个提交消息的开头添加"[栏]"。
此功能将远程repo克隆到本地repo dir,合并所有提交后将保存,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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" } |
如何使用:
1 2 | cd current/package git-add-repo https://github.com/example/example dir/to/save |
如果稍作更改,甚至可以将合并的repo的文件/目录移动到不同的路径,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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" |
通告路径通过
基于本文,使用子树对我来说是有效的,并且只转移了适用的历史。如果有人需要这些步骤,请在此处发布(确保用适用于您的值替换占位符):
在源存储库中,将子文件夹拆分为新的分支
在目标中,在拆分结果分支中进行repo合并
1 2 3 4 | 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 |
验证更改并提交
1 2 | git status git commit |
别忘了
删除
删除为从源repo获取数据而添加的远程
加上另一个答案,因为我认为这有点简单。将repo dest拉入repo-to-import,然后执行push-set-upstream url:repo dest master。
这种方法对我来说很有效,将几个较小的回购协议导入到一个较大的回购协议中。
如何导入:repo1_to_import to repo dest
1 2 3 4 5 6 7 8 9 10 11 12 | # 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 |
在执行导入之前,重命名文件和目录或将其移动到原始repo中所需的位置。例如
1 2 3 4 5 6 | 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
在我的例子中,我只想从另一个存储库(XXX)导入一些文件。子树对我来说太复杂了,其他的解决方案都不起作用。我就是这样做的:
1 2 | ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr ' ' ' ') |
这将为您提供一个空间分隔的列表,其中列出了影响我要导入的文件(zzz)的所有提交(您可能还需要添加--follow来捕获重命名)。然后我进入目标存储库(YYY),将另一个存储库(XXX)作为远程存储库添加,从中提取数据,最后:
1 | git cherry-pick $ALL_COMMITS |
这会将所有提交添加到您的分支中,这样您就拥有了所有具有其历史记录的文件,并且可以对它们执行您想要的任何操作,就好像它们一直都在这个存储库中一样。
我可以为您的问题建议另一个解决方案(Git子模块的替代方案)-Gil(Git链接)工具
它允许描述和管理复杂的Git存储库依赖关系。
它还提供了一个解决Git递归子模块依赖性问题的方法。
假设您有以下项目依赖项:Git存储库依赖关系图示例
然后您可以使用存储库关系描述定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # 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 |
每行以以下格式描述git链接:
最后,您必须更新您的根示例存储库:
1 2 3 4 5 6 | # Clone and link all git links dependencies from .gitlinks file gil clone gil link # The same result with a single command gil update |
因此,您将克隆所有必需的项目,并以适当的方式将它们相互链接。
如果您想用子链接存储库中的所有更改提交某个存储库中的所有更改,您可以用一个命令完成:
1 | gil commit -a -m"Some big update" |
pull、push命令的工作方式类似:
1 2 | gil pull gil push |
gil(git links)工具支持以下命令:
1 2 3 4 5 6 7 8 9 10 | 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 |
关于git递归子模块依赖性问题的更多信息。
See Basic example in this article and consider such mapping on repositories:
-
A 子目录<->YYY , B <->XXX 。
在完成本章描述的所有活动后(合并后),删除分支
1 | $ git branch -d B-master |
然后,推动改变。
它对我有用。
我当时正在寻找
我最后做的是
1 2 3 4 5 6 7 8 9 10 11 | # 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 |
因此,现在我的
我不知道一个简单的方法。你可以这样做:
如果这听起来有吸引力,我可以用细节来编辑。
我想你可以用"git mv"和"git pull"来实现。
我是一个公平的Git Noob-所以要小心你的主要存储库-但我只是在一个临时目录中尝试了这个,它似乎可以工作。
首先-重命名XXX的结构,使其与您希望它在YYY中时的外观相匹配:
1 2 3 4 | cd XXX mkdir tmp git mv ZZZ tmp/ZZZ git mv tmp ZZZ |
现在,XXX看起来是这样的:
1 2 3 | XXX |- ZZZ |- ZZZ |
现在使用"git pull"获取以下内容的更改:
1 2 | cd ../YYY git pull ../XXX |
现在,YYY看起来像这样:
1 2 3 4 | YYY |- ZZZ |- ZZZ |- (other folders that already were in YYY) |