Як знайти / визначити великі комісії в історії git?


366

У мене є ріпо 300 Гбіт. Загальний розмір моїх зареєстрованих файлів - 2 Мб, а загальний розмір решти git repo - 298 Мб. Це, в основному, репо тільки для коду, який не повинен бути більше кількох МБ.

Я підозрюю, що хтось випадково вчинив великі файли (відео, зображення тощо), а потім видалив їх ... але не з git, тому історія все ще містить непотрібні великі файли. Як можна знайти великі файли в історії git? Існує 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 ', '

Це дасть вам ім'я об'єкта (SHA1sum) блобу, і тоді ви можете використовувати такий сценарій:

... знайти комісію, яка вказує на кожну з цих крапок.


31
Ця відповідь була дуже корисною, адже вона надіслала мене до публікації вище. Поки сценарій публікації працював, я вважав це болісно повільним. Тому я переписав це, і тепер це значно швидше у великих сховищах. Подивіться: gist.github.com/nk9/b150542ef72abc7974cb
Nick K9

7
Будь ласка, включіть до своїх відповідей повні інструкції, а не лише посилання на сторону; Що ми робимо, коли stubbisms.wordpress.com неминуче знижується?
ThorSummoner

@ NickK9 цікаво, що я отримую різні результати від вашого сценарію та іншого. є купа більших предметів, які, здається, пропускають ваші. Щось мені не вистачає?
UpAndAdam

О, круто! Дякуємо, що зробили мій сценарій швидшим @nick \ k9: D @UpAndAdam, ти кажеш, мій сценарій дав неправильний вихід?
Антоні Стаббс

1
Ці коментарі звучать так, ніби ми повідомляємо розмір у байтах, але я отримую кілобайти.
Кет

681

One Світло-швидкий одноколісний корпус оболонки 🚀

Цей скрипт оболонки відображає всі об'єкти blob у сховищі, відсортовані від найменшого до найбільшого.

Для мого зразка репо, він пробіг приблизно в 100 разів швидше, ніж інші, знайдені тут.
У моїй надійній системі Athlon II X4 він обробляє сховище Linux Kernel зі своїми 5,6 мільйонами об’єктів за трохи більше хвилини .

Базовий сценарій

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

Користувачі macOS : оскільки numfmtце не доступно для macOS, ви можете опустити останній рядок і мати справу з необмеженими розмірами байтів або 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

Видалення файлу

Щоб реально видалити файл, ознайомтесь із цим питанням ТА на цю тему .


14
Це заслуговує більшого, ніж просто моя пропозиція! Особлива подяка за надання читабельних результатів для комп'ютера та людини.
Мішель Юнг

2
Це надзвичайно швидко та просто у використанні!
Чин

31
Для того, щоб використовувати це на Mac вам потрібно , brew install coreutilsа потім замінити cutз gcutі numfmtз gnumfmt.
Nick Sweeting

2
Дозвольте ще раз підкреслити - це набагато швидше, ніж усі інші переліки, які я бачив.
Шрідхар Сарнобат

4
це робить приголомшливий псевдонім git :) git largeкого-небудь?
anarcat

160

Я знайшов однолінійне рішення на вікі-сторінці відомства фізичних наук Цюріха (близько до кінця цієї сторінки). Просто зробіть, 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 тепер має плагін, який робить це в інтерфейсі (і також обробляє історію переписує).

Діалогове вікно "Знайти великі файли" GitExtensions


8
Цей однокласник працює лише в тому випадку, якщо ви хочете отримати один найбільший файл (тобто використовувати хвостик -1). Нові лінії перешкоджають чомусь більшому. Ви можете використовувати sed для перетворення нових рядків, щоб 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: Немає такого файлу чи каталогу
Джонатан Аллард

1
Посилання на wiki перейшло до: readme.phys.ethz.ch/documentation/git_advanced_hints
outsmartin

11
Знайти GitExtensions - це як знайти золотий горщик і кінець веселки - дякую!
ckapilla

3
Чи є також розширення, яке друкує розмір файлів?
Майкл

27

Крок 1 Запишіть весь файл SHA1 у текстовий файл:

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

Крок 2 Сортуйте краплі від найбільшого до найменшого та запишіть результати у текстовий файл:

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;

Тепер ви можете подивитися файл bigtosmall.txt, щоб визначити, які файли потрібно видалити з історії Git.

Крок 4 Щоб виконати видалення (зауважте, що ця частина є повільною, оскільки вона буде вивчати кожну комісію вашої історії на предмет даних про визначений вами файл):

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

Джерело

Кроки 1-3а були скопійовані з пошуку та очищення великих файлів із історії Git

EDIT

Статтю було видалено десь у другій половині 2017 року, але до архівної копії її все ще можна отримати доступ за допомогою машини Wayback .


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
Іван Аукамп

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. Зверніть увагу , що ви повинні ввести фактичний символ табуляції після того, як join -t'з CTRL + V <TAB> в geekbraindump.blogspot.ru/2009/04/unix-join-with-tabs.html
Nickolay

