我目前考虑将版本控制系统(从Subversion)更改为Git。在Git存储库中,是否有可能限制提交时的文件大小?例如:在Subversion中,有一个钩子:http://www.davidgrant.ca/limit_size_of_subversion_commits_with_this_hook
从我的经验来看,人们,特别是那些没有经验的人,有时候会提交不应该放入版本控制系统的文件(例如大型文件系统映像)。
我目前考虑将版本控制系统(从Subversion)更改为Git。在Git存储库中,是否有可能限制提交时的文件大小?例如:在Subversion中,有一个钩子:http://www.davidgrant.ca/limit_size_of_subversion_commits_with_this_hook
从我的经验来看,人们,特别是那些没有经验的人,有时候会提交不应该放入版本控制系统的文件(例如大型文件系统映像)。
我曾经遇到了类似的问题,即使有描述也很难理解。这对其他人也很有帮助,因此我想分享如何实现J16 SDiZ描述的方法。
以下是我对服务器端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
这个脚本 相当不错:
#!/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
如果您正在使用 gitolite,您也可以尝试使用 VREF。默认情况下已经提供了一个VREF(代码在gitolite/src/VREF/MAX_NEWBIN_SIZE中)。它被称为MAX_NEWBIN_SIZE。它的工作方式如下:
repo name
RW+ = username
- VREF/MAX_NEWBIN_SIZE/1000 = usernames
1000代表字节阈值。
此VREF类似于更新钩子,如果要推送的某个文件大于阈值,则会拒绝您的推送。
我正在使用gitolite,并且已经在使用更新钩子 - 但是我使用了pre-receive钩子而不是更新钩子。Chriki发布的脚本非常出色,唯一的问题是数据是通过stdin传递的 - 所以我进行了一行更改:
- refname=$3
+ read a b refname
可能有更优雅的方法来完成这个任务,但这种方式可行。
.gitignore
文件,这样可以防止特定扩展名的文件显示在状态中。.gitignore
文件,以避免添加.exe
、.dll
、.iso
等文件。
git ls-tree
"(man) 的 "--format=<format>
" 中使用的原子,在 git ls-files
(man) 中并不支持,尽管它们在后者的上下文中是相关的。adlternative
)完成。gitster
--在提交 32fe7ff中合并,2023年6月13日)
"
ls-files
:将格式原子与ls-tree对齐签名:ZheNing Hu
git ls-files --format
"(man) 可以用来格式化索引中多个文件条目的输出,而 "git ls-tree --format
"(man) 可以用来格式化树对象的内容。git ls-files --format
" 目前支持的 %(objecttype)、"(objectsize)" 和 "%(objectsize:padded)" 原子是 git ls-tree --format
(man) 中可用原子的一个子集。
用户有时需要在索引和树之间建立统一视图,这可以帮助比较或转换两者之间的差异。
因此,此补丁将缺失的原子添加到 "git ls-files
"(man) 的 --format 选项中。
%(objecttype)
" 可用于检索与索引中的文件对应的对象类型,%(objectsize)
" 可用于检索与索引中的文件对应的对象大小,以及%(objectsize:padded)
" 与 "%(objectsize)
" 相同,只是格式上有填充。git ls-files
现在在其man page中包含以下内容:
objecttype
文件在索引中记录的对象类型。
git ls-files
现在在其man page中包含以下内容:
objectsize[:padded]
文件在索引中记录的对象大小(如果对象是
commit
或tree
,则为"-
")。 它还支持带有"%(objectsize:padded)
"的填充格式的大小。
根据我所见,当有人检入一个200Mb甚至更大的文件时,这将是一个非常罕见的情况。
虽然你可以通过使用服务器端钩子来防止这种情况发生(不确定客户端钩子是否可行,因为你必须依赖于安装了钩子的人),就像在SVN中一样,但你还必须考虑到在Git中,从仓库中删除这样的文件/提交要容易得多。在SVN中,你没有这样的奢侈,至少没有简单的方法。
git rm
是从当前提交中删除文件的命令。它不会改变历史记录。您还有其他命令,如git commit --amend
和git filter-branch
。 - manojlds此钩子 (https://github.com/mgit-at/git-max-filesize) 处理上述两种情况,并似乎还正确处理了边缘情况,例如新分支推送和分支删除。