git history에서 큰 커밋을 찾고 식별하는 방법은 무엇입니까?


366

300MB git repo가 ​​있습니다. 현재 체크 아웃 된 파일의 총 크기는 2MB이고 나머지 git repo의 총 크기는 298MB입니다. 기본적으로 코드 전용 저장소이며 몇 MB를 넘지 않아야합니다.

누군가 실수로 큰 파일 (비디오, 이미지 등)을 커밋 한 다음 git에서 제거하지는 않았으므로 기록에는 여전히 쓸모없는 큰 파일이 포함되어 있습니다. git history에서 큰 파일을 어떻게 찾을 수 있습니까? 400 개 이상의 커밋이 있으므로 하나씩 진행하는 것은 실용적이지 않습니다.

참고 : 내 질문 파일을 제거하는 방법이 아니라 처음부터 파일찾는 방법 에 관한 것입니다.



답변:


143

이 스크립트는 과거에 git 저장소에서 크고 (명백하지 않은) 객체를 찾는 데 매우 유용하다는 것을 알았습니다.


#!/bin/bash
#set -x 

# Shows you the largest objects in your repo's pack file.
# Written for osx.
#
# @see https://stubbisms.wordpress.com/2009/07/10/git-script-to-show-largest-pack-objects-and-trim-your-waist-line/
# @author Antony Stubbs

# set the internal field separator to line break, so that we can iterate easily over the verify-pack output
IFS=$'\n';

# list all objects including their size, sort by size, take top 10
objects=`git verify-pack -v .git/objects/pack/pack-*.idx | grep -v chain | sort -k3nr | head`

echo "All sizes are in kB's. The pack column is the size of the object, compressed, inside the pack file."

output="size,pack,SHA,location"
allObjects=`git rev-list --all --objects`
for y in $objects
do
    # extract the size in bytes
    size=$((`echo $y | cut -f 5 -d ' '`/1024))
    # extract the compressed size in bytes
    compressedSize=$((`echo $y | cut -f 6 -d ' '`/1024))
    # extract the SHA
    sha=`echo $y | cut -f 1 -d ' '`
    # find the objects location in the repository tree
    other=`echo "${allObjects}" | grep $sha`
    #lineBreak=`echo -e "\n"`
    output="${output}\n${size},${compressedSize},${other}"
done

echo -e $output | column -t -s ', '

그러면 Blob의 오브젝트 이름 (SHA1sum)이 표시되고 다음과 같은 스크립트를 사용할 수 있습니다.

... 각 해당 Blob을 가리키는 커밋을 찾습니다.


31
이 답변은 위의 게시물로 나를 보냈기 때문에 정말 도움이되었습니다. 게시물의 스크립트가 작동하는 동안 고통스럽게 느립니다. 그래서 나는 그것을 다시 썼고 지금은 큰 저장소에서 훨씬 빠릅니다. 보세요 : gist.github.com/nk9/b150542ef72abc7974cb
Nick K9

7
오프 사이트 링크뿐만 아니라 답변에 전체 지침을 포함하십시오. stubbisms.wordpress.com이 필연적으로 다운 될 때 어떻게해야합니까?
ThorSummoner

@ NickK9 흥미롭게도 나는 당신의 스크립트와 다른 것과 다른 결과를 얻습니다. 당신이 놓친 것보다 더 큰 물건들이 많이 있습니다. 내가 놓친 것이 있습니까?
UpAndAdam

오 쿨! @nick \ k9 : D @UpAndAdam 스크립트를 더 빠르게 만들어 주셔서 감사합니다. 스크립트가 잘못된 출력을 생성했다고 말하고 있습니까?
Antony Stubbs

1
이 의견은 크기를 바이트 단위로보고하는 것처럼 들리지만 킬로바이트를 얻습니다.
Kat

681

🚀 엄청나게 빠른 쉘 원 라이너 🚀

이 쉘 스크립트는 저장소의 모든 Blob 오브젝트를 가장 작은 것에서 가장 큰 것으로 정렬하여 표시합니다.

내 샘플 저장소의 경우 여기에있는 다른 것보다 약 100 배 빠릅니다 .
신뢰할 수있는 Athlon II X4 시스템에서 1 분만 에 560 만 개의 객체로 Linux 커널 저장소 를 처리합니다 .

기본 스크립트

git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| sed -n 's/^blob //p' \
| sort --numeric-sort --key=2 \
| cut -c 1-12,41- \
| $(command -v gnumfmt || echo numfmt) --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest

