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

发布于 2024-11-07 09:01:51 字数 1371 浏览 0 评论 0原文

我正在使用 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

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

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

谢谢!

I'm using default branch for ongoing development, and now going to create a new named branch to mark a release. All further development will be on the default branch, all production bugfixes will be done on the new one (with subsequent merge to default), like this:

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

The above seems the way to go, however it seems easy to mess things up and merge the other way around (from default to aristotle which means all the new features will appear in the production branch).

Maybe my fears are groundless because one will notice the mess before pushing the commit to the central repo, but I'd like to see if it's possible to make the approach more foolproof.

So I started looking into hooks:

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

..but then realized it's not what I need, because this will not allow me to push aristotle changes at all.

So I'm not sure what to do. Ideally, I want developers to see the "wrong way merge" message when they attempt to commit a merge from default to aristotle locally (obviously, there should be a double-check on the central repo), while merging from production branch to the default one should be possible.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

好听的两个字的网名 2024-11-14 09:01:51

这应该可以做到。它使用 revset 查询 查找任何与 aristotle 的合并 来自默认

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

我最近需要自己解决这个问题,但也需要确保default 没有间接合并到我的发布分支中,即default ->功能->发布。

This should do it. It uses a revset query to find any merges into aristotle from default.

hg log -r 'children(p2(::aristotle and ::default and merge()) and branch(default)) and branch(aristotle)'
  • ::aristotle and ::default and merge() finds all merges that are ancestors of both aristotle and default branches
  • p2(...) and branch(default) grabs the second parent (incoming changeset) that are on the default branch.
  • children(...) and branch(aristotle) then grabs the actual merge changeset that is on the aristotle branch.

I recently needed to figure this out myself but also needed to ensure that default wasn't indirectly merged into my release branch, i.e. default -> feature -> release.

药祭#氼 2024-11-14 09:01:51

这几乎与几天前的问题完全相同确保分支之间的合并发生在一个方向

但我不喜欢我对这个问题的回答,那么这个怎么样:

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

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

然后您有一个仅包含亚里士多德的克隆,并且您不会意外地将其合并到该克隆中的默认值。

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

This is pretty much an exact duplicate of a question from a few days ago Ensuring a merge between branches happens in one direction

but I didn't like my answer to that one, so how about this one:

Keep separate clones for past releases. When, in your example above, you decide you need to do an emergency fix on aristotle do this instead:

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

then you have a clone with only aristotle in it and you can't accidentally merge it to default in that clone.

That said, it's still not a great answer. The only real consolation is that if you merge in a direction you don't like you can always re-clone the two heads and merge in the other direction.

无力看清 2024-11-14 09:01:51

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

我们有名为release-xyz的发布分支,并且默认正在进行开发。我们使用基于 https://www.mercurial-scm.org 中的示例 precommit_badbranch_badmerge 的挂钩/wiki/HookExamples

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

请注意,这仅执行简单的“立即”检查,它不会捕获有人从默认分支合并到发布分支的问题

。合并到默认值的策略必须与发布分支上的原始更改同时推送,这避免了其他人必须进行合并才能合并他们自己的更改 - 这强制我正在寻找解决方案请

注意,您可以使用下面的 server/central repo

[hooks]
pretxnchangegroup.prevent_bad_merges = python:<path_to_hook>\prevent_bad_merges.py:prevent_bad_merges

python hook 的 hooks 部分来调用它:

# 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

I found this question whilst looking for a solution to a related problem, I know its an old question, but thought I would share our solution.

We have release branches called release-x.y.z and ongoing development on default. We use a hook based on the example precommit_badbranch_badmerge found at https://www.mercurial-scm.org/wiki/HookExamples.

We basically extract the version number for each branch involved in any merge and make sure it is going the right way (default is treated as 9999.9999.9999 anything that isn't a release or default branch gets -1,-1,-1 (can be merged into anything).

Note that this only does a simple 'immediate' check, it doesn't catch issues where someone merges from default to the blah branch and then onto a release branch.

As a side note, we also have a policy that the merge to default must be pushed at the same time as the original change on the release branch, this avoids someone else having to do the merge in order to merge their own changes - it is enforcing that that I was looking for a solution for.

Note, you invoke this using the hooks section of the server/central repo

[hooks]
pretxnchangegroup.prevent_bad_merges = python:<path_to_hook>\prevent_bad_merges.py:prevent_bad_merges

python hook below:

# 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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文