【问题标题】:Can I do a better job with Mercurial branching and merging than I'm currently doing or can mercurial do better than it does in this sample test-case?我可以在 Mercurial 分支和合并方面做得比我现在做的更好,还是 mercurial 可以比在这个示例测试用例中做得更好?
【发布时间】:2012-11-20 21:11:51
【问题描述】:

在商业开发的版本控制中,一个项目通常有多个分支。在我的 mercurial 管理的项目中,我经常有一个稳定版本和一个主干版本,它们来自一些共同的父级,如下所示:

      V8     (the parent of both V8-stable and V9, revisions 1..100)
       |
       +---  V8_stable  (bug fix branch, revisions 101 to 500 )
       |
       +---  V9_trunk   (trunk, revisions 501 to 1000 )

以上所有版本的项目都有一个文件,我们称之为helloworldapp.py。这些示例使用 Python,因为它是我所知道的最类似于伪代码的语言,但问题不是 Python 问题。现在,在 mercurial 中,您可以使用克隆进行分支,也可以使用 Mercurial 的内置分支功能。在此示例中,我使用整个存储库的克隆进行分支。

这里是 V8 helloworldapp.py,它是公共父版本:

# helloworldapp.py rev 100 (v8)
def func1(a,b,c,d,e):
    print "stuff"
def func2(a,b,c,d,e):
    func1(a,b,c,d,e)
def func3(a,b,c,d,e):
    func2(a,b,c,d,e)
def func4(a,b,c,d,e):
    func3(a,b,c,d,e)
def func5(a,b,c,d,e):
    func4(a,b,c,d,e)
def func6(a,b,c,d,e):
    func5(a,b,c,d,e)
def func7(a,b,c,d,e):
    func6(a,b,c,d,e)

func7(1,2,3,4,5)

这是修订版 500,V8 稳定分支的一部分:

# helloworldapp.py rev 500 (v8_stable)
def func1(a,b,c,d,e):
    print "stuff"

def morestuff():
   print "morestuff"

morestuff()
func1(1,2,3,4,5)

这是 V9 主干分支的修订版 1000 部分:

# helloworldapp.py rev 1000 (v9_trunk)
def func1(a,b,c,d,e):
    print "stuff"

def func4(a,b,c,d,e):
    morestuff()
    morestuff()
    morestuff()
    func1(a,b,c,d,e)

def morestuff():
   print "hello"

func4(1,2,3,4,5)

以上文件是分支不同步时发生的情况的非常简短的示例,在面向文件的版本控制中,当我必须管理“稳定到主干”合并时,这给我带来了困难(通过拉取然后合并)。

这是您在合并时最终遇到的情况。请注意,在现实世界中,对我来说,这些问题通常会放大 100 倍到 1000 倍。我经常在数百个文件中遇到 5000 多行 Hg merge 无法解决的冲突,这需要我在 KDiff3 中手动合并每一个。

除了不让分支永远不同步之外,Mercurial 用户还能做些什么来让这种情况下的合并更容易?在我上面说明的情况下,“在文件的分支 A 的第 6 行工作”与“在文件的分支 B 的第 6 行工作”相同,并且逐行合并会产生合并冲突,其中两个用户实际上(在他们自己的脑海中)在一个文件中的不同功能上工作。通过从一个文件中删除不再使用的函数的行为,因为不再需要它,您现在几乎可以确保更多的合并冲突机会。您几乎必须制定诸如“永远不要在上游稳定分支中移动或删除行”之类的规则。我知道,如果您使用的是 Smalltalk 基于图像的版本控制系统,“它在哪一行”的意外将不再进入它。如果 Mercurial 使用的 diff-tool 理解 Python,它可以在 Python 代码上做得更好,但代价是让它在 C# 代码上可能根本无法正常工作。因此,这是基于文件的编程和 DVCS 工具的最新技术。很好。

因此,在此处所示的合并情况下,

(click here for larger version)

* 我看到的是合并冲突,我希望它可以找出我想要全部或不想要“更多”功能:

我希望得到什么(自动):

我在 bitbucket 上发布了示例存储库: helloworldapp_v9 helloworldapp_v8_stable

要尝试这个“最简单的示例,我可以构建当您尝试如何解决合并冲突时会发生什么”,克隆上述其中一个,然后从另一个中提取,然后合并。最后我的问题是:Mercurial 的专业用户如何处理这种情况?理解这个例子是人为的,但显示了 DVCS 工具的用户遇到的一些复杂性,但在现实世界中,这些冲突通常比我在这里展示的简单案例复杂得多。我已经学会了使用 TortoiseHG 和 KDIFF3 相当快地处理大多数小而简单的冲突,但我发现合并地狱比我记得的使用 Subversion 或 Perforce 的问题要小得多,但我仍然不认为那(a)程序员(我)或(b)我的工具已经做了所有可以帮助管理和理解这种复杂性的事情。我有一种感觉,我还没有尝试或了解过使用 Mercurial 的替代解决方案、工具和工作策略。也许 MQ 和 Rebase?

【问题讨论】:

    标签: mercurial branching-and-merging


    【解决方案1】:

    实际上,根据我的经验,现实世界通常不是那样的,原因有两个:

    1. 我们更频繁地合并(在您的示例中从 v8 到 v9),以便 Mercurial 逐渐了解两个分支之间的事物如何组合在一起。 500 个变更集是一步完成的大量变更(我以前也这样做过!)。

    2. 在合并我们的分支时,我们的更改不会像您的示例那样残酷地发生冲突。由于每次更改的冲突更少,上下文更多,合并工具在将它们组合在一起方面做得更好。

    我们确实有碰撞,需要弄清楚如何手动将它们放在一起,如果我们推迟它可能会很头疼。但是,一旦我们这样做了,现在共同的祖先就是合并,我们不需要再次重新访问该碰撞。通过更频繁地合并,您可以减少自共同祖先以来变更集的数量。

    我有兴趣通过从 V8_stable 到 V9_trunk 的定期合并来重新运行您的示例。如果您在函数 defs 之间添加一些文本以便为 hg 提供更多上下文以区分更改,我也会感兴趣。


    尝试与您的示例进行更频繁的合并,但效果不佳。如果您的版本差异很大(只有 15%-25% 相同),它们就会成为根本不同的代码库,合并不再有意义。那时,我尝试将这些部分分解为单独的文件,以允许两个版本(以及我的理智!)和平共存。

    【讨论】:

    • 这几乎就像我们需要一个 .hgnomerge 文件来与 .hgignore 一起使用。因为重命名文件只是临时工作。当分支不同时,版本控制系统应该停止尝试合并某些文件。
    • 有一些事情需要处理 - 尝试阅读 hg help mergetools 以及 hg help hgrcmerge-patternsmerge-tools 部分。可用的工具之一是internal:fail,如果文件需要合并,它就会失败,并留给用户进行合并。另一个允许您选择要保留的文件,而其他人则说始终保留本地文件或始终保留其他文件。您还可以配置一个始终显示 GUI 而不是自己尝试的合并工具。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-31
    相关资源
    最近更新 更多