有没有好的方法绕过GitHub对文本文件100MB文件大小限制?

33

我有一个190 MB的纯文本文件,想在GitHub上跟踪。

这个文本文件是我们的文本转语音引擎的发音词典文件。我们经常添加和修改文本文件中的行,差异非常小,因此从这个意义上讲,它非常适合git。

然而,GitHub有严格的100 MB文件大小限制。我已经尝试了GitHub大型文件存储服务,但是每次更改都会上传整个190 MB文件的新版本-如果我走这条路,那么这个文件很快就会增长到几千兆字节。

我想保持这个文件作为一个文件,而不是拆分它,因为这是我们当前的工作流程,如果要在我们的工具中允许多个文本文件作为输入/输出,那么需要一些编码(而我们没有太多开发资源)。

我想到的一个主意是,也许可以设置一些预提交和后提交钩子来自动拆分和连接大文件?这可以实现吗?

其他想法?

编辑:我知道在StackOverflow上有类似问题中描述的100 MB文件大小限制,但我不认为我的问题是重复的,因为我正在询问特定情况,即差异小而频繁(我不尝试上传大的ZIP文件或任何其他东西)。但是,我的理解是git-lfs仅适用于很少更改的文件,而常规git非常适合我描述的文件类型;只是GitHub有一个文件大小限制。

更新:昨天我尝试创建一个使用Git钩子将文件拆分并合并为较小文件的小型跨平台程序进行实验。它有点有效,但不是很令人满意。您需要通过 .gitignore 将大文本文件排除在外,这使得Git无法知道它是否已更改。拆分文件最初不会被 git statusgit commit 检测到,并导致与此SO问题中描述的相同问题,这非常恼人:Pre-commit script creates mysqldump file, but "nothing to commit (working directory clean)"? 设置cron作业(Linux)和计划任务(Windows)以定期自动重新生成拆分文件可能会修复此问题,但自动设置不容易,可能会影响用户计算机的性能,并且不是一种非常优雅的解决方案。某些hacky的解决方法,例如动态修改.gitignore,也可能是必需的,而且您根本无法获得实际文本文件的差异,只能获得拆分文件的差异(尽管这可能可以接受,因为它们非常相似)。

所以,经过一夜的思考,今天我认为Git钩子方法实际上不是一个好选择,因为它有太多的怪癖。正如@PyRulez建议的那样,我认为我必须看一下GitHub之外的其他服务(不幸的是,因为我喜欢GitHub)。托管解决方案比自己管理服务器更可取。我也希望它是公开的...

更新2:我已经查看了一些替代GitHub的方案,目前我倾向于使用GitLab。我已经联系了GitHub支持团队,询问是否可以提高100MB限制,但如果他们不这样做,我就会将此特定项目切换到GitLab。


2
可能是 无法将大于100MB的文件推送到GitHub 的重复问题。 - Mayuso
2
@Mayuso,我知道这听起来与其他问题类似,但该问题涉及到一个文本文件,其中有频繁但较小的差异,如果可能以某种方式解决100 MB限制的问题。我知道二进制文件将不可能实现。 - josteinaj
1
我想我没有很好地理解问题,已经回答了,抱歉 :) - Mayuso
也许可以使用除GitHub以外的其他东西? - PyRulez
@PyRulez 如果你知道其他允许我跟踪190MB文本文件的git服务,我也很乐意听取其他建议(尽管我有点喜欢我们的Windows用户使用GitHub桌面版)。 - josteinaj
显示剩余7条评论
3个回答

17

清理和压缩

你可以使用清理和压缩来压缩文件。通常情况下这不是必要的,因为git会在内部进行压缩,但是由于GitHub的异常情况可能有所帮助。主要命令如下:

git config filter.compress.clean gzip
git config filter.compress.smudge gzip -d

GitHub会将此视为压缩文件,但在每台计算机上,它将显示为文本文件。

有关更多详细信息,请参见https://git-scm.com/book/en/v2/Customizing-Git-Git-Attributes

或者,您可以将干净的帖子发布到在线pastebin中,并从pastebin中提取smudge,例如http://pastebin.com/。 使用清洁和涂黑的许多其他组合也是可能的。


