我已经开始在我的项目中使用Git,其中前两个提交只是一些初始设置(.gitignore和.gitattributes),第三个提交 M2 添加了内容SVN主干:
I1 -- I2 -- M2 -- N -- .. -- Z
我已在名为 svn 的分支中导入SVN历史记录,其中 M1 是SVN主干(内容与 M2 相同,除了.gitignore和.gitattributes):
A -- B -- ... -- K -- L -- M1
问:合并两个分支的最佳方法是什么?
我可以将 M1 和 M2 合并到 M3 ,然后进行rebase,但我不知道如何删除 I1 和 I2 提交,如果我可以安全地删除 M3 提交(我已经找到一些建议来保留合并提交,但在这种情况下 M3 不再需要了。)
A -- B -- ... -- K -- L -- M1
\
M3 -- N' -- .. -- Z'
/
I1 -- I2 -- M2 -- N -- .. -- Z
另一种方法是手动挑选 N .. Z 提交到 svn 分支,但我想避免这种方法。
最优雅的解决方案是在 svn 分支之上修改由 N .. Z 提交引入的更改,但我没有找不到没有共同祖先的两个分支所需的语法。
答案 0 :(得分:25)
免责声明:我自己只在玩具库中使用过“贪污点”。但这是一个你可能没有听说过的晦涩难懂的特征,而且对你的情况有帮助。
您可以使用“嫁接点”伪造祖先信息。例如,请参阅What are .git/info/grafts for?或立即进入the git wiki entry on graft points。
从本质上讲,你会创建一个文件.git/info/grafts
,让git认为提交 M1 是提交 M2 的祖先:
$ cat .git/info/grafts
<your M2 commit hash> <your M1 commit hash>
随后, M2 似乎是一个空提交,只是将 I2 和 M1 合并到一个公共树中。
主要缺点 :嫁接点未提交;因此,它不会被签出,但需要手动添加到存储库的每个本地工作副本中。
更新: 改为使用git replace --graft
。
如上所述,移植点已被取代。运行
git replace --graft <your M2 commit hash> <your M1 commit hash>
创建移植物。这存储在.git/refs/replace/
中。虽然git默认不提取或推送这些引用,但它们可以使用以下命令在存储库之间进行同步:
git push origin 'refs/replace/*'
git fetch origin 'refs/replace/*:refs/replace/*'
(StackOverflow: How to push 'refs/replace' without pushing any other refs in git?)
答案 1 :(得分:3)
我会做以下事情:
git checkout M1;
git cherry-pick I1;
git cherry-pick I2;
将.gitignore和.gitattributes添加到包含更好历史记录的分支。
然后只需将新提交设置在那个提交之上:
git filter-branch --parent-filter 'if $GIT_COMMIT = $hash_of_N; then printf -- '-p
$hash_of_cherrypicked_I2\n'; else cat; fi'
这样做的缺点是你重写了历史。
所以另一种方法是创建一个类似于the one for the Linux kernel的脚本 并将其放入您的存储库。
答案 2 :(得分:3)
最优雅的解决方案是重新定义N .. Z在svn分支之上提交的更改,但是我没有找到没有共同祖先的两个分支所需的语法。
尝试首先将I1和I2挑选到M1上,然后使用命令git rebase --onto M1' M2 Z
(其中M1'是M1-I1-I2分支)。我不确定rebase --onto是否在没有共同祖先时起作用,但如果没有,则有可能使用补丁。使用git format-patch
生成M2..Z的补丁,然后使用git am
将其应用于M1顶部。 Here are some experience reports使用它来转换旧的SVN和CVS存储库。