16

我已经阅读了许多关于git-svn和合并的 SO 问题和博客。其中一些(尤其是git-svn手册页)警告不要在我计划的分支附近的任何地方使用合并dcommit

其他人,例如这个SO answer,声明只要我使用我之前删除的一次性dcommitting功能分支,合并就可以了。

我有两个长期存在的 SVN(和 git)分支的设置:

  • trunk (git-svn: svn/trunk, git: master) - 稳定的分支
  • 分支/开发(git-svn: svn/devel, git: devel) - 我从中分出功能分支的主要开发分支(我可以忍受将它们重新定位devel而不是合并)。

源树中当前状态的快照

在上图中,(1)显示了过去的 SVN 历史,其中branches/devel已合并到trunk.

(2)表明我已经成功地将dcommitted我当前的开发返回到 SVN。

问题:我可以git-svn用来合并两个 SVN 分支,以便历史记录显示合并点(正如 SVN 本身可以做的那样)?换句话说:如果我dcommit要从(3)master中显示的那样会发生什么?

这会弄乱我的 SVN(或 git)历史吗?或者这会完全忘记devel被合并master的吗?

编辑:如果git只是忘记devel合并到中,从点(3)和将所有提交作为单个补丁master应用之间有区别吗?dcommiting

4

2 回答 2

14

深入研究一下,我发现 git在'ingsvn:mergeinfo时支持在 SVN 分支上设置属性:dcommit

git svn dcommit --mergeinfo "/branches/somebranch:1-3"

注意!svn:mergeinfo被命令行上给出的任何内容覆盖,所以也要小心列出以前的合并

虽然最近的 git 版本添加了 config 参数来自动设置这个属性:

config key: svn.pushmergeinfo

我在使用自动合并信息时遇到了一些问题——出于某种原因或其他 GIT 计算错误,我无法让它工作。

解决方案:git-merge-svn

为了自动化这个过程,我编写了一个 shell 脚本git-merge-svn,它可以用来合并两个svn:mergeinfodcommit 上正确设置的 SVN 分支。

该脚本处理两种情况:

  • 分支没有在 git 中合并 - 会git merge事先做
  • 分支已经在 git 中合并(但不在 SVN 中) - 将遍历直到合并提交修订的前一个祖先。

使用此脚本,我能够仅在 git 端生成这些合并并保留合并信息,以便 GIT 图表很好地显示日志:

git-merge-svn 结果

示例用法:

  1. 在devel6上做一些提交
  2. dcommit devel6到 SVN(需要获取提交的 SVN 修订号)
  3. 查看testtunk6 - 是的,我知道我的名字打错了 ;-)
  4. git merge-svn devel6

最后一条命令输出:

% git merge-svn devel6
About to do an SVN merge: devel6 -> testtunk6

* NEW MERGE COMMIT
|\
| * devel6 [7b71187] (r102)
* | testtunk6 [0682a45] (r101)
 \|
  * [273d6d6] (r100)


STEP 1: GIT merge
Executing:
  git merge devel6

Continue? (y/n) [n]: y
Merge made by the 'recursive' strategy.
 testfile | 1 +
 1 file changed, 1 insertion(+)

STEP 2: SVN dcommit

executing:
git svn dcommit --mergeinfo
/idp/branches/devel:9-32,35-41 /idp/branches/devel6:89 /idp/branches/devel6:94 /idp/branches/devel6:93 /idp/branches/devel6:96 /idp/branches/devel6:97 /idp/branches/devel6:99 /idp/branches/devel6:100 /idp/branches/devel6:102

Continue? (y/n) [n]: y
Committing to https://my.svn.host/svn/auth/idp/branches/testtunk6 ...
  M testfile
Committed r103
  M testfile
Found merge parent (svn:mergeinfo prop): 7b71187fc371d3f86658c5850009e63be88157ac
r103 = 87759323cbadd38bac78087d57b6870a926287e7 (refs/remotes/svn/testtunk6)
No changes between 3fb2168cfbbe605fbd810d76513443203a85a549 and refs/remotes/svn/testtunk6
Resetting to the latest refs/remotes/svn/testtunk6
于 2013-04-23T09:20:23.117 回答
4

Git 和 Svn 有不同的数据结构来保存历史。

Svn 使用一个简单的树。即每个提交只有一个父级,但一个提交可以有多个子级(分支)。所以,Svn 不支持合并。他们称之为“合并”的东西实际上是变更集移植,在 git 中最接近的类比是rebase或者可能是cherry-pick. 从 1.5 版开始,Svn 支持一个元信息属性svn:mergeinfo,它有助于以某种方式跟踪“合并”的内容,但它看起来主要是一种解决方法,这解释了为什么 Svn 中的分支如此难以使用。合并点是一种提交,它将所有合并的变更集和冲突解决方案压缩到一个变更集中,可能使用svn:mergeinfo属性进行注释。

Git 使用有向无环图,这是一种非常自然的方式来描述历史合并和分支。合并点只是具有两个(或更多!)父母的另一个提交。因此,图片上的 (3) 是合并提交。所以,所有的历史看起来都很自然,它的所有提交都可以在历史图表上从当前点到达。

Git-Svn 桥尝试尽最大努力处理 Svn 设计缺陷,在此之前dcommit通常会将所有合并提交重新设置在当前 Svn 分支的顶部。Asof ~2 年前, git-svn 无法提供svn:mergeinfo,所以你问的事情是不可能的。此外,当你dcommiting 时,git 会创建一个“几乎”链接到 svn 提交的“孪生”提交,因此它会重写原始提交。

于 2013-04-18T20:49:15.723 回答