源代码控制中的二进制文件


30

当针对嵌入式设备和其他奇异世界进行开发时,很可能您的构建过程将包含多个专有二进制文件,并使用它们的特定版本。所以问题是,它们是否在源代码管理中?我的办公室遵循“从源代码管理中签出包含编译代码所需的一切”的规则,这引起了一些严重的争论。

我反对这一观点的主要论点是使源代码控制数据库过大,缺少差异二进制文件(请参阅有关该主题的先前问题)。这违背了签出,构建,知道您具有先前开发人员想要的精确环境并且没有收集适当文件(具有特定版本的情况!)的能力。


3
另外,您可以编写bash / python / perl / bat脚本来检出源代码,并在一个步骤中下载所有其他相关组件。但是,我仍然建议将二进制文件签入版本控制中,只是为了保留修订版本。唯一不应该检入到存储库中的文件是可以轻松地从版本控制文件中重新生成的文件。磁盘空间很便宜,不应成为主要考虑因素。
Lie Ryan

Answers:


28

版本控制(误称:源代码控制)的想法是让您回溯历史记录,恢复更改的效果,查看更改及其原因。这是一系列要求,其中一些要求使用二进制事物,而有些则不需要。

示例:要进行嵌入式固件工作,通常会具有完整的工具链:要么花费很多钱的专有编译器,要么使用某些版本的gcc。为了获得运输可执行文件,您需要工具链以及源代码。

将工具链检入版本控制是一件很痛苦的事情,diff实用程序非常糟糕(如果有的话),但是别无选择。如果您希望保留供5年之内来查看您的代码以弄清楚它做什么的家伙的工具链,那么您别无选择:您还必须使该工具链处于版本控制之下。

多年来,我发现执行此操作的最简单方法是制作安装CD的ZIP或ISO映像并将其检入。检入注释必须是工具链的特定制造商版本号。如果是gcc或类似的东西,则将您正在使用的所有内容捆绑到一个大ZIP中,然后执行相同操作。

我做过的最极端的情况是Windows XP Embedded,其中的“工具链”是运行中的Windows XP VM,其中包括(当时)SQL Server和一堆配置文件以及成百上千个补丁文件。安装整个批次并进行更新需要大约2-3天的时间。保留后代意味着将ENTIRE VM放入版本控制中。看到虚拟磁盘由大约6 x 2GB的映像组成,它实际上运行得很好。听起来很重要,但是5年后,追随我并不得不使用它的人的生活变得非常轻松。

简介:版本控制是一种工具。使用它是有效的,不要迷恋诸如单词含义之类的东西,也不要称其为“源代码控制”,因为它比这更大。


1
何时需要将VM更新到12 GB的回购气球?即使您具有良好的二进制差异,您仍然在谈论10GB +存储库
TheLQ 2011年

3
好吧,不。如果使用VMWare,则可以使用磁盘快照。它们存储原始的基准磁盘映像,并添加仅包含增量的新文件,这些增量非常小。您只需要记住签入新创建的文件。最后我看一下,更新添加了大约250K-鸡饲料。此外,担心回购规模是没有意义的-磁盘很便宜。
quick_now 2011年

什么时候你的嵌入式工具链依赖于网络许可证:)什么

18

尼尔·福特(Neal Ford)在《生产力程序员》中指出,您应该将二进制文件保留在源代码控制中:

为什么要保留二进制文件?今天的项目取决于大量外部工具和库。假设您正在使用一种流行的日志记录框架(如Log4J或Log4Net)。如果在构建过程中没有为该日志库生成二进制文件,则应将其保留在版本控制中。即使有问题的框架或库消失了(或更可能在新版本中引入了重大更改),这仍使您可以继续构建软件。始终保持在版本控制中构建软件所需的整个Universe(减去操作系统,甚至可以使用虚拟化;请参见本章后面的“使用虚拟化”)。您可以通过将二进制文件保留在版本控制中和共享的网络驱动器上来优化保留二进制文件。这样,您不必每小时处理一次,但是可以保存它们,以防一年后需要重建某些内容。您永远不知道是否需要重建某些内容。您对其进行构建,直到它起作用为止,然后再忽略它。意识到您需要从两年前开始重建某些东西并且没有所有部分,这引起了恐慌。

