Làm thế nào để tìm / xác định các cam kết lớn trong lịch sử git?


366

Tôi có một repo 300 MB git. Tổng kích thước của các tệp thanh toán hiện tại của tôi là 2 MB và tổng kích thước của phần còn lại của repo git là 298 MB. Đây về cơ bản là một repo chỉ có mã không nên nhiều hơn một vài MB.

Tôi nghi ngờ ai đó đã vô tình phạm một số tệp lớn (video, hình ảnh, v.v.), sau đó xóa chúng ... nhưng không phải từ git, vì vậy lịch sử vẫn chứa các tệp lớn vô dụng. Làm thế nào có thể tìm thấy các tập tin lớn trong lịch sử git? Có hơn 400 cam kết, vì vậy việc thực hiện từng bước một là không thực tế.

LƯU Ý : câu hỏi của tôi không phải là về cách loại bỏ tệp , mà là làm thế nào để tìm thấy nó ở nơi đầu tiên.



Câu trả lời:


143

Trước đây tôi đã thấy tập lệnh này rất hữu ích để tìm các đối tượng lớn (và không rõ ràng) trong kho 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 ', '

Điều đó sẽ cung cấp cho bạn tên đối tượng (SHA1sum) của blob, và sau đó bạn có thể sử dụng một tập lệnh như thế này:

... Để tìm ra cam kết chỉ ra từng đốm màu đó.


31
Câu trả lời này thực sự hữu ích, vì nó đã gửi tôi đến bài viết ở trên. Trong khi kịch bản của bài viết hoạt động, tôi thấy nó rất chậm. Vì vậy, tôi đã viết lại nó, và bây giờ nó nhanh hơn đáng kể trên các kho lưu trữ lớn. Có một cái nhìn: gist.github.com/nk9/b150542ef72abc7974cb
Nick K9

7
Vui lòng bao gồm các hướng dẫn đầy đủ trong câu trả lời của bạn và không chỉ các liên kết ngoài trang web; Chúng ta phải làm gì khi stubbism.wordpress.com chắc chắn đi xuống nhỉ?
ThorSummoner

@ NickK9 thú vị tôi nhận được đầu ra khác nhau từ kịch bản của bạn và khác. có một loạt các vật thể lớn hơn mà bạn dường như bỏ lỡ. Có thiếu điều gì không?
UpAndAdam

Ồ tuyệt! Cảm ơn vì đã làm cho tập lệnh của tôi nhanh hơn @nick \ k9: D @UpAndAdam, bạn có nói rằng tập lệnh của tôi tạo ra đầu ra không chính xác không?
Stony Stubbs

1
Những nhận xét này nghe có vẻ như chúng tôi đang báo cáo kích thước theo byte, nhưng tôi nhận được kilobyte.
Kat

682

Một lớp vỏ nhanh chóng rực rỡ

Kịch bản shell này hiển thị tất cả các đối tượng blob trong kho lưu trữ, được sắp xếp từ nhỏ nhất đến lớn nhất.

Đối với repo mẫu của tôi, nó chạy nhanh hơn khoảng 100 lần so với những cái khác được tìm thấy ở đây.
Trên hệ thống Athlon II X4 đáng tin cậy của tôi, nó xử lý kho lưu trữ Linux Kernel với 5,6 triệu đối tượng chỉ trong hơn một phút .

Tập lệnh cơ sở

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

Khi bạn chạy mã trên, bạn sẽ nhận được đầu ra đẹp như người đọc như thế này:

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

Người dùng macOS : Vì numfmtkhông có sẵn trên macOS, bạn có thể bỏ qua dòng cuối cùng và xử lý các kích thước byte thô hoặc brew install coreutils.

Lọc

Để đạt được lọc thêm , chèn thêm bất kỳ của các dòng sau đây trước khi sortdòng .

Để loại trừ các tệp có trongHEAD , chèn dòng sau:

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

Để chỉ hiển thị các tệp vượt quá kích thước đã cho (ví dụ 1 MiB = 2 20  B), hãy chèn dòng sau:

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

Đầu ra cho máy tính

Để tạo đầu ra phù hợp hơn cho máy tính xử lý thêm , hãy bỏ qua hai dòng cuối của tập lệnh cơ sở. Họ làm tất cả các định dạng. Điều này sẽ để lại cho bạn một cái gì đó như thế này:

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

