我们最近开始使用git并且当有人提交了一个大的(~1.5GB文件)时出现了一个令人讨厌的问题,然后导致git在各种32位操作系统上崩溃。这似乎是一个已知的错误(git mmaps文件到内存中,如果它无法获得足够的空间,它就无法工作),这不会很快得到修复。
简单(对我们而言)的解决方案是让git拒绝任何大于100MB左右的提交,但我无法想办法做到这一点。
编辑:问题来自意外提交大文件,在这种情况下是一个大的程序输出转储。目的是避免意外提交,只是因为如果开发人员意外地提交了一个大文件,然后尝试将其恢复到存储库是一个下午,没有人可以做任何工作,并且必须修复所有本地分支他们有
答案 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}