Mercurial:允许从发布分支合并到默认分支,但反之亦然

时间:2011-05-16 14:43:10

标签: mercurial

我正在使用default分支进行持续开发,现在将创建一个新的命名分支来标记版本。所有进一步的开发将在默认分支上,所有生产错误修正将在新的上进行(随后合并到default),如下所示:

#>hg branches
   aristotle   42:dbd...
   default     41:da5...
#>hg branch
   default
#>echo "Feature #1 for the next release" >> feature1.txt
#>hg add
#>hg commit -m "Implement feature #1 for the next release" 

...... eek, need to make an urgent fix on Production .....

#>hg update aristotle
#>echo "Fixed urgent bug #123 on Production" >> fix123.txt
#>hg add
#>hg commit -m "Fixed bug #123 on Production" 
   created new head
#>hg update default
#>hg merge aristotle
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, dont forget to commit)
#>hg commit -m "Merge in the fix for bug #123"
#>hg push

以上似乎是要走的路,然而似乎很容易搞砸事情并以相反的方式合并(从defaultaristotle这意味着所有新的功能将出现在生产分支中。)

也许我的恐惧是毫无根据的,因为人们会在将提交内容推送到中央回购之前注意到混乱,但我想看看是否有可能使这种方法变得更加万无一失。

所以我开始研究钩子:

[hooks]
pretxnchangegroup.branch = hg heads --template "{branches} " | find "aristotle" && exit 1 || exit 0

..但后来意识到这不是我需要的,因为这根本不允许我推动亚里士多德的改变。

所以我不知道该怎么做。理想情况下,我希望开发人员在尝试从default提交到aristotle 本地的合并时看到“错误的合并方式”消息(显然,应该有一个双重 - 检查中央仓库),同时应该可以从生产分支合并到默认分支。

谢谢!

3 个答案:

答案 0 :(得分:1)

这应该这样做。它使用revset queryaristotle中找到default的任何合并。

hg log -r 'children(p2(::aristotle and ::default and merge()) and branch(default)) and branch(aristotle)'
  • ::aristotle and ::default and merge()查找作为aristotledefault分支的祖先的所有合并
  • p2(...) and branch(default)抓取default分支上的第二个父级(传入变更集)。
  • children(...) and branch(aristotle)然后抓取aristotle分支上的实际合并变更集。

I recently needed to figure this out myself但还需要确保default间接合并到我的发布分支中,即默认 - >功能 - >释放。

答案 1 :(得分:0)

这与几天前的问题Ensuring a merge between branches happens in one direction

完全相同

但是我不喜欢我对那个的答案,那么这个怎么样:

为过去的版本保留单独的克隆。在上面的示例中,当您决定需要对亚里士多德进行紧急修复时,请执行以下操作:

cd ..
hg clone -r aristotle myrepo myrepo-aristotle

然后你有一个只有亚里士多德的克隆,你不能不小心将它合并到那个克隆中的默认值。

那就是说,它仍然不是一个好的答案。唯一真正的安慰是,如果你在一个你不喜欢的方向上合并,你总是可以重新克隆两个头并合并到另一个方向。

答案 2 :(得分:0)

我在寻找相关问题的解决方案时发现了这个问题,我知道这是一个老问题,但我想我会分享我们的解决方案。

我们发布了名为release-x.y.z的分支和默认的持续开发。我们使用基于https://www.mercurial-scm.org/wiki/HookExamples上的示例precommit_badbranch_badmerge的钩子。

我们基本上提取任何合并中涉及的每个分支的版本号,并确保它以正确的方式(默认被视为9999.9999.9999任何不是发布或默认分支获得-1,-1, -1(可以合并为任何东西)。

请注意,这只是一个简单的“立即”检查,它不会捕获有人从默认值转换为blah分支然后合并到发布分支的问题。

作为旁注,我们还有一个策略,即默认合并必须与发布分支上的原始更改同时推送,这样可以避免其他人必须进行合并才能合并自己的更改 - 这是强制我正在寻找解决方案。

请注意,您可以使用server / central repo

的hooks部分调用它
[hooks]
pretxnchangegroup.prevent_bad_merges = python:<path_to_hook>\prevent_bad_merges.py:prevent_bad_merges

下面的python hook:

# based on precommit_badbranch_badmerge
# at https://www.mercurial-scm.org/wiki/HookExamples
import re

# this isnt a proper compare, it will just return numbers > 0 if source is after target
def compare_versions(source_version, target_version):
    # if either side is -1, ignore it
    if (source_version[0] == -1) or (target_version[0] == -1):
        return 0;

    if source_version[0] > target_version[0]:
        return 1
    elif source_version[0] == target_version[0]:
        if source_version[1] > target_version[1]:
            return 2
        elif source_version[1] == target_version[1]:
            if source_version[2] > target_version[2]:
                return 3        
    return 0

def get_version(branch):
    if branch == 'default':
        major=9999
        minor=9999
        revision=9999        
    else:    
        # note python uses ?P for named matches
        match = re.match('(release-(?P<major>\d)\.(?P<minor>\d)\.(?P<revision>\d))', branch)
        if match:
            major = int(match.group('major'))
            minor = int(match.group('minor'))
            revision = int(match.group('revision'))
        else:
            major = -1
            minor = -1
            revision = -1

    return [major,minor,revision]

def prevent_bad_merges(ui, repo, node, **kwargs):
    ui.debug("in hook\n")
    for rev in xrange(repo[node].rev(), len(repo)):
        ui.debug("in loop\n")
        # get context (change)
        ctx = repo[rev]
        ui.debug("got ctx\n")
        if len(ctx.parents()) > 1:
            ui.debug("got a merge\n")
            branch = ctx.branch()
            ui.debug(branch +"\n")
            parent1 = ctx.parents()[0]
            ui.debug("got parent1\n")
            parent2 = ctx.parents()[1]
            ui.debug("got parent2\n")

            target_branch = repo[parent1.node()].branch()
            ui.debug("got parent1 branch\n")
            target_version = get_version(target_branch)
            ui.debug("got parent1 version\n")

            source_branch = repo[parent2.node()].branch()
            ui.debug("got parent2 branch\n")
            source_version = get_version(source_branch)
            ui.debug("got parent2 version\n")

            # This could happen if someone does
            #   hg update 1.1-branch
            #   hg branch 1.2-branch
            #   hg merge 1.0-branch
            # which is a strange thing to do.  So disallow it.
            if target_branch != branch:
                ui.warn('PREVENT BAD MERGE HOOK FAILED : \n'
                        'merging to a different branch from first parent '
                        'is just weird: please don\'t do that\n')
                return True

            ui.debug(source_branch, "\n")
            ui.debug(str(source_version[0]), "\n")
            #ui.debug("major:", source_version[0], "\n")
            #ui.debug("minor:", source_version[1], "\n")
            #ui.debug("revn :", source_version[2], "\n")

            ui.debug(target_branch, "\n")
            ui.debug(str(target_version[0]), "\n")
            #ui.debug("major:", target_version[0], "\n")
            #ui.debug("minor:", target_version[1], "\n")
            #ui.debug("revn :", target_version[2], "\n")

            # Check for backwards merge.
            if compare_versions(source_version, target_version) > 0:
                ui.warn('PREVENT BAD MERGE HOOK FAILED : \n'
                        'invalid backwards merge from %r to %r\n'
                        % (source_branch, target_branch))
                return True
        else:
            ui.debug("Not a merge\n")
            # Not merging: nothing more to check.


    return False