考虑以下场景:
我在自己的Git repo中开发了一个小型实验项目a。它现在已经成熟,我希望A成为更大的项目B的一部分,该项目有自己的大仓库。现在我想将A添加为B的子目录。
我如何将A合并为B,而不丢失任何方面的历史?
考虑以下场景:
我在自己的Git repo中开发了一个小型实验项目a。它现在已经成熟,我希望A成为更大的项目B的一部分,该项目有自己的大仓库。现在我想将A添加为B的子目录。
我如何将A合并为B,而不丢失任何方面的历史?
当前回答
以下是两种可能的解决方案:
子模块
要么将存储库A复制到较大项目B中的单独目录中,要么(也许更好)将存储库B克隆到项目B的子目录中。然后使用git子模块将此存储库设置为存储库B的子模块。
对于松散耦合的存储库来说,这是一个很好的解决方案,存储库a中的开发仍在继续,而开发的主要部分是a中的独立开发。另请参阅GitWiki上的SubmoduleSupport和GitSubmoduleTutorial页面。
子树合并
您可以使用子树合并策略将存储库A合并到项目B的子目录中。Markus Prinz在《子树合并与你》中描述了这一点。
git remote add -f Bproject /path/to/B
git merge -s ours --allow-unrelated-histories --no-commit Bproject/master
git read-tree --prefix=dir-B/ -u Bproject/master
git commit -m "Merge B project as our subdirectory"
git pull -s subtree Bproject master
(选项--Git>=2.9.0需要允许不相关的历史记录。)
或者你可以使用apenwarr(Avery Pennarun)的git子树工具(GitHub上的存储库),例如,在他的博客文章《git子模块的新替代方案:git子树》中宣布了这一点。
我认为在您的情况下(A是大型项目B的一部分),正确的解决方案是使用子树合并。
其他回答
如果您想单独维护项目,子模块方法是很好的。然而,如果您真的想将两个项目合并到同一个存储库中,那么您还有更多的工作要做。
第一件事是使用gitfilter分支将第二个存储库中所有内容的名称重写到您希望它们结束的子目录中。因此,您将使用projb/foo.c和projb/bar.html代替foo.c和bar.html。
然后,您应该能够执行以下操作:
git remote add projb [wherever]
git pull projb
git pull将执行git fetch,然后执行git merge。如果您要拉到的存储库还没有projb/目录,那么应该不会有冲突。
进一步搜索表明,在将gitk合并为git时也做了类似的操作。Junio C Hamano在这里写道:http://www.mail-archive.com/git@vger.kernel.org/msg03395.html
几天来我一直在尝试做同样的事情,我使用的是git2.7.2。子树不会保留历史。
如果不再使用旧项目,可以使用此方法。
我建议你先在B分支机构工作,然后在该分支机构工作。
以下是没有分支的步骤:
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
如果您现在在分区A中记录任何文件,您将获得完整的历史记录
git log --follow A/<file>
这是帮助我做到这一点的帖子:
http://saintgimp.org/2013/01/22/merging-two-git-repositories-into-one-repository-without-losing-file-history/
另一个存储库的单个分支可以很容易地放在保留其历史的子目录下。例如:
git subtree add --prefix=rails git://github.com/rails/rails.git master
这将显示为一次提交,其中Rails主分支的所有文件都添加到“Rails”目录中。然而,提交的标题包含对旧历史树的引用:
从提交添加“rails/”<rev>
其中<rev>是SHA-1提交哈希。你仍然可以看到历史,责怪一些变化。
git log <rev>
git blame <rev> -- README.md
注意,从这里看不到目录前缀,因为这是一个完整的旧分支。您应该像通常的文件移动提交一样对待它:当到达它时,您需要额外的跳转。
# finishes with all files added at once commit
git log rails/README.md
# then continue from original tree
git log <rev> -- README.md
还有一些更复杂的解决方案,如手动执行此操作或如其他答案所述重写历史。
git子树命令是git contrib的一部分,一些数据包管理器默认安装它(OS X Homebrew)。但除了git之外,您可能还需要自己安装它。
当您希望在一次提交中合并三个或更多项目时,请执行其他答案中所述的步骤(远程添加-f,合并)。然后,(软)将索引重置为旧头(没有合并)。添加所有文件(git-Add-A)并提交它们(消息“将项目A、B、C和D合并到一个项目中”)。这现在是master的提交id。
现在,使用以下内容创建.git/info/places:
<commit-id of master> <list of commit ids of all parents>
运行gitfilter分支--head^。。头头^2.头头^3.头。如果你有三个以上的分支,就加上同样多的头。。当你有树枝的时候,就把头伸过去。要更新标记,请追加--tag-namefilter cat。不要总是添加,因为这可能会导致某些提交的重写。有关详细信息,请参阅过滤器分支的手册页,搜索“移植物”。
现在,你的最后一次承诺与正确的父母相关。
以下是两种可能的解决方案:
子模块
要么将存储库A复制到较大项目B中的单独目录中,要么(也许更好)将存储库B克隆到项目B的子目录中。然后使用git子模块将此存储库设置为存储库B的子模块。
对于松散耦合的存储库来说,这是一个很好的解决方案,存储库a中的开发仍在继续,而开发的主要部分是a中的独立开发。另请参阅GitWiki上的SubmoduleSupport和GitSubmoduleTutorial页面。
子树合并
您可以使用子树合并策略将存储库A合并到项目B的子目录中。Markus Prinz在《子树合并与你》中描述了这一点。
git remote add -f Bproject /path/to/B
git merge -s ours --allow-unrelated-histories --no-commit Bproject/master
git read-tree --prefix=dir-B/ -u Bproject/master
git commit -m "Merge B project as our subdirectory"
git pull -s subtree Bproject master
(选项--Git>=2.9.0需要允许不相关的历史记录。)
或者你可以使用apenwarr(Avery Pennarun)的git子树工具(GitHub上的存储库),例如,在他的博客文章《git子模块的新替代方案:git子树》中宣布了这一点。
我认为在您的情况下(A是大型项目B的一部分),正确的解决方案是使用子树合并。