Close

Git 和项目依赖关系

Nicola Paolucci 头像
Nicola Paolucci

开发人员推广人员

考虑以下问题。您如何使用 git 处理项目依赖关系?我们的项目由多个相互依赖的存储库组成。目前,我们使用 svn:externals 来管理这些文件。用 git 处理这些问题的最好方法是什么?如何使用 git 将一个非常大的存储库拆分为较小的组件?以下是我们在最近的 Geting Git Right 巡回展欧洲站遇到的最常见问题的一些示例。

对于许多采用 git 的软件团队来说,这个话题似乎是一个很大的痛点,所以在本文中,我将尝试阐明这个问题。

显然,项目依赖关系和构建基础架构是两个交织在一起的领域,即使在 Atlassian 内部,也引发了关于“构建未来”的讨论。

使用单独的存储库而不是单个存储库可能会使某些事情变得更加困难。但这是软件项目演变中相对自然(有时是强制性的)步骤,这至少有两个主要原因:构建时间的增加以及项目之间的共同依赖关系。

考虑以下问题。您如何使用 git 处理项目依赖关系?我们的项目由多个相互依赖的存储库组成。目前,我们使用 svn:externals 来管理这些文件。用 git 处理这些问题的最好方法是什么?如何使用 git 将一个非常大的存储库拆分为较小的组件?以下是我们在最近的 Geting Git Right 巡回展欧洲站遇到的最常见问题的一些示例。

对于许多采用 git 的软件团队来说,这个话题似乎是一个很大的痛点,所以在本文中,我将尝试阐明这个问题。

显然,项目依赖关系和构建基础架构是两个交织在一起的领域,即使在 Atlassian 内部,也引发了关于“构建未来”的讨论。

使用单独的存储库而不是单个存储库可能会使某些事情变得更加困难。但这是软件项目演变中相对自然(有时是强制性的)步骤,这至少有两个主要原因:构建时间的增加以及项目之间的共同依赖关系。


简要介绍:指导方针和次优解决方案


那么回到问题:如何使用 git 跟踪和管理项目依赖关系?

如果可能的话,您不能!

玩笑到此为止,我先粗略地回答一下,然后再深入介绍。请注意,没有灵丹妙药(无论是 git 还是其他方式)可以轻松解决与项目依赖关系相关的所有问题。

一旦项目增长到一定规模,将其拆分成逻辑组件是有意义的,但不要等到单个存储库中有一亿多行代码后再这样做。因此,以下只是指导方针,以便您可以设计自己的方法。

Git 徽标
相关资料

安装 Git

Bitbucket 徽标
查看解决方案

了解 Bitbucket Cloud 的 Git

第一选择:使用适当的构建/依赖关系工具代替 git


我目前推荐使用依赖关系管理工具来处理大型项目的成长难题和构建时间。

