Gemfile.lock 应该列入 .gitignore 吗?

544

我对bundler及其生成的文件还比较新。我有一个来自GitHub的git仓库副本,许多人正在对其进行贡献,所以我很惊讶发现bundler创建了一个在仓库中不存在且不在.gitignore列表中的文件。

由于我已经fork了它,我知道将其添加到仓库中不会对主仓库造成任何影响,但如果我提交pull request,会引起问题吗?

Gemfile.lock应该包含在仓库中吗?


相关:https://dev59.com/NGYr5IYBdhLWcg3wAFg0 - ripper234
2
如果您来到这里是因为Linux和Windows的机器共享同一个repo,请参考Joe Yang的答案。在我写这篇文章时,它排名第三。另外请参见https://dev59.com/NGYr5IYBdhLWcg3wAFg0 - Peter Berg
9个回答

598

2022年更新来自TrinitronX

快进到2021年,现在Bundler文档[网络存档]中建议将Gemfile.lock提交到gem内...¯_(ツ)_/¯ 我猜这对开发人员和项目的易用性是有意义的。然而,现在CI任务需要确保删除任何杂散的Gemfile.lock文件以测试其他版本。

2010年左右

假设你不是写Ruby gem,那么Gemfile.lock应该在你的代码仓库中。它被用作所有所需gem及其依赖项的快照。这样一来,每次部署等操作时,bundler就不必重新计算所有gem的依赖关系。

以下是cowboycoded的评论:

如果你正在开发一个gem,则不要检入你的Gemfile.lock。 如果你正在开发Rails应用程序,则应检入Gemfile.lock。

这篇文章解释了锁定文件的作用。


93
取决于你正在处理什么内容。如果你正在处理宝石(gem),那么不要提交Gemfile.lock文件。如果你正在处理Rails应用程序,那么确保提交Gemfile.lock文件。更多信息请参考这里- http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/ - johnmcaliley
1
你应该在你的回答中加入cowboycoded关于gems的建议。 - aarona
那我应该开发一个带有 bundle install; bundle exec 命令并忽略 Gemfile.lock 的 Ruby gem 吗? - Ciro Santilli OurBigBook.com
5
请不要这样做!保留你的Gemfile.lock在原地!就像这里这里所说的那样。 - Ricardo Ruwer
1
快进到2021年,现在Bundler文档 [网络存档]建议将Gemfile.lock提交到gem中... ¯\(ツ)/¯ 我想这对于开发人员和项目启动时的使用方便是有意义的。然而,现在CI作业需要确保删除任何杂散的Gemfile.lock文件以测试其他版本。 - TrinitronX
显示剩余5条评论

53

当你在使用一个需要可配置数据库适配器的开源 Rails 应用程序时,实际问题出现了。我正在开发 Fat Free CRM 的 Rails 3 分支。

我更喜欢 postgres,但我们希望默认数据库是 mysql2。

在这种情况下,Gemfile.lock仍然需要与默认的一组 gems 一起检入,但我需要忽略我在本地所做的更改。为了实现这一点,我运行:

git update-index --assume-unchanged Gemfile.lock

然后进行反转:

git update-index --no-assume-unchanged Gemfile.lock

在您的Gemfile中加入以下代码也非常有用。这将根据您的database.yml加载相应的数据库适配器Gem。

# Loads the database adapter gem based on config/database.yml (Default: mysql2)
# -----------------------------------------------------------------------------
db_gems = {"mysql2"     => ["mysql2", ">= 0.2.6"],
           "postgresql" => ["pg",     ">= 0.9.0"],
           "sqlite3"    => ["sqlite3"]}
adapter = if File.exists?(db_config = File.join(File.dirname(__FILE__),"config","database.yml"))
  db = YAML.load_file(db_config)
  # Fetch the first configured adapter from config/database.yml
  (db["production"] || db["development"] || db["test"])["adapter"]
else
  "mysql2"
end
gem *db_gems[adapter]
# -----------------------------------------------------------------------------

我不确定这是否是一个已经确立的最佳实践,但对我来说它运作良好。


2
非常有用的信息...不确定为什么你只有3个点,而一个不太有用的答案却有50多个点。哦,是的,请看日期戳。(SO的一个巨大失败之一就是在问题提出后尽快回答会获得不成比例的好处。) - iconoclast
1
@iconoclast:非常感谢您发布的内容。我认为,许多人来到这篇文章,包括我在内,都被问题标题“蒙住了眼”。我现在意识到,我的答案只回答了一个特定的用例,并不一定是这个问题的正确答案。我将在不久的将来更新它。话虽如此,如果我的答案不能满足提问者的需求,他/她不应该将其标记为正确答案。 - rwilliams

41

我和我的同事因为使用不同的操作系统(Windows和Mac),所以我们的Gemfile.lock文件不同。而我们的服务器是Linux系统。

我们决定在代码仓库中删除Gemfile.lock文件,并创建一个名为Gemfile.lock.server的文件,就像database.yml一样。然后,在部署到服务器之前,我们使用cap deploy hook将Gemfile.lock.server复制到服务器上的Gemfile.lock文件中。


7
我有一个在OSX上开发的应用程序,需要部署到Windows服务器上。使用Git追踪Gemfile.lock证明不是个好主意,所以它被写进了我的.gitignore文件中。很多gem需要在不同的环境下使用不同的版本。理想情况下,你应该尽量避免这种情况,但我没有选择(该死的IT部门!)。 - brad

12

同意r-dub的观点,将其存储在源代码控制中,但对我来说,真正的好处是:

