【问题标题】:How to manage concurrent development with mercurial?如何使用 mercurial 管理并发开发?
【发布时间】:2011-04-12 18:03:25
【问题描述】:

这是一个最佳实践问题,我希望答案是“视情况而定”。我只是希望了解更多真实世界的场景和工作流程。

首先,我说的是同一个项目的不同变化,所以请不要使用 subrepo。

假设您的代码库位于 hg 存储库中。您开始研究一个复杂的新功能 A,然后您信任的测试人员报告了一个复杂的错误 B(您有测试人员,对吗?)。

如果(修复)B 依赖于 A,这很简单。您只需 ci A 然后 ci B。

我的问题是当他们独立时该怎么办(或者至少现在看起来如此)。

我可以想到以下几种方式:

  1. 为 B 使用单独的克隆。
  2. 在同一存储库中使用匿名或命名分支或书签。
  3. 使用 MQ(B 补丁位于 A 之上)。
  4. 使用分支 MQ(我稍后会解释)。
  5. 使用多个 MQ(自 1.6 起)

1 和 2 由 @Steve Losh 的 excellent blog 覆盖,从 slightly related question 链接。

与其他选择相比,1 的一个巨大优势是,当您从一件事切换到另一件事时,它不需要任何重建,因为文件在物理上是分开且独立的。因此,例如,如果 A 和/或 B 涉及定义三态布尔值并被数千个 C 文件包含的头文件(不要告诉我你没有见过这样的遗留代码),那么它确实是唯一的选择基础)。

3 可能是最简单的(就设置和开销而言),如果 B 是一个小和/或紧急修复,您可以颠倒 A 和 B 的顺序。但是,如果 A 和 B 接触相同的文件,它会变得很棘手。如果 A 和 B 更改在同一个文件中是正交的,则修复无法应用的补丁块很容易,但从概念上讲它仍然有点风险。

4 可能会让你头晕目眩,但它是最强大、最灵活和可扩展的方式。我默认 hg qinit-c 因为我想标记正在进行的补丁并推送/拉取它们,但确实需要一个概念上的飞跃才能意识到您也可以在 MQ 存储库中分支。以下是步骤(mq = hg --mq):

  1. hg qnew bugA;对 A 进行更改; hg qref
  2. mq branch branchA; hg qci
  3. hg qpop; mq up -rtip^
  4. hg qnew bugB;对 B 进行更改; hg qref
  5. mq branch branchB; hg qci
  6. 再次处理 A:hg qpop; mq up branchA; hg qpush

采取这么多步骤似乎很疯狂,每当你需要换工作时,你必须hg qci; hg qpop; mq up <branch>; hg qpush。但是考虑一下:您在同一个存储库中有多个命名的发布分支,并且您需要同时处理多个项目并为所有这些分支进行错误修复(您最好获得此类工作的保证奖金)。使用其他方法你很快就会迷路。

现在我的汞爱好者,还有其他/更好的选择吗?


(更新)qqueue 几乎使 #4 过时了。请参阅 Steve Losh 的优雅描述 here

【问题讨论】:

  • 好像不是很明显,我要求一种基于 hg 的方法。但是,如果您可以从另一个适用于所有情况的 SCM 给我一个命令,那么我将放弃 hg。把它当作一个挑战吧,git 爱好者。
  • 我认为您需要更好地说明您的问题。为什么这不适用于所有情况:修复错误 A,提交补丁,修复错误 B,提交补丁?
  • 对不起,我有点啰嗦,Spolsky-ishly。我提到了一个简单的案例:“如果 B 是一个小的和/或紧急的修复”。更现实的情况是 A 和 B 都是需要大量提交和迭代的长期项目。另外,如果您有像我公司那样的代码审查流程,您可能已经完成了 A 并将其发送给审查,您不能坐在那里摆弄拇指等待审查者,所以您必须同时去接 B .但是随后审阅者可能会破坏您的代码,您必须进行更改以取悦他/她。所以一路上可能会有无数的项目切换。
  • 我刚刚看到一个答案 (stackoverflow.com/questions/970669/…) 涵盖了 1 和 4 以获得更详细的示例。
  • 好吧,看来您只是在独自完成这个项目。如果你在一个团队中工作,你可能永远不会问这个问题,因为并行开发将是你简单地做的事情,并且从容应对它的相对成本。在进行并行开发时,最佳实践显然是为每个功能指定一个命名分支,然后在功能或错误完成后合并到主干,根据需要从主干到您的分支进行尽可能多的合并,以在期间保持其他更改的最新状态此功能的开发。在运行测试之前不合并。

标签: mercurial workflow dvcs mercurial-queue


【解决方案1】:

我总是使用命名分支,因为这让 Mercurial 可以完成它的工作:保留您的项目历史记录,并记住您为什么以什么顺序对源代码进行了哪些更改。至少考虑到我的工作方式,在磁盘上放置一个还是两个克隆通常很容易:

  1. 您的项目是否缺少构建过程,以便您可以直接从源代码进行测试和运行?然后我会很想只有一个克隆,当我需要在另一个分支上工作时来回hg up

  2. 1234563很大的痛苦,尤其是在涉及设置示例数据库之类的事情时。在那种情况下,我肯定会使用两个克隆,一个坐在树干的顶端,一个坐在紧急功能分支的顶端。