Xóa tệp

Để xóa tệp thực tế, hãy xem câu hỏi SO này về chủ đề .


14
Điều này xứng đáng nhiều hơn chỉ là upvote của tôi! Cảm ơn đặc biệt vì đã cung cấp cả hai, đầu ra máy tính và con người có thể đọc được.
Michel Jung

2
Điều này cực kỳ nhanh chóng và dễ sử dụng!
Chin

32
Để sử dụng điều này trên Mac, bạn cần phải brew install coreutilsthay thế cutbằng gcutnumfmtbằng gnumfmt.
Nick Sweeting

2
Hãy để tôi nhấn mạnh lại - điều này nhanh hơn nhiều so với tất cả các danh sách khác tôi đã thấy.
Sridhar Sarnobat

4
Điều này làm cho một bí danh git tuyệt vời :) git largebất cứ ai?
Anarcat

160

Tôi đã tìm thấy một giải pháp một lớp trên trang wiki của Khoa Vật lý ETH Zurich (gần cuối trang đó). Chỉ cần làm một git gcđể loại bỏ rác cũ, và sau đó

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

sẽ cung cấp cho bạn 10 tệp lớn nhất trong kho lưu trữ.

Hiện tại cũng có một giải pháp lười hơn, GitExtensions hiện có một plugin thực hiện điều này trong UI (và cũng xử lý các ghi lại lịch sử).

Hộp thoại 'Tìm tệp lớn' của GitExtensions


8
Đó là một lớp chỉ hoạt động nếu bạn muốn có được tệp lớn nhất (nghĩa là sử dụng đuôi -1). Dòng mới có được trong cách cho bất cứ điều gì lớn hơn. Bạn có thể sử dụng sed để chuyển đổi các dòng mới để grep sẽ chơi tốt: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: Không có tập tin hoặc thư mục như vậy
Jonathan Allard

1
Liên kết wiki đã được chuyển đến: readme.phys.ethz.ch/documentation/git_advified_hints
outsmartin

11
Tìm kiếm GitExtensions giống như tìm nồi vàng và sự kết thúc của cầu vồng - cảm ơn bạn!
ckapilla

3
Ngoài ra còn có một phần mở rộng in kích thước của các tập tin?
Michael

27

Bước 1 Viết tất cả tệp SHA1 vào tệp văn bản:

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

Bước 2 Sắp xếp các đốm màu từ lớn nhất đến nhỏ nhất và ghi kết quả vào tệp văn bản:

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

Bước 3a Kết hợp cả hai tệp văn bản để nhận thông tin tên tệp / sha1 / size:

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;

Bước 3b Nếu bạn có tên tệp hoặc tên đường dẫn chứa khoảng trắng, hãy thử biến thể này của Bước 3a. Nó sử dụng cutthay vì awkđể có được các cột mong muốn bao gồm. khoảng trắng từ cột 7 đến cuối dòng:

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;

Bây giờ bạn có thể xem tệp bigtosmall.txt để quyết định tệp nào bạn muốn xóa khỏi lịch sử Git của mình.

Bước 4 Để thực hiện xóa (lưu ý phần này chậm vì nó sẽ kiểm tra mọi cam kết trong lịch sử của bạn để tìm dữ liệu về tệp bạn đã xác định):

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

Nguồn

Các bước 1-3a đã được sao chép từ Tìm kiếm và thanh lọc các tệp lớn từ Lịch sử Git

BIÊN TẬP

Bài viết đã bị xóa vào khoảng nửa cuối năm 2017, nhưng bản sao lưu trữ của nó vẫn có thể được truy cập bằng Wayback Machine .


6
Một lớp lót để làm điều tương tự: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 5/03/2015

1
@Iwan, cảm ơn vì một lót! Nó không xử lý tên tệp có khoảng trắng trong đó, điều này dường như : 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. Lưu ý rằng bạn phải nhập ký tự TAB thực tế sau join -t'với CTRL + V <TAB> mỗi geekbraindump.blogspot.ru/2009/04/unix-join-with-tabs.html
Nickolay

2
@Nickolay với bash $'\t'sẽ cung cấp cho bạn một tab. echo -n $'\t' | xxd -ps->09
Iwan Aucamp

