36

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

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

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

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

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

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

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

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

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

4

3 回答 3

33

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

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

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

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

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

主要缺点:嫁接点不承诺;因此,它没有被检出,但需要手动添加到存储库的每个本地工作副本中。



更新: 改用git replace --graft

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

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

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

git push origin 'refs/replace/*'
git fetch origin 'refs/replace/*:refs/replace/*'

StackOverflow:如何在不推送 git 中的任何其他 refs 的情况下推送“refs/replace”?

于 2009-09-29T07:25:25.927 回答
5

我会做以下事情:

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'

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

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

于 2011-01-17T20:05:55.223 回答
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 存储库的经验报告。

于 2011-01-17T22:03:52.523 回答