合并:Hg / Git与SVN


144

我经常读到Hg(以及Git和...)比SVN更擅长合并,但是我从未见过Hg / Git可以合并SVN失败(或SVN需要手动干预的地方)的实际示例。您能否发布分支/修改/提交/...-操作的分步列表,以显示在Hg / Git继续快乐地运行时SVN失败的地方?请实用,不是非常例外的情况...

背景知识:我们有数十名开发人员使用SVN进行项目开发,每个项目(或类似项目组)都在其自己的存储库中。我们知道如何应用发布分支和功能分支,因此我们不会经常遇到问题(即,我们去过那里,但是我们已经学会了克服Joel的 “一个程序员给整个团队造成创伤”的问题)或“需要六个开发人员两个星期才能重新整合分支机构”)。我们有非常稳定的发行分支,仅用于应用错误修正。我们的中继应该足够稳定,以便能够在一周内创建发行版。我们拥有功能分支,单个开发人员或一组开发人员都可以使用。是的,它们在重新集成后会被删除,因此不会使存储库混乱。;)

因此,我仍在尝试寻找Hg / Git优于SVN的优势。我很想获得一些实践经验,但是还没有任何更大的项目可以迁移到Hg / Git,因此我只能玩一些仅包含少量文件的小型人工项目。我正在寻找一些案例,您可以感受到Hg / Git的强大功能,因为到目前为止,我经常阅读有关它们的信息,但我自己却找不到。



11
我已经读过第一个,另一个是新的。但是他们已经有1-2岁了,似乎主要是关于svn-1.5之前的问题(其中svn尚无合并跟踪)。
stmax 2010年

2
只需说明一下,您也可以将义卖市场混入git / hg作为另一个DVCS,它将正确处理以下问题。而且,由于您提到了尝试寻找优势的方法:git / hg / bzr的一个简单的后勤优势是分支并不像svn那样具有全局性。当只有一对申请者时,您不必看到67个分支。每个人都在“私有”分支中完成工作,然后使用出色的合并功能合并回去,而不必担心合并是否在99%的情况下都能正常进行。
wadesworld'4

5
@wade:您是否将“私人”分支机构视为公司环境中的优势?我担心备份。我经常有功能分支是活1-2个月重返前..
stmax

9
@stmax:一个有效的关注点。但是,在许多具有颠覆性的公司环境中发现的是,人们推迟了签入直到他们的代码完美为止,并且在那里您拥有相同的风险。
wadesworld'4

Answers:


91

我自己没有使用Subversion,但是从Subversion 1.5发行说明中:合并跟踪(基础性)看起来与在Git或Mercurial等完整DAG版本控制系统中的合并跟踪的工作方式存在以下差异。

  • 将主干合并到分支不同于将分支合并到主干:出于某种原因,将主干合并到分支需要--reintegrate选项svn merge

    在像Git或Mercurial这样的分布式版本控制系统中,主干和分支之间没有技术上的区别:所有分支的创建都是相同的(尽管可能存在社会差异)。在任一方向上的合并都以相同的方式进行。

  • 您需要提供new -g--use-merge-history)选项,svn logsvn blame考虑合并跟踪。

    在Git和Mercurial中,显示历史记录(日志)和责备时会自动考虑合并跟踪。在Git中,您可以要求仅跟随第一父项--first-parent(我想Mercurial也存在类似的选项)来“丢弃”中的合并跟踪信息git log

  • 据我了解,svn:mergeinfo属性存储有关冲突的每个路径的信息(Subversion基于更改集),而在Git和Mercurial中,它只是提交对象,可以具有多个父对象。

  • Subversion中用于合并跟踪的“已知问题”小节建议重复/循环/反射合并可能无法正常工作。这意味着在以下历史记录中,第二次合并可能不会做正确的事情(“ A”可以是主干或分支,而“ B”可以是分支或主干):

    * --- * --- x --- * --- y --- * --- * --- * --- M2 <-A
             \
              -* ---- M1 --- * --- * --- / <-B
    

    在上述ASCII技巧被破坏的情况下:从版本“ x”的分支“ A”创建(分支)分支“ B”,然后在版本“ y”将更高版本的分支“ A”合并为分支“ B”,合并“ M1”,最后将分支“ B”合并为分支“ A”作为合并“ M2”。

    * --- * --- x --- * ----- M1-* --- * --- M2 <-A
             \ / / 
              \-* --- y --- * --- * --- / <-B
    

    在上述ASCII技巧被破坏的情况下:从版本“ x”的分支“ A”创建(分支)分支“ B”,将其合并为“ y”的分支“ A”作为“ M1”,随后再次合并为“ M2”分支“ A”。

  • Subversion可能不支持纵横交错合并的高级情况。

    * --- b ----- B1--M1-* --- M3
         \ \ / /
          \ X /
           \ / \ /
            \-B2--M2-*
    

    在实践中,Git使用“递归”合并策略可以很好地处理这种情况。我不确定Mercurial。

  • “已知问题”中,警告说合并跟踪可能不适用于文件重命名,例如,当一侧重命名文件(并可能对其进行了修改),而第二侧却在不重命名的情况下修改了文件(使用旧名称)。

    实际上,Git和Mercurial都可以处理这种情况:Git使用重命名检测,Mercurial使用重命名跟踪

