考虑以下场景:
我在自己的Git repo中开发了一个小型实验项目a。它现在已经成熟,我希望A成为更大的项目B的一部分,该项目有自己的大仓库。现在我想将A添加为B的子目录。
我如何将A合并为B,而不丢失任何方面的历史?
考虑以下场景:
我在自己的Git repo中开发了一个小型实验项目a。它现在已经成熟,我希望A成为更大的项目B的一部分,该项目有自己的大仓库。现在我想将A添加为B的子目录。
我如何将A合并为B,而不丢失任何方面的历史?
当前回答
另一个存储库的单个分支可以很容易地放在保留其历史的子目录下。例如:
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之外,您可能还需要自己安装它。
其他回答
我稍微手动合并项目,这使我可以避免处理合并冲突。
首先,从另一个项目中复制文件,无论您需要什么。
cp -R myotherproject newdirectory
git add newdirectory
历史上的下一次拉力
git fetch path_or_url_to_other_repo
告诉git在上次获取的历史记录中合并
echo 'FETCH_HEAD' > .git/MERGE_HEAD
现在按您通常的方式提交
git commit
git子树很好,但它可能不是您想要的。
例如,如果projectA是在B中创建的目录,在git子树之后,
git log projectA
仅列出一个提交:合并。合并项目的提交针对不同的路径,因此不会显示。
格雷格·休吉尔(Greg Hewgill)的答案最接近,但实际上并没有说明如何重写路径。
解决方案出奇地简单。
(1) 在A中,
PREFIX=projectA #adjust this
git filter-branch --index-filter '
git ls-files -s |
sed "s,\t,&'"$PREFIX"'/," |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE
' HEAD
注意:这将重写历史;你可能想先备份a。
注意Bene:如果在文件名或路径中使用非ascii字符(或白色字符),则必须修改sed命令中的替代脚本。在这种情况下,“ls files-s”生成的记录中的文件位置以引号开头。
(2) 然后在B中,运行
git pull path/to/A
瞧!在B中有一个projectA目录。如果运行git-log-projectA,您将看到a中的所有提交。
在我的例子中,我需要两个子目录,projectA和projectB。在这种情况下,我也执行了步骤(1)到B。
当您希望在一次提交中合并三个或更多项目时,请执行其他答案中所述的步骤(远程添加-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。不要总是添加,因为这可能会导致某些提交的重写。有关详细信息,请参阅过滤器分支的手册页,搜索“移植物”。
现在,你的最后一次承诺与正确的父母相关。
我想把一个小项目移到一个大项目的子目录中。由于我的小项目没有很多提交,所以我使用了git格式的patch--output directory/path/to/patch dir。然后在更大的项目中,我使用了git-am-directory=dir/in/project/path/to/patch-dir/*。
这感觉比过滤器分支不那么可怕,也更干净。当然,它可能不适用于所有情况。
https://github.com/hraban/tomono作为基于脚本的解决方案的另一个例子。
我不是作者,但使用了它,它完成了任务。
一个积极的方面是,你将所有分支机构和所有历史记录纳入最终回购。对于我的repo(repo中没有重复的文件夹-实际上,它们来自tfs2git迁移),没有冲突,一切都是自动运行的。
它主要用于(参见名称)创建monoreos。
对于Windows用户:gitbash可以执行.sh文件。它带有标准的git安装。