위의 코드를 실행하면 다음 과 같이 사람이 읽을 수있는 좋은 결과를 얻을 수 있습니다 .

...
0d99bb931299  530KiB path/to/some-image.jpg
2ba44098e28f   12MiB path/to/hires-image.png
bd1741ddce0d   63MiB path/to/some-video-1080p.mp4

맥 OS 사용자 : 때문에 numfmt맥 OS에서 사용할 수 없습니다, 당신은 원시 바이트 크기 나와 마지막 줄과 거래를 생략하거나 할 수 있습니다 brew install coreutils.

필터링

얻기 위해 상기 필터링 , 다음 행의 임의 삽입 전과 sort라인 .

에있는 파일HEAD제외 하려면 다음 줄을 삽입하십시오.

| grep -vF --file=<(git ls-tree -r HEAD | awk '{print $3}') \

지정된 크기를 초과하는 파일 만 표시 하려면 (예 : 1 MiB = 2 20  B) 다음 줄을 삽입하십시오.

| awk '$2 >= 2^20' \

컴퓨터 출력

컴퓨터의 추가 처리더 적합한 출력을 생성하려면 기본 스크립트의 마지막 두 줄을 생략하십시오. 그들은 모든 서식을 수행합니다. 이것은 다음과 같은 것을 남길 것입니다.

...
0d99bb93129939b72069df14af0d0dbda7eb6dba 542455 path/to/some-image.jpg
2ba44098e28f8f66bac5e21210c2774085d2319b 12446815 path/to/hires-image.png
bd1741ddce0d07b72ccf69ed281e09bf8a2d0b2f 65183843 path/to/some-video-1080p.mp4

파일 제거

실제 파일 제거 에 대해서는 주제에서이 SO 질문을 확인하십시오 .


14
이것은 단지 내 공짜보다 더 가치가 있습니다! 컴퓨터와 사람이 읽을 수있는 출력을 모두 제공해 주셔서 감사합니다.
Michel Jung

2
이것은 매우 빠르고 사용하기 쉽습니다!

31
Mac에서이를 사용하려면 다음을 수행해야 brew install coreutils하고 교체 cutgcutnumfmt함께 gnumfmt.
Nick Sweeting

2
다시 강조하겠습니다-이것은 내가 본 다른 모든 목록보다 훨씬 빠릅니다.
Sridhar Sarnobat

4
이것은 멋진 자식 별칭을 만듭니다 :) git large누구?
anarcat

160

ETH Zurich Department of Physics Wiki 페이지 에서 한 줄짜리 솔루션을 찾았 습니다 ( 페이지 끝 부분에 가깝습니다). 단지는 않습니다 git gc다음 오래된 쓰레기를 제거하려면

git rev-list --objects --all \
  | grep "$(git verify-pack -v .git/objects/pack/*.idx \
           | sort -k 3 -n \
           | tail -10 \
           | awk '{print$1}')"

리포지토리에서 가장 큰 10 개의 파일을 제공합니다.

이제는 더 게으른 솔루션을 사용할 수 있으며 GitExtensions는 이제 UI 에서이 작업을 수행하는 플러그인을 가지고 있습니다 (이력 다시 쓰기도 처리 함).

GitExtensions '큰 파일 찾기'대화 상자


8
하나의 라이너는 가장 큰 단일 파일을 얻으려는 경우에만 작동합니다 (즉, tail -1 사용). 줄 바꿈은 더 큰 무엇이든 방해합니다. sed를 사용하여 줄 바꿈을 변환하여 grep이 git rev-list --objects --all | grep -E `git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -10 | awk '{print$1}' | sed ':a;N;$!ba;s/\n/|/g'`
훌륭하게

10
grep : a70783fca9bfbec1ade1519a41b6cc4ee36faea0 : 해당 파일이나 디렉토리가 없음
Jonathan Allard

1
위키 링크는 다음으로 이동했습니다 : readme.phys.ethz.ch/documentation/git_advanced_hints
outsmartin

11
GitExtensions를 찾는 것은 금 냄비와 무지개의 끝을 찾는 것과 같습니다. 감사합니다!
ckapilla 2016 년

3
파일 크기를 인쇄하는 확장명도 있습니까?
Michael

27

1 단계 모든 파일 SHA1을 텍스트 파일에 씁니다.

git rev-list --objects --all | sort -k 2 > allfileshas.txt

2 단계 Blob을 가장 큰 것부터 가장 작은 것까지 정렬하고 결과를 텍스트 파일에 씁니다.

