替代Subversion中的二进制文件

时间:2009-04-02 11:43:54

标签: java svn

我的一些同事确信将构建工件提交到subversion存储库是个好主意。这样的论点就是这样,测试机器上的安装和更新很容易 - 只需“svn up”!

我确信有很多反对这种不良做法的论据,但我能想到的只是“它占用更多空间”等蹩脚的行为。没有做到这一点的最佳原因是什么?我们应该采取哪些其他方法呢?

这适用于Java代码,如果这有所不同。一切都是从Eclipse编译的(没有自动PDE构建)。

当我说添加构建工件时,我的意思是提交看起来像这样:

"Added the new Whizbang feature"

 M src/foo/bar/Foo.java
 M bin/Foo.jar

每个代码更改都有相应的生成的jar文件。

15 个答案:

答案 0 :(得分:24)

在我看来,代码存储库应该只包含源代码以及编译此源代码所需的第三方库(在构建过程中也可以使用某些依赖管理工具检索第三方库)。生成的二进制文件不应与源代码一起签入。

我认为您的问题是您没有适当的构建脚本。这就是为什么从源代码构建二进制文件涉及一些工作,如启动eclipse,导入项目,调整classpathes等...

如果有构建脚本,可以使用如下命令来获取二进制文件:

svn update; ant dist

我认为不检查二进制文件和源代码的最重要原因是存储库的最终大小。这将导致:

  • 较大的存储库,版本控制系统服务器上的空间可能太少
  • 版本控制系统服务器与客户端之间的流量很大
  • 更新时间更长(假设您从互联网上进行SVN更新......)

另一个原因可能是:

  • 源代码很容易比较,因此版本控制系统的许多功能都很有意义。但你不能轻易比较二进制文件......

此外,您的方法在我看来引入了很多开销。如果开发人员忘记更新相应的jar文件会怎么样?

答案 1 :(得分:16)

首先,Subversion(以及现在所有其他人)不是源代码控制管理器(我一直认为SCM意味着软件配置管理),而是版本控制系统。 这意味着它们存储对存储在其中的东西的更改,它不一定是源代码,它可以是图像文件,位图资源,配置文件(文本或xml),各种东西。建立二进制文件不应被视为此列表的一部分只有一个原因,那是因为您可以重建它们。

但是,请考虑为什么要将已发布的二进制文件存储在那里。

首先,它是一个帮助您的系统,而不是告诉您应该如何构建应用程序。让计算机为您工作,而不是对您。那么如果存储二进制文件占用空间怎么办呢 - 你有数百GB的磁盘空间和超快的网络。将二进制对象存储在那里并不是什么大问题(而十年前它可能是一个问题 - 这也许是人们将SCM中的二进制文件视为一种不良做法的原因)。

其次,作为开发人员,您可能习惯使用系统重建任何版本的应用程序,但其他可能使用它的人(例如qa,test,support)可能不会。这意味着您需要一个替代系统来存储二进制文件,实际上,您已经拥有了这样一个系统,它就是您的SCM!利用它。

第三,您假设可以从源代码重建。显然,您将所有源代码存储在那里,但是您不存储编译器,库,sdks和所有所需的其他相关位。当有人出现时会发生什么,并问“你可以建立我2年前发布的版本,客户对该版本有问题”。 2年是如今的永恒,你甚至拥有当时使用的相同编译器吗?当您检查所有源代码时发现新更新的sdk与您的源不兼容并且失败并出现错误会发生什么?您是否擦除了开发框并重新安装所有依赖项以构建此应用程序?你能记住所有的依赖关系吗?!

最后一点是最重要的一点,为了节省几k的磁盘空间,如果不是数周的痛苦,你可能会花费几天时间。 (并且索德定律还说,无论你需要重建哪个应用程序都需要最晦涩,难以设置的依赖,你很高兴摆脱它)

所以将二进制文件存储在SCM中,不要担心琐事。

PS。我们将每个二进制文件都放在每个项目的“release”目录中,然后当我们想要更新一台机器时,我们使用一个特殊的“setup”项目,除了svn:externals之外什么都没有。您导出安装项目并完成它,因为它获取正确的东西并将它们放入正确的目录结构。

答案 2 :(得分:6)

Hudson这样的持续集成服务器可以存档构建工件。它没有帮助你的论点“为什么不”,但至少它是另一种选择。

