왜 내 자식 저장소가 그렇게 큰가요? 각 분기의 끝에서 뒤로

145M = .git / objects / pack /

필자는 각 분기의 끝에서 뒤로 가기 전에 각 커밋과 커밋의 차이점을 더하는 스크립트를 작성했습니다. 압축되지 않고 지점 간 동일한 파일과 지점 간의 공통 기록을 고려하지 않은 129MB를 얻습니다.

Git은 모든 것을 고려하여 훨씬 작은 저장소를 기대합니다. 왜 .git이 그렇게 큰가요?

내가 한 :

git fsck --full
git gc --prune=today --aggressive
git repack

얼마나 많은 파일 / 커밋에 대해 대답하기 위해 각각 약 40 개의 파일에 19 개의 분기가 있습니다. 다음을 사용하여 찾은 287 커밋

git log --oneline --all|wc -l

이에 대한 정보를 저장하는 데 10MB가 걸리지 않아야합니다.



답변

최근에 잘못된 원격 저장소를 로컬 저장소 ( git remote add ...git remote update) 로 가져 왔습니다 . 원치 않는 원격 참조, 지사 및 태그를 삭제 한 후에도 여전히 저장소에 1.4GB (!)의 공간이 낭비되었습니다. 나는 그것을 복제하여 이것을 제거 할 수있었습니다 git clone file:///path/to/repository. (가) 있습니다 file://만 참조 된 개체가 아닌 전체 디렉토리 구조를 통해 복사됩니다 – 로컬 저장소를 복제 할 때 차이의 세계를 만든다.

편집 : 다음은 새로운 리포지토리의 모든 분기를 재생성하는 Ian의 한 라이너입니다.

d1=#original repo
d2=#new repo (must already exist)
cd $d1
for b in $(git branch | cut -c 3-)
do
    git checkout $b
    x=$(git rev-parse HEAD)
    cd $d2
    git checkout -b $b $x
    cd $d1
done

답변

내가 사용하는 일부 스크립트 :

자식 팻 파일

git rev-list --all --objects | \
    sed -n $(git rev-list --objects --all | \
    cut -f1 -d' ' | \
    git cat-file --batch-check | \
    grep blob | \
    sort -n -k 3 | \
    tail -n40 | \
    while read hash type size; do
         echo -n "-e s/$hash/$size/p ";
    done) | \
    sort -n -k1
...
89076 images/screenshots/properties.png
103472 images/screenshots/signals.png
9434202 video/parasite-intro.avi

더 많은 라인을 원하면 이웃 답변의 Perl 버전도 참조하십시오 : https://stackoverflow.com/a/45366030/266720

자식 삭제 (의 경우 video/parasite.avi) :

git filter-branch -f  --index-filter \
    'git rm --force --cached --ignore-unmatch video/parasite-intro.avi' \
     -- --all
rm -Rf .git/refs/original && \
    git reflog expire --expire=now --all && \
    git gc --aggressive && \
    git prune

참고 : 두 번째 스크립트는 Git에서 정보를 완전히 제거하도록 설계되었습니다 (reflogs의 모든 정보 포함). 주의해서 사용하십시오.


답변

git gc이미 git repack특수한 옵션을 전달하지 않는 한 수동으로 재 포장하는 것은 의미가 없습니다.

첫 번째 단계는 대부분의 공간이 객체 데이터베이스에 있는지 (보통 경우와 같이) 확인하는 것입니다.

git count-objects -v

이를 통해 리포지토리에 압축 해제 된 개체 수, 차지하는 공간, 압축 파일 수 및 차지하는 공간에 대한 보고서가 제공됩니다.

재 포장 후에는 압축을 푼 객체와 팩 파일이 없지만 현재 분기에서 직접 참조하지 않는 일부 객체가 여전히 존재하고 압축이 풀리는 것이 일반적입니다.

하나의 큰 팩이 있고 공간을 차지하는 것이 무엇인지 알고 싶다면 팩을 구성하는 객체와 저장 방법을 나열 할 수 있습니다.

git verify-pack -v .git/objects/pack/pack-*.idx

참고 verify-pack인덱스 파일이 아닌 팩 파일 자체를합니다. 팩의 모든 객체, 실제 크기 및 압축 된 크기와 델타 체인의 출처에 대한 정보를 제공합니다.

저장소에 비정상적으로 큰 객체가 있는지 확인하기 위해 네 번째 열의 세 번째 열 (예 :)에서 출력을 숫자로 정렬 할 수 있습니다 | sort -k3n.

이 출력에서 git show명령을 사용하여 오브젝트의 컨텐츠를 볼 수 있지만 저장소의 커미트 히스토리에서 오브젝트가 참조되는 위치를 정확하게 볼 수는 없습니다. 이 작업을 수행해야하는 경우이 질문 에서 무언가를 시도 하십시오 .


답변

참고로, 원하지 않는 객체가 유지되는 가장 큰 이유는 git이 reflog를 유지하기 때문입니다.

참조 지점은 실수로 마스터 브랜치를 삭제하거나 어쨌든 저장소를 치명적으로 손상시킬 때 엉덩이를 저장하기 위해 있습니다.

이 문제를 해결하는 가장 쉬운 방법은 압축하기 전에 참조 로그를 자르는 것입니다 (참조 로그의 커밋으로 돌아 가지 않도록하십시오).

git gc --prune=now --aggressive
git repack

이는 git gc --prune=today전체 reflog가 즉시 만료된다는 점 과 다릅니다 .


답변

git 저장소에서 공간을 차지하는 파일을 찾으려면 다음을 실행하십시오.

