Bagaimana menemukan / mengidentifikasi komit besar dalam sejarah git?

366

Saya punya repo git 300 MB. Ukuran total file yang saya periksa saat ini adalah 2 MB, dan ukuran total dari sisa repo git adalah 298 MB. Ini pada dasarnya adalah repo khusus kode yang tidak boleh lebih dari beberapa MB.

Saya mencurigai seseorang secara tidak sengaja melakukan beberapa file besar (video, gambar, dll), dan kemudian menghapusnya ... tetapi tidak dari git, jadi sejarahnya masih berisi file besar yang tidak berguna. Bagaimana menemukan file besar dalam sejarah git? Ada 400+ komitmen, jadi satu per satu tidaklah praktis.

CATATAN : pertanyaan saya bukan tentang cara menghapus file , tetapi bagaimana menemukannya di tempat pertama.

celana
sumber

Jawaban:

143

Saya menemukan skrip ini sangat berguna di masa lalu untuk menemukan objek besar (dan tidak jelas) di repositori 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 ', '

Itu akan memberi Anda nama objek (SHA1sum) gumpalan, dan kemudian Anda bisa menggunakan skrip seperti ini:

... untuk menemukan komit yang menunjuk ke masing-masing gumpalan itu.

Mark Longair
sumber
31
Jawaban ini sangat membantu, karena mengirim saya ke pos di atas. Sementara skrip posting bekerja, saya merasa lambat. Jadi saya menulis ulang, dan sekarang secara signifikan lebih cepat pada repositori besar. Silahkan lihat: gist.github.com/nk9/b150542ef72abc7974cb
Nick K9
7
Harap sertakan instruksi lengkap dalam jawaban Anda dan bukan hanya tautan di luar kantor; Apa yang kita lakukan ketika stubbisms.wordpress.com mau tidak mau turun eh?
ThorSummoner
@ NickK9 yang menarik saya mendapatkan hasil yang berbeda dari skrip Anda dan yang lainnya. ada banyak benda besar yang sepertinya Anda lewatkan. Apakah ada sesuatu yang saya lewatkan?
UpAndAdam
Oh keren! Terima kasih telah membuat skrip saya lebih cepat @nick \ k9: D @UpAndAdam, apakah Anda mengatakan skrip saya menghasilkan keluaran yang salah?
Antony Stubbs
1
Komentar ini membuatnya terdengar seperti kami melaporkan ukuran dalam byte, tetapi saya mendapatkan kilobyte.
Kat
683

🚀 Shell satu-liner yang sangat cepat 🚀

Skrip shell ini menampilkan semua objek gumpalan di repositori, diurutkan dari yang terkecil hingga yang terbesar.

Untuk repo sampel saya, ini berjalan sekitar 100 kali lebih cepat daripada yang lain yang ditemukan di sini.
Pada sistem Athlon II X4 saya yang tepercaya, ia menangani repositori Kernel Linux dengan 5,6 juta objek hanya dalam satu menit .

Skrip Dasar

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

Ketika Anda menjalankan kode di atas, Anda akan mendapatkan output yang dapat dibaca manusia seperti ini:

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

pengguna macOS : Karena numfmttidak tersedia di macOS, Anda bisa menghilangkan baris terakhir dan berurusan dengan ukuran byte mentah ataubrew install coreutils .

Penyaringan

Untuk mencapai pemfilteran lebih lanjut , masukkan baris berikut sebelum sortbaris .

Untuk mengecualikan file yang ada diHEAD , masukkan baris berikut:

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

Untuk hanya menampilkan file yang melebihi ukuran yang diberikan (mis. 1 MiB = 2 20  B), masukkan baris berikut:

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

Output untuk Komputer

Untuk menghasilkan output yang lebih cocok untuk diproses lebih lanjut oleh komputer, abaikan dua baris terakhir dari skrip dasar. Mereka melakukan semua pemformatan. Ini akan memberi Anda sesuatu seperti ini:

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

Penghapusan File