我完全同意。虽然这可以说是颠覆了VCS并非为它设计的任务(保留二进制文件),但我认为其好处大于潜在的缺点。但是,正如作者稍后指出的那样,有时将二进制文件保存在VCS中可能不是一个实用的解决方案,因此应考虑其他选择-例如将其保存在映射的网络驱动器上。

如果二进制文件不太大,我肯定会将它们保留在VCS中。对于您的情况,这似乎更为正确,因为二进制文件可能很小,并且您使用的是非常特定的版本。由于多种原因,它们可能也很难找到(作者关闭了他们的网站,或者不再列出您需要的版本以供下载)。尽管不太可能,但您永远都不知道几年后会发生什么。

我希望几年前当我在使用图形库(这是dll文件)开发游戏时阅读本书。我中断了一段时间的开发,当我想继续时,由于项目终止,我无法再次找到该dll。


2
是的,这种情况经常发生。我有一个业余项目,我依靠的是3-4年前被其作者抛弃的扫描仪生成器。幸运的是,它一直处于版本控制之下。
Christian Klauser

9

原则上,我赞赏“检查构建源代码管理所需的一切”阵营,但是在过去的几年中,依赖管理已经通过Maven,Ivy和NuGet之类的工具发展了很多。

另外,在实践中,我发现签入二进制文件会产生许多不愉快的副作用。例如,Git / Mercurial并没有真正针对它进行调整,在合并包含二进制文件的分支时,Subversion和Perforce会让您发疯。

使用依赖项管理解决方案,您可以在项目中的源代码控制文件中指定项目所依赖的软件包名称和版本。几乎所有的依赖项管理工具都允许您按照某种版本控制和命名约定为依赖项创建一个私有存储库。进行构建时,依赖项管理工具将从已批准的来源列表中解析所有开源和专有依赖项,然后将它们填充到本地缓存中。下次使用相同的版本依赖性进行构建时,所有内容都已存在,并且运行速度更快。

然后,可以使用常规文件系统备份工具来备份您的专用存储库。

这样可以避免从源代码树中提取大量二进制文件时遇到的速度变慢的情况,并避免您的存储库中包含许多难以区分的文件。对于任何给定的依赖项,只有一个位置(按名称和版本号),因此没有合并冲突要处理,本地文件系统缓存意味着您不必花费时间评估本地副本是否已更改的开销。你拉更新。


8

源代码控制适用于源代码。来源是您无法通过其他方式构建的。某些符合源条件的文件恰好是二进制文件。

我的VCS签入了很多二进制文件,但每个二进制文件都是我未编写且未维护的某些产品的发行单位。这可能类似于GNU ccRTP,它作为压缩的tarball发布。该tarball是我的来源,并且可以通过一个自动化的步骤将其与所需的基础结构一起进行检入,以将其转换为成品(在我的情况下为Makefile和RPM规范)。当有新版本的ccRTP时,我将新的tarball视为已更改的源:将其放入已检出副本中,进行构建,测试并提交回VCS。对于不附带源代码的商业产品(编译器,库等),我也进行了相同的处理,并且其工作方式相同。除了解压缩配置编译包以外,它还只是解压缩包。每晚执行的软件不会make 并获得成品。

大多数VCS具有使人类可读源更易于处理和更有效存储的功能,但是如果二进制文件毫不费力地返回,则说它们不适合二进制文件并不是真的。VCS在内部如何处理二进制文件完全取决于其作者是否认为仅存储差异是值得的。就我个人而言,我认为将ccRTP发行版的完整副本存储在一个弹出窗口中的能力为600K,这远远超出了与我的所有其他来源一起标记版本的能力。


4

这使我想起了Java以前的“存储库中的罐子”问题。人们使用构建Java应用程序的方式将其依赖项(二进制jar文件)推送到存储库中。每个人都对此感到满意,因为我们将拥有“一键式”构建系统,并且磁盘空间很便宜,所以谁在乎。然后出现了Maven,您可以摆脱所有的二进制混乱,并且仅使用本地缓存的存储库仍可以维护bullet-prof生成。仍然可以使用“一键式”构建系统,但是源代码控制不必乱码没有意义的二进制文件。