【讨论】:

  • 我投了赞成票,但没有接受,因为我自己提到了命名分支,我在寻求替代方案 :) 我们确实有一个构建过程(不是很现代,就像代码库本身一样),所以#2确实是使用两个克隆的论点。但是,在两个克隆之间切换调试工作流程可能会很痛苦,例如,您需要在 Eclipse 中进行两个启动配置。
  • 作为命名分支的替代品,你可以给它们编号 :) 换句话说,没有好的替代品,轮子已经发明了。
【解决方案2】:

所以问题是,当您被告知停止开发功能 A 并开始独立功能 B 时,还有哪些替代选项:如何使用 mercurial 管理并发开发?

让我们看看删除并发的问题,就像编写线程代码一样——定义一个简单的工作流来解决给你的任何问题,并将其应用于每个问题。一旦完成,Mercurial 将加入这项工作。因此,程序员 A 将处理功能 A。程序员 B 将处理功能 B。两者都恰好是你。 (要是我们有多核大脑就好了:)

我总是使用命名分支,因为这让 Mercurial 可以完成它的工作:保留您的项目历史记录,并记住您为什么以什么顺序对源代码进行了哪些更改。

我同意 Brandon 的观点,但我想知道他是否忽略了功能 A 尚未经过测试?在最坏的情况下,代码可以编译并通过单元测试,但有些方法实现了以前的要求,有些方法实现了新的要求。 与以前签入的差异是我会使用的工具帮助我回到功能 A 的正轨。

您的功能 A 的代码是否在您通常会签入的时候? 从功能 A 切换到功能 B 并不是将代码提交到头部或分支的理由.只签入编译并通过测试的代码。 我的理由是,如果程序员 C 需要开始功能 C,那么重新检查这个分支不再是最好的开始。 保持你的分支头部健康,意味着你可以快速响应,更可靠错误修复。

目标是让您的(经过测试和验证的)代码运行,因此您希望所有代码最终都合并到(您的开发和遗留分支的)头部。我的观点似乎是,我看到分支使用效率低下:代码变得陈旧然后不使用,合并变得比原始问题更难。

只有您的选项 1 对我有意义。一般来说:

  1. 您应该在其他人看到之前认为您的代码有效。
  2. 优先考虑头部而不是分支。
  3. 如果其他人解决了问题,请分支并签到。
  4. 如果您的自动化系统或测试人员只需要您的代码,则进行分支。
  5. 如果您是团队的一员,则在解决问题时进行分支。将其视为头部,参见 1-4。

除了配置文件,构建过程应该是一个检查和一个构建命令。在克隆之间切换应该不会比新程序员加入项目更困难。 (我承认我的项目需要在这里做一些工作。)

【讨论】:

  • 感谢您的回答,但您似乎是在 svn 等传统(集中式)VCS 的上下文中解决它。在 hg(或任何 DVCS)中,您的提交是本地的,直到它们被推/拉,所以在测试之前提交是非常安全的(并且推荐)。
  • 我主要使用svn,所以我确信它会影响我的观点。在 hg 中,推送后,日志是否显示推送提交和本地提交之间的差异?你用 github 吗?这很棒,但很难确定要克隆哪个存储库 - 哪个 fork 包含您需要的错误修复,或者哪个是活动的还是放弃的。回到分支,我可以看到有多少提交点对作者有帮助。我认为您是在说 hg 保护其他开发人员免于看到临时提交,因为它们没有被推送。我认为集成测试可能不是真的。
  • 抱歉,我没有注意到您的评论。一旦提交(更准确地说,变更集)被推送,它们就不再是本地的了。日志不显示是否已推送变更集。 outgoing 命令告诉你它是否没有。本地提交没有“保护”。任何人都可以随意拉动它们。
【解决方案3】:

似乎没有比我在问题中列出的选择更多或更好的选择。所以他们又来了。

  1. 每个项目使用一个克隆。
    • 优点:完全分离,因此在切换项目时无需重新构建。
    • 缺点:工具链需要在两个克隆之间切换。
  2. 在同一存储库中使用匿名或命名分支或书签。
    • 优点:标准 hg(或任何 DVCS)实践;干净整洁。
    • 缺点:必须在切换之前提交并在之后重建。
  3. 在每个项目中使用带有一个补丁(或多个连续补丁)的 MQ。
    • 优点:简单易行。
    • 缺点:切换前必须qrefresh,后重建;如果项目不是正交的,则会很棘手且有风险。
  4. 每个项目使用一个 MQ 分支(或 1.6+ 中的 qqueue)。
    • 优点:超灵活和可扩展(针对并发项目的数量)
    • 缺点:切换前必须qrefreshqcommit后重建;感觉很复杂。

与往常一样,没有灵丹妙药,因此请挑选适合工作的那个。


(更新)对于喜欢 MQ 的任何人,在常规分支 (#2 + #3) 之上使用 MQ 可能是最常见和更可取的做法。

如果您在两个分支上有两个具有基线的并发项目(例如下一个版本和当前版本),那么像这样在它们之间跳转是微不足道的:

hg qnew; {coding}; hg qrefresh; {repeat}
hg qfinish -a
hg update -r <branch/bookmark/rev>
hg qimport -r <rev>; {repeat}

对于最后一步,qimport 应该添加一个-a 选项来一次导入一行变更集。我希望Meister Geisler 注意到这一点:)

【讨论】:

    猜你喜欢
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    • 2010-11-10
    • 2012-08-20
    • 1970-01-01
    • 1970-01-01
    • 2016-11-21
    • 1970-01-01
    相关资源
    最近更新 更多