将模块分隔在各个存储库中,并使用专为工作构建的工具管理它们的相互依赖关系。(几乎)每个技术堆栈都有一个。一些示例:

  • 如果您使用 Java 的话 Maven(或 Gradle
  • 用于节点应用的 Npm
  • BowerComponent.io 等,如果您使用 Javascript(已更新!) 的话
  • 如果您使用 Python 的话,则为 Pip 和 requirements.txt
  • RubyGems,如果您使用 Ruby,则是 Bundler
  • 适用于 .NET 的 NuGet
  • 适用于 C++ 的 Ivy(或一些自定义 CMake 操作)(已更新!)
  • 适用于 Cocoa 的 CocoaPods iOS 应用
  • PHP 的 ComposerPhing(已添加!)
  • Go 中,构建/依赖关系基础架构在某种程度上内置在语言中(尽管人们一直在研究更完整的解决方案,请参阅 godep)。对于我们的 Git 服务器 Bitbucket Server,我们同时使用 Maven 和 Bower。构建时,所选工具将拉取正确版本的依赖关系,以便可以构建您的主项目。其中一些工具存在局限性,所做的假设不是最优的,但经过验证且可行。

拆分项目的痛苦

简单地说,在项目开始时,所有内容都打包在一个版本中。但是随着项目的发展,这可能会导致您的构建太慢——这时您需要“缓存”,而这正是依赖关系管理的用武之地。顺便说一句,这意味着子模块(见下文)非常适合动态语言。基本上我认为大多数人需要在某个时候担心构建时间,这就是为什么您应该使用依赖关系管理工具。

将组件拆分成不同的存储库会带来一些严重的麻烦。顺序不分先后:

  • 变更组件需要发布版本
  • 需要时间,可能会因为很多愚蠢的原因而失败
  • 对微小的变更感觉很愚蠢
  • 它需要为每个组件手动设置新的版本
  • 阻碍存储库的可发现性
  • 在单个存储库中并非所有源代码都可用时进行重构
  • 在某些设置(比如我们的设置)中,更新 API 需要产品的里程碑版本,然后是插件,然后是产品。我们可能漏了几样,但您应该懂的。距离这个问题的完美解决方案还很远。

第二选择:使用 git 子模块


如果您不能或不想使用依赖关系工具,git 有处理 submodules 的功能。子模块可能很方便,尤其是对于动态语言来说。但是,它们不一定能使您免于缓慢的构建时间。我已经写了一些这方面的指导方针和提示,还探讨了替代方案。网上也有人反对使用子模块

svn:external 和 git 之间的一对一匹配

但是!如果您正在寻求 svn:externalsgit 之间的一比一匹配,您可以使用 submodules 来确保 submodules 只跟踪发布分支,而不是随机提交。

第三种选择:使用其他构建和跨堆栈依赖关系工具


您不会一直想要一个完全统一、可以用单个工具构建和组装的项目。例如,一些移动项目需要兼顾 Java 和 C++ 依赖关系,或者使用专有工具来生成资产。对于那些更复杂的情况,您可以在上面多加一层来增强 git。这个领域一个很好的例子是安卓的代码存储库

其他值得探索的构建工具:

结论和进一步阅读


Charles O'Farrell 深思熟虑地提出了有关建设基础架构(和 Maven)主题的更多建议,这非常值得深思:

最后,我想引用上面后一篇文章中的精彩引文。尽管它与 Maven 有关,但它同样可以应用于其他构建和依赖关系工具:

“缓存除了加快速度之外没有其他用处。您可以完全移除缓存,相关系统能照常工作,只是速度会变慢。缓存也没有副作用。无论您过去对缓存执行了哪些操作,将来对缓存的给定查询都会给同一个查询返回相同的值。

Maven 的体验与我描述的截然不同!Maven 存储库的使用方式与缓存类似,但没有缓存的属性。当您从 Maven 存储库请求某些内容时,您过去执行的操作非常重要。它会返回您最近输入的内容。如果您在放入之前请求一些内容,它甚至可能会失败。”

Nicola Paolucci

Nicola is an all-round hacker who loves exploring and teaching bleeding edge technologies. He writes and talks about Git, development workflows, code collaboration and more recently about Docker. Prior to his current role as Developer Instigator at Atlassian he led software teams, built crowd sourcing applications for geo-spacial data, worked on huge e-commerce deployments. Little known facts about Nicola: he gesticulates a lot while speaking (being Italian), lives in Amsterdam and rides a Ducati.


分享此文章

推荐阅读

将这些资源加入书签,以了解 DevOps 团队的类型,或获取 Atlassian 关于 DevOps 的持续更新。

人们通过满是工具的墙进行协作

Bitbucket 博客

Devops 示意图

DevOps 学习路径

与 Atlassian 专家一起进行 Den 功能演示

Bitbucket Cloud 与 Atlassian Open DevOps 如何协同工作

注册以获取我们的 DevOps 新闻资讯

Thank you for signing up