69

Subversion 允许您使用externals嵌入其他存储库的工作副本,从而允许在您的项目中轻松控制第三方库软件的版本。

虽然这些似乎非常适合库的重用和供应商软件的版本控制,但它们并非没有批评者

请不要使用 Subversion 外部工具(或其他工具中的类似工具),它们是反模式,因此没有必要

使用外挂是否存在隐患?请解释为什么它们会被视为反模式。

4

7 回答 7

71

我是问题中引用的作者,该引用来自先前的答案

杰森对我这样的简短陈述持怀疑态度并要求解释是正确的。当然,如果我完全解释了该答案中的所有内容,我将需要写一本书。

Mike 也正确地指出了svn:external类似功能的问题之一是目标源中的更改可能会破坏您自己的源,特别是如果该目标源位于您不拥有的存储库中。

在进一步解释我的评论时,让我首先说有一些“安全”的方式可以使用svn:external-like 功能,就像使用任何其他工具或功能一样。但是,我将其称为反模式,因为该功能更容易被滥用。根据我的经验,它一直被滥用,我发现自己不太可能以这种安全的方式使用它,也不推荐这种使用方式。请进一步注意,我不是贬低 Subversion 团队的意思——我喜欢 Subversion,尽管我计划继续前往 Bazaar。

此功能的主要问题是它鼓励并且通常用于将一个构建(“项目”)的源直接链接到另一个构建的源,或将项目链接到二进制文件(DLL、JAR 等)它取决于它。这些用法都不明智,它们构成了一种反模式。

正如我在另一个答案中所说,我相信软件构建的一个基本原则是每个项目都构建一个二进制或主要可交付成果。这可以被认为是关注点分离原则在构建过程中的应用。对于一个项目直接引用另一个项目的源代码尤其如此,这也违反了封装原则。这种违规的另一种形式是尝试通过递归调用子构建来创建构建层次结构以构建整个系统或子系统。Maven 强烈鼓励/强制执行这种行为,这是我不推荐它的众多原因之一。

最后,我发现有各种实际问题使此功能不可取。一方面,svn:external有一些有趣的行为特征(但细节暂时让我无法理解)。另一方面,我总是发现我需要这些依赖项对我的项目(构建过程)明确可见,而不是作为一些源代码控制元数据隐藏。

那么,使用此功能的“安全”方式是什么?我认为这是仅由一个人临时使用的情况,例如“配置”工作环境的一种方式。我可以看到程序员可能在存储库中创建自己的文件夹(或为每个程序员创建一个文件夹),他们将在其中配置svn:external指向他们当前正在处理的存储库的各个其他部分的链接。然后,签出该文件夹将创建他们所有当前项目的工作副本。添加或完成项目时,svn:external可以调整定义并适当更新工作副本。但是,我更喜欢不依赖于特定源代码控制系统的方法,例如使用调用检出的脚本来执行此操作。

作为记录,我最近一次接触到这个问题是在 2008 年夏天,svn:external在一个大规模使用的咨询客户那里——所有东西都被交叉链接以生成一个主工作副本。他们基于 Ant 和 Jython(用于 WebLogic)的构建脚本是在这个主工作副本之上构建的。最终结果:没有任何东西可以独立构建,实际上有几十个子项目,但没有一个可以安全地自行检查/工作。因此,在这个系统上的任何工作首先需要检出/更新超过 2 GB 的文件(他们也将二进制文件放入存储库中)。完成任何事情都是徒劳的,我在尝试了三个月后离开了(还有许多其他反模式)。

编辑:阐述递归构建 -

多年来(尤其是过去十年),我为财富 500 强公司和大型政府机构构建了庞大的系统,涉及数十个子项目,这些子项目排列在多个层次的目录层次结构中。我已经使用 Microsoft Visual Studio 项目/解决方案来组织基于 .NET 的系统,Ant 或 Maven 2 用于基于 Java 的系统,并且我已经开始将 distutils 和 setuptools (easyinstall) 用于基于 Python 的系统。这些系统还包括通常在 Oracle 或 Microsoft SQL Server 中的大型数据库。

我在设计这些大型构建以实现易用性和可重复性方面取得了巨大成功。我的设计标准是新开发人员可以在第一天出现,获得新工作站(可能直接来自戴尔,只需安装典型的操作系统),获得简单的设置文档(通常只有一页安装说明),并且能够在半天或更短的时间内完全设置工作站并从源头构建完整的系统,无人监督,无人协助。调用构建本身包括打开一个命令 shell,切换到源代码树的根目录,然后发出一条命令来构建所有内容。