高温超导


莫名其妙地(Markdown解析器中的错误?)<pre>...</pre>块之后的部分没有缩进,应该是……
JakubNarębski2010年

1
+1是许多详细示例。我还不明白为什么第一个ascii技术中的示例可能会引起问题。它看起来像对待要素分支的标准方法:假设A是主干,B是要素分支。您每周从A合并到B,并且完成该功能后,就将所有内容从B合并到A,然后删除B。这一直对我有用。我误解了图吗?
stmax,2010年

1
请注意,我不知道(尚未检查)上面给出的示例确实在Subversion中带来问题。我认为,重命名和纵横交叉合并是SVN中的真正问题。
JakubNarębski10年

2
重新整合合并是一种特殊的选择,可以在最常见的情况下为您提供帮助-svn中的分支和主干之间也没有技术差异。我倾向于从不使用它,并坚持使用标准合并选项。但是,svn merge的唯一问题是它将移动/重命名视为删除+添加。
gbjbaanb

--reintegrate不推荐使用。
naught101

120

我也一直在寻找这样一个案例,例如Subversion无法合并分支,而Mercurial(以及Git,Bazaar等)做正确的事情。

SVN手册描述了重命名文件的错误合并方法。这适用于颠覆1.51.61.71.8!我试图重新创建以下情况:

cd / tmp
RM - 射频SVN - 回购SVN - 结帐
svnadmin的创建SVN - 回购
svn签文件:/// tmp目录/ SVN - 回购SVN - 结帐
CD SVN - 结帐
mkdir树干分支
回声“再见,世界!” > 行李箱/ 您好文本 
svn添加主干分支
svn commit - m '初始导入'。
svn复制'^ / trunk' '^ / branches / rename' - m '创建分支。' 
svn开关'^ / trunk' 
回声“你好,世界!” > 你好文本    
svn commit - m '在主干上更新'。
svn开关'^ / branchches / rename' 
svn重命名你好txt你好en 文本 
svn commit - m '在分支上重命名。
svn开关'^ / trunk' 
SVN合并- 再集成'^ /分支机构/重命名' 

根据这本书,合并应该完全完成,但是由于trunk忘记了更新,因此重命名文件中的数据错误。相反,我遇到了树冲突(这是Subversion 1.6.17,这是撰写本文时Debian中的最新版本):

---将存储库URL之间的差异合并为“。”:
hello.en.txt
   C hello.txt
冲突摘要:
  树冲突:1

根本不应该有任何冲突-更新应合并到文件的新名称中。当Subversion失败时,Mercurial会正确处理此问题:

rm -rf /tmp/hg-repo
hg init /tmp/hg-repo
cd /tmp/hg-repo
echo 'Goodbye, World!' > hello.txt
hg add hello.txt
hg commit -m 'Initial import.'
echo 'Hello, World!' > hello.txt
hg commit -m 'Update.'
hg update 0
hg rename hello.txt hello.en.txt
hg commit -m 'Rename.'
hg merge

