首页 文章

合并:Hg / Git与SVN

提问于
浏览
141

我经常读到Hg(和Git和......)在合并方面比SVN更好但是我从未见过Hg / Git可以合并SVN失败的地方(或者SVN需要人工干预的地方)的实际例子 . 您可以发布一些分支/修改/提交/ ....-操作的逐步列表,显示SVN在Hg / Git愉快地移动时会失败的位置吗?实用,非常特殊的情况请...

一些背景:我们有几十个开发人员在使用SVN进行项目,每个项目(或一组类似项目)都在自己的存储库中 . 我们知道如何应用发布和功能分支,所以我们不会在那里,但我们已经学会克服"one programmer causing trauma to the whole team"的"one programmer causing trauma to the whole team"或"needing six developers for two weeks to reintegrate a branch") . 我们的发布分支非常稳定,仅用于应用错误修正 . 我们的中继线应该足够稳定,能够在一周内创建发布 . 我们还有一些开发人员或开发人员可以使用的功能分支 . 是的,它们在重新集成后被删除,因此它们不会使存储库混乱 . ;)

所以我仍然试图找到Hg / Git优于SVN的优势 . 我很想获得一些实践经验,但是还没有任何更大的项目我们可以转移到Hg / Git,所以我一直在玩那些只包含一些组成文件的小型人工项目 . 而且我正在寻找一些你可以感受到Hg / Git令人印象深刻的力量的案例,因为到目前为止我经常读到它们但却未能自己找到它们 .