是的,您可以从源代码管理中获取二进制文件,但这需要您调整构建系统,以便在构建时获取它们。如果没有专用软件(例如Maven),则可能要花很多精力才能将它们发布出来。


1
我担心使构建过程复杂化,主要是因为团队中很大一部分是数学家,而不是过程的忠实拥护者。
丹尼尔·戈德堡

3

您的源代码控制将代码保存在您的工作中。如果可以从源中重构给定的二进制Blob,则它不是源,因此不应放入源代码存储库中。在源代码管理中,只有不可重现的Blob应该存在。

通常,您会通过源的时间创建另一个 二进制Blob的存储库网络文件夹。这些可以部署到客户或用于项目中(而不是每次都从头开始构建所有内容)。

因此,如果它是来源,则将其放入。如果没有就不要。


谁会否决这个?有趣的原因:D

不是我,但我怀疑谁不同意答案的第二部分。
Joel Coehoorn

@JoelCoehoorn,很有意思,因为这正是Maven存储库。

2

目的是能够获得最新的代码并构建它,而无需安装/设置任何东西(因此,是“单击”构建)。

在我去过的许多地方,这意味着要检查依赖项的二进制文件。在其他情况下,这意味着构建脚本会自动下载并获取依赖项。

请参阅Derek Greer撰写的有关此主题的博客文章


2

我正在一个有两个不同构建阶段的项目中工作

  • 与数千个源代码文本文件相比,“主程序构建”只需要几个二进制文件,因此这些二进制文件将被检入到存储库中。这很好。

  • 安装程序的构建需要很多第三方组件(其中一些只是复制到安装CD中,如Adobe Reader)。我们不会将它们放入存储库中。相反,这些组件驻留在网络驱动器上(甚至是它们的较旧版本),并且构建脚本将它们复制到正确的位置。当然,要生成可复制的版本,任何人都必须注意不要更改存储第三方组件的任何文件夹。

两种策略都可以正常工作,并且满足“从源代码管理中检出包括编译代码所需的所有内容”的要求。


1

您需要保留将来在某个时候重建产品特定版本所需的一切。

但是,您不必将所有内容保留在“源代码管理”中。

一家公司保留了冻结的服务器机架(因为操作系统仅在该特定硬件上运行,而工具链仅在该操作系统上运行,并且源依赖于该工具链)。无法将其检入Source Control。

如果确实需要拆分构建要求,那么您将面临使两个版本控制系统保持同步的会计问题。例如,此壁橱中的硬件盒,或此保留的备份卷中的VM或二进制文件,均与此SVN源代码版本一起使用,等等。这与使用单个源控制系统相比更混乱,但可以解决。


0

在我看来,将二进制文件检入到SCM中非常混乱。我运行了一个非常复杂的项目,该项目对第三方库有很多依赖性。我们采用的原则:

  1. 所有源代码均由SCM管理
  2. 所有依赖项都由具有良好的Eclipse集成的Ivy管理。

效果很好。我们有一个与每个外部库的版本有关的配置文件,可使用该库编译源代码。此配置文件已签入SCM,因此随着源代码的发展而发展。通过应用这种方法,我们可以准确地复制构建,而不会弄乱外部库的版本。


0

就个人而言,从哲学上讲,我倾向于让源代码管理程序检入指向大型二进制文件(较小的二进制资源是可以的)的指针,而不是文件的内容。该指针将包含二进制文件内容的哈希。

二进制文件本身将不受源代码管理的管理。它会存储在某种类型的库中,在其中可以使用指针或专门的哈希来检索它。

Git LFS和git annex可以做到这一点,但是他们也在一定程度上尝试管理二进制文件,我不希望他们这样做。我希望Git仅存储校验和,并告诉我我的二进制文件是否已更改-但我不希望它尝试管理它们并存储它们。我想自己做。

我认为git可以处理中小型二进制文件,但我不确定它是否是管理大型二进制文件的正确工具。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.