尽管取得了成功,但构建如此庞大的构建系统需要非常小心并严格遵守可靠的设计原则,就像构建大规模的关键业务应用程序/系统一样。我发现一个关键部分是每个项目(生成单个工件/可交付成果)必须有一个构建脚本,该脚本必须有一个定义明确的接口(用于调用构建过程部分的命令),并且它必须能够正常运行单独从所有其他(子)项目。从历史上看,构建整个系统很容易,但很难/不可能只构建一个系统。直到最近我才学会仔细确保每个项目真正独立。

实际上,这意味着必须至少有两层构建脚本。最低层是生成每个交付物/工件的项目构建脚本。每个这样的脚本都驻留在其项目源代码树的根目录中(实际上,该脚本定义了它的项目源代码树),这些脚本对源代码控制一无所知,它们希望从命令行运行,它们引用项目中的所有相关内容到构建脚本,它们基于一些可配置的设置(环境变量、配置文件等)引用它们的外部依赖项(工具或二进制工件,没有其他源项目)。

构建脚本的第二层也打算从命令行调用,但这些都知道源代码控制。实际上,第二层通常是一个单独的脚本,它使用项目名称和版本调用,然后它将命名项目的源代码检出到新的临时目录(可能在命令行上指定)并调用其构建脚本。

可能需要更多变化来适应持续集成服务器、多个平台和各种发布场景。

有时,为了构建整个项目集的特定子集,需要调用第二层脚本(调用第一层)的第三层脚本。例如,每个开发人员可能都有自己的脚本来构建他们今天正在从事的项目。可能有一个脚本来构建所有内容以生成主文档或计算指标。

无论如何,我发现试图将系统视为项目的层次结构会适得其反。它将项目彼此联系在一起,因此它们不能单独自由构建,也不能在任意位置(持续集成服务器上的临时目录)或以任意顺序(假设满足依赖关系)构建。通常,尝试强制分层会破坏任何可能尝试的 IDE 集成。

最后,构建庞大的项目层次结构可能会过于消耗性能。例如,在 2007 年春天,我尝试使用 Ant 构建一个适度的源代码层次结构(Java 加 Oracle),但最终失败了,因为构建总是因 Java OutOfMemoryException 而中止。这是在具有 3.5 GB 交换空间的 2 GB RAM 工作站上,我已针对该工作站调整 JVM 以使用所有可用内存。应用程序/系统在代码量方面相对微不足道,但递归构建调用最终会耗尽内存,无论我给它多少内存。当然,它也需要很长时间才能执行(30-60 分钟很常见,在它中止之前)。我非常了解如何调整,但最终我只是超出了工具的限制(在这种情况下是 Java/Ant)。

所以帮自己一个忙,将您的构建构建为独立项目,然后将它们组合成一个完整的系统。保持轻便灵活。享受。

编辑:更多关于反模式

严格来说,反模式是一种常见的解决方案,看起来它解决了问题,但实际上并没有,因为它留下了重要的空白,或者因为它引入了额外的问题(通常比原始问题更糟糕)。解决方案必然涉及一个或多个工具以及将它们应用于手头问题的技术。因此,将工具或工具的特定功能称为反模式是一种延伸,似乎人们正在检测这种延伸并对此做出反应——这很公平。

另一方面,由于我们行业中的常见做法似乎是专注于工具而不是技术,因此受到关注的是工具/功能(在 StackOverflow 上对问题的随意调查似乎很容易说明)。我的评论,以及这个问题本身,都反映了这种做法。

然而,有时进行这种延伸似乎特别合理,例如在这种情况下。有些工具似乎会“引导”用户使用特定的技术来应用它们,以至于有些人认为工具塑造了思想(稍微改写了)。主要是本着这种精神,我建议这svn:external是一种反模式。

为了更严格地说明这个问题,反模式是设计一个构建解决方案,包括在源代码级别将项目捆绑在一起,或者隐式版本化项目之间的依赖关系,或者允许这种依赖关系隐式更改,因为这些调用中的每一个都非常负面结果。类似特征的性质svn:external使得避免这些负面后果非常困难。

正确处理项目之间的依赖关系涉及解决这些动态以及基本问题,并且工具和技术会导致不同的路径。应该考虑的一个例子是Ivy,它以类似于 Maven 的方式提供帮助,但没有许多缺点。我正在研究 Ivy,加上 Ant,作为我对 Java 构建问题的短期解决方案。从长远来看,我希望将核心概念和功能整合到一个有助于多平台解决方案的开源工具中。

于 2008-12-05T22:34:10.393 回答
66