6 回答

  • 17

    我自己并没有使用Subversion,但是从release notes for Subversion 1.5: Merge tracking (foundational)看起来,在Git或Mercurial这样的完整版本控制系统中,合并跟踪的工作方式有以下不同之处 .

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

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

    • 您需要为 svn logsvn blame 提供新的 -g--use-merge-history )选项以考虑合并跟踪 .

    在Git和Mercurial中,在显示历史记录(日志)和责备时会自动考虑合并跟踪 . 在Git中,您可以请求仅跟随第一个父母 --first-parent (我猜Mercurial也存在类似选项)到"discard"合并 git log 中的跟踪信息 .

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

    Subversion中的合并跟踪

    • "Known Issues"子部分表明重复/循环/反射合并可能无法正常工作 . 这意味着在以下历史记录中,第二次合并可能不会做正确的事情('A'可以是主干或分支,'B'可以分别是分支或主干):
    *---*---x---*---y---*---*---*---M2        <-- A
             \       \             /
              --*----M1---*---*---/           <-- B
    

    在上述ASCII艺术被破坏的情况下:分支'B'在修订版'x'处从分支'A'创建(分叉),然后分支'A'在修订版'y'处合并到分支'B'中合并'M1',最后将分支'B'合并为分支'A'作为合并'M2' .

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

    在上述ASCII艺术被破坏的情况下:分支'B'在修订版'x'处从分支'A'创建(分叉),它在'y'处合并为分支'A'作为'M1',稍后再次合并为分支'A'作为'M2' .

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

    Git在实践中使用“递归”合并策略处理这种情况 . 我不确定Mercurial .

    • 在"Known Issues"中,有警告说合并跟踪migh不能与文件重命名一起使用,例如当一方重命名文件(并可能修改它),第二方修改文件而不重命名(旧名称下) .

    在实践中,Git和Mercurial都处理了这样的情况:Git使用 rename detection ,Mercurial使用 rename tracking .

    HTH

  • 5

    我也一直在寻找一个案例,比如说,Subversion无法合并分支,而Mercurial(和Git,Bazaar,......)做对了 .

    SVN Book describes how renamed files are merged incorrectly . 这适用于Subversion 1.51.61.71.8!我试图重新创建以下情况:

    cd /tmp
    rm -rf svn-repo svn-checkout
    svnadmin create svn-repo
    svn checkout file:///tmp/svn-repo svn-checkout
    cd svn-checkout
    mkdir trunk branches
    echo 'Goodbye, World!' > trunk/hello.txt
    svn add trunk branches
    svn commit -m 'Initial import.'
    svn copy '^/trunk' '^/branches/rename' -m 'Create branch.'
    svn switch '^/trunk' .
    echo 'Hello, World!' > hello.txt
    svn commit -m 'Update on trunk.'
    svn switch '^/branches/rename' .
    svn rename hello.txt hello.en.txt
    svn commit -m 'Rename on branch.'
    svn switch '^/trunk' .
    svn merge --reintegrate '^/branches/rename'
    

    根据这本书,合并应该干净利落,但错误的数据重命名文件,因为 trunk 上的更新被遗忘了 . 相反,我得到了一个树冲突(这是与Subversion 1.6.17,在撰写本文时Debian中的最新版本):

    --- Merging differences between repository URLs into '.':
    A    hello.en.txt
       C hello.txt
    Summary of conflicts:
      Tree conflicts: 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 ):

    @  changeset:   2:6502899164cc
    |  tag:         tip
    |  parent:      0:d08bcebadd9e
    |  user:        Martin Geisler 
    |  date:        Thu Apr 01 12:29:19 2010 +0200
    |  summary:     Rename.
    |
    | o  changeset:   1:9d06fa155634
    |/   user:        Martin Geisler 
    |    date:        Thu Apr 01 12:29:18 2010 +0200
    |    summary:     Update.
    |
    o  changeset:   0:d08bcebadd9e
       user:        Martin Geisler 
       date:        Thu Apr 01 12:29:18 2010 +0200
       summary:     Initial import.
    

    合并的输出是:

    merging hello.en.txt and hello.txt to hello.en.txt
    0 files updated, 1 files merged, 0 files removed, 0 files unresolved
    (branch merge, don't forget to commit)
    

    换句话说:Mercurial从修订版1中获取了更改,并将其合并到修订版2( hello.en.txt )中的新文件名中 . 处理这种情况当然是必不可少的,以支持重构和重构,这正是你想要在分支上做的事情 .

  • 119

    如果不谈论通常的优势(离线提交,publication process,...),这里有一个"merge"例子我喜欢:

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

    如何只合并主分支上的两个功能之一?
    或者,您如何在自己的分支中隔离这两个功能?

    您可以尝试生成某种补丁,问题是您不再确定 functional dependencies 之间可能存在的:

    • 补丁中使用的提交(或SVN修订版)

    • 其他提交不是补丁的一部分

    我认为Git(以及Mercurial)建议使用rebase --onto选项来重组(重置分支的根)部分:

    来自Jefromi's post

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

    ,允许您:

    • 单独测试每个分支以检查所有内容是否按预期编译/工作

    • 仅合并您想要的主要内容 .


    我喜欢的另一个功能(影响合并)是能够squash commits(在尚未推送到另一个仓库的分支中)以呈现:

    • 更清洁的历史

    • 提交更连贯(而不是function1的commit1,function2的commit2,function1的commit3 ......)

    这确保合并更容易,冲突更少 .

  • 90

    我们最近从SVN迁移到GIT,并面临同样的不确定性 . 有很多轶事证据表明GIT更好,但很难找到任何例子 .

    我可以告诉你, GIT is A LOT BETTER 合并比SVN . 这显然是轶事,但有一个表可供遵循 .

    以下是我们发现的一些事情:

    • SVN曾经在GIT中似乎不应该发生很多树冲突 .

    • 虽然更好,但GIT要复杂得多 . 花一些时间进行培训 .

    • 我们习惯了Tortoise SVN,我们很喜欢 . Tortoise GIT不是那么好,这可能会让你失望 . 但是我现在使用GIT命令行,我更喜欢Tortoise SVN或任何GIT GUI .

    当我们评估GIT时,我们运行了以下测试 . 这些显示GIT在合并方面是赢家,但不是那么多 . 在实践中,差异要大得多,但我想我们还没有设法复制SVN处理不当的情况 .

    GIT vs SVN Merging Evaluation

  • 3

    其他人已经涵盖了更多的理论方面 . 也许我可以提供更实际的观点 .

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

    • 无法在 Baggage 箱上完成任何工作

    • 每个开发人员都可以创建自己的分支

    • 分支机构应在任务期间持续使用

    • 每个任务都应该拥有自己的分支

    • 合并回主干需要获得授权(通常通过bugzilla)

    • 有时需要高级别的控制时,合并可以由网守完成

    一般来说,它的工作原理 . SVN可以用于这样的流程,但它并不完美 . SVN的某些方面妨碍了人类的行为 . 这给了它一些消极方面 .

    • 我们遇到了一些问题,人们从低于 ^/trunk 的点分支 . 此垃圾合并整个树中的信息记录,并最终打破合并跟踪 . 虚假冲突开始出现,混乱占主导地位 .

    • 从树干到树枝的拾取变化相对简单向前 . svn merge 做你想要的 . 合并您的更改需要(我们被告知) --reintegrate 在merge命令上 . 我再次合并到主干 . 这意味着它是一个死分支,你必须创建一个新分支继续工作 . (见说明)

    • 在创建和删除分支时,通过URL在服务器上进行操作的整个过程确实让人感到困惑和恐慌 . 所以他们避免它 .

    • 在分支之间切换容易出错,留下树的一部分看着分支A,而留下另一部分看分支B.所以人们更喜欢在一个分支中完成所有工作 .

    往往会发生的是工程师在第1天创建分支 . 他开始工作并忘记了它 . 一段时间后,一位老板走了进来,询问他是否可以将他的工作释放到 Baggage 箱 . 工程师一直担心这一天因为重新融合意味着:

    • 将他长期存在的分支合并回主干并解决所有冲突,并释放应该在单独分支中的不相关代码,但事实并非如此 .

    • 删除他的分支

    • 创建一个新分支

    • 将其工作副本切换到新分支

    ......而且因为工程师尽可能少地做到这一点,他们不记得每一步的“神奇咒语” . 错误的交换机和URL发生了,突然间它们变得一团糟,他们就会得到“专家” .

    最终一切都安定下来,人们学习如何处理缺点,但每个新的首发都会遇到同样的问题 . 最终的现实(与我在他开始时提出的相反)是:

    • 在干线上没有工作

    • 每个开发人员都有一个主要分支

    • 分支持续到需要释放工作

    • Ticketed bug修复往往会得到自己的分支

    • 授权后合并回主干

    ...但...

    • 有时工作使它成为主干,因为它不应该与其他东西在同一个分支中 .

    • 人们避免所有合并(即使是简单的东西),所以人们经常在他们自己的小泡泡中工作

    • 大合并往往会发生,并造成有限的混乱 .

    值得庆幸的是,团队足够小,可以应付,但不会扩大规模 . 事实上,这并不是CVCS的问题,但更多的是因为合并不如DVCS那么重要,因此它们不是那么光滑 . “合并摩擦”导致行为,这意味着“特征分支”模型开始崩溃 . 良好的合并需要成为所有VCS的功能,而不仅仅是DVCS .


    根据this,现在有一个 --record-only 开关可用于解决 --reintegrate 问题,而apparently v1.8选择何时自动重新整合,并且它不会导致分支死亡

  • 6

    在颠覆1.5之前(如果我没有记错的话),颠覆有一个显着的缺点,因为它不会记住合并历史 .

    让我们来看看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的情况 .

    另一个可能是Hginit: Subversion Re-education的合并行为更相关的差异:

    想象一下,你和我正在研究一些代码,我们分支这些代码,然后我们分别进入我们各自的工作区,分别对这些代码进行大量的修改,所以它们有很多分歧 . 当我们必须合并时,Subversion会尝试查看两个版本 - 我的修改后的代码和修改后的代码 - 它会尝试猜测如何在一个大的邪恶混乱中将它们粉碎在一起 . 它通常会失败,产生并非真正冲突的“合并冲突”的页面和页面,只是Subversion无法弄清楚我们做了什么的地方 . 相比之下,当我们在Mercurial中单独工作时,Mercurial正忙着保留一系列变更集 . 因此,当我们想要将我们的代码合并在一起时,Mercurial实际上拥有更多信息:它知道我们每个人都改变了什么并且可以重新应用这些更改,而不仅仅是查看最终产品并尝试猜测如何把它放在一起 .

    简而言之,Mercurial分析差异的方式(优于?)优于颠覆 .

相关问题