2009-05-13 12 views
7

Wir haben vor kurzem begonnen, Git zu verwenden und hatten ein unangenehmes Problem, wenn jemand eine große Datei (~ 1,5GB) verübte, die dann auf verschiedenen 32bit-Betriebssystemen zum Absturz brachte. Dies scheint ein bekannter Fehler zu sein (git mppaps files in memory, der nicht funktioniert, wenn er nicht genug Platz findet), der in absehbarer Zeit nicht behoben wird.Große Dateien in Git ablehnen

Die einfache (für uns) Lösung wäre git, alle Commits größer als 100MB oder so abzulehnen, aber ich kann keinen Weg finden, dies zu tun.

EDIT: Das Problem kommt von der versehentlichen Übermittlung von großen Datei, in diesem Fall eine große Dump der Programmausgabe. Das Ziel ist, versehentliche Übermittlung zu vermeiden, nur weil, wenn ein Entwickler versehentlich eine große Datei einreicht, versucht, sie dann wieder aus dem Repository zu bekommen, ein Nachmittag ist, an dem niemand arbeiten kann, und alle lokalen Zweige reparieren muss haben.

+0

Ist die Absicht, versehentliche Commits von großen Dateien zu verhindern? –

Antwort

2

Wann genau ist das Problem aufgetreten? Wenn sie die Datei ursprünglich festgeschrieben haben oder wenn sie woanders hingedrängt wurde? Wenn Sie über ein Staging-Repo verfügen, an das alle weiterleiten, können Sie einen Update-Hook implementieren, um ändernde Refs für große Dateien sowie andere Berechtigungen usw. zu prüfen.

Sehr notdürftig Beispiel:

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) }'; 

(nur um zu zeigen geht, kann alles mit einem Perl-Einzeiler getan werden, obwohl es mehrere Zeilen dauern kann;))

Called In der Art, wie $ GIT_DIR/hooks/update aufgerufen wird (args sind ref-name, old-rev, new-rev; zB "refs/heads/master master ~ 2 master") zeigt dies die hinzugefügten Dateien an und bricht sie ab hinzugefügt, das ist zu groß.

Beachten Sie, dass ich sagen würde, wenn Sie solche Dinge überwachen, brauchen Sie einen zentralen Punkt, an dem Sie das tun können. Wenn Sie darauf vertrauen, dass Ihr Team nur Änderungen miteinander austauschen kann, sollten Sie darauf vertrauen, dass sie lernen, dass das Hinzufügen riesiger Binärdateien eine schlechte Sache ist.

1

Wenn Sie Kontrolle über die Toolchain Ihrer Committer haben, kann es einfach sein, git commit so zu modifizieren, dass es einen Plausibilitätstest für die Dateigröße vor dem "echten" Commit durchführt. Da eine solche Änderung im Kern alle Git-Benutzer bei jedem Commit belasten würde und die alternative Strategie "jeden zu verbannen, der eine Änderung von 1,5 GB vornehmen würde" eine ansprechende Einfachheit hat, vermute ich, dass ein solcher Test niemals im Kern akzeptiert wird. Ich schlage vor, Sie wiegen die Last, eine lokale Gabelung von Git - Nannygit zu halten - gegen die Last, einen abgestürzten Git nach einem übertriebenen Commit zu reparieren.

Ich muss zugeben, ich bin neugierig, wie ein 1,5 GB Commit zu sein kam. Sind Videodateien beteiligt?

2

Sie können einen Pre-Commit Hook verteilen, der Commits verhindert. In zentralen Repositories können Sie einen Pre-Receive-Hook haben, der große Blobs ablehnt, indem die empfangenen Daten analysiert und verhindert werden, dass sie referenziert werden. Daten werden empfangen, aber da Sie Aktualisierungen von refs ablehnen, werden alle neu empfangenen Objekte nicht referenziert und können von git gc übernommen und gelöscht werden.

Ich habe kein Skript für Sie.

0
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}