我认为这根本不是反模式。我在谷歌上做了一些快速搜索,基本上什么都没找到……没有人抱怨使用 svn:externals 是坏的或有害的。当然,您必须注意一些注意事项...而且您不应该将其大量散布到所有存储库中...但是至于原始引文,这只是他的个人(和主观)意见. 他从来没有真正讨论过 svn:externals,只是谴责它们是一种反模式。这种笼统的陈述没有任何支持或至少没有关于这个人是如何做出陈述的推理的,总是令人怀疑的。

也就是说,使用外部存在一些问题。就像迈克回答的那样,它们对于指向已发布软件的稳定分支非常有帮助……尤其是您已经控制的软件。我们在许多实用程序库等项目中内部使用它们。我们有一个小组负责增强实用程序库的基础并在此基础上工作,但该基础代码在多个项目中共享。我们不希望各个团队只检查实用程序项目代码,也不希望处理一百万个分支,所以对我们来说 svn:externals 工作得很好。对于某些人来说,他们可能不是答案。但是,我强烈不同意“请不要使用……”的说法,并且这些工具代表了一种反模式。

于 2008-12-03T22:37:02.700 回答
19

使用 svn:externals 的主要风险是引用的存储库将被更改,从而破坏您的代码或引入安全漏洞。如果外部存储库也在您的控制之下,那么这可能是可以接受的。

就个人而言,我只使用 svn:externals 来指向我拥有的存储库的“稳定”分支。

于 2008-12-03T21:56:18.320 回答
18

一个旧线程,但我想解决不断变化的外部可能会破坏您的代码的担忧。如前所述,这通常是由于对外部属性的错误使用造成的。几乎在所有情况下,外部引用都应指向外部存储库 URI 中的特定修订号。这可确保外部永远不会更改,除非您将其更改为指向不同的修订号。

对于我们在最终用户项目中用作外部的一些内部库,我发现在 Major.Minor 版本中创建库的标签很有用,我们不会强制执行任何重大更改。使用四点版本控制方案 (Major.Minor.BugFix.Build),我们允许标签与 BugFix.Build 更改保持同步(同样,强制不发生重大更改)。这允许我们在没有修订号的情况下使用对标签的外部引用。如果发生重大或其他重大更改,则会创建一个新标签。

外部本身并不坏,但这并不能阻止人们创建它们的糟糕实现。无需太多研究,只需阅读一些文档,即可了解如何安全有效地使用它们。

于 2010-10-05T17:34:49.597 回答
9

如果纯外部是一种反模式,因为它可能会破坏您的存储库,那么具有显式修订的版本不应该。

摘自svn 书

外部定义是本地目录到版本化资源的 URL**(可能还有特定修订版)**的映射。

我认为这完全取决于您使用该功能的目的,它本身并不是反模式。

于 2008-12-05T22:54:21.647 回答
9

subversion externals 存在明显的缺陷,但我们似乎合理地成功地使用了它们来包含当前项目所依赖的库(我们自己的和供应商的)。所以我不认为它们是“反模式”。对我来说重要的使用点是:

  • 它们指向另一个项目的特定修订或标签(绝不是头)。
  • 它们被插入到远离其自己的源代码等的当前项目中(例如,在称为“支持文件”的子目录中)。
  • 它们仅引用其他项目的“接口”文件(例如包含文件夹)和二进制库(即我们没有获得其他项目的完整源代码)。

我也会对这种安排的任何重大风险以及更好的方法感兴趣。

于 2008-12-05T23:40:59.977 回答
2

除非你说出为什么会这样,否则说a 是 b不会使a a b成立。

我在 subversion 中看到的外部引用的主要缺陷是,当您更新工作副本时,不能保证存储库存在。

Subversion 外部引用可以被使用,也可以被滥用,而这个特性本身只不过是一个特性。它不能说是一种模式,也不能说是一种反模式

我已经阅读了您引用的人的答案,我必须说我不同意。如果您的项目需要来自存储库的文件版本 XYZ,外部 subversion 参考可以轻松地为您提供。

是的,您可以通过不具体指定您需要哪个版本的参考来错误地使用它。这会给你带来麻烦吗?可能!

它是反模式吗?这要看情况。如果您按照您引用的文本作者提供的链接,即。在这里,然后没有。可以使用某些东西来提供糟糕的解决方案并不会使这样做的整个方法成为反模式。如果这是规则,那么我会说编程语言大体上都是反模式,因为在每种编程语言中,你都可以做出糟糕的解决方案

于 2008-12-05T23:51:37.040 回答