合并之前,存储库如下所示(来自hg glog):

@变更集:2:6502899164cc
| 标签:小费
| 父母:0:d08bcebadd9e
| 使用者:Martin Geisler
| 日期:2010年4月1日星期四12:29:19 +0200
| 摘要:重命名。
|
| o变更集:1:9d06fa155634
| /用户:马丁·盖斯勒(Martin Geisler) 
| 日期:2010年4月1日星期四12:29:18 +0200
| 摘要:更新。
|
o变更集:0:d08bcebadd9e
   使用者:Martin Geisler 
   日期:2010年4月1日星期四12:29:18 +0200
   摘要:初始导入。

合并的输出为:

将hello.en.txt和hello.txt合并到hello.en.txt
更新了0个文件,合并了1个文件,删除了0个文件,未解决0个文件
(分支合并,别忘了提交)

换句话说:Mercurial接受了版本1的更改,并将其合并到版本2(hello.en.txt)的新文件名中。为了支持重构,处理这种情况当然是必不可少的,而重构正是您要在分支上执行的操作。


+1为详细示例,您可以敲击键盘并自己查看会发生什么。作为Mercurial菜鸟,我想知道此示例的hg版本是否以明显的方式逐行遵循吗?
DarenW

4
@DarenW:我添加了相应的Mercurial命令,希望它使事情变得更清楚!
Martin Geisler

17

在不谈论通常的优点(脱机提交,发布过程等)的情况下,我喜欢这里的“合并”示例:

我一直看到的主要场景是一个分支,在该分支上…… 实际开发了两个不相关的任务
(它从一个功能开始,但是导致了另一功能的开发。
或者它从补丁开始,但是导致了开发另一个功能)。

如何只合并主分支上的两个功能之一?
或如何隔离它们各自分支中的两个功能?

您可以尝试生成某种补丁,但问题是您不确定在以下之间可能存在的功能依赖性

  • 补丁中使用的提交(或SVN的修订版)
  • 其他提交不属于补丁程序

Git(我想也是Mercurial)建议使用rebase --onto选项来重新设置(重置分支的根)分支的一部分:

Jefromi的帖子

- x - x - x (v2) - x - x - x (v2.1)
           \
            x - x - x (v2-only) - x - x - x (wss)

您可以将这种情况(其中具有适用于v2的补丁程序以及新的wss功能)解开:

- x - x - x (v2) - x - x - x (v2.1)
          |\
          |  x - x - x (v2-only)
           \
             x - x - x (wss)

,使您能够:

  • 单独测试每个分支,以检查是否一切都能按预期进行编译/工作
  • 仅合并您要维护的内容。

我喜欢的另一个功能(影响合并)是挤压提交(在尚未推送到另一个仓库的分支中)以便呈现的功能:

  • 更清洁的历史
  • 更加连贯的提交(而不是功能1的commit1,功能2的commit2,功能1的commit3……)

这样可以确保合并更加容易,并且冲突更少。


svn没有离线提交?rofl?如果是这样的话,怎么会有人甚至远程考虑使用它呢?
o0'。

@Lohoris当SVN出现时,没有广泛使用的开源DVCS。在这一点上,我认为人们仍然在使用它主要是惯性。
Max Nanasy

@MaxNanasy的惯性很差……但是,现在选择它只是愚蠢的。
o0'。

在小型团队中,@ Lohoris Online(更准确地说是集中式)提交并不是什么大问题,在这个小型团队中,存储库可以简单地位于共享的本地服务器上。DVCS主要是为地理上分散的大型团队(git和mercurial旨在管理Linux内核代码)和开源项目(因此GitHub受欢迎)而发明的。惯性也可以看作是评估团队工作流程中重要工具的风险与收益的评估。
IMSoP 2014年

1
@Lohoris我想您误解了我对DB,防火墙等的观点:如果我不能真正先运行该代码,那么我可以在我的家用计算机上提交的信息就毫无意义。我可以盲目工作,但是我不能在某个地方做任何事情并不是让我失望的主要因素。
IMSoP 2014年

8

我们最近从SVN迁移到GIT,并面临同样的不确定性。有许多传闻表明,GIT更好,但是很难找到任何实例。

