在Git中对大型文本文件进行版本控制

17

我已经使用git进行源代码控制有一段时间了,非常喜欢它。所以我开始研究使用git来存储大量的二进制文件,但是我发现这似乎不是git的强项。那么对于大文本文件呢?看起来git应该能够很好地处理它们,但我也遇到了问题。

我正在使用一个550mb大小的mbox格式文本文件进行测试。我初始化了一个新的仓库进行测试。以下是我的结果:

  • 使用git add和git commit - 仓库总大小为306mb - 仓库包含一个306mb大小的对象
  • 向邮箱文件添加一封电子邮件并git commit - 仓库总大小为611mb - 仓库包含两个大小均为306mb的对象
  • 再添加一封电子邮件并git commit - 仓库总大小为917mb - 仓库包含三个大小均为306mb的对象

因此,每次提交都会向仓库添加电子邮件的新副本。现在我想尝试将仓库的大小减少到可管理的范围。以下是我的结果:

  • git repack -adf - 仓库总大小为877mb - 仓库包含一个876mb大小的pack文件
  • git gc --aggressive - 仓库总大小为877mb - 仓库包含一个876mb大小的pack文件

我希望能够将仓库大小减小到大约306mb左右,但我无法弄清楚如何做到。任何更大的值都意味着存储了大量重复数据。

我的希望是,仓库只会增加新电子邮件的大小,而不是整个邮箱的大小。我并不想在这里使用版本控制来控制电子邮件,但这似乎是我从使用每晚脚本增量备份用户主目录中受阻的关键问题。

有没有什么建议可以避免在向非常大的文本文件末尾插入少量文本时导致存储库大小急剧增加的情况?

我已经看过 bup 和 git annex,但如果可能的话,我想坚持使用原生的 git。

感谢您的帮助!


1
请检查您的邮件客户端是否压缩或加密了 mbox 文件。另外,请问您正在使用哪个版本的 git? - Schwern
1
并没有真正回答你所问的问题。但也许可以使用maildir代替? - Edward Thomson
mbox文件是纯文本格式,git diff仅显示新电子邮件附加到文件末尾。我使用的是git版本1.7.5.4(我没有意识到我已经落后了),现在已升级到git版本1.7.7,但仍然看到与我最初发布的相同行为。我还尝试在一个64mb的mbox文件上执行相同的过程,即使有几封新邮件和提交,git gc仍将该存储库保持在约34mb左右。因此,似乎mbox文件的大小是问题所在(原始测试文件为550mb)。 - user1020774
1
我刚发现了 core.bigFileThreshold 这个选项,并将其设置为1024mb,现在 git gc 尝试进行增量压缩,而之前它会非常快速地通过这一步骤。但现在我遇到了内存不足的错误。我正在尝试一些其他的配置选项来看是否能够解决此问题。 - user1020774
1
我在配置文件中添加了pack.windowMemory,并将其设置为256m。现在运行git gc正常工作,而且即使有更多的新邮件和提交,我的存储库大小也不到300mb。因此,解决方案似乎是使用最新版本的git并将core.bigFileThreshold和pack.windowMemory设置为适当的值。 - user1020774
显示剩余2条评论
4个回答

5
Git并非最佳备份工具,但它应该能够高效地处理向文本文件追加的操作。我对您的结果持怀疑态度。我在OS X上使用了354兆字节的文件和git 1.7.7重复了您的实验。以下是我的操作及.git文件的大小。
1. git init(52K) 2. git add mbox && git commit(110M) 3. cat mail1 >> mbox && git commit -a -m(219M) 4. git gc(95M) 5. cat mail2 >> mbox && git commit -a -m(204M) 6. git gc(95M)
如您所见,git非常高效。94兆是压缩后的mbox大小,它已经不能再更小了。
我猜测您可能正在使用旧版本的git,或者您的邮件客户端正在压缩或加密您的mbox文件。
请注意以下事项:
- 检查git所看到的mbox内容是否为纯文本。 - 如果您未使用最新版本的git,请升级并重试。

3

我认为Git通常不能很好地存储增量,即使您可以将其弄成这样,它也不会是确定性的。尽管如此,根据http://metalinguist.wordpress.com/2007/12/06/the-woes-of-git-gc-aggressive-and-how-git-deltas-work/,您可以尝试使用git repack -a -d --depth=250 --window=250

我认为您最好的选择是使用git --rebase截断您的历史记录,只存储过去几个备份。您可以使用git分支来实现这一点。创建一个名为yearly、monthly和daily的分支。每天,提交到daily,然后使用git rebase --onto HEAD~4 HEAD~3 daily删除早于3天的备份。每周的第一天,检出weekly并git cherry-pick daily,然后执行相同的git rebase以删除早于3周的周备份。最后,在每年的第一天,遵循类似的过程。在每次完成此序列后,您可能需要执行git gc以释放旧空间。
但如果您这样做,就没有充分利用git,并且滥用了它的工作方式。我认为适合您的最佳备份解决方案不涉及git。

我对2007年关于Git的任何信息都持怀疑态度,特别是内部信息,尤其是垃圾收集方面。因为当时Git仅有两年的历史。 - Schwern
引用的 WordPress 网站已被删除。 - zBeeble

2

大文件的一个副作用是git diff可能会耗尽内存。

虽然Git不是正确的工具(如其他答案中所提到的),但至少在Git 2.2.0(2014年第四季度)中减轻了git diff问题。
请参见Nguyễn Thái Ngọc Duy (pclouds)的提交commit 6bf3b81

diff --stat:标记任何大于core.bigfilethreshold的文件为二进制文件

过大的文件可能导致无法分配内存。
如果发生这种情况,它可能会影响涉及diff的许多命令。
此外,太大的文件无论如何比较效率低下(并且很可能是非文本文件),因此将它们标记为二进制文件并跳过查看其内容。


1

尽管打包对象后能看到多大的差异取决于文件类型等因素,但是Git不是备份工具,也不应该用于备份。如果您看一下Git的整个哲学,它基于磁盘空间便宜和对操作速度进行优化的假设。此外,无论文件类型是二进制还是文本,Git都会以相同的方式存储它们,当然,如上所述,文件类型将决定打包后您能看到多少差异。只有为了进行差异比较和其他目的,Git才区分二进制文件和文本文件,而不是用于存储。

使用适当的备份工具,这样可以节省磁盘空间。像ZFS这样的备份工具值得一试:https://svn.oss.prd/repos/SHAW/BuildAndReleaseTransition/TeamCity/TeamCityConfiguration-39/TeamCityConfiguration.docx


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