git gc && git verify-pack -v .git/objects/pack/pack-*.idx | egrep "^\w+ blob\W+[0-9]+ [0-9]+ [0-9]+$" | sort -k 3 -n -r > bigobjects.txt

3a 단계 두 텍스트 파일을 결합하여 파일 이름 / sha1 / 크기 정보를 얻습니다.

for SHA in `cut -f 1 -d\  < bigobjects.txt`; do
echo $(grep $SHA bigobjects.txt) $(grep $SHA allfileshas.txt) | awk '{print $1,$3,$7}' >> bigtosmall.txt
done;

3b 단계 공백 이 포함 된 파일 이름 또는 경로 이름 이 있으면이 3a 단계의 변형을 시도하십시오. cut대신 awk원하는 열을 얻는 대신 사용 합니다 . 7 열에서 줄 끝까지의 공백 :

for SHA in `cut -f 1 -d\  < bigobjects.txt`; do
echo $(grep $SHA bigobjects.txt) $(grep $SHA allfileshas.txt) | cut -d ' ' -f'1,3,7-' >> bigtosmall.txt
done;

이제 bigitsmall.txt 파일을보고 Git 기록에서 제거 할 파일을 결정할 수 있습니다.

4 단계 제거를 수행하려면 (이 부분은 사용자가 식별 한 파일에 대한 데이터에 대한 히스토리의 모든 커밋을 검사하므로 느리게 진행됩니다) :

git filter-branch --tree-filter 'rm -f myLargeFile.log' HEAD

출처

1-3a 단계는 Git 히스토리에서 큰 파일 찾기 및 제거 에서 복사되었습니다.

편집하다

이 기사는 2017 년 하반기에 삭제되었지만 Wayback Machine을 사용하여 보관 된 사본에 계속 액세스 할 수 있습니다 .


6
같은 일을하는 한 라이너 :git gc && join -e ERROR -a 2 -j 1 -o 2.1,2.3,1.2 --check-order <( git rev-list --objects --all | sort -k 1 ) <( git verify-pack -v .git/objects/pack/pack-*.idx | gawk '( NF == 5 && $2 == "blob" ){print}' | sort -k1 ) | sort -k2gr
Iwan Aucamp

1
@Iwan, 하나의 라이너에 감사드립니다! 공백이있는 파일 이름을 처리하지 않습니다 join -t' ' -e ERROR -a 2 -j 1 -o 2.1,2.3,1.2 --check-order <( git rev-list --objects --all | sed 's/[[:space:]]/\t/' | sort -k 1 ) <( git verify-pack -v .git/objects/pack/pack-*.idx | gawk '( NF == 5 && $2 == "blob" ){print}' | sort -k1 | sed 's/[[:space:]]\+/\t/g' ) | sort -k2gr | less. geekbraindump.blogspot.ru/2009/04/unix-join-with-tabs.html에join -t' 따라 CTRL + V <TAB> 을 사용한 후 실제 TAB 문자를 입력해야합니다.
Nickolay

2
bash $'\t'가있는 @Nickolay 는 탭을 제공해야합니다. echo -n $'\t' | xxd -ps->09
Iwan Aucamp

1
@IwanAucamp : 팁을 주셔서 감사합니다! (너무 나쁘다 나는 이전의 코멘트를 편집 할 수 없다. 아 ..)
Nickolay

1
@ Sridhar-Sarnobat 기사가 Wayback Machine에 의해 저장되었습니다! :) web.archive.org/web/20170621125743/http://www.naleid.com/blog/...
friederbluemle

18

BFG Repo-Cleaner를 사용해야합니다 .

웹 사이트에 따르면 :

BFG는 Git 리포지토리 기록에서 불량 데이터를 정리하기위한 git-filter-branch에 대한보다 간단하고 빠른 대안입니다.

  • 미친 큰 파일 제거
  • 비밀번호, 자격 증명 및 기타 개인 데이터 제거

리포지토리 크기를 줄이는 일반적인 절차는 다음과 같습니다.

git clone --mirror git://example.com/some-big-repo.git
java -jar bfg.jar --strip-biggest-blobs 500 some-big-repo.git
cd some-big-repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push

4
BFG Repo-Cleaner는 매우 좋습니다. 빠르게 밝아지고 매우 안정적으로 작동합니다.
fschmitt

30
그러나 가장 큰 파일을 모두 나열하는 방법을 알려주지는 않습니다.
Andi Jay

5
이 문제는 실제로 제거하지 않고 큰 파일이 무엇인지 볼 수 없다는 것입니다. 나는 단순히 큰 파일을 나열하는 드라이 런이 없으면이 작업을 편안하게 느끼지 못합니다.
Sridhar Sarnobat