1
@IwanAucamp: thậm chí tốt hơn, cảm ơn vì tiền boa! (Quá tệ, tôi không thể chỉnh sửa nhận xét trước đó .. ồ tốt.)
Nickolay

1
@ Sridhar-Sarnobat Bài viết đã được lưu bởi Wayback Machine! :) web.archive.org/web/20170621125743/http://www.naleid.com/blog/...
friederbluemle

18

Bạn nên sử dụng BFG Repo-Cleaner .

Theo trang web:

BFG là một thay thế đơn giản hơn, nhanh hơn cho nhánh bộ lọc git để xóa dữ liệu xấu khỏi lịch sử kho lưu trữ Git của bạn:

  • Xóa tập tin Crazy Big
  • Xóa mật khẩu, thông tin xác thực và dữ liệu riêng tư khác

Quy trình cổ điển để giảm kích thước của kho lưu trữ sẽ là:

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 rất tốt. Nó sáng nhanh và hoạt động rất đáng tin cậy.
fschmitt

30
Điều này không cho bạn biết làm thế nào để liệt kê tất cả các tệp lớn nhất.
Andi Jay

5
Vấn đề với điều này là bạn không thể XEM NHỮNG tệp lớn nào mà không thực sự loại bỏ chúng. Tôi không cảm thấy thoải mái khi làm điều này mà không cần chạy khô trước mà chỉ liệt kê các tệp lớn.
Sridhar Sarnobat

Không gì --strip-biggest-blobs 500làm gì?
2540625

git sẽ từ chối những thay đổi mà công cụ này thực hiện.
Christopher

9

Nếu bạn chỉ muốn có một danh sách các tệp lớn, thì tôi muốn cung cấp cho bạn một lớp lót sau:

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

Sản lượng của ai sẽ là:

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

Mục cuối cùng trong danh sách trỏ đến tệp lớn nhất trong lịch sử git của bạn.

Bạn có thể sử dụng đầu ra này để đảm bảo rằng bạn không xóa nội dung bằng BFG mà bạn sẽ cần trong lịch sử của mình.


2
Tuyệt vời!! Tuy nhiên, bạn cần lưu ý rằng bạn cần sao chép repo với các tùy chọn --mirror trước khi chạy lệnh này.
Andi Jay

Tôi tò mò, những 1.1, 1.2, 2.3con số để làm gì?
ympostor

Các con số là một danh sách <filenumber>.<field>chỉ định thứ tự của sự kết hợp. Xem man.cx/join để biết thêm thông tin.
schmijos

6

Nếu bạn đang ở trên Windows, đây là tập lệnh PowerShell sẽ in 10 tệp lớn nhất trong kho lưu trữ của bạn:

$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
Điều này tạo ra một câu trả lời khác với @raphinesse, thiếu một loạt các tệp lớn nhất trên kho lưu trữ của tôi. Ngoài ra khi một tệp lớn có nhiều sửa đổi, chỉ có kích thước lớn nhất được báo cáo.
kristianp

Kịch bản này thất bại đối với tôi, với lỗi : You cannot call a method on a null-valued expression. At line: 2 char: 1. Tuy nhiên, câu trả lời này đã có hiệu quả: stackoverflow.com/a/57793716/2441655 (nó cũng ngắn hơn)
Venryx

4

Hãy thử git ls-files | xargs du -hs --threshold=1M.

Chúng tôi sử dụng lệnh dưới đây trong đường ống CI của chúng tôi, nó dừng lại nếu tìm thấy bất kỳ tệp lớn nào trong 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

Tôi không thể sử dụng câu trả lời phổ biến nhất vì --batch-checkchuyển đổi dòng lệnh sang Git 1.8.3 (mà tôi phải sử dụng) không chấp nhận bất kỳ đối số nào. Các bước tiếp theo đã được thử trên CentOS 6.5 với Bash 4.1.2

Ý chính

Trong Git, thuật ngữ blob ngụ ý nội dung của một tệp. Lưu ý rằng một cam kết có thể thay đổi nội dung của tệp hoặc tên đường dẫn. Do đó, cùng một tệp có thể đề cập đến một blob khác nhau tùy thuộc vào cam kết. Một tệp nhất định có thể là lớn nhất trong hệ thống phân cấp thư mục trong một cam kết, trong khi không phải trong một tệp khác. Do đó, câu hỏi về việc tìm kiếm các cam kết lớn thay vì các tệp lớn, đặt các vấn đề theo quan điểm chính xác.