相同环境下的协作(忽略windohs和linux/mac等不同操作系统)。在Gemfile.lock之前,安装项目的下一个人可能会看到各种令人困惑的错误,怪罪自己,但他只是那个非常幸运的人获得了超级宝石的下一个版本,破坏了现有的依赖关系。

更糟糕的是,这种情况发生在服务器上,除非遵循纪律并安装完全相同的版本,否则会得到未经测试的版本。而Gemfile.lock明确地指出了这一点,并且它会明确告诉您版本的差异。

注意:要将内容分组,如:开发(:development)和测试(:test)。


11

打包程序文档也提到了这个问题:

原文链接:http://gembundler.com/v1.3/rationale.html

编辑后链接:http://web.archive.org/web/20160309170442/http://bundler.io/v1.3/rationale.html

请查看名为“将您的代码提交到版本控制”的部分:

开发应用一段时间后,将应用和 Gemfile 以及 Gemfile.lock 快照一起提交。这样,你的代码库就记录了所有 gem 的确切版本,这些 gem 是上次你确定应用程序正常工作时使用的。请记住,尽管 Gemfile 列出的仅有三个 gem(具有不同程度的版本严格性),但是考虑到你所依赖的所有 gem 的隐式要求,你的应用程序实际上依赖于数十个 gem。
这很重要:Gemfile.lock 使你的应用程序成为一个单一的包,其中包含你自己的代码和上次你确定一切正常运行时运行的第三方代码。在 Gemfile 中指定第三方代码的确切版本不会提供相同的保证,因为 gem 通常会为其依赖项声明一个版本范围。
下次在同一台机器上运行 bundle install 时,bundler 将看到它已经拥有你需要的所有依赖项,并跳过安装过程。
不要检入 .bundle 目录或其中的任何文件。这些文件对每个特定的机器都是特定的,并用于在运行 bundle install 命令之间持久化安装选项。
如果你运行了 bundle pack,那么你的 bundle 所需的 gem(尽管不包括 git gem)将被下载到 vendor/cache 中。如果你需要的所有 gem 都存在于该文件夹中并检入到你的源代码控制中,那么 Bundler 可以在不连接到互联网(或 RubyGems 服务器)的情况下运行。这是一个可选步骤,不建议使用,因为会增加源代码控制库的大小。

11
在2021年,与Rubygems相关的Gemfile.lock也应该被包含在版本控制中。原来接受的答案已经有11年了。
以下是一些理由(从评论中挑选):
@josevalim https://github.com/heartcombo/devise/pull/3147#issuecomment-52193788 “Gemfile.lock应该留在存储库中,因为贡献者和开发人员应该能够fork项目并使用保证可用的版本运行它。”
@rafaelfranca https://github.com/rails/rails/pull/18951#issuecomment-74888396 “我不认为忽略锁文件即使对于插件也是个好主意。这意味着“git clone; bundle; rake test”序列不能保证通过,因为您的几十个依赖项中的一个被升级并导致代码中断。 此外,正如@chancancode所说,它使二分法更加困难。”
Rails也在git中使用Gemfile.lock:

7
没有 Gemfile.lock 意味着:
  • 新的贡献者无法运行测试,因为奇怪的错误会出现,这样他们就不会做出贡献或者提交失败的 pull request...这是一次糟糕的体验。
  • 如果你没有本地的 Gemfile.lock,那么你不能回到 x 年前的项目并修复一个 bug,而不必更新 / 重写整个项目。
-> 始终检查 Gemfile.lock, 如果需要更加彻底,可以让 Travis 删除它。 https://grosser.it/2015/08/14/check-in-your-gemfile-lock/

4
有点晚了,但我还是花了一些时间和外来阅读才理解了这个问题。所以我想总结一下我对于Gemfile.lock的了解。
在构建Rails应用程序时,您在本地机器上使用某些gem版本。如果您想避免生产模式和其他分支中出现错误,则必须在各处使用那个Gemfile.lock文件,并告诉bundler每次更改时重新构建gems。
如果在生产机器上Gemfile.lock已更改,而Git又不允许您进行git pull操作,您应该写入git reset --hard以避免该文件更改,然后再执行git pull操作。

如果一个文件会自动更改,例如通过构建过程,那么这明显表明它不应该被添加到版本控制中。 - Thomas S.

0
其他答案是正确的:是的,您的 Ruby 应用程序(而不是 Ruby gem)应该在仓库中包含 Gemfile.lock。为了更好地解释 为什么 应该这样做,请继续阅读:
我曾错误地认为每个环境(开发、测试、暂存、生产等)各自都会执行 bundle install 来构建自己的 Gemfile.lock。我的假设基于 Gemfile.lock 不包含任何分组数据(如:test、prod 等)。但是这种假设是错误的,我在本地问题上也发现了这一点。
经过进一步调查,我感到困惑的是我的 Jenkins 构建显示成功获取了特定的 gem(ffaker,FWIW),但当应用加载并要求 ffaker 时,它却显示文件未找到。WTF?
通过一些更深入的调查和实验,我明白了这两个文件的作用:
首先,它使用 Gemfile.lock 获取所有的 gem,即使这些 gem 在此环境中不会被使用。然后,它使用 Gemfile 来选择在此环境中实际使用哪些已获取的 gem。

因此,尽管它基于Gemfile.lock在第一步中获取了gem,但由于Gemfile中的分组,在我的:test环境中并没有包含它。

解决方法(在我的情况下)是将 gem 'ffaker' 从:development组移动到主要组,以便所有环境都可以使用它。(或者根据实际情况将其仅添加到 :development、:test 中)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接