Untuk penghapusan file yang sebenarnya, lihat pertanyaan SO pada topik ini .

raphinesse
sumber
14
Ini layak lebih dari sekadar upvote saya! Terima kasih khusus untuk menyediakan output yang dapat dibaca komputer dan manusia.
Michel Jung
2
Ini sangat cepat dan mudah digunakan!
Dagu
32
Untuk menggunakan ini di Mac Anda harus brew install coreutilsdan kemudian ganti cutdengan gcutdan numfmtdengan gnumfmt.
Nick Sweeting
2
Izinkan saya menekankan kembali - ini jauh lebih cepat daripada semua daftar lain yang pernah saya lihat.
Sridhar Sarnobat
4
ini membuat git alias hebat :) git largesiapa pun?
anarcat
160

Saya telah menemukan solusi satu-liner pada halaman wiki Departemen Fisika Zurich ETH (dekat dengan akhir halaman itu). Lakukan saja git gcuntuk menghapus sampah basi, dan kemudian

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

akan memberi Anda 10 file terbesar di repositori.

Ada juga solusi malas yang sekarang tersedia, GitExtensions sekarang memiliki plugin yang melakukan ini di UI (dan juga menangani penulisan ulang riwayat).

Dialog GitExtensions 'Cari file besar'

skolima
sumber
8
One-liner itu hanya berfungsi jika Anda ingin mendapatkan file terbesar tunggal (yaitu, gunakan tail -1). Newline menghalangi apa pun yang lebih besar. Anda dapat menggunakan sed untuk mengonversi baris baru sehingga grep akan bermain bagus: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'`
Throctukes
10
grep: a70783fca9bfbec1ade1519a41b6cc4ee36faea0: Tidak ada file atau direktori seperti itu
Jonathan Allard
1
Link wiki pindah ke: readme.phys.ethz.ch/documentation/git_advanced_hints
outsmartin
11
Menemukan GitExtensions seperti menemukan pot emas dan akhir pelangi - terima kasih!
ckapilla
3
Apakah ada ekstensi yang mencetak ukuran file?
Michael
27

Langkah 1 Tulis semua file SHA1s ke file teks:

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

Langkah 2 Mengurutkan gumpalan dari yang terbesar ke yang terkecil dan menulis hasil ke file teks:

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

Langkah 3a Gabungkan kedua file teks untuk mendapatkan informasi nama file / sha1 / ukuran:

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;

Langkah 3b Jika Anda memiliki nama file atau nama jalur yang berisi spasi coba variasi Langkah 3a ini. Ini digunakan cutalih-alih awkuntuk mendapatkan kolom yang diinginkan termasuk. spasi dari kolom 7 hingga akhir baris:

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;

Sekarang Anda dapat melihat file bigtosmall.txt untuk memutuskan file mana yang ingin Anda hapus dari riwayat Git Anda.

Langkah 4 Untuk melakukan penghapusan (perhatikan bagian ini lambat karena akan memeriksa setiap komit dalam riwayat Anda untuk data tentang file yang Anda identifikasi):

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

Sumber

Langkah 1-3a disalin dari Finding and Purging Big Files From Git History

EDIT

Artikel itu dihapus pada paruh kedua 2017, tetapi salinan yang diarsipkan masih dapat diakses menggunakan Wayback Machine .

friederbluemle
sumber
6
Satu liner untuk melakukan hal yang sama: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, terima kasih untuk one-liner! Tidak menangani nama file dengan ruang di dalamnya, ini tampaknya: 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. Perhatikan bahwa Anda harus memasukkan karakter TAB yang sebenarnya setelah join -t'dengan CTRL + V <TAB> per geekbraindump.blogspot.ru/2009/04/unix-join-with-tabs.html
Nickolay
2
@Nickolay dengan bash $'\t'akan memberi Anda tab. echo -n $'\t' | xxd -ps->09
Iwan Aucamp
1
@IwanAucamp: lebih baik lagi, terima kasih atas tipnya! (Sayang sekali saya tidak dapat mengedit komentar sebelumnya .. oh well.)
Nickolay
1
@ Sridhar-Sarnobat Artikel ini disimpan oleh Wayback Machine! :) web.archive.org/web/20170621125743/http://www.naleid.com/blog/…
friederbluemle
18