答案 3 :(得分:5)

  

我确信有重要的论据   反对这种不良做法

你错误地认为将“构建工件”提交给版本控制是一个坏主意(除非你错误地表达了你的问题)。事实并非如此。

在版本控制中保留你所谓的“构建工件”是可以的,而且非常重要。除此之外,您还应该保留编译器以及用于将源文件集转换为成品的任何其他内容。

从现在开始的五年内,您肯定会使用不同的编译器和不同的构建环境,无论出于何种原因,可能无法编译今天的项目版本。修复遗留版本中的错误可能是一个简单的小改动,将变成将旧软件移植到当前编译器和构建工具的噩梦,只需重新编译具有单行更改的源文件。

所以,你没有理由害怕在版本控制中存储“构建工件”。您可能想要做的是将它们放在不同的地方。

我建议将它们分开:

 ProjectName
 |--- /trunk
 |    |--- /build
 |    |    |--- /bin        <-- compilers go here
 |    |    |--- /lib        <-- libraries (*.dll, *.jar) go here
 |    |    '--- /object     <-- object files (*.class, *.jar) go here
 |    '--- /source          <-- sources (*.java) go here
 |         |--- package1    <-- sources (*.java) go here
 |         |--- package2    <-- sources (*.java) go here

您必须配置IDE或构建脚本以将对象文件放在/ ProjectName / trunk / build / object中(甚至可以在... / source下重新创建目录结构)。

这样,您可以让用户选择签出/ ProjectName / trunk以获取完整的构建环境,或者/ ProjectName / trunk / source来获取应用程序的源。

在../build/bin和../build/lib中,您必须放置用于编译最终产品的编译器和库,用于将软件发送给用户的编译器和库。在5或10年内,您将拥有它们,可供您在某些情况下使用。

答案 4 :(得分:5)

“将构建工件提交到subversion存储库”可能是一个好主意如果你知道为什么

发布管理目的是一个好主意,更具体地说是:

1 /包装问题

如果构建工件不仅仅是exe(或dll或......),还包括:

  • 一些配置文件
  • 启动/停止/重启工件的一些脚本
  • 一些用于更新数据库的SQL
  • 一些来源(压缩成文件)以方便调试
  • 一些文档(在文件中压缩的javadoc)

然后最好有一个构建工件存储在VCS中的所有相关文件。
(因为它不再仅仅是“重建”工件的问题,而是“检索”所有那些将使该工件运行的额外文件的问题)

2 /部署问题

假设您需要在不同的环境中部署许多工件(测试,认证,预生产,生产)。
如果:

  • 你制作了许多构建工件
  • 这些工件很难从头开始重建

然后在VCS中包含这些工件是个好主意,以避免重新创建它们。
您可以从环境到环境查询它们。

但你需要记住:

  • 1 /您无法存储在VCS中创建的每个工件:为了持续集成而进行的所有中间构建必须存储在VCS中(或者你最终得到一个包含许多无用版二进制文件的巨大存储库) 只需要参考认证和生产所需的版本 对于中间构建,您需要一个外部存储库(maven或共享目录),以便快速发布/测试这些构建。

  • 2 /您不应该将它们存储在同一个Subversion存储库中,因为您的开发承诺(修订版号)比您的重要版本(认为值得认证和生产部署的版本)更频繁 这意味着存储在第二个存储库中的工件必须具有标记(或属性)的命名约定,以便轻松检索构建它们的开发的修订版号。

答案 5 :(得分:4)

根据我的经验,可以将SVG中的Jars存放在混乱中 我认为最好将Jar文件保存在像Nexus这样的Maven-Repository中 这也有好处,你可以使用像Maven或Ivy这样的依赖管理工具。

答案 6 :(得分:4)

二进制文件,尤其是您自己的二进制文件,以及第三方,在SVN等源代码控制工具中没有位置。

理想情况下,你应该有一个构建脚本来构建自己的二进制文件(然后可以使用许多精确的自动构建工具之一自动化,可以直接从SVN检查源代码。)

对于第三方二进制文件,您需要一个依赖管理工具,如Maven2。然后,您可以设置本地Maven存储库来处理所有第三方二进制文件(或仅依赖于公共文件)。本地仓库也可以管理您自己的二进制文件。

答案 7 :(得分:4)

