如何在没有共同祖先的情况下合并两个分支?

ale*_*rul 30 git merge rebase

我已经开始在我的项目中使用Git,其中前两个提交只是一些初始设置(.gitignore和.gitattributes),第三个提交M2添加了SVN主干的内容:

I1 -- I2 -- M2 -- N -- .. -- Z
Run Code Online (Sandbox Code Playgroud)

我已经在名为svn的分支中导入了SVN历史记录,其中M1是SVN中继(内容与M2相同,除了.gitignore和.gitattributes):

A -- B -- ... -- K -- L -- M1
Run Code Online (Sandbox Code Playgroud)

问:合并两个分支的最佳方法是什么?

我可以将M1M2合并到M3中,然后重新绑定,但我不知道如何删除I1I2提交,如果我可以安全地删除M3提交(我已经找到一些建议来保留合并提交,但是这种情况M3不再需要了).

A -- B -- ... -- K -- L -- M1
                             \
                              M3 -- N' -- .. -- Z'
                             /
               I1 -- I2 -- M2 -- N -- .. -- Z
Run Code Online (Sandbox Code Playgroud)

另一种方法是樱桃挑选N .. Z手工提交到svn分支,但我想避免这种方法.

最优雅的解决方案是重新定义N .. Zsvn分支之上提交的更改,但是我没有找到没有共同祖先的两个分支所需的语法.

jan*_*nko 26

免责声明:我自己只在玩具库中使用过"贪污点".但这是一个你可能没有听说过的晦涩难懂的特征,而且可能对你的情况有所帮助.

您可以使用"嫁接点"伪造祖先信息.参见,例如,什么是.git/info/grafts?或立即进入嫁接点的git wiki条目.

从本质上讲,你会创建一个文件.git/info/grafts,欺骗git认为提交M1是提交M2的祖先:

$ cat .git/info/grafts
<your M2 commit hash> <your M1 commit hash>
Run Code Online (Sandbox Code Playgroud)

随后,看起来M2是一个空提交,只是将I2M1合并到一个公共树中.

主要缺点:贪污点没有承诺; 因此,它没有签出,但需要手动添加到存储库的每个本地工作副本.



更新: 改为使用git replace --graft.

如上所述,移植点已被取代.跑

git replace --graft <your M2 commit hash> <your M1 commit hash>
Run Code Online (Sandbox Code Playgroud)

创造移植物.这存储在.git/refs/replace/.虽然git默认不提取或推送这些引用,但是可以使用以下命令在存储库之间同步它们:

git push origin 'refs/replace/*'
git fetch origin 'refs/replace/*:refs/replace/*'
Run Code Online (Sandbox Code Playgroud)

(StackOverflow:如何推送'refs/replace'而不在git中推送任何其他引用?)

  • 你可以使用"`git filter-branch`"来重写历史**,根据移植,将嫁接的父母变成真正的亲子关系.但这改写了历史. (4认同)
  • 值得注意的是,移植物已经被"git replace"取代,以此来伪造共同的祖先. (4认同)

Uwe*_*nig 5

我会做以下事情:

git checkout M1
git cherry-pick I1
git cherry-pick I2
Run Code Online (Sandbox Code Playgroud)

这会将 .gitignore 和 .gitattributes 添加到包含更好历史记录的分支中。

然后将新的提交设置在该提交之上:

git filter-branch --parent-filter 'if $GIT_COMMIT = $hash_of_N; then printf -- '-p
$hash_of_cherrypicked_I2\n'; else cat; fi'
Run Code Online (Sandbox Code Playgroud)

这样做的缺点是你重写了历史。

因此,另一种方法是创建一个类似于Linux 内核的脚本 并将其放入您的存储库中。


Esk*_*ola 5

最优雅的解决方案是将 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 之上。这里有一些使用它转换旧的 SVN 和 CVS 存储库的经验报告