Anda harus menggunakan BFG Repo-Cleaner .

Menurut situs web:

BFG adalah alternatif yang lebih sederhana dan lebih cepat untuk git-filter-branch untuk membersihkan data buruk dari riwayat repositori Git Anda:

  • Menghapus File Besar Gila
  • Menghapus Kata Sandi, Kredensial & Data pribadi lainnya

Prosedur klasik untuk mengurangi ukuran repositori adalah:

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
Warren Seine
sumber
4
Repo-Cleaner BFG sangat bagus. Sangat cepat dan bekerja dengan sangat andal.
fschmitt
30
Ini tidak memberi tahu Anda bagaimana mendaftar semua file terbesar sekalipun.
Andi Jay
5
Masalahnya adalah Anda tidak bisa MELIHAT apa saja file besar tanpa benar-benar menghapusnya. Saya tidak merasa nyaman melakukan ini tanpa menjalankan kering terlebih dahulu yang hanya daftar file besar.
Sridhar Sarnobat
Apa yang --strip-biggest-blobs 500harus dilakukan
2540625
git akan menolak perubahan yang dibuat alat ini.
Christopher
9

Jika Anda hanya ingin memiliki daftar file besar, maka saya ingin memberi Anda satu-liner berikut:

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

Output siapa adalah:

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

Entri terakhir dalam daftar menunjuk ke file terbesar dalam sejarah git Anda.

Anda dapat menggunakan output ini untuk memastikan bahwa Anda tidak menghapus hal-hal dengan BFG yang Anda perlukan dalam sejarah Anda.

schmijos
sumber
2
Hebat !! Namun, Anda harus mencatat bahwa Anda perlu mengkloning repo dengan opsi --mirror sebelum menjalankan perintah ini.
Andi Jay
Saya ingin tahu, untuk apa 1.1, 1.2, 2.3angka - angkanya?
ympostor
Angka-angka adalah daftar <filenumber>.<field>menentukan urutan kombinasi. Lihat man.cx/join untuk informasi lebih lanjut.
schmijos
6

Jika Anda menggunakan Windows, berikut ini adalah skrip PowerShell yang akan mencetak 10 file terbesar di repositori Anda:

$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
Julia Schwarz
sumber
1
Ini menghasilkan jawaban yang berbeda dengan @raphinesse, melewatkan banyak file terbesar di repositori saya. Juga ketika satu file besar memiliki banyak modifikasi, hanya ukuran terbesar yang dilaporkan.
kristianp
Script ini gagal untuk saya, dengan kesalahan: You cannot call a method on a null-valued expression. At line: 2 char: 1. Namun, jawaban ini berhasil: stackoverflow.com/a/57793716/2441655 (juga lebih pendek)
Venryx
4

Coba git ls-files | xargs du -hs --threshold=1M.

Kami menggunakan perintah di bawah ini dalam pipa CI kami, itu berhenti jika menemukan file besar di repo git:

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
Vojtech Vitek
sumber
2

Saya tidak dapat menggunakan jawaban yang paling populer karena --batch-check pergantian baris perintah ke Git 1.8.3 (yang harus saya gunakan) tidak menerima argumen apa pun. Langkah selanjutnya telah dicoba pada CentOS 6.5 dengan Bash 4.1.2

Konsep Kunci

Dalam Git, istilah gumpalan menyiratkan isi file. Perhatikan bahwa komit dapat mengubah konten file atau pathname. Dengan demikian, file yang sama dapat merujuk ke gumpalan yang berbeda tergantung pada komit. File tertentu bisa menjadi yang terbesar dalam hierarki direktori dalam satu komit, sementara tidak di yang lain. Oleh karena itu, pertanyaan menemukan commit besar dan bukan file besar, menempatkan hal-hal dalam perspektif yang benar.