Dành cho người thiếu kiên nhẫn

Lệnh in danh sách các đốm màu theo thứ tự kích thước giảm dần là:

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

Đầu ra mẫu:

3a51a45e12d4aedcad53d3a0d4cf42079c62958e blob 305971200
7c357f2c2a7b33f939f9b7125b155adbd7890be2 blob 289163620

Để loại bỏ các đốm màu như vậy, hãy sử dụng BFG Repo Cleaner , như được đề cập trong các câu trả lời khác. Cho một tệp blobs.txtchỉ chứa băm blob, ví dụ:

3a51a45e12d4aedcad53d3a0d4cf42079c62958e
7c357f2c2a7b33f939f9b7125b155adbd7890be2

Làm:

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

Câu hỏi là về việc tìm kiếm các cam kết, công việc này nhiều hơn là tìm các đốm màu. Để biết, xin vui lòng đọc tiếp.

Công việc tiếp theo

Đưa ra một hàm băm cam kết, một lệnh in băm của tất cả các đối tượng được liên kết với nó, bao gồm các đốm màu, là:

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

Vì vậy, nếu chúng ta có sẵn các đầu ra như vậy cho tất cả các xác nhận trong repo, sau đó đưa ra một hàm băm blob, thì các bó xác nhận là các kết quả khớp với bất kỳ đầu ra nào. Ý tưởng này được mã hóa trong đoạn script sau:

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

Nếu nội dung được lưu trong một tệp có tên find-commits.shthì một lệnh gọi thông thường sẽ như dưới đây:

cat blobs.txt | find-commits.sh

Như trước đó, tập tin blobs.txtliệt kê băm blob, mỗi dòng một dòng. Các create_db()chức năng tiết kiệm bộ nhớ cache của tất cả các cam kết danh sách trong một thư mục con trong thư mục hiện hành.

Một số thống kê từ các thử nghiệm của tôi trên một hệ thống có hai bộ xử lý CPU Intel (R) Xeon (R) CPU E5-2620 2.00GHz được HĐH trình bày dưới dạng 24 lõi ảo:

  • Tổng số lần xác nhận trong repo = gần 11.000
  • Tốc độ tạo tệp = 126 tệp / s. Kịch bản tạo một tệp duy nhất cho mỗi lần xác nhận. Điều này chỉ xảy ra khi bộ đệm được tạo lần đầu tiên.
  • Chi phí tạo bộ nhớ cache = 87 s.
  • Tốc độ tìm kiếm trung bình = 522 lần xác nhận / s. Việc tối ưu hóa bộ đệm giúp giảm 80% thời gian chạy.

Lưu ý rằng tập lệnh là luồng đơn. Do đó, chỉ một lõi sẽ được sử dụng bất cứ lúc nào.


2

Giải pháp Powershell cho windows git, tìm các tệp lớn nhất:

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

Làm cách nào tôi có thể theo dõi các tệp lớn trong lịch sử git?

Bắt đầu bằng cách phân tích, xác nhận và chọn nguyên nhân gốc. Sử dụng git-repo-analysisđể giúp đỡ.

Bạn cũng có thể tìm thấy một số giá trị trong các báo cáo chi tiết được tạo bởi BFG Repo-Cleaner , có thể được chạy rất nhanh bằng cách sao chép vào một giọt Digital Ocean bằng cách sử dụng thông lượng mạng 10MiB / s của chúng.


Tôi nghĩ rằng bạn có một câu trả lời chung chung trong đề xuất BFG, nhưng bạn làm hỏng nó bằng cách không cung cấp bất kỳ chi tiết nào và sau đó bằng cách đề xuất sử dụng dịch vụ của bên thứ ba khác (cũng không có bất kỳ lời giải thích nào). Bạn có thể dọn sạch cái này để cung cấp một ví dụ dòng lệnh về việc sử dụng BFG này không?
phord

0

Tôi tình cờ thấy điều này vì lý do tương tự như bất kỳ ai khác. Nhưng các kịch bản được trích dẫn không làm việc cho tôi. Tôi đã tạo một cái giống với những thứ tôi đã thấy và nó hiện đang sống ở đây - https://gitlab.com/inorton/git-size-calc

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.