拒绝git中的大文件

时间:2009-05-13 14:27:19

标签: git large-files

我们最近开始使用git并且当有人提交了一个大的(~1.5GB文件)时出现了一个令人讨厌的问题,然后导致git在各种32位操作系统上崩溃。这似乎是一个已知的错误(git mmaps文件到内存中,如果它无法获得足够的空间,它就无法工作),这不会很快得到修复。

简单(对我们而言)的解决方案是让git拒绝任何大于100MB左右的提交,但我无法想办法做到这一点。

编辑:问题来自意外提交大文件,在这种情况下是一个大的程序输出转储。目的是避免意外提交,只是因为如果开发人员意外地提交了一个大文件,然后尝试将其恢复到存储库是一个下午,没有人可以做任何工作,并且必须修复所有本地分支他们有

4 个答案:

答案 0 :(得分:2)

什么时候出现问题?当他们最初提交文件或将文件推送到其他地方时?如果你有一个每个人都推送的暂存仓库,你可以实现一个更新挂钩来扫描更改大文件的refs,以及其他权限等检查。

非常粗略和准备好的例子:

git --no-pager log --pretty=oneline --name-status $2..$3 -- | \
  perl -MGit -lne 'if (/^[0-9a-f]{40}/) { ($rev, $message) = split(/\s+/, $_, 2) }
     else { ($action, $file) = split(/\s+/, $_, 2); next unless $action eq "A"; 
       $filesize = Git::command_oneline("cat-file", "-s", "$rev:$file");
       print "$rev added $file ($filesize bytes)"; die "$file too big" if ($filesize > 1024*1024*1024) }';

(只是去展示,一切都可以通过Perl单行完成,虽然可能需要多行;)

调用$ GIT_DIR / hooks / update的方式调用(args是ref-name,old-rev,new-rev;例如“refs / heads / master master~2 master”)这将显示添加的文件如果添加的太大,则中止。

请注意,我会说,如果你要警察这种事情,你需要一个集中点来做这件事。如果您相信您的团队只是互相交换更改,您应该相信他们了解添加巨大的二进制文件是一件坏事。

答案 1 :(得分:2)

您可以分发一个阻止提交的预提交挂钩。在中央存储库中,您可以通过分析接收到的数据并防止其被引用来获得预接收挂钩,该挂钩拒绝大块。将收到数据,但由于您拒绝更新refs,所有收到的新对象都将被取消引用,并且可以通过git gc接收和删除。

我没有你的剧本。

答案 2 :(得分:1)

如果您可以控制提交者的工具链,则可以直接修改git commit,以便在“实际”提交之前对文件大小执行合理性测试。由于核心的这种变化会给每个提交的所有git用户带来负担,并且“消除任何会进行1.5GB更改的人”的替代策略具有吸引人的简单性,我怀疑这样的测试永远不会被核心所接受。我建议你权衡维持一个当地的git - nannygit的负担 - 与过度雄心勃勃的承诺修复一个崩溃的git的负担。

我必须承认我很好奇1.5 GB的提交方式。是否涉及视频文件?

答案 3 :(得分:0)

Here is my solution. I must admit it doesn't look like others I have seen, but to me it makes the most sense. It only checks the inbound commit. It does detect when a new file is too large, or an existing file becomes too big. It is a pre-receive hook. Since tags are size 0, it does not check them.

    #!/usr/bin/env bash
#
# This script is run after receive-pack has accepted a pack and the
# repository has been updated.  It is passed arguments in through stdin
# in the form
#  <oldrev> <newrev> <refname>
# For example:
#  aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
#
# see contrib/hooks/ for an sample, or uncomment the next line (on debian)
#

set -e

let max=1024*1024
count=0
echo "Checking file sizes..."
while read oldrev newrev refname
do
#   echo $oldrev $newrev $refname
    # skip the size check for tag refs
    if [[ ${refname} =~ ^refs/tags/* ]]
    then
        continue
    fi

    if [[ ${newrev} =~ ^[0]+$ ]]
    then
        continue
    fi

    # find all refs we don't care about and exclude them from diff
    if [[ ! ${oldrev} =~ ^[0]+$ ]]
    then
        excludes=^${oldrev}
    else
        excludes=( $(git for-each-ref --format '^%(refname:short)' refs/heads/) )
    fi
#   echo "excludes " ${excludes}
    commits=$(git rev-list $newrev "${excludes[@]}")
    for commit in ${commits};
    do
#       echo "commit " ${commit}
        # get a list of the file changes in this commit
        rawdiff=$(git diff-tree --no-commit-id ${commit})
        while read oldmode newmode oldsha newsha code fname
        do
#           echo "reading " ${oldmode} ${newmode} ${oldsha} ${newsha} ${code} ${fname}
            # if diff-tree returns anything, new sha is not all 0's, and it is a file (blob)
            if [[ "${newsha}" != "" ]] && [[ ! ${newsha} =~ ^[0]+$ ]] && [[ $(git cat-file -t ${newsha}) == "blob" ]]
            then
                echo -n "${fname} "
                newsize=$(git cat-file -s ${newsha})
                if (( ${newsize} > ${max} ))
                then
                    echo " size ${newsize}B > ${max}B"
                    let "count+=1"
                else
                    echo "ok"
                fi
            fi
        done <<< "${rawdiff}"
    done
done

exit ${count}