不过,我可以告诉您,GIT在合并方面比SVN好得多。这显然是轶事,但有一张桌子可循。

这是我们发现的一些东西:

  • SVN曾经在看起来不应该的情况下引发很多树冲突。我们从来没有深入了解这个问题,但是在GIT中却没有发生。
  • 虽然更好,但GIT却要复杂得多。花一些时间在培训上。
  • 我们习惯了喜欢的Tortoise SVN。乌龟GIT效果不佳,这可能会让您失望。但是,我现在使用GIT命令行,我更喜欢Tortoise SVN或任何GIT GUI。

在评估GIT时,我们进行了以下测试。这些显示出GIT在合并方面是赢家,但并没有那么多。实际上,两者之间的差异要大得多,但是我想我们还没有能够复制SVN处理不好的情况。

GIT与SVN合并评估


5

其他人则涵盖了更多的理论方面。也许我可以提出更实际的看法。

我目前正在为一家在“功能分支”开发模型中使用SVN的公司工作。那是:

  • 树干上无法做任何工作
  • 每个开发人员都可以创建自己的分支
  • 分支机构应在完成任务的整个过程中持续存在
  • 每个任务都应该有自己的分支
  • 合并回主干需要授权(通常通过bugzilla)
  • 在需要高度控制的时候,可以由网守进行合并

通常,它可以工作。SVN可以用于这样的流程,但这并不完美。SVN的某些方面会妨碍并影响人类行为。这给了它一些消极的方面。

  • 人们从低于的点分支时遇到了很多问题^/trunk。这些垃圾将信息记录合并到整个树中,并最终破坏合并跟踪。错误的冲突开始出现,混乱充斥。
  • 从主干到分支的更改相对简单。svn merge做你想要的。合并您的更改需要(被告知)--reintegratemerge命令。我从未真正理解过此开关,但是这意味着分支无法再次合并到中继中。这意味着它是一个死掉的分支,您必须创建一个新分支才能继续工作。(见说明)
  • 在创建和删除分支时通过URL在服务器上进行操作的整个业务确实使人们感到困惑和恐惧。所以他们避免了。
  • 分支之间的切换很容易出错,将树的一部分放在分支A上,而将另一部分放在分支B上。因此,人们更喜欢在一个分支上完成所有工作。

往往发生的情况是,工程师在第一天就创建了一个分支。他开始工作后就忘记了。一段时间后,老板来了,问他是否可以把工作放到行李箱上。工程师今天一直很恐惧,因为重新集成意味着:

  • 将他长期存在的分支合并到主干中并解决所有冲突,并释放不相关的代码,这些代码本来应该放在单独的分支中,但事实并非如此。
  • 删除他的分支
  • 创建一个新分支
  • 将他的工作副本切换到新分支

...并且因为工程师尽其所能做到这一点,所以他们不记得做每个步骤的“魔咒”。错误的开关和URL发生了,突然之间一团糟,他们得到了“专家”。

最终,一切都解决了,人们学习了如何解决这些缺点,但是每个新手都遇到了相同的问题。最终的现实(与我一开始就提出的相反)是:

  • 树干上没有任何工作
  • 每个开发人员都有一个主要分支
  • 分支会持续到需要释放工作为止
  • 票务错误修复倾向于获得自己的分支
  • 授权后合并回主干

...但...

  • 有时,由于它与其他对象位于同一分支中,因此在不应该进行的工作中将其变为主干。
  • 人们避免所有合并(甚至是简单的事情),因此人们经常在自己的小泡泡中工作
  • 大合并往往会发生,并且会造成有限的混乱。

值得庆幸的是,该团队足够小,可以应付,但规模不大。事实是,CVCS都不是问题,但更重要的是,由于合并不像DVCS那样重要,因此合并也不那么灵活。“合并摩擦”会导致行为,这意味着“功能分支”模型开始崩溃。良好的合并需要成为所有VCS的功能,而不仅仅是DVCS。


根据这个现在有一个--record-only可用于解决开关--reintegrate问题,显然 V1.8选时自动做了复兴,它不会导致分支已经死了之后


