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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1488753/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 03:51:56  来源:igfitidea点击:

How to merge two branches without a common ancestor?

gitmergerebase

提问by alexandrul

I have started using Git in the middle of my project, where the first two commits are just some initial settings (.gitignore and .gitattributes), and the third commit M2adds the content of the SVN trunk:

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

I1 -- I2 -- M2 -- N -- .. -- Z

I have imported the SVN history in a branch named svn, where M1is the SVN trunk (with the same content as M2, except .gitignore and .gitattributes):

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

A -- B -- ... -- K -- L -- M1

Q:What is the best approach in merging both branches?

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

I could merge M1and M2into M3, and then rebase, but I don't know how to delete the I1and I2commits and if I can safely remove the M3commit (I have found some advices to preserve the merge commits, but in this case M3it's not necessary anymore).

我可以将M1M2合并到M3 中,然后变基,但我不知道如何删除I1I2提交,以及我是否可以安全地删除M3提交(我找到了一些保留合并提交的建议,但是在这种情况下M3不再需要了)。

A -- B -- ... -- K -- L -- M1
                             \
                              M3 -- N' -- .. -- Z'
                             /
               I1 -- I2 -- M2 -- N -- .. -- Z

Another way would be to cherry-pick the N.. Zcommits into svnbranch by hand, but I would like to avoid this approach.

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

The most elegant solution would be to rebase the changes introduced by N.. Zcommits on top of svnbranch, but I didn't found yet the required syntax for two branches without a common ancestor.

最优雅的解决方案是将N.. Z提交引入的更改重新设置在svn分支之上,但我还没有找到没有共同祖先的两个分支所需的语法。

采纳答案by janko

Disclaimer: I've only used "graft points" myself once in a toy repository. But it is an obscure feature which you may not have heard of, and which _may_ be helpful in your situation.

免责声明我自己只在玩具库中使用过一次“移植点”。但它是一个您可能没有听说过的晦涩功能,并且_可能_对您的情况有所帮助。

You could use "graft points" to fake the ancestry information. See, e.g., What are .git/info/grafts for?or proceed immediately to the git wiki entry on graft points.

您可以使用“嫁接点”来伪造祖先信息。参见,例如,什么是 .git/info/grafts?或立即前往有关嫁接点的 git wiki 条目

In essence, you would create a file .git/info/graftsthat tricks git into thinking that commit M1is an ancestor of commit M2:

本质上,您将创建一个文件.git/info/grafts,让 git 认为 commit M1是 commit M2的祖先:

$ cat .git/info/grafts
<your M2 commit hash> <your M1 commit hash>

Subsequently, it would look like M2was an empty commit that just merged I2and M1into a common tree.

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

The major downside: the graft point is not committed; therefore, it is not checked out, but needs to be added to each local working copy of the repository manually.

主要缺点:未提交嫁接点;因此,它不会被检出,而是需要手动添加到存储库的每个本地工作副本中。





Update:use git replace --graftinstead.

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

Graft points, as described above, have been superseded. Run

如上所述,嫁接点已被取代。跑

git replace --graft <your M2 commit hash> <your M1 commit hash>

to create the graft. This is stored in .git/refs/replace/. Although git does not fetch, or push, these refs by default, they can be synchronized between repositories using:

创建移植物。这存储在.git/refs/replace/. 尽管 git 默认不获取或推送这些 refs,但它们可以使用以下方法在存储库之间同步:

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?)

StackOverflow:如何在 git 中推送“引用/替换”而不推送任何其他引用?

回答by Esko Luontola

The most elegant solution would be to rebase the changes introduced by N .. Z commits on top of svn branch, but I didn't found yet the required syntax for two branches without a common ancestor.

最优雅的解决方案是将 N .. Z 提交引入的更改重新设置在 svn 分支之上,但我还没有找到没有共同祖先的两个分支所需的语法。

Try to first cherry-pick I1 and I2 onto M1, and after that use the command git rebase --onto M1' M2 Z(where M1' is the M1-I1-I2 branch). I'm not sure whether rebase --onto works when there are no common ancestors, but if it doesn't, there is the possibility of using patches. Use the git format-patchto generate the patches of M2..Z and then git amto apply them on top of M1. Here are some experience reportson using it in converting old SVN and CVS repositories.

尝试首先将 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 存储库的经验报告

回答by Uwe Kleine-K?nig

I'd do the following:

我会做以下事情:

git checkout M1
git cherry-pick I1
git cherry-pick I2

That adds .gitignore and .gitattributes to your branch containing the nicer history.

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

And then just set the new commits on top of that one:

然后只需在该提交之上设置新提交:

git filter-branch --parent-filter 'if $GIT_COMMIT = $hash_of_N; then printf -- '-p
$hash_of_cherrypicked_I2\n'; else cat; fi'

The downside of this is you rewrite history.

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

So an alternative approach is to create a script similar to the one for the Linux kerneland put that in your repository.

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