考虑以下场景:

我在自己的Git repo中开发了一个小型实验项目a。它现在已经成熟,我希望A成为更大的项目B的一部分,该项目有自己的大仓库。现在我想将A添加为B的子目录。

我如何将A合并为B,而不丢失任何方面的历史?


当前回答

如果您试图简单地将两个存储库粘合在一起,那么子模块和子树合并是错误的工具,因为它们不能保留所有的文件历史记录(正如人们在其他答案中所指出的)。请参阅此处的答案,了解简单而正确的方法。

其他回答

如果两个存储库都有相同类型的文件(就像两个Rails存储库用于不同的项目),您可以将辅助存储库的数据提取到当前存储库中:

git fetch git://repository.url/repo.git master:branch_name

然后将其合并到当前存储库:

git merge --allow-unrelated-histories branch_name

如果您的Git版本小于2.9,请删除--允许不相关的历史记录。

在此之后,可能会发生冲突。例如,可以使用gitmergetool来解析它们。kdiff3只能与键盘一起使用,因此在读取代码时仅需几分钟,就会产生5个冲突文件。

记住完成合并:

git commit

与@Smar类似,但使用在PRIMARY和SECONDARY中设置的文件系统路径:

PRIMARY=~/Code/project1
SECONDARY=~/Code/project2
cd $PRIMARY
git remote add test $SECONDARY && git fetch test
git merge test/master

然后手动合并。

(改编自阿纳尔·马纳福夫的帖子)

给定命令是我建议的最佳解决方案。

git subtree add --prefix=MY_PROJECT git://github.com/project/my_project.git master

要在B中合并a:

1) 在项目A中

git fast-export --all --date-order > /tmp/ProjectAExport

2) 在项目B中

git checkout -b projectA
git fast-import --force < /tmp/ProjectAExport

在这个分支中,执行所有需要执行的操作并提交它们。

C) 然后回到主节点,两个分支之间进行经典合并:

git checkout master
git merge projectA

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。