git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -5

그런 다음 가장 많은 공간을 차지하는 Blob 참조 (마지막 줄)를 추출하고 너무 많은 공간을 차지하는 파일 이름을 확인하십시오.

git rev-list --objects --all | grep <reference>

이 파일은으로 제거한 파일 일 수도 git rm있지만 태그, 리모컨 및 reflog와 같이 여전히 참조가 있기 때문에 git 은 파일을 기억합니다.

어떤 파일을 제거하고 싶은지 알고 나면 다음을 사용하는 것이 좋습니다. git forget-blob

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

사용하기 쉽습니다.

git forget-blob file-to-forget

이것은 git에서 모든 참조를 제거하고 히스토리의 모든 커밋에서 blob을 제거하고 가비지 콜렉션을 실행하여 공간을 확보합니다.


답변

Vi의 답변에서 git-fatfiles 스크립트는 모든 blob의 크기를보고 싶지만 사용할 수 없을 정도로 느립니다. 40 줄 출력 제한을 제거하고 마무리하는 대신 내 컴퓨터의 모든 RAM을 사용하려고했습니다. 그래서 나는 이것을 다시 썼습니다 : 이것은 수천 배 빠르며 기능 (옵션)을 추가했으며 이상한 버그가 제거되었습니다. 오래된 버전은 파일이 사용한 총 공간을보기 위해 출력을 합산하면 정확하지 않은 수를 줄 것입니다.

#!/usr/bin/perl
use warnings;
use strict;
use IPC::Open2;
use v5.14;

# Try to get the "format_bytes" function:
my $canFormat = eval {
    require Number::Bytes::Human;
    Number::Bytes::Human->import('format_bytes');
    1;
};
my $format_bytes;
if ($canFormat) {
    $format_bytes = \&format_bytes;
}
else {
    $format_bytes = sub { return shift; };
}

# parse arguments:
my ($directories, $sum);
{
    my $arg = $ARGV[0] // "";
    if ($arg eq "--sum" || $arg eq "-s") {
        $sum = 1;
    }
    elsif ($arg eq "--directories" || $arg eq "-d") {
        $directories = 1;
        $sum = 1;
    }
    elsif ($arg) {
        print "Usage: $0 [ --sum, -s | --directories, -d ]\n";
        exit 1;
    }
}

# the format is [hash, file]
my %revList = map { (split(' ', $_))[0 => 1]; } qx(git rev-list --all --objects);
my $pid = open2(my $childOut, my $childIn, "git cat-file --batch-check");

# The format is (hash => size)
my %hashSizes = map {
    print $childIn $_ . "\n";
    my @blobData = split(' ', <$childOut>);
    if ($blobData[1] eq 'blob') {
        # [hash, size]
        $blobData[0] => $blobData[2];
    }
    else {
        ();
    }
} keys %revList;
close($childIn);
waitpid($pid, 0);

# Need to filter because some aren't files--there are useless directories in this list.
# Format is name => size.
my %fileSizes =
    map { exists($hashSizes{$_}) ? ($revList{$_} => $hashSizes{$_}) : () } keys %revList;


my @sortedSizes;
if ($sum) {
    my %fileSizeSums;
    if ($directories) {
        while (my ($name, $size) = each %fileSizes) {
            # strip off the trailing part of the filename:
            $fileSizeSums{$name =~ s|/[^/]*$||r} += $size;
        }
    }
    else {
        while (my ($name, $size) = each %fileSizes) {
            $fileSizeSums{$name} += $size;
        }
    }

    @sortedSizes = map { [$_, $fileSizeSums{$_}] }
        sort { $fileSizeSums{$a} <=> $fileSizeSums{$b} } keys %fileSizeSums;
}
else {
    # Print the space taken by each file/blob, sorted by size
    @sortedSizes = map { [$_, $fileSizes{$_}] }
        sort { $fileSizes{$a} <=> $fileSizes{$b} } keys %fileSizes;

}

for my $fileSize (@sortedSizes) {
    printf "%s\t%s\n", $format_bytes->($fileSize->[1]), $fileSize->[0];
}

이 git-fatfiles.pl의 이름을 지정하고 실행하십시오. 파일의 모든 개정에서 사용 된 디스크 공간을 보려면 --sum옵션을 사용하십시오 . 같은 것을 볼 수 있지만 각 디렉토리 내의 파일에 대해서는 --directories옵션을 사용하십시오 . Number :: Bytes :: Human cpan 모듈 을 설치 하면 ( “cpan Number :: Bytes :: Human”실행) 크기가 “21M /path/to/file.mp4″로 형식이 지정됩니다.


답변

.idx 파일이 아닌 .pack 파일 만 세시겠습니까? 이들은 .pack 파일과 동일한 디렉토리에 있지만 리포지토리 데이터는 없습니다 (확장자에서 알 수 있듯이 해당 팩의 인덱스에 지나지 않습니다). 사실 올바른 명령을 알고 있다면 팩 파일에서 파일을 쉽게 재생성하고 팩 파일 만 기본 git 프로토콜을 사용하여 전송되므로 복제 할 때 git 자체가 수행합니다.

대표적인 샘플로서, linux-2.6 저장소의 로컬 복제본을 살펴 보았습니다.

$ du -c *.pack
505888  total

$ du -c *.idx
34300   total

약 7 %의 확장이 일반적이어야 함을 나타냅니다.

외부 파일도 있습니다 objects/. 내 개인적인 경험에서, 그들 indexgitk.cache(리눅스 2.6 저장소의 내 복제에서 11M에 달하는) 가장 큰 사람이 될 경향이있다.