1
有趣的解决方案,谢谢!这可能会使190MB变小于100MB。我猜gzipped文件不可比较,所以每次文件更改时都会创建一个新文件。如果gzip将190MB压缩到50MB左右,每次提交仍会增加50MB的新内容。 - josteinaj
3
请参考 https://git-scm.com/book/zh/v2/Git-属性定制#二进制文件,了解如何正确地进行差异比较。 - PyRulez
2
@josteinaj https://git-scm.com/docs/gitattributes 有更深入的材料来回答这个问题。 - PyRulez
2
+1 这是一个绝对精彩的答案!我只有一个文件大小为116MB。我添加了两个过滤器,然后在.gitattributes中命名了我需要压缩的单个文件。优雅! - aardvarkk
2
@pyrulez,你能提供一些有关在.gitattributes文件中添加什么的更多信息吗? - Afflatus
显示剩余4条评论

10

2
是的,我尝试过了,但是我经常对文本文件进行更改,所以它会在LFS中经常创建一个新的190MB文件。据我所知,LFS最适合那些很少更改的文件。 - josteinaj
1
我同意GitHub中的git-lfs表现良好。我遇到的问题是它有带宽限制,对于企业系统来说,这个限制很快就会被超过或变得非常昂贵。他们不仅要收取存储文件的费用,而且在带宽的情况下,每次开发人员拉取您的LFS repo或每次拉取都需要付费。更糟糕的是,如果您有一个CIS。想象一下一个构建系统,其中一个二进制文件大小为300MB,您在发布之前有1300个构建。每个构建都会拉取Git LFS repo。你最终会发现GitHub变得有点昂贵。 - ConfusedDeer
1
很好,这正是我正在寻找的! - Tiago Martins Peres

4
你可以使用任何语言编写脚本/程序来划分或合并文件。下面是一个用Java编写的划分文件示例(我选择了Java因为我比其他语言更熟悉它,但其他语言也可以,有些甚至比Java更好)。
public static void main(String[] args) throws Exception
{
    RandomAccessFile raf = new RandomAccessFile("test.csv", "r");
    long numSplits = 10; //from user input, extract it from args
    long sourceSize = raf.length();
    long bytesPerSplit = sourceSize/numSplits ;
    long remainingBytes = sourceSize % numSplits;

    int maxReadBufferSize = 8 * 1024; //8KB
    for(int destIx=1; destIx <= numSplits; destIx++) {
        BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream("split."+destIx));
        if(bytesPerSplit > maxReadBufferSize) {
            long numReads = bytesPerSplit/maxReadBufferSize;
            long numRemainingRead = bytesPerSplit % maxReadBufferSize;
            for(int i=0; i<numReads; i++) {
                readWrite(raf, bw, maxReadBufferSize);
            }
            if(numRemainingRead > 0) {
                readWrite(raf, bw, numRemainingRead);
            }
        }else {
            readWrite(raf, bw, bytesPerSplit);
        }
        bw.close();
    }
    if(remainingBytes > 0) {
        BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream("split."+(numSplits+1)));
        readWrite(raf, bw, remainingBytes);
        bw.close();
    }
        raf.close();
}

static void readWrite(RandomAccessFile raf, BufferedOutputStream bw, long numBytes) throws IOException {
    byte[] buf = new byte[(int) numBytes];
    int val = raf.read(buf);
    if(val != -1) {
        bw.write(buf);
    }
}

这几乎不会花费任何时间和金钱。
编辑:您可以创建一个Java可执行文件并将其添加到您的存储库中,或者更简单的方法是创建一个Python(或任何其他语言)脚本来完成此操作,并将其保存为纯文本格式在您的存储库中。

1
谢谢!你知道在提交之前自动运行它并在检出后自动合并是否可能吗? - josteinaj
5
请查看 Unix/Linux 的 splitcat 命令。split -b 100M big-file big-file- ... cat big-file-* > big-file。其中,split 命令将大文件按照指定大小(这里是 100M)拆分成多个文件,并以 big-file- 作为拆分后的文件名前缀;而 cat 命令则用于合并多个文件的内容,big-file-* 表示匹配所有以 big-file- 开头的文件名,最后通过重定向符 > 将合并后的文件输出到一个名为 big-file 的文件中。 - Keith Thompson
@KeithThompson谢谢。我知道这些,但放弃了这个想法,因为我希望它也能在Windows上运行。然而,似乎Git即使在Windows上也在bash环境下运行其Git钩子,所以那些命令可能也可以在那里使用,我不确定。它们肯定比自己实现要简单得多(我用golang创建了一个小程序进行测试)。 - josteinaj

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