据我了解,--reintegrate选项告诉svn在合并到功能分支时,您已经解决了冲突的更改。实际上,它已将合并历史记录中的所有主干修订版本都合并到了分支中,而不是将其视为补丁,而是使用分支版本覆盖了整个文件。
IMSoP 2014年

@IMSoP:可能,这是有道理的。但这并没有向我解释为什么有必要,或者为什么它使从该分支进一步合并成为不可能。对该选项在很大程度上也没有记录也没有帮助。
Paul S

我只是通过TortoiseSVN使用过它,在合并UI中总是对它进行了突出说明。我相信SVN 1.8会自动选择正确的策略,并且不需要单独的选项,但是我不知道他们是否已修复正常的合并算法来正确处理以这种方式重置的分支。
IMSoP 2014年

3

在Subversion 1.5之前(如果我没记错的话),Subversion的一个重大缺点是它不会记住合并历史记录。

让我们看一下VonC概述的情况:

- x - x - x (v2) - x - x - x (v2.1)
          |\
          |  x - A - x (v2-only)
           \
             x - B - x (wss)

注意修订版A和B。假设您将更改从“ wss”分支上的修订版A合并到修订版B上的“仅v2”分支(无论出于何种原因),但继续使用这两个分支。如果您尝试再次使用mercurial合并两个分支,则它将仅合并修订版A和B之后的更改。使用Subversion时,您将必须合并所有内容,就像以前没有进行过合并一样。

这是我个人经验的一个例子,由于代码量大,从B合并到A花费了几个小时:再次经历这真是一件痛苦的事情,而1.5版之前的Subversion就是这种情况。

Hginit在合并行为上的另一个可能更相关的区别:Subversion Re-education

想象一下,您和我正在处理一些代码,然后分支该代码,然后每个人进入各自的工作区,分别对该代码进行很多更改,因此它们之间的分歧很大。

当我们必须合并时,Subversion会尝试查看两个修订版本(我的修改后的代码和您的修改后的代码),并且尝试猜测如何将它们粉碎成一团糟。它通常会失败,并产生并非真正冲突的页面和“合并冲突”页面,只是在Subversion无法弄清楚我们所做的事情的地方。

相比之下,当我们在Mercurial中分别工作时,Mercurial忙于保持一系列变更集。因此,当我们想将代码合并在一起时,Mercurial实际上拥有更多的信息:它知道我们每个人所做的更改,并且可以重新应用这些更改,而不仅仅是查看最终产品并尝试猜测如何放置它一起。

简而言之,Mercurial分析差异的方法优于(颠覆)颠覆方法。


5
我读过hginit。太糟糕了,它没有显示出hg比svn更好的实际例子。.基本上,它告诉您“信任joel”,hg更好。他显示的简单示例也可能也可以用svn完成。实际上,这就是为什么我提出了这个问题。
stmax 2010年

1
基于这句话,天真的问题浮现在脑海:如果将Mercurial的合并算法放入Subversion会怎样?svn会和hg一样好吗?不可以,因为hg的优点在于组织高级,而不是合并文件中行的底层文本。这是我们svn用户需要了解的新颖想法。
2010年

@stmax:我明白你的意思了。但是,乔尔(Joel)或其他人的意见并不重要:一种技术比另一种技术(对于一组用例)要好。@DarenW和@stmax:从我个人的经验来看,Hg因其分布式操作(我一直都没有连接),性能(很多本地操作),非常直观的分支(由高级合并算法提供支持)而胜负, hg回滚,模板化日志输出,hg glog,单个.hg文件夹...我可以继续进行下去...除了git和bazaar之外的任何事情都感觉像是一件夹克。
Tomislav Nakic-Alfirevic,2010年

关于“变更集”的hg评论对我来说似乎是不准确的。SVN非常清楚自己正在合并的更改(更改集基本上是两个快照之间的差异,反之亦然,对吗?),并且可以根据需要依次应用每个快照。当然不必“猜测”任何东西。如果它造成“一个大的邪恶”,那就是一个实现问题,而不是设计的根本问题。在当前体系结构设计之上很难解决的主要问题是文件移动/复制/重命名。
IMSoP 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.