Вопрос

Недавно мы начали использовать git, и у нас возникла неприятная проблема, когда кто-то зафиксировал большой файл (около 1,5 ГБ), что затем приводило к сбою git на различных 32-битных ОС.Кажется, это известная ошибка (файлы git mmaps помещаются в память, что не работает, если не хватает непрерывного пространства), которая не будет исправлена ​​в ближайшее время.

Самым простым (для нас) решением было бы заставить git отклонять любые коммиты размером более 100 МБ или около того, но я не могу придумать, как это сделать.

РЕДАКТИРОВАТЬ:Проблема возникает из-за случайной отправки большого файла, в данном случае большого дампа вывода программы.Цель состоит в том, чтобы избежать случайной отправки, просто потому, что если разработчик случайно отправляет большой файл, то попытка вернуть его обратно из репозитория — это полдень, когда никто не может выполнять какую-либо работу, и ему приходится исправлять все локальные ветки, которые они иметь.

Это было полезно?

Решение

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

Очень грубый и готовый пример:

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 (аргументы: ref-name, old-rev, new-rev;например«refs/heads/master master~2 master»), это покажет добавленные файлы и прекратит работу, если будет добавлен слишком большой файл.

Обратите внимание: я бы сказал, что если вы собираетесь контролировать подобные вещи, вам нужен централизованный пункт, где это можно делать.Если вы доверяете своей команде просто обмениваться изменениями друг с другом, вы должны доверять им и понять, что добавление гигантских двоичных файлов — это плохо.

Другие советы

Вы можете распространить ловушку предварительной фиксации, которая предотвращает фиксации.В центральных репозиториях вы можете использовать перехватчик предварительного получения, который отклоняет большие BLOB-объекты, анализируя полученные данные и предотвращая обращение к ним.Данные будут получены, но поскольку вы отклоняете обновления ссылок, все полученные новые объекты не будут иметь ссылок и могут быть выбраны и удалены с помощью git gc.

Хотя у меня нет для вас сценария.

Если у вас есть контроль над набором инструментов ваших коммиттеров, возможно, вам будет несложно изменить git commit так, чтобы он выполнял проверку разумности размера файла перед «настоящим» коммитом.Поскольку такое изменение в ядре будет обременять всех пользователей git при каждом коммите, а альтернативная стратегия «изгнать любого, кто сделает 1,5 ГБ изменения» имеет привлекательную простоту, я подозреваю, что такой тест никогда не будет принят в ядре.Я предлагаю вам взвесить бремя поддержки локальной ветки git — nannygit — с бременем восстановления сбойного git после чрезмерно амбициозного коммита.

Должен признаться, мне любопытно, как появился коммит размером 1,5 ГБ.Участвуют ли видеофайлы?

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}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top