2
@Nickolay з bash $'\t'повинен дати вам вкладку. echo -n $'\t' | xxd -ps->09
Iwan Aucamp

1
@IwanAucamp: ще краще, дякую за пораду! (Шкода, що я не можу редагувати попередній коментар .. ой добре.)
Nickolay

1
@ Шрідхар-Сарнобат Стаття була збережена машиною Wayback! :) web.archive.org/web/20170621125743/http://www.naleid.com/blog/…
friederbluemle

18

Вам слід використовувати BFG Repo-Cleaner .

За даними веб-сайту:

BFG - це простіша, швидша альтернатива гіт-фільтр-гілці для очищення поганих даних із історії вашого сховища Git:

  • Видалення божевільних великих файлів
  • Видалення паролів, облікових даних та інших приватних даних

Класична процедура зменшення розміру сховища буде:

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
Проблема в цьому полягає в тому, що ви не можете просто ВИДАЧИТИ, які є великі файли, фактично не видаляючи їх. Мені не комфортно робити це без першого запуску, який просто перераховує великі файли.
Шрідхар Сарнобат

Що робить --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.

Ви можете використовувати цей вихід, щоб переконатися, що ви не видаляєте речі з BFG , які знадобляться вам у вашій історії.


2
Дивовижно !! Однак слід зауважити, що вам потрібно клонувати репо з параметрами --mirror перед запуском цієї команди.
Енді Джей

Мені цікаво, для чого такі 1.1, 1.2, 2.3цифри?
ympostor

Цифри - це список із <filenumber>.<field>зазначенням порядку комбінації. Для отримання додаткової інформації див. Man.cx/join .
schmijos

6

Якщо ви перебуваєте в Windows, ось сценарій PowerShell, який буде друкувати 10 найбільших файлів у вашому сховищі:

$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, пропускаючи купу найбільших файлів у моєму сховищі. Також, коли один великий файл має багато модифікацій, повідомляється лише про найбільший розмір.
Крістіанп

Цей сценарій не зміг для мене, з помилкою: 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-checkперехід командного рядка на Git 1.8.3 (який я маю використовувати) не приймає жодних аргументів. Подальші кроки були випробувані на CentOS 6.5 з Bash 4.1.2

Основні поняття

У Git термін 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.txtякий містить просто хеші блобу, наприклад:

3a51a45e12d4aedcad53d3a0d4cf42079c62958e
7c357f2c2a7b33f939f9b7125b155adbd7890be2

Зробіть:

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

Питання в тому, щоб знайти коміти, це більше роботи, ніж пошук крапів. Щоб знати, читайте далі.

Подальша робота

З огляду на хеш комітів, команда, яка друкує хеші всіх об'єктів, пов'язаних з ним, включаючи краплі, є:

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

Отже, якщо у нас є такі виходи для всіх комітетів у репо, тоді, якщо дано хеш-блош, група комітетів є тими, які відповідають будь-якому з результатів. Ця ідея закодована у наступному сценарії:

#!/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списку файлів є хеші блоків, по одному на рядок. create_db()Функція зберігає кеш всіх фіксації списків в підкаталог в поточному каталозі.

Деякі статистичні дані з моїх експериментів над системою з двома процесорами Intel (R) Xeon (R) процесора 2,00 ГГц, що представлені ОС як 24 віртуальних ядра:

  • Загальна кількість комісій у РЕПО = майже 11 000
  • Швидкість створення файлу = 126 файлів / с. Сценарій створює один файл на комісію. Це відбувається лише тоді, коли кеш створюється вперше.
  • Створення кешу накладних витрат = 87 с.
  • Середня швидкість пошуку = 522 коміти / с. Оптимізація кешу призвела до скорочення часу на 80%.

Зауважте, що сценарій є однопоточним. Тому за один раз буде використано лише одне ядро.


2

Рішення Powershell для Windows git, знайдіть найбільші файли:

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?

Почніть з аналізу, перевірки та вибору першопричини. Використовуйте, git-repo-analysisщоб допомогти.

Ви також можете знайти деяку цінність у детальних звітах, створених BFG Repo-Cleaner , які можна запустити дуже швидко, клонувавши до краплі Digital Ocean, використовуючи пропускну здатність мережі 10 Мбіт / с.


Я думаю, у вас є гарна загальна відповідь у пропозиції BFG, але ви зіпсуєте її, не надаючи жодних деталей, а потім запропонувавши скористатися іншою стороною послугою (також без будь-яких пояснень). Чи можете ви це очистити, щоб надати приклад командного рядка цього використання BFG?
фрад

0

Я наткнувся на це з тієї ж причини, що і будь-хто інший. Але цитовані сценарії не дуже спрацювали для мене. Я зробив той, який є більш гібридом тих, кого я бачив, і він зараз живе тут - https://gitlab.com/inorton/git-size-calc

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.