Subversion允许您使用外部嵌入其他存储库的工作副本,从而允许在项目中轻松控制第三方库软件.
虽然这些似乎是重用库和供应商软件版本控制的理想选择,但它们并非没有批评:
请不要使用Subversion外部(或其他工具中的类似),它们是反模式,因此是不必要的
使用外部设备是否存在隐患?请解释为什么他们会被视为反模式.
我是问题中引用的作者,它来自之前的回答.
杰森对我这样的简短陈述持怀疑态度是正确的,并要求解释.当然,如果我完全解释了答案中的所有内容,我就需要写一本书.
Mike也正确地指出,svn:external
类似功能的一个问题是目标源的更改可能会破坏您自己的源,特别是如果该目标源位于您不拥有的存储库中.
在进一步解释我的评论时,我首先要说的是,使用svn:external
类似功能的方法有"安全" ,就像使用任何其他工具或功能一样.但是,我将其称为反模式,因为该功能更容易被滥用.根据我的经验,它一直被滥用,我发现自己不太可能以安全的方式使用它,也不建议使用它.请进一步注意,我的意思是没有对Subversion团队的贬低 - 我喜欢Subversion,尽管我打算继续前往Bazaar.
此功能的主要问题是它鼓励并且通常用于将一个构建("项目")的源直接链接到另一个构建的源,或者将项目链接到二进制(DLL,JAR等).取决于它.这些用途都不是明智的,它们构成了反模式.
正如我在其他答案中所说的那样,我认为软件构建的一个基本原则是每个项目都构建一个二进制或主要可交付成果.这可以被视为将关注点分离原则应用于构建过程.对于一个直接引用另一个项目源的项目来说尤其如此,这也违反了封装原则.此类违规的另一种形式是尝试通过递归调用子构建来创建构建层次结构以构造整个系统或子系统.Maven强烈鼓励/强制执行此行为,这是我不推荐它的众多原因之一.
最后,我发现有各种实际问题使这个功能不受欢迎.首先,它svn:external
有一些有趣的行为特征(但细节暂时让我失望).另一方面,我总是发现我需要这样的依赖项对我的项目(构建过程)明确可见,而不是作为一些源控件元数据.
那么,使用此功能的"安全"方式是什么?我认为只有一个人暂时使用它,例如"配置"工作环境的方式.我可以看到程序员可以在存储库中创建自己的文件夹(或者每个程序员一个),在那里他们将配置svn:external
指向他们当前正在处理的存储库的各个其他部分的链接.然后,检查该文件夹将创建其所有当前项目的工作副本.添加或完成项目时,svn:external
可以调整定义并适当更新工作副本.但是,我更喜欢一种与特定源控制系统无关的方法,例如使用调用结帐的脚本来执行此操作.
为了记录,我最近接触到这个问题是在2008年夏天svn:external
在一个大规模使用的咨询客户端发生的 - 一切都被交叉链接以产生一个主要的工作副本.他们的基于Ant和Jython(用于WebLogic)构建脚本构建在此主工作副本之上.最终结果:NOTHING可以独立构建,实际上有几十个子项目,但没有一个可以自行检查/工作.因此,对该系统的任何工作首先需要检出/更新超过2 GB的文件(它们还将二进制文件放入存储库中).做任何事都是徒劳无功的,我尝试了三个月后离开了(还有很多其他的反模式).
编辑:解释递归构建 -
多年来(特别是过去十年),我为财富500强公司和大型政府机构建立了大量系统,涉及许多层次深度的目录层次结构中安排的许多子项目.我已经使用Microsoft Visual Studio项目/解决方案为基于Java的系统组织基于.NET的系统,Ant或Maven 2,并且我已经开始为基于Python的系统使用distutils和setuptools(easyinstall).这些系统还包括通常在Oracle或Microsoft SQL Server中的大型数据库.
我在设计这些大型构建方面取得了巨大成功,易于使用和可重复性.我的设计标准是新开发人员可以在第一天出现,获得一个新的工作站(可能直接来自戴尔,只需要一个典型的操作系统安装),给出一个简单的安装文档(通常只有一页安装说明),并且能够完全设置工作站并从源头,无人监督,无需辅助以及半天或更短时间内构建完整系统.调用构建本身涉及打开命令shell,更改为源树的根目录,并发出一行命令来构建一切.
尽管取得了这样的成功,构建如此庞大的构建系统需要非常谨慎并严格遵守可靠的设计原则,就像构建一个庞大的业务关键应用程序/系统一样.我发现一个关键的部分是每个项目(产生一个工件/可交付物)必须有一个构建脚本,它必须有一个定义良好的接口(用于调用构建过程的部分的命令),它必须站立独自所有其他(子)项目.从历史上看,构建整个系统很容易,但很难/不可能只构建一个系统.直到最近,我才学会了仔细确保每个项目真正独立.
实际上,这意味着必须至少有两层构建脚本.最低层是生成每个可交付物/工件的项目构建脚本.每个这样的脚本都驻留在其项目源代码树的根目录中(实际上,这个脚本定义了它的项目源代码树),这些脚本对源代码控制一无所知,它们希望从命令行运行,它们引用项目相对的所有内容.到构建脚本,它们基于一些可配置的设置(环境变量,配置文件等)引用它们的外部依赖项(工具或二进制工件,没有其他源项目).
第二层构建脚本也可以从命令行调用,但这些脚本知道源代码控制.实际上,第二层通常是使用项目名称和版本调用的单个脚本,然后它将命名项目的源检出到新的临时目录(可能在命令行中指定)并调用其构建脚本.
可能需要更多变化来适应持续集成服务器,多个平台和各种发布方案.
有时需要第三层脚本调用第二层脚本(调用第一层),以便构建整个项目集的特定子集.例如,每个开发人员可能都有自己的脚本来构建他们今天正在处理的项目.可能有一个脚本来构建所有内容以生成主文档或计算度量.
无论如何,我发现尝试将系统视为项目的层次结构会适得其反.它将项目彼此联系起来,以便它们不能单独构建,也不能在任意位置(连续集成服务器上的临时目录)或任意顺序(假设满足依赖性)自由构建.通常,尝试强制层次结构会破坏可能尝试的任何IDE集成.
最后,构建大规模的项目层次结构可能只是性能太强.例如,在2007年春天,我尝试使用Ant构建的适度的源层次结构(Java加Oracle),最终失败,因为构建总是因Java OutOfMemoryException而中止.这是在2 GB RAM工作站上,具有3.5 GB交换空间,我已经调整了JVM以便能够使用所有可用内存.应用程序/系统在代码量方面相对微不足道,但无论我给它多少内存,递归构建调用最终耗尽内存.当然,它也需要永远执行(30-60分钟是常见的,在它中止之前).我知道如何很好地调整,但最终我只是超出了工具的限制(在这种情况下是Java/Ant).
所以,帮自己一个忙,将您的构建构建为独立项目,然后将它们组合成一个完整的系统.保持轻盈灵活.请享用.
编辑:更多关于反模式
严格地说,反模式是一种常见的解决方案,它看起来像是解决了问题,但却没有,因为它留下了重要的空白,或者因为它引入了其他问题(通常比原来的问题更糟).解决方案必然涉及一个或多个工具以及将它们应用于手头问题的技术.因此,将工具或工具的特定特征称为反模式是一种延伸,似乎人们正在检测并对该范围作出反应 - 足够公平.
另一方面,由于我们行业中通常的做法是专注于工具而不是技术,因此工具/功能得到了关注(StackOverflow上的问题随意调查似乎很容易说明).我的评论和这个问题本身反映了这种做法.
然而,有时似乎特别合理的做法,例如在这种情况下.一些工具似乎"引导"用户使用特定的技术来应用它们,以至于有些人认为工具会形成思想(略微改写).主要是在这种精神上,我认为这svn:external
是一个反模式.
为了更严格地说明问题,反模式是设计一个构建解决方案,包括在源级别将项目捆绑在一起,或隐式版本化项目之间的依赖关系,或允许这些依赖项隐式更改,因为每个都调用非常消极后果.类似特征的性质svn:external
使得避免这些负面后果非常困难.
正确处理项目之间的依赖关系涉及解决这些动态以及基本问题,并且工具和技术引导不同的路径.应该考虑的一个例子是Ivy,它以类似于Maven的方式提供帮助但没有许多缺点.我正在调查Ivy,加上Ant,作为我对Java构建问题的短期解决方案.从长远来看,我希望将核心概念和功能整合到一个开源工具中,以促进多平台解决方案.
我认为这根本不是一种反模式.我在google上做了一些快速搜索,基本上什么都没有...没有人抱怨使用svn:externals是坏的还是有害的.当然,你必须要注意一些警告......而且你应该只是大量地放入你的所有存储库中......但是对于原始的引用,这只是他的个人(和主观)意见.他从未真正讨论过svn:externals,除非将它们定为反模式.在没有任何支持或至少推理该人如何作出陈述的情况下,如此彻底的陈述总是令人怀疑.
也就是说,使用外部设备存在一些问题.就像迈克回答的那样,他们可以非常有助于指出已发布软件的稳定分支......尤其是你已经掌控的软件.我们在许多项目中内部使用它们用于实用程序库等.我们有一个小组,可以增强和使用实用程序库库,但该基本代码在许多项目中共享.我们不希望各个团队只是检查实用程序项目代码,我们不想处理一百万个分支,所以对于我们svn:externals非常有效.对某些人来说,他们可能不是答案.但是,我强烈反对声明"请不要使用......",并且这些工具代表了反模式.
使用svn:externals的主要风险是引用的存储库将以破坏代码或引入安全漏洞的方式进行更改.如果外部存储库也在您的控制之下,那么这可能是可以接受的.
就个人而言,我只使用svn:externals指向我拥有的存储库的"稳定"分支.
一个旧线程,但我想解决一个问题,即更改外部可能会破坏您的代码.如前所述,这通常是由于外部属性的使用不正确.在几乎所有情况下,外部引用都应指向外部存储库URI中的特定修订号.这可确保外部永远不会更改,除非您将其更改为指向不同的版本号.
对于我们在最终用户项目中用作外部的一些内部库,我发现在Major.Minor版本中创建库的标记很有用,我们强制执行没有重大更改.使用四点版本控制方案(Major.Minor.BugFix.Build),我们允许标签与BugFix.Build更改保持最新(再次强制执行没有重大更改).这允许我们使用对标签的外部引用而没有修订号.在主要或其他重大更改的情况下,将创建新标记.
外部本身并不坏,但这并不能阻止人们创建错误的实现.它不需要太多的研究,只需要阅读一些文档,就可以学习如何安全有效地使用它们.
如果普通外部是反模式,因为它可能会破坏您的存储库,那么具有明确修订的那个不应该.
摘自svn书:
外部定义是本地目录到版本化资源的URL**(可能是特定版本)**的映射.
我认为这完全取决于您使用该功能的目的,它本身并不是反模式.
颠覆外部存在明确的缺陷,但我们似乎合理地成功地使用它们来包括当前项目所依赖的库(我们自己的和供应商).所以我不认为它们是"反模式".对我来说重要的用途是:
它们指向另一个项目的特定修订或标记(从不是头部).
它们远离其自己的源代码等插入到当前项目中(例如,在名为"支持文件"的子目录中).
它们仅指代其他项目"接口"文件(例如包含文件夹)和二进制库(即我们没有获得其他项目的完整源代码).
我也对这种安排的任何重大风险以及更好的方法感兴趣.