将二进制文件放在主干或分支机构中肯定是矫枉过正。除了像你提到的占用空间一样,它还会导致源和二进制文件之间的不一致。当您引用修订版1234时,您不想知道这是否意味着“修订版1234中源代码生成的内容”与“修订版1234中的二进制文件”。避免不一致的相同规则适用于自动生成的代码。您不应该对构建可以生成的内容进行版本化。

OTOH我将二进制文件放入标签或多或少。这样,其他项目很容易通过svn:externals使用其他项目的二进制文件,而无需构建所有这些依赖项。它还使测试人员能够轻松地在标签之间切换,而无需完整的构建环境。

要在标记中获取二进制文件,可以使用以下过程:

  1. 查看干净的工作副本
  2. 运行构建脚本并评估任何测试结果
  3. 如果构建正常,svn添加 二进制文件
  4. 而不是承诺到行李箱 或分支,直接从您的标签 像这样的工作副本:svn copy myWorkingCopyFolder myTagURL
  5. 丢弃工作副本以避免 意外提交的二进制文件 树干或树枝
  6. 我们有一个tagbuild脚本可以自动执行第3步和第4步。

答案 8 :(得分:2)

一个很好的理由是快速获得在新机器上运行的可执行文件。特别是如果构建环境需要一段时间来设置。 (加载编译器,第三方库和工具等)

答案 9 :(得分:1)

签入重要二进制文件违反了源代码/ SVN的使用原则,即源代码管理中的文件应具有差异的有意义属性。

今天的源文件与昨天的源文件有很大的不同;差异将产生一组对人类读者有意义的变化。办公室前面的今天图片确实对昨天的办公室照片有一个有意义的差异。

因为图像之类的东西不具备差异的概念,为什么要将它们存储在存在记录的系统中并在文件之间存储差异

基于版本的存储是关于存储文件更改的历史记录。 (例如)JPEG文件的数据没有任何变化的历史记录。这些文件也可以完美地存储在目录中。

更实际的是,在SVN中存储大文件 - 构建输出文件 - 使得结帐速度变慢。有可能滥用SVN作为通用二进制存储库。一开始看起来一切都很好 - 因为二进制文件不多。当然,文件数量会随着时间的推移而增加;我见过模块需要小时才能结账。

最好将大型关联二进制文件(和输出文件)存储在目录结构中,并从构建过程中引用它们。

答案 10 :(得分:1)

在我的项目中,我通常使用服务器上的特殊工作副本构建后构建挂钩,即从HTTP浏览器可以访问的路径。这意味着,每次提交后,任何[可以阅读内部网络]的人都可以轻松下载相关的二进制文件。 没有一致性问题,即时更新+自动化测试的途径。

答案 11 :(得分:1)

版本控制应该包含你需要做的所有事情:svn co然后构建。它不应该有中间体或最终产品,因为这会破坏目的。您可以在SVN中为结果创建一个新项目,并单独生成二进制结果的版本(如果需要,可以为版本和补丁)。

答案 12 :(得分:0)

您是说您在同一个存储库中拥有源代码和构建结果?

这是日常构建的一个很好的论据,版本化的构建脚本位于单独的存储库中。存储库本身的二进制文件也不错,但源代码+构建结果对我来说看起来很糟糕

如果你构建了几个二进制文件并且没有注意到某个地方的构建破坏,那么你最终会得到来自不同版本的二进制文件,并且你正准备自己进行一些微妙的bug追踪。

倡导每日单独版本的autobuild脚本,而不是仅仅针对二进制文件+代码

答案 13 :(得分:0)

  • Subversion是源控制管理器 - &gt;二进制文件不是源
  • 如果使用“svn up”命令更新生产,所有具有提交权限的开发人员都可以更新/修改/破坏生产?

替代方案:使用Hudson或Cruise Control等持续集成。

答案 14 :(得分:0)

我认为,当二进制文件被用于VCS时,做坏事的感觉是由于资源经济性和双重数据管理的缺点所致的基本理念,即永远不应该将多余的东西放在存档中。 / p>

这就是为什么:如果您可以从该特定版本的其他文件轻松地重建您的归档工作状态,例如简单的重新编译或安装标准设置,您就不应该提交此类二进制文件,而是提交类似README或安装文件。如果重建失败的困难或风险太大,请提交。