Untuk Yang Tidak Sabar

Perintah untuk mencetak daftar gumpalan dalam urutan ukuran menurun adalah:

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

Output sampel:

3a51a45e12d4aedcad53d3a0d4cf42079c62958e blob 305971200
7c357f2c2a7b33f939f9b7125b155adbd7890be2 blob 289163620

Untuk menghilangkan gumpalan seperti itu, gunakan BFG Repo Cleaner , seperti disebutkan dalam jawaban lain. Diberikan file blobs.txtyang hanya berisi hash gumpalan, misalnya:

3a51a45e12d4aedcad53d3a0d4cf42079c62958e
7c357f2c2a7b33f939f9b7125b155adbd7890be2

Melakukan:

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

Pertanyaannya adalah tentang menemukan komitmen, yang lebih berfungsi daripada menemukan gumpalan. Untuk tahu, silakan baca terus.

Pekerjaan selanjutnya

Diberi hash komit, perintah yang mencetak hash dari semua objek yang terkait dengannya, termasuk gumpalan, adalah:

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

Jadi, jika kita memiliki output seperti itu tersedia untuk semua komit di repo, maka diberi hash gumpalan, banyak komit adalah yang cocok dengan salah satu output. Ide ini dikodekan dalam skrip berikut:

#!/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

Jika konten disimpan dalam nama file find-commits.shmaka doa biasa akan seperti di bawah:

cat blobs.txt | find-commits.sh

Seperti sebelumnya, file blobs.txtmencantumkan hash gumpalan, satu per baris. Itucreate_db() fungsi menyimpan cache dari semua berkomitmen listing di sub-direktori dalam direktori saat ini.

Beberapa statistik dari percobaan saya pada sistem dengan dua prosesor Intel (R) Xeon (R) CPU E5-2620 2.00GHz yang disajikan oleh OS sebagai 24 inti virtual:

  • Total jumlah komitmen dalam repo = hampir 11.000
  • Kecepatan pembuatan file = 126 file / s. Script membuat satu file per komit. Ini terjadi hanya ketika cache sedang dibuat untuk pertama kalinya.
  • Overhead pembuatan cache = 87 dtk.
  • Kecepatan pencarian rata-rata = 522 komit / s. Optimasi cache menghasilkan pengurangan 80% dalam waktu berjalan.

Perhatikan bahwa skrip ini adalah utas tunggal. Karena itu, hanya satu inti yang akan digunakan pada satu waktu.

pdp
sumber
2

Solusi Powershell untuk windows git, cari file terbesar:

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
Harun
sumber
0

Bagaimana saya bisa melacak file besar dalam sejarah git?

Mulailah dengan menganalisis, memvalidasi, dan memilih akar permasalahan. Gunakan git-repo-analysisuntuk membantu.

Anda juga dapat menemukan beberapa nilai dalam laporan terperinci yang dihasilkan oleh BFG Repo-Cleaner , yang dapat dijalankan dengan sangat cepat dengan mengkloning ke tetesan Digital Ocean menggunakan 10MiB / s throughput jaringan mereka.

Josh Habdas
sumber
Saya pikir Anda memiliki jawaban umum yang bagus dalam saran BFG, tetapi Anda merusaknya dengan tidak memberikan perincian dan kemudian dengan menyarankan menggunakan layanan pihak ketiga yang berbeda (juga tanpa penjelasan apa pun). Bisakah Anda membersihkan ini beberapa untuk memberikan contoh baris perintah dari penggunaan BFG ini?
phord
0

Saya menemukan ini karena alasan yang sama seperti orang lain. Tetapi skrip yang dikutip tidak bekerja untuk saya. Saya telah membuat satu yang lebih hibrida dari yang saya lihat dan sekarang tinggal di sini - https://gitlab.com/inorton/git-size-calc

IanNorton
sumber