Отклонение больших файлов в git - PullRequest
7 голосов
/ 13 мая 2009

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

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

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

Ответы [ 4 ]

2 голосов
/ 13 мая 2009

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

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

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»), это покажет добавленные файлы и прервите, если добавлено, что является слишком большим.

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

2 голосов
/ 14 мая 2009

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

У меня нет сценария для вас.

1 голос
/ 13 мая 2009

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

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

0 голосов
/ 27 июня 2014
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}
...