무엇을 --strip-biggest-blobs 500합니까?
2540625

git 은이 도구의 변경 사항을 거부합니다.
크리스토퍼

9

큰 파일 목록 만 갖고 싶다면 다음과 같은 단일 라이너를 제공하고 싶습니다.

join -o "1.1 1.2 2.3" <(git rev-list --objects --all | sort) <(git verify-pack -v objects/pack/*.idx | sort -k3 -n | tail -5 | sort) | sort -k3 -n

그 결과는 다음과 같습니다.

commit       file name                                  size in bytes

72e1e6d20... db/players.sql 818314
ea20b964a... app/assets/images/background_final2.png 6739212
f8344b9b5... data_test/pg_xlog/000000010000000000000001 1625545
1ecc2395c... data_development/pg_xlog/000000010000000000000001 16777216
bc83d216d... app/assets/images/background_1forfinal.psd 95533848

목록의 마지막 항목은 git history에서 가장 큰 파일을 가리 킵니다.

이 출력을 사용 하여 히스토리에 필요한 BFG로 항목 을 삭제하지 않도록 할 수 있습니다.


2
대박!! 그러나이 명령을 실행하기 전에 --mirror 옵션을 사용하여 repo를 복제해야합니다.
Andi Jay

궁금합니다. 1.1, 1.2, 2.3숫자는 무엇입니까?
ympostor

숫자는 <filenumber>.<field>조합 순서를 지정 하는 목록입니다 . 자세한 내용은 man.cx/join 을 참조하십시오.
schmijos 2012 년

6

Windows를 사용하는 경우 리포지토리에서 10 개의 가장 큰 파일을 인쇄하는 PowerShell 스크립트는 다음과 같습니다.

$revision_objects = git rev-list --objects --all;
$files = $revision_objects.Split() | Where-Object {$_.Length -gt 0 -and $(Test-Path -Path $_ -PathType Leaf) };
$files | Get-Item -Force | select fullname, length | sort -Descending -Property Length | select -First 10

1
이것은 @raphinesse와 다른 답변을 생성하여 내 저장소에서 가장 큰 파일이 많이 누락되었습니다. 또한 하나의 큰 파일에 많은 수정 사항이있는 경우 가장 큰 크기 만보고됩니다.
kristianp

이 스크립트는 다음과 같은 오류로 인해 실패했습니다 You cannot call a method on a null-valued expression. At line: 2 char: 1. 그러나이 답변은 stackoverflow.com/a/57793716/2441655 (더 짧습니다)
Venryx

4

시도하십시오 git ls-files | xargs du -hs --threshold=1M.

CI 파이프 라인에서 아래 명령을 사용하면 git repo에서 큰 파일을 찾으면 중지됩니다.

test $(git ls-files | xargs du -hs --threshold=1M 2>/dev/null | tee /dev/stderr | wc -l) -gt 0 && { echo; echo "Aborting due to big files in the git repository."; exit 1; } || true

2

--batch-checkGit 1.8.3으로 의 명령 행 스위치 (사용해야 함)가 인수를 허용하지 않기 때문에 가장 인기있는 답변 을 사용할 수 없었습니다. 다음 단계는 Bash 4.1.2가 설치된 CentOS 6.5에서 시도되었습니다.

주요 컨셉

Git에서 Blob 이라는 용어 는 파일의 내용을 의미합니다. 커밋은 파일 또는 경로 이름의 내용을 변경할 수 있습니다. 따라서 커밋에 따라 동일한 파일이 다른 얼룩을 참조 할 수 있습니다. 특정 파일은 한 커밋에서 다른 계층이 아닌 디렉토리 계층 구조에서 가장 클 수 있습니다. 따라서 큰 파일 대신 큰 커밋을 찾는 문제는 올바른 관점에 있습니다.

참을성없는 사람들을 위해

내림차순으로 BLOB 목록을 인쇄하는 명령은 다음과 같습니다.

git cat-file --batch-check < <(git rev-list --all --objects  | \
awk '{print $1}')  | grep blob  | sort -n -r -k 3

샘플 출력 :

3a51a45e12d4aedcad53d3a0d4cf42079c62958e blob 305971200
7c357f2c2a7b33f939f9b7125b155adbd7890be2 blob 289163620

이러한 얼룩을 제거하려면 다른 답변에서 언급 한대로 BFG Repo Cleaner를 사용하십시오 . blobs.txtBlob 해시 만 포함 된 파일 을 예로 들면 다음과 같습니다.

3a51a45e12d4aedcad53d3a0d4cf42079c62958e
7c357f2c2a7b33f939f9b7125b155adbd7890be2

하다:

java -jar bfg.jar -bi blobs.txt <repo_dir>

문제는 커밋을 찾는 것입니다. 이는 커밋을 찾는 것보다 더 많은 작업입니다. 알고 싶다면 계속 읽으십시오.

추가 작업

커밋 해시가 주어지면 Blob을 포함하여 연결된 모든 객체의 해시를 인쇄하는 명령은 다음과 같습니다.

git ls-tree -r --full-tree <commit_hash>

따라서 리포지토리의 모든 커밋에 대해 이러한 출력을 사용할 수 있다면 Blob 해시가 주어지면 커밋이 출력과 일치하는 커밋입니다. 이 아이디어는 다음 스크립트로 인코딩됩니다.

#!/bin/bash
DB_DIR='trees-db'

find_commit() {
    cd ${DB_DIR}
    for f in *; do
        if grep -q $1 ${f}; then
            echo ${f}
        fi
    done
    cd - > /dev/null
}

create_db() {
    local tfile='/tmp/commits.txt'
    mkdir -p ${DB_DIR} && cd ${DB_DIR}
    git rev-list --all > ${tfile}

    while read commit_hash; do
        if [[ ! -e ${commit_hash} ]]; then
            git ls-tree -r --full-tree ${commit_hash} > ${commit_hash}
        fi
    done < ${tfile}
    cd - > /dev/null
    rm -f ${tfile}
}

create_db

while read id; do
    find_commit ${id};
done

내용이 파일에 저장되면 find-commits.sh일반적인 호출은 다음과 같습니다.

cat blobs.txt | find-commits.sh

이전과 같이 파일 blobs.txt에는 한 줄에 하나씩 Blob 해시가 나열됩니다. 이 create_db()함수는 모든 커밋 목록의 캐시를 현재 디렉토리의 하위 디렉토리에 저장합니다.

OS에서 24 개의 가상 코어로 제공되는 두 개의 Intel Xeon CPU E5-2620 2.00GHz 프로세서가있는 시스템에서 실험 한 일부 통계 :

  • 리포지토리의 총 커밋 수 = 거의 11,000
  • 파일 생성 속도 = 126 파일 / 초 스크립트는 커밋 당 하나의 파일을 만듭니다. 캐시가 처음 생성 될 때만 발생합니다.
  • 캐시 생성 오버 헤드 = 87 초
  • 평균 검색 속도 = 522 commits / s. 캐시 최적화로 실행 시간이 80 % 단축되었습니다.

스크립트는 단일 스레드입니다. 따라서 한 번에 하나의 코어 만 사용됩니다.


2

Windows Git 용 Powershell 솔루션, 가장 큰 파일 찾기 :

git ls-tree -r -t -l --full-name HEAD | Where-Object {
 $_ -match '(.+)\s+(.+)\s+(.+)\s+(\d+)\s+(.*)'
 } | ForEach-Object {
 New-Object -Type PSObject -Property @{
     'col1'        = $matches[1]
     'col2'      = $matches[2]
     'col3' = $matches[3]
     'Size'      = [int]$matches[4]
     'path'     = $matches[5]
 }
 } | sort -Property Size -Top 10 -Descending

0

git history에서 큰 파일을 어떻게 추적 할 수 있습니까?

근본 원인을 분석, 검증 및 선택하여 시작하십시오. git-repo-analysis도움을주기 위해 사용하십시오 .

BFG Repo-Cleaner 에서 생성 한 상세 보고서 에서 10MiB / s 네트워크 처리량을 사용하여 Digital Ocean 드롭 릿에 복제하여 매우 빠르게 실행할 수있는 일부 값을 찾을 수도 있습니다 .


나는 당신이 BFG 제안에 대한 일반적인 대답을 가지고 있다고 생각하지만, 세부 사항을 제공하지 않고 다른 타사 서비스 (설명없이)를 사용하여 제안함으로써 그것을 망칠 수 있습니다. 이 BFG 사용법의 명령 행 예제를 제공하기 위해이를 정리할 수 있습니까?
phord

0

나는 다른 사람과 같은 이유로 이것을 우연히 발견했습니다. 그러나 인용 된 스크립트는 나에게 효과적이지 않았습니다. 나는 내가 본 사람들의 하이브리드 인 하나를 만들었고 지금 여기에 산다-https: //gitlab.com/inorton/git-size-calc

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.