Подтвердить что ты не робот

Ограничение размера файла в репозитории git

В настоящее время я думаю об изменении моего VCS (от subversion) до git. Можно ли ограничить размер файла внутри коммита в репозитории git? Для e. г. subversion есть крючок: http://www.davidgrant.ca/limit_size_of_subversion_commits_with_this_hook

По моему опыту люди, особенно неопытные, иногда склонны передавать файлы, которые не должны входить в VCS (например, изображения большой файловой системы).

4b9b3361

Ответ 1

Какое-то время я боролся с этим, даже с описанием, и думаю, что это актуально и для других, я подумал, что опубликую реализацию того, как описанная Ji 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

Обратите внимание, что было прокомментировано, что этот код проверяет только последнюю фиксацию, поэтому этот код необходимо настроить, чтобы выполнить итерации между $ 2 и $ 3 и выполнить проверку для всех них.

Ответ 2

Ответы eis и J-16 SDiZ страдают от серьезной проблемы. Они проверяют только состояние финализации $3 или $newrev. Они также должны проверить, что подано в других коммитах между $2 (или $oldrev) и $3 (или $newrev) в udpate hook.

J-16 SDiZ ближе к правильному ответу.

Большая ошибка заключается в том, что кто-то, чей ведомственный сервер имеет этот крюк обновления, установленный для его защиты, обнаружит трудный путь, который:

После использования git rm для удаления большого файла, случайно проверяемого, то текущее древо или последнее совершение будет прекрасным, и это будет вытащить всю цепочку коммитов, включая большой файл, который был удален, создавая опухшую несчастную жирную историю, которую никто не хочет.

К решению нужно либо проверить фиксацию от $oldrev до $newrev, либо указать весь диапазон $oldrev.. $newrev. Будьте уверены, что вы не просто проверяете $newrev самостоятельно, либо это не удастся с массивным барахлом в вашей истории git, вытолкнули, чтобы поделиться с другими, а затем трудно или невозможно удалить после этого.

Ответ 3

Если вы используете гитолит, вы также можете попробовать VREF. Существует один VREF, уже предоставленный по умолчанию (код находится в gitolite/src/VREF/MAX_NEWBIN_SIZE). Он называется MAX_NEWBIN_SIZE. Он работает следующим образом:

repo name
RW+     =   username
-   VREF/MAX_NEWBIN_SIZE/1000   =   usernames 

Где 1000 - пример порога в байтах.

Этот VREF работает как крюк обновления, и он отклонит ваш push, если один файл, который вы должны нажать, больше порога.

Ответ 4

Да, git имеет крючки (git hooks). Но это зависит от фактического потока работы, который вы будете использовать.

Если у вас есть неопытные пользователи, гораздо безопаснее тянуть, а затем позволить им толкнуть. Таким образом, вы можете убедиться, что они не испортят главный репозиторий.

Ответ 5

Другим способом является версия a .gitignore, которая предотвратит появление в статусе какого-либо файла с определенным расширением.
У вас все еще есть крючки (по нисходящему или восходящему потоку, как предложено другими ответами), но по крайней мере все последующие репо могут включать в себя .gitignore, чтобы избежать добавления .exe, .dll, .iso,...

Ответ 6

Это будет очень редкий случай из того, что я видел, когда кто-то проверяет, скажем, файл размером 200 МБ или даже больше.

В то время как вы можете предотвратить это, используя крючки на стороне сервера (не уверен, что на крючках на стороне клиента, так как вы должны полагаться на человека, у которого установлены крючки), как и в SVN, вы также должны учитывать что в Git гораздо проще удалить такой файл/фиксацию из репозитория. У вас не было такой роскоши в SVN, по крайней мере, нелегко.

Ответ 7

Я использую gitolite, и крюк обновления уже используется - вместо использования крюка обновления я использовал крюк pre-receive. script, опубликованный Chriki, работал сказочно, за исключением того, что данные передаются через stdin - поэтому я сделал одно изменение строки:

- refname=$3
+ read a b refname

(может быть более элегантный способ сделать это, но он работает)

Ответ 8

Этот довольно хорош:

#!/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 <[email protected]>
# 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

Ответ 9

Вы можете использовать hook, либо pre-commit hook (на клиенте), либо update hook (на сервере), Сделайте 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 ] для проверки размера и т.д.

Извините, я думаю, что если вы программист, вы должны сделать это самостоятельно.