【问题标题】:TFS: Merge best practicesTFS:合并最佳实践
【发布时间】:2009-12-07 15:34:47
【问题描述】:

我们有一个标准的分支架构,每个团队都有一个开发分支,一个 公共集成分支(所有开发分支都从这里分支)和生产分支从集成分支。

在开发阶段,我对开发分支进行了大量提交。在阶段结束时,我将我的更改合并到集成中,然后再合并到生产中。

单独合并每个提交是否有意义,复制原始提交描述 并链接到原始任务?另一种选择当然是通过一次合并操作一次合并所有提交。我的问题的原因是第一种方法需要很多时间。我在 TFS 中看不到任何将合并到其他分支与原始提交链接的自动化工具。

我想听听您对最佳做法的意见。

【问题讨论】:

    标签: version-control tfs


    【解决方案1】:
    1. 来自 Dev* 的合并 -> 集成和集成 -> 生产应始终是“复制”合并。这是保持下游分支稳定性的最安全方法。
      1. 首先在另一个方向合并(例如集成 -> Dev2)以获取目标分支的最新更改。
      2. 如果存在冲突,请根据具体情况处理差异。 AcceptMerge(自动或手动)通常是所需的结果,但有时您可能希望保留一个或另一个分支的副本不变。
      3. 使用源分支(在我们的例子中是 Dev #2)来完全整合、响应和稳定这些变化。
      4. 按所需方向合并(例如 Dev2 -> 集成)。以 AcceptTheirs [又名“从源代码复制”] 的形式解决所有冲突。
      5. 确保在步骤#1-4 之间目标分支没有变化。如果 Dev 分支像它应该的那样尽早且经常地接受合并,那么在这个希望很短的过程中锁定目标分支不应该是繁重的。如果您出于某种原因预期死亡的“大爆炸”合并,那么锁定很有可能会阻止其他团队并行执行相同的操作,因此您可能必须重复执行步骤 #1-4,直到您准备好.
    2. 尽可能“赶上”合并。也就是说,按照它们签入的顺序合并事物。如果变更集 10、20 和 30 是从 A -> B 合并的候选者,那么这些合并范围中的任何一个都是“赶上”:10~10、10 ~20、10~30。任何跳过#10 的变更集范围都被称为“樱桃选择”。一旦你开始采摘樱桃,你就会遇到一些危险:
      1. 您不能使用上述merge down, copy up 模型。仅凭这一点,劳拉·温格德就会说你正在跳过路边。
      2. 如果在您的范围内触及的任何文件之前也被触及,则您必须执行 3 路内容合并,以便仅传播精心挑选的差异。没有任何差异工具是完美的;您正在增加一个非零风险,即带来比预期更多的代码、意外覆盖目标中所做的更改、在两个分支分歧的地方引入逻辑错误等。
      3. 您正在提升到本应更稳定的分支中的一组更改代表以前从未构建或测试过的配置。您可以对目标分支的最终状态做出正确的猜测。 “我正在合并影响模块 Foo 的所有更改,并且我在 Dev 中测试了新版本的 Foo,所以这就是 Foo 在集成中的行为方式,对吧?”当然...也许...如果您可以跟踪头脑中的每个依赖项(包括在测试 Dev 时可能在集成中发生变化的所有内容)。但您的 SCM 工具链绝不会知道或验证这些猜测。
      4. 特别是在 TFS 中,涉及命名空间更改的樱桃采摘只是要求被烧毁。如果您的版本范围和/或路径范围不包括重命名的来源,它将作为分支出现。如果您排除目标,它将挂起删除。如果您的路径范围不包括取消删除的根目录,您将得到神秘的错误。如果您的范围跨越取消删除和重新删除之间的时间,即使您不包含取消删除本身,您也会在目标中出现“幻像”文件。如果您将 Move 与您的所有路径和版本范围正确合并,但这样做是无序的,那么即使在所有候选变更集都已用尽之后,最终目标名称也可能与源名称不同。我敢肯定,目前还没有想到这个组合会出错的更多方法……相信我。
    3. 在合并之前始终在目标分支上执行 Get。终极安全的高级版本:将您将要合并的工作区同步到位于或靠近 Tip 的特定变更集编号,然后也 [catch-up] 合并到相同的变更集。这样做可以避免一些潜在的问题:
      1. 合并到陈旧的代码中,产生令人困惑的三向差异,这些差异似乎删除您在 Tip 中看到的更改。 [你最终会在 Checkin + Resolve 上让它们回来,但是当你可以避免两个风险差异时,没有理由去经历两个风险差异]
      2. 必须通过冲突解决过程两次:一次在合并,一次在签入。在一般情况下无法避免这种情况,但大多数情况下,与您在工作区中可能数天或数周后遇到的更改数相比,合并 + 解决时同时进行的更改数很少日期。
    4. 除非您真的知道自己在做什么,否则不要按标签(或工作区)进行合并。让我们回顾一下 TFS 标签提供的功能,然后剖析为什么每个标签都不适合安全和一致的合并。
      1. 标签可以代表多个时间点。如果标签代表 VCS 的一致快照 - 并且始终如此 - 那么它在日期或变更集上没有技术优势。不幸的是,很难判断一个标签是否随着时间的推移实际上是一致的。如果不是,按标签合并可能会导致:
        1. 如果范围以一个标签开始,该标签指向一个项目 @ 早于其第一个候选者的时间
        2. 无意排除,如果范围以一个标签开始
        3. 无意排除,如果范围以一个标签结束,该标签指向范围开始之前的某个时间@某个项目
      2. 标签版本规范代表一组特定的项目。它们可用于故意排除纯递归查询会看到的文件和文件夹。此功能也不适合 Merge 操作。 (同样,如果您不需要需要此功能,那么您将承担以下风险,而不会获得任何日期和变更集。)
        1. 标签中不存在的项目将被简单地忽略,而不是合并为挂起的删除。与到目前为止所涵盖的一些边缘案例不同,这是一个很可能在主流场景中发生但大多数人都错过的大问题。 [因此,TFS 2010 增加了对标签内已删除项目的支持。]
        2. 无意中摘樱桃,如果您将已存在一段时间但由于上述副作用之一而被排除在先前合并之外的项目添加到标签中。
        3. 故意采摘樱桃。此功能为 Merge 带来的全部优势是违反了我们的准则之一,因此显然这根本不是一个好的理由。此外,它会导致 file 级别的樱桃采摘,这比变更集的“普通”樱桃采摘更加危险。
      3. 标签具有友好的可自定义名称、所有者和 cmets。因此,与日期/变更集相比,我们有纯粹的可用性差异;没有技术优势。但即使在这里,它也不像看起来那么有吸引力。 TFS 在 UI 中实际显示标签并没有做太多工作,而您可以在各处看到变更集 cmets。所有者查询速度很快(服务器端),但大多数其他搜索速度很慢(客户端),除非您知道确切的标签名称。管理设施几乎不存在。没有变更日志或审计,只有时间戳。总而言之,这些都不是放弃变更集提供的保证的理由。
    5. 总是一次合并整个分支。合并文件或子树有时很诱人,但最终只是换了个新花样。
    6. 提前计划。不幸的是,在 TFS 中重新创建分支是一个痛苦的话题。有时很艰巨,有时只有几步,但从不明显;没有内置命令(直到 2010 年)。在 2005/2008 年实现它需要非常深入地了解您当前的分支结构、所需的结构以及如何滥用各种 TF 命令的副作用。
    7. 不要在分支内部创建分支。例如,有时建议将分支和合并作为维护松散耦合项目之间的通用模块或二进制文件的一种方式。我不认为这是一个很好的建议——让你的构建系统正确地完成它的主要工作,而不是硬塞你的源代码控制系统去做一些它不是真正设计要做的事情。无论如何,这种“共享”策略与为了 SCM 目的而存在于更广泛的分支层次结构中的项目本身发生了可怕的冲突。如果您不小心,TFS 会很乐意让您在版本控制项之间创建任意多对多的分支关系。祝你好运(我曾经不得不为客户做这件事,不漂亮。)
    8. 不要在两个分支中独立创建相对路径相同的文件;使用 Merge 将它们分支,否则您将花费​​数小时追逐命名空间冲突。 (2010 年不适用)
    9. 不要在曾经存在其他项目的路径上重新添加文件。无论旧项目是重命名/移走,还是简单地删除,您都会在合并时面临有趣的挑战;至少,它需要两个 Checkin 才能完全传播。 (2010 年不适用,虽然体验仍然有些下降;只需要 1 次签入,项目内容被保留,但名称历史记录在该分支和所有下游分支中)
    10. 除非您知道自己在做什么,否则不要使用 /force 标志。所有 /force 合并实际上都是精选,导致非常相似的风险(代码在解决过程中丢失等)。
    11. 除非您真的知道自己在做什么,否则不要使用 /baseless 标志。你错过了删除——类似于标签,除了重命名 always 会变成分支,而不仅仅是在不幸的边缘情况下。您不会得到任何借记/贷记保护。最可怕的是,您将创建新的分支关系。有时。 (对于每个目标项目是新的、具有新关系的旧项目还是具有现有关系的旧项目,不会向用户显示任何反馈)
    12. 尽可能避免 /discard (以及等价的 AcceptYours 决议)。丢弃一些变更集只接受后续的变更集是挑选樱桃的另一个名称:)
    13. 一般要小心您的分辨率。除了对手头合并的影响之外,每个都有独特的下游影响。
      1. AcceptTheirs 是获得“复制”合并的一种快速而强大的方法,正如第一条指南所提倡的那样。如果您也在其他场景中使用它,请记住您只是告诉 TFS 使文件内容相同。您是在告诉它,这两个文件在版本控制 POV 中完全同步。也就是说,一旦您签入 AcceptTheirs,之前对目标文件所做的任何可能以相反方向合并的更改都将不再被视为候选。
      2. 请注意,其结果内容与源文件相同的 AcceptMerge(自动或手动)将被服务器视为 AcceptTheirs。 Checkin Web 服务协议中没有任何区别。
      3. 在涉及重命名时使用 AcceptYours 会扭曲您的大脑。您很快就会遇到“相同”项目在不同分支中具有不同名称的情况。假设您首先有充分的理由放弃更改,这种现象本身并不是不安全的——事实上,可能有必要避免构建中断或对您的 makefile 进行一次性定制。这只会让人类感到困惑,并且很可能会破坏您拥有的任何假设树结构在分支之间保持一致的自动化脚本。
      4. AcceptMerge 是默认设置是有原因的。它有时会导致比看起来严格必要的版本冲突更多,但在需要真正合并时是最安全的选择。 (例如,主要指南的第 1 步“向下合并,向上复制”。)只要您遵循其他指南,需要手动注意的合并数量应该会下降 - 如果您来自工作流程很繁重。
    14. 应该将错误链接到实际进行修复的变更集。如果您稍后需要深入下游分支以查看错误修复的传播时间、地点(以及可能的方式),那是纯粹的源代码控制功能。无需用额外的包袱污染工作项目,更不用说改变您从根本上执行合并的方式。在 2005/2008 年,您可以使用“tf merges”命令或像 Attrice SideKicks 这样的第 3 方 UI 来遍历合并历史记录。在 2010 年,您可以在 Visual Studio 中获得漂亮的可视化效果。 Instructions & screenshots on MSDN.

    【讨论】:

    • 我刚刚看了video.google.com/videoplay?docid=-577744660535947210,你在网上其他地方推荐的。您如何看待在创建 release2.0 时重新设置 release1.0,以便 Main-Release2.0-Release1.0 成为分支树中的一条路径,如视频所示?
    • 我有一种感觉,用 git 解释相同的事情会减少 80% 的单词来获得相同的结果!坦率地说,与 SVN、Git、Mercurial hell 甚至 CVS 相比,我不理解 TFS 似乎更好!不要误会我的意思,我非常喜欢 TFS 的任务项,使用正确的模板真是太棒了....但是我从来没有在源代码控制系统上遇到过这么多麻烦。使用 VPN 在 wan 上部署它,每次提交都会变成 S&M 会话出错!
    【解决方案2】:

    我一直只将一系列提交合并到集成分支中,只指定我合并的变更集的范围。

    与开发阶段的各个工作项相关的工作项是开发阶段的工作项。我认为没有必要将它们推广到集成或发布。

    您尚未指定记录客户的错误/功能请求的位置。如果您将这些分配给发布分支,您可能正在为开发分支创建其他更详细的工作项,并且在合并时,您只需将错误修复的所有问题标记为您正在合并的分支的已解决。

    所以总结一下,我认为没有理由不进行批量合并。

    【讨论】:

      猜你喜欢
      • 2011-11-26
      • 2012-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-09
      • 1970-01-01
      • 1970-01-01
      • 2012-02-16
      相关资源
      最近更新 更多