限制 git 存储库中的文件大小
Posted
技术标签:
【中文标题】限制 git 存储库中的文件大小【英文标题】:Limiting file size in git repository 【发布时间】:2011-11-01 03:21:13 【问题描述】:我目前正在考虑将我的 VCS(从颠覆)更改为 git。是否可以在 git 存储库的提交中限制文件大小?前面。 G。颠覆有钩:http://www.davidgrant.ca/limit_size_of_subversion_commits_with_this_hook
根据我的经验,人们,尤其是那些没有经验的人,有时倾向于提交不应该进入 VCS 的文件(例如大文件系统映像)。
【问题讨论】:
【参考方案1】:由于我一直在努力解决它,即使是描述,而且我认为这也与其他人相关,我想我会发布一个如何实现 J16 SDiZ described 的实现。
所以,我对服务器端 update
钩子的看法是防止太大的文件被推送:
#!/bin/bash
# Script to limit the size of a push to git repository.
# Git repo has issues with big pushes, and we shouldn't have a real need for those
#
# eis/02.02.2012
# --- Safety check, should not be run from command line
if [ -z "$GIT_DIR" ]; then
echo "Don't run this script from the command line." >&2
echo " (if you want, you could supply GIT_DIR then run" >&2
echo " $0 <ref> <oldrev> <newrev>)" >&2
exit 1
fi
# Test that tab replacement works, issue in some Solaris envs at least
testvariable=`echo -e "\t" | sed 's/\s//'`
if [ "$testvariable" != "" ]; then
echo "Environment check failed - please contact git hosting." >&2
exit 1
fi
# File size limit is meant to be configured through 'hooks.filesizelimit' setting
filesizelimit=$(git config hooks.filesizelimit)
# If we haven't configured a file size limit, use default value of about 100M
if [ -z "$filesizelimit" ]; then
filesizelimit=100000000
fi
# Reference to incoming checkin can be found at $3
refname=$3
# With this command, we can find information about the file coming in that has biggest size
# We also normalize the line for excess whitespace
biggest_checkin_normalized=$(git ls-tree --full-tree -r -l $refname | sort -k 4 -n -r | head -1 | sed 's/^ *//;s/ *$//;s/\s\1,\/ /g' )
# Based on that, we can find what we are interested about
filesize=`echo $biggest_checkin_normalized | cut -d ' ' -f4,4`
# Actual comparison
# To cancel a push, we exit with status code 1
# It is also a good idea to print out some info about the cause of rejection
if [ $filesize -gt $filesizelimit ]; then
# To be more user-friendly, we also look up the name of the offending file
filename=`echo $biggest_checkin_normalized | cut -d ' ' -f5,5`
echo "Error: Too large push attempted." >&2
echo >&2
echo "File size limit is $filesizelimit, and you tried to push file named $filename of size $filesize." >&2
echo "Contact configuration team if you really need to do this." >&2
exit 1
fi
exit 0
请注意,commented 表示此代码仅检查最新提交,因此需要调整此代码以迭代 $2 和 $3 之间的提交并对所有提交进行检查。
【讨论】:
怎么用?每次提交前都执行这个文件? 是的。但是我不知道如何在git中配置它。【参考方案2】:eis 和 J-16 SDiZ 的答案存在严重问题。 他们只检查结局提交 $3 或 $newrev 的状态。 他们还需要检查其他提交中提交的内容 udpate 挂钩中的 $2(或 $oldrev)和 $3(或 $newrev)之间。
J-16 SDiZ 更接近正确答案。
最大的缺陷是部门服务器安装了这个更新挂钩来保护它的人会发现:
使用 git rm 删除不小心签入的大文件后, 那么当前树或最后一次提交就可以了,它会 拉入整个提交链,包括 被删除,创造了一个没人想要的肿的不愉快的胖历史。
解决方案是检查从 $oldrev 到 $newrev 的每个提交,或者指定整个范围 $oldrev..$newrev。 确保你不只是单独检查 $newrev ,否则这将失败 在你的 git 历史中有大量垃圾,被推出来与他人分享, 然后很难或不可能移除。
【讨论】:
【参考方案3】:This one还不错:
#!/bin/bash -u
#
# git-max-filesize
#
# git pre-receive hook to reject large files that should be commited
# via git-lfs (large file support) instead.
#
# Author: Christoph Hack <chack@mgit.at>
# Copyright (c) 2017 mgIT GmbH. All rights reserved.
# Distributed under the Apache License. See LICENSE for details.
#
set -o pipefail
readonly DEFAULT_MAXSIZE="5242880" # 5MB
readonly CONFIG_NAME="hooks.maxfilesize"
readonly NULLSHA="0000000000000000000000000000000000000000"
readonly EXIT_SUCCESS="0"
readonly EXIT_FAILURE="1"
# main entry point
function main()
local status="$EXIT_SUCCESS"
# get maximum filesize (from repository-specific config)
local maxsize
maxsize="$(get_maxsize)"
if [[ "$?" != 0 ]]; then
echo "failed to get $CONFIG_NAME from config"
exit "$EXIT_FAILURE"
fi
# skip this hook entirely if maxsize is 0.
if [[ "$maxsize" == 0 ]]; then
cat > /dev/null
exit "$EXIT_SUCCESS"
fi
# read lines from stdin (format: "<oldref> <newref> <refname>\n")
local oldref
local newref
local refname
while read oldref newref refname; do
# skip branch deletions
if [[ "$newref" == "$NULLSHA" ]]; then
continue
fi
# find large objects
# check all objects from $oldref (possible $NULLSHA) to $newref, but
# skip all objects that have already been accepted (i.e. are referenced by
# another branch or tag).
local target
if [[ "$oldref" == "$NULLSHA" ]]; then
target="$newref"
else
target="$oldref..$newref"
fi
local large_files
large_files="$(git rev-list --objects "$target" --not --branches=\* --tags=\* | \
git cat-file $'--batch-check=%(objectname)\t%(objecttype)\t%(objectsize)\t%(rest)' | \
awk -F '\t' -v maxbytes="$maxsize" '$3 > maxbytes' | cut -f 4-)"
if [[ "$?" != 0 ]]; then
echo "failed to check for large files in ref $refname"
continue
fi
IFS=$'\n'
for file in $large_files; do
if [[ "$status" == 0 ]]; then
echo ""
echo "-------------------------------------------------------------------------"
echo "Your push was rejected because it contains files larger than $(numfmt --to=iec "$maxsize")."
echo "Please use https://git-lfs.github.com/ to store larger files."
echo "-------------------------------------------------------------------------"
echo ""
echo "Offending files:"
status="$EXIT_FAILURE"
fi
echo " - $file (ref: $refname)"
done
unset IFS
done
exit "$status"
# get the maximum filesize configured for this repository or the default
# value if no specific option has been set. Suffixes like 5k, 5m, 5g, etc.
# can be used (see git config --int).
function get_maxsize()
local value;
value="$(git config --int "$CONFIG_NAME")"
if [[ "$?" != 0 ]] || [[ -z "$value" ]]; then
echo "$DEFAULT_MAXSIZE"
return "$EXIT_SUCCESS"
fi
echo "$value"
return "$EXIT_SUCCESS"
main
可以在服务器端config
文件中配置大小,添加:
[hooks]
maxfilesize = 1048576 # 1 MiB
【讨论】:
这很棒,有很多不错的技巧和对细节的关注!【参考方案4】:如果您使用的是 gitolite,您也可以尝试 VREF。 默认情况下已经提供了一个 VREF(代码在 gitolite/src/VREF/MAX_NEWBIN_SIZE 中)。 它被称为 MAX_NEWBIN_SIZE。 它的工作原理是这样的:
repo name
RW+ = username
- VREF/MAX_NEWBIN_SIZE/1000 = usernames
其中 1000 是以字节为单位的示例阈值。
此 VREF 就像一个更新挂钩,如果您要推送的文件大于阈值,它将拒绝您的推送。
【讨论】:
【参考方案5】:是的,git 也有钩子 (git hooks)。但这有点取决于您将使用的实际工作流程。
如果您有没有经验的用户,拉动比让他们推更安全。这样,您可以确保他们不会搞砸主存储库。
【讨论】:
【参考方案6】:我想强调在拉取请求阶段解决此问题的另一组方法:GitHub 操作和应用程序。它不会阻止大文件被提交到分支中,但如果它们在合并之前被删除,那么生成的基础分支将不会在历史记录中包含大文件。
最近开发了一项操作,可根据用户定义的参考值(lfs-warning)检查添加的文件大小(通过 GitHub API)。
我还亲自编写了一个 Probot 应用程序来筛选 PR 中的大文件大小(针对用户定义的值),但效率要低得多:sizeCheck
【讨论】:
【参考方案7】:另一种方法是版本.gitignore
,这将阻止任何具有特定扩展名的文件显示在状态中。
您仍然可以有钩子(在下游或上游,如其他答案所建议的那样),但至少所有下游回购都可以包含 .gitignore
以避免添加 .exe
、.dll
、.iso
、.. .
【讨论】:
注意:钩子不会通过克隆传播:***.com/questions/5165239/…)【参考方案8】:当有人签入时,例如 200Mb 甚至更大的文件,这将是一种非常罕见的情况。
虽然您可以通过使用服务器端挂钩来防止这种情况发生(不确定客户端挂钩,因为您必须依赖安装挂钩的人),就像您在 SVN 中所做的那样,您还必须考虑在 Git 中,从存储库中删除这样的文件/提交要容易得多。你在 SVN 中没有这样的奢侈,至少不是一个简单的方法。
【讨论】:
其实在git里面不是更难吗?文件的 'git rm' 实际上并没有从 repo 中删除它,它只是使它不会出现在以后的修订版中。你仍然为它浪费空间/带宽。 @JosephGarvin - 怎么样?git rm
是从当前提交中删除文件的命令。它不会改变历史。您还有其他命令,例如 git commit --amend
和 git filter-branch
【参考方案9】:
我正在使用 gitolite 并且已经使用了更新挂钩 - 我没有使用更新挂钩,而是使用了预接收挂钩。 Chriki 发布的脚本非常出色,只是数据是通过标准输入传递的——所以我做了一行更改:
- refname=$3
+ read a b refname
(可能有更优雅的方式来做到这一点,但它确实有效)
【讨论】:
【参考方案10】:您需要一个满足以下场景的解决方案。
-
如果有人将多个提交一起推送,则挂钩应检查该推送中的所有提交(在 oldref 和 newref 之间)是否有大于特定限制的文件
钩子应该为所有用户运行。如果您编写客户端挂钩,则并非所有用户都可以使用它,因为在您执行 git push 时不会推送此类挂钩。所以,需要一个服务器端的钩子,例如预接收钩子。
这个钩子 (https://github.com/mgit-at/git-max-filesize) 处理上述 2 种情况,似乎也能正确处理边缘情况,例如新分支推送和分支删除。
【讨论】:
【参考方案11】:您可以使用hook、pre-commit
挂钩(在客户端)或update
挂钩(在服务器上)。执行git ls-files --cached
(用于预提交)或git ls-tree --full-tree -r -l $3
(用于更新)并采取相应措施。
git ls-tree -l
会给出这样的结果:
100644 blob 97293e358a9870ac4ddf1daf44b10e10e8273d57 3301 file1
100644 blob 02937b0e158ff8d3895c6e93ebf0cbc37d81cac1 507 file2
抓住第四列,它是大小。使用git ls-tree --full-tree -r -l HEAD | sort -k 4 -n -r | head -1
获取最大的文件。 cut
提取,if [ a -lt b ]
检查大小等。
对不起,我想如果你是程序员,你应该可以自己做。
【讨论】:
@J-16SDiZ 非常不成熟的答案。以上是关于限制 git 存储库中的文件大小的主要内容,如果未能解决你的问题,请参考以下文章