TFS:合并最佳实践
我们有一个标准的分支架构,我们为每个团队开发了一个分支,一个通用的集成分支(所有开发分支都是分支的),生产分支从集成分支。
在开发阶段,我对开发分支做了大量的工作。 在阶段结束时,我将自己的更改合并到后来的生产中。
将每个提交单独合并,将原始提交描述和链接复制到原始任务是否合理? 另一种select当然是一次合并所有的提交和一个合并操作。 我的问题的原因是,第一种方式需要很多时间。 我没有看到TFS中的任何自动化工具将合并到其他分支链接到原始提交。
我想听听你对最佳实践的看法。
- 从开发合并* – >集成和集成 – >生产应该总是“复制”合并。 这是保持下游分行稳定的最安全的方式。
- 首先在另一个方向合并(例如,Integration – > Dev2)以从目标分支中选取最新的更改。
- 如果存在冲突,请根据具体情况处理差异。 AcceptMerge(自动或手动)通常是期望的结果,但有时你会想要保持一个或另一个分支的副本不变。
- 使用源代码分支(在我们的例子中是Dev#2)来完全包含,反应并稳定这些变化。
- 合并到所需的方向(例如Dev2 – > Integration)。 将所有冲突解决为AcceptTheirs [又名“从源文件复制”]。
- 确保步骤#1-4之间的目标分支没有变化。 如果开发分支早期和经常地接受合并,那么在这个希望短的过程中locking目标分支不应该是麻烦的。 如果你因为某种原因预计死亡会“大爆炸”,那么有一个很好的机会locking会阻止其他团队同时做同样的事情,所以你可能需要重复步骤#1-4,直到你准备好。
- 尽可能地“赶上”合并。 也就是说,按照与他们签入的顺序相同的顺序合并东西。如果变更集10,20和30是从A→B合并的候选者,那么这些合并范围中的任何一个都是“赶上来”:10〜10,10 〜20,10〜30。 任何跳过#10的变化范围都称为“樱桃树”。 一旦你开始采摘樱桃,你会遇到一些危险:
- 你不能使用合并,复制上面描述的模型。 就这一点而言,劳拉·温格德会说你正在跳过路边。
- 如果您的范围内的任何文件触及了以前,您将不得不做一个3路内容合并,以便只有樱花挑选的差异传播。 没有diff工具是完美的; 你会添加一个非零的风险,比引入更多的代码,意外覆盖目标中所做的更改,引入两个分支分歧的逻辑错误等等。
- 你正在推广到一个更稳定的分支的变化代表了一个从来没有build立或testing过的configuration。 你可以对目标分支的最终状态做出一个合适的猜测。 “我正在合并所有影响Module Foo的变化,并且我在Dev中testing了Foo的新版本,这就是Foo在Integration中的performance,对不对? 当然……也许……如果你能跟踪你脑海中的每一个依赖(包括你在testingDev时可能已经改变了的所有内容)。 但是这些猜测无法被您的SCM工具链所了解或validation。
- 特别是在TFS中,涉及命名空间变化的樱桃挑选只是要求被烧毁。 如果您的版本范围和/或path范围排除了重命名的来源,它将作为分支而不是。 如果你排除目标,它会挂起一个删除。 如果你的path范围不包括取消删除的根,你会得到神秘的错误。 如果你的范围跨越了一段时间之间的删除和重新删除,你会得到“幻影”文件出现在目标,即使你不包括反删除本身。 如果将所有path和版本范围的Moves合并为正确的,但不按顺序执行,则即使所有候选变更集已耗尽,也有可能以源名称为目标名称。 我敢肯定还有更多的方法让这个组合出现错误,现在还没有想到…只要相信我。
- 总是在合并之前执行一次获取目标分支。 高级版本的最终安全性:将要合并的工作区同步到提示处或附近的特定变更集编号,然后[追赶]合并到同一变更集。 这样做可以避免一些潜在的问题:
- 合并成陈旧的代码,产生令人困惑的三方差异,似乎消除您在提示中看到的变化。 [你最终会把它们还给Checkin + Resolve,但没有理由通过两个冒险的差异,当你可以避免]
- 必须经过两次冲突解决过程:一次合并,一次签入。 在一般情况下,没有办法避免这种情况,但大多数情况下,在合并+parsing期间同时进行的更改与在工作空间中遇到的更改数量相比是微不足道的,这些更改可能需要几天或几周的时间date。
- 除非您真的知道自己在做什么,否则不要通过标签(或工作区)进行合并。 让我们回顾一下TFS标签提供的function,然后剖析为什么每个标签都不适合安全和一致的合并。
- 标签可以代表多个时间点。 如果一个标签代表了VCS的一致快照,并且始终是这样的 – 那么它在date或变更集#上没有技术优势。 不幸的是,很难判断一个标签是否在一段时间内是一致的。 如果不是,按标签合并可能会导致:
- 无意中摘樱桃,如果范围从一个标签指向一个项目@提前一个候选人
- 意外的排除,如果范围以一个标签指向一个项目@在该范围的结束之前一个时间
- 无意排除,如果范围以指向范围开始之前一个时间的项目的标签结束
- 标签versionspecs代表一组特定的项目。 它们可以用来故意排除纯粹的recursion查询将会看到的文件和文件夹。 这个function也是合并操作的一个不好的匹配。 (再说一次,如果你不需要这个能力的话,那么就会产生下面的风险而没有获得任何超过date和变更集的东西。)
- 不存在于标签中的项目将被忽略,而不是合并为待处理的删除。 与迄今为止所涉及的一些边缘案例不同,这是一个很大的交易,很可能在主stream场景中发生,但大多数人都错过了。 [因此,TFS 2010增加了对标签内已删除项目的支持。]
- 无意中摘樱桃,如果你添加一个项目的标签已经存在了一段时间,但由于上述副作用之一,被排除在前合并。
- 故意采摘樱桃。 这个特性给Merge带来的全部优势是打破了我们的一个准则,显然这不是一个好理由。 而且在档案层面引起樱桃采摘,比“普通”樱桃采摘更加危险。
- 标签具有友好的可定制名称,所有者和评论。 因此,我们有一个纯粹的可用性差异与date/变更集; 没有技术优势。 但即使在这里,它也不像看起来那么有吸引力。 TFS在UI中实际上并没有做太多的表面标签,而你可以在整个地方看到变更集注释。 所有者查询速度很快(服务器端),但大多数其他search都很慢(客户端),除非您知道确切的标签名称。 pipe理设施几乎不存在。 没有更改日志或审计,只有一个时间戳。 总而言之,这些并不是放弃变更集所提供的保证的理由。
- 标签可以代表多个时间点。 如果一个标签代表了VCS的一致快照,并且始终是这样的 – 那么它在date或变更集#上没有技术优势。 不幸的是,很难判断一个标签是否在一段时间内是一致的。 如果不是,按标签合并可能会导致:
- 始终合并整个分支。 合并文件或子树有时是诱人的,但最终仅仅是以新的方式挑选樱桃。
- 未雨绸缪。 不幸的是,在TFS中重新分支的分支是一个痛苦的话题。 有时很艰难,有时只有几步,但从来没有明显的 ; 没有内置命令(直到2010年)。 在2005/2008年取消它需要对当前分支结构,所需结构以及如何滥用各种TF命令的副作用有非常深入的了解。
- 不要在分支内创build分支。 例如,有时build议使用分支和合并作为在松散耦合项目之间维护常见模块或二进制文件的一种方法。 我不认为这是一个很好的build议,最好让你的构build系统正确地完成它的主要工作,而不是让你的源代码pipe理系统去做一些其实没有的东西。 无论如何,这种“共享”策略与项目本身非常相似,它们活在一个更广泛的分支层次结构中,以供SCM使用。 如果你不小心,TFS会很高兴地让你创build任意的版本控制项目之间的多对多分支关系。 祝你好运sorting(我曾经为客户做过,不漂亮。)
- 不要在两个分支独立创build具有相同相对path的文件; 使用合并来分支它们,否则你将花费数小时来追赶命名空间的冲突。 (2010年不适用)
- 不要在其他项目曾经存在的path上重新添加文件。 无论旧的项目是重命名/移走还是简单地删除,在合并时您都将面临有趣的挑战; 至less需要两个Checkins完全传播。 (2010年不适用,虽然经验还是有所下降,只需要签入一次,项目内容仍然保留,但名称历史logging在该分支和所有下游分支中)
- 除非你知道你在做什么,否则不要使用/ force标志。 所有/强制合并实际上是樱桃select,导致非常相似的风险(代码在解决过程中迷路等)。
- 除非你真的知道你在做什么,否则不要使用/ baseless标志。 你错过了删除 – 类似于标签,除了重命名总是变形成分支,而不是在不幸的边缘情况下。 无论如何您都没有获得任何借记/信贷保护。 而最可怕的是,你将会创build新的分支关系。 有时。 (没有反馈向用户显示每个目标项目是新的,旧的与新的关系,还是旧的与现有的关系)
- 尽可能避免/丢弃(和等效的AcceptYours解决scheme)。 舍弃一些变更集,只接受后续的select,是樱桃采摘的另一个名字:)
- 一般来说,要小心你的决议。 除了对手头合并的影响外,每个手段都有其独特的下游效应。
- 接受他们是一个快速和强大的方式来获得“复制”合并,如第一指导原则所倡导的。 如果你在其他情况下使用它,记住你不只是告诉TFS使文件内容相同。 你告诉它,这两个文件完全同步从一个版本化的POV。 换句话说,一旦您签入AcceptTheirs,可能已经在相反方向合并的目标文件的任何先前更改将不再被视为候选人。
- 请注意,AcceptMerge(自动或手动)的结果内容与源文件相同,将被视为服务器的AcceptTheirs。 Checkin webservice协议没有区别。
- 当涉及重命名时使用AcceptYours可以扭曲你的大脑。 在“同一个”项目在不同分支中具有不同名称的情况下,您将很快结束。 假设你有一个很好的理由放弃更改,这种现象本身并不是不安全的 – 事实上,可能有必要避免构build或一次性定制makefile。 这只是让人感到困惑,而且很有可能会破坏你所拥有的任何自动化脚本,假设树结构从分支到分支是一致的。
- AcceptMerge是默认的原因。 它有时会导致更多的版本冲突,而不是严格意义上的冲突,但是当需要真正的合并时,这是最安全的select。 (例如,主要指南的第一步“合并,复制”)。只要你遵循其他的指导方针,需要手动注意的合并数量应该下降 – 如果你来自樱桃采摘工作量很大。
- 应该将错误链接到实际进行修复的修改集。 如果您以后需要钻入下游分支来查看错误修复何时(以及可能如何)传播,那么这是纯粹的源代码pipe理function。 没有必要用额外的行李污染工作项目,更不用说改变你从根本上进行合并的方式。 在2005/2008年,你可以通过“tf merges”命令或像Attrice SideKicks这样的第三方UI来遍历合并历史。 在2010年,您将获得内置于Visual Studio中的漂亮可视化。 在MSDN上的说明和屏幕截图。
我总是将一系列的提交合并到集成分支中,只是指定了我合并的变更集的范围。
与开发阶段各个工作项目相关的工作项目是开发阶段工作项目。 我不认为有必要把它们推到集成或发布。
您尚未指定要logging来自客户的错误/function请求的位置。 如果您将这些分配给发布分支,那么您可能会为开发分支创build其他更详细的工作项目,并且在合并时将简单地标记错误修复为您正在合并的分支解决的所有问题。
所以总结起来,我没有理由不去大批合并。