Як видалити невирізані краплі з мого git repo


124

У мене є репо GitHub, який мав дві гілки - master & release.

Відділення випуску містило двійкові файли розподілу, які сприяли дуже великому розміру репо (> 250 МБ), тому я вирішив очистити речі.

Спочатку я видалив гілку віддаленого випуску через git push origin :release

Потім я видалив місцеву гілку випуску. Спершу я спробував git branch -d release, але git сказав "помилка: гілка" реліз "не є родоначальником вашої нинішньої голови." що правда, то я тоді git branch -D releaseзмусив її видалити.

Але мій розмір сховища, як локально, так і на GitHub, все ще був величезним. Тож я пробіг звичайний список команд git, мовляв git gc --prune=today --aggressive, не пощастило.

Дотримуючись вказівок Чарльза Бейлі в SO 1029969, я зміг отримати список SHA1 для найбільших крапок. Потім я використовував сценарій SO 460331, щоб знайти краплі ... і п'ять найбільших не існує, хоча знайдені менші крапки, тому я знаю, що сценарій працює.

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


Яку версію Git ви використовуєте? А ви спробували stackoverflow.com/questions/1106529/… ?
VonC

git версія 1.6.2.3 Я спробував gc та prune w / різні аргументи. Я не пробував перепакувати -a -d -l, просто запустив його, без змін.
kkrugler

2
Нова інформація - свіжий клон від GitHub більше не має невирішених крапель, і він знижується до "лише" 84 Мб від 250 МБ.
kkrugler

Відповіді:


219

... і без зайвих прихильностей, чи можу я представити вам цю корисну команду "git-gc-all", гарантовано видаляючи всі ваші сміття git, поки вони не можуть з’явити додаткові змінні конфігурації:

git -c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 -c gc.rerereresolved=0 -c gc.rerereunresolved=0 -c gc.pruneExpire=now gc

Вам також може знадобитися запустити щось на кшталт цих перших, о дорогий, git складний !!

git remote rm origin
rm -rf .git/refs/original/ .git/refs/remotes/ .git/*_HEAD .git/logs/
git for-each-ref --format="%(refname)" refs/original/ | xargs -n1 --no-run-if-empty git update-ref -d

Також вам може знадобитися видалити деякі теги, завдяки Zitrax:

git tag | xargs git tag -d

Я все це вкладаю в сценарій: git-gc-all-ferocious .


1
Цікаво. Хороша альтернатива моїй більш загальній відповіді. +1
VonC

10
Це заслуговує більше голосів. Зрештою, він позбувся багатьох об’єктів git, якими були б інші методи. Дякую!
Жан-Філіп Пеллет

1
Отримано. Нічого собі, я не знаю, що тільки що робив, але, здається, чистить багато. Чи можете ви детальніше розглянути, що це робить? У мене є відчуття, що це очистило все моє objects. Що це, і чому вони (мабуть) не мають значення?
Редсандро

1
@Redsandro, наскільки я розумію, ці команди "git rm origin", "rm" та "git update-ref -d" видаляють посилання на старі комітети для віддалених програм та такі, які можуть перешкоджати збору сміття. Опції "git gc" говорять йому не затримуватися на різних старих комісіях, інакше він затримається на них деякий час. Наприклад, gc.rerereiled - це "записи конфліктного злиття, які ви вирішили раніше", за замовчуванням зберігаються протягом 60 днів. Ці варіанти знаходяться на сторінці git-gc. Я не фахівець з git і не знаю точно, що всі ці речі роблять. Я знайшов їх з manpages, і хапаючи .git для внесення змін.
Сем Уоткінс

1
Об'єкт git - це стиснутий файл або дерево або виконувати у вашому git repo, включаючи старі речі з історії. git gc очищає непотрібні об'єкти. Він зберігає об’єкти, які все ще потрібні для вашого поточного репо, та його історію.
Сем Уоткінс

81

Як описано тут , якщо ви хочете назавжди видалити все, на що посилається, лише за допомогою перемикання , просто скористайтеся

git reflog expire --expire-unreachable=now --all
git gc --prune=now

git reflog expire --expire-unreachable=now --all видаляє всі посилання недоступних комісій у reflog .

git gc --prune=now видаляє коміти самі.

Увага : Тільки використання git gc --prune=nowне буде працювати, оскільки на ці комісії все ще посилаються у рефлозі. Тому очищення рефлогу є обов'язковим. Також зауважте, що якщо ви користуєтесь rerereцим, ви маєте додаткові посилання, не очищені цими командами. Дивіться git help rerereдокладнішу інформацію. Крім того, будь-які комісії, на які посилаються місцеві або віддалені гілки або теги, не будуть видалені, оскільки вони вважаються цінними даними git.


14
Це спрацювало, але я якось втратив свої врятовані сховища в процесі (нічого серйозного в моєму випадку, лише застереження для інших)
Amro

1
чому б ні --агресивно?
JoelFan

2
Я думаю, що ця відповідь потребує чіткого попередження, бажано вгорі. Мою пропозицію щодо редагування було відхилено, тому що я думаю, що я повинен запропонувати її автору в коментарі? Будь ласка, приймайте це редагування stackoverflow.com/review/sugges-edits/26023988 або додайте попередження власним чином. Також це скидає всі ваші стаси . Це також слід зазначити у попередженні!
Ініго

Я перевірив git версії 2.17, і приховані комісії не будуть видалені вищезазначеними командами. Ви впевнені, що не виконали додаткових команд?
Мікко Ранталайнен

1
git fetch --pruneще більше зменшіть розмір через видалення локальних крапель.
hectorpal

33

Як було сказано у цій відповіді ТА , git gcдійсно можна збільшити розмір репо!

Дивіться також цю нитку

Тепер у git є механізм безпеки, щоб не видаляти нерозв'язані об'єкти відразу під час запуску ' git gc'.
За замовчуванням необмежені об'єкти зберігаються протягом 2 тижнів. Це полегшить вам відновлення випадково видалених гілок або комітетів, або уникнути перегонів, коли щойно створений об’єкт у процесі існування, але ще не посилається на нього, може бути видалений git gcпроцесом ' ', який працює паралельно.

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

У будь-якому випадку ... Щоб вирішити свою проблему, вам просто потрібно запустити " git gc" з --prune=nowаргументом, щоб вимкнути цей пільговий період та позбутися негайно цих невизначених об'єктів (безпечно лише в тому випадку, якщо в той же час не проводяться інші дії git, які повинні бути легким для забезпечення на робочому місці).

І BTW, використовуючи " git gc --aggressive" з пізнішою версією git (або " git repack -a -f -d --window=250 --depth=250")

Ця ж нитка згадує :

 git config pack.deltaCacheSize 1

Це обмежує розмір кеша дельти одним байтом (фактично його вимикаючи) замість за замовчуванням 0, що означає необмежений. З цим я можу перепакувати це сховище за допомогою наведеної вище git repackкоманди в системі x86-64 з 4 Гб оперативної пам’яті та використанням 4 потоків (це чотирьохядерний ядро). Однак споживання постійної пам'яті зростає майже до 3,3 ГБ.

Якщо ваша машина SMP і у вас недостатньо оперативної пам’яті, ви можете зменшити кількість потоків до однієї:

git config pack.threads 1

Крім того, ви можете додатково обмежити використання пам'яті --window-memory argumentзначенням ' git repack'.
Наприклад, використання --window-memory=128Mмає підтримувати розумну верхню межу використання дельта-пошукової пам'яті, хоча це може призвести до менш оптимальної відповідності дельта, якщо репо містить багато великих файлів.


На передній частині гілки фільтра ви можете розглянути (з обережністю) цей сценарій

#!/bin/bash
set -o errexit

# Author: David Underhill
# Script to permanently delete files/folders from your git repository.  To use 
# it, cd to your repository's root and then run the script with a list of paths
# you want to delete, e.g., git-delete-history path1 path2

if [ $# -eq 0 ]; then
    exit 0
fi

# make sure we're at the root of git repo
if [ ! -d .git ]; then
    echo "Error: must run this script from the root of a git repository"
    exit 1
fi

# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch $files" HEAD

# remove the temporary history git-filter-branch otherwise leaves behind for a long time
rm -rf .git/refs/original/ && git reflog expire --all &&  git gc --aggressive --prune

stackoverflow.com/questions/359424/… також є гарним початком для використання filter-branchкоманди.
VonC

Привіт VonC - Я намагався git gc prune = тепер без везіння. Це дійсно схоже на помилку git, тому що я завершив невідрегульовані краплі місцево після видалення гілки, але їх там немає зі свіжим клоном репортажу GitHub ... тож це просто локальна проблема репо. Але у мене є додаткові файли, які я хочу очистити, тому сценарій, на який ви посилалися вище, чудовий - дякую!
kkrugler


12

Кожен раз, коли ваша HEAD рухається, git відстежує це в reflog. Якщо ви видалили комікси, у вас все ще залишаються "звисаючі коміти", оскільки на них досі посилаються reflog~ 30 днів. Це захисна сітка, коли ви видаляєте комісії випадково.

Ви можете скористатися git reflogкомандою видалити конкретні комісії, переупакувати тощо., Або просто команду високого рівня:

git gc --prune=now

5

Можна використовувати git forget-blob.

Використання досить просте git forget-blob file-to-forget. Більше інформації ви можете отримати тут

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

Він зникне з усіх зобов’язань у вашій історії, рефлогування, тегів тощо

Раз у раз я стикаюся з однією і тією ж проблемою, і щоразу мені доводиться повертатися на цю посаду та інші, тому я автоматизував процес.

Кредити для дописувачів, таких як Сем Уоткінс


2

Спробуйте використовувати git-filter-branch - це не видаляє великі краплі, але може видалити великі файли, які ви вказали з усієї репо. Для мене це зменшує розмір репо з сотень МБ до 12 Мб.


6
Тепер , що це страшна команда :) Я повинен дати йому спробувати , коли мій ГИТ-фу відчуває себе сильнішим.
kkrugler

ти можеш сказати це знову. Я завжди остерігаюся будь-яких команд, які маніпулюють історією сховища. Речі, як правило, йдуть дуже неправильно, коли багато людей штовхаються та витягуються з цього сховища, і раптом купа предметів, яких очікує, там вже немає.
Джонатан Дюман

1

Іноді причиною того, що "gc" не приносить великої користі, є те, що існує незавершена база даних або схована скринька, заснована на старому документі.


Або на стару фіксацію посилаються HEAD, ORIG_HEAD, FETCH_HEAD, reflog або якась інша річ, яка git автоматично продовжує намагатися переконатися, що вона ніколи не втрачає нічого цінного. Якщо ви дійсно хочете втратити все це, вам доведеться пройти додаткову милю, щоб зробити це.
Мікко Ранталайнен

1

Щоб додати ще одну пораду, не забудьте скористатися віддаленим чорносливом Git, щоб видалити застарілі гілки ваших віддалених, перш ніж використовувати git gc

їх можна побачити з гіткою git -a

Це часто корисно, коли ви збираєтесь із сховищ github та forked ...


1

Перед тим як робити git filter-branchі git gc, ви повинні розглянути тег, які присутні у вашому репо. Будь-яка реальна система, яка має автоматичне тегування для таких речей, як безперервна інтеграція та розгортання, зробить небажані об’єкти ще посилаються цими тегами, отжеgc не зможете їх видалити, і ви все одно будете цікавитись, чому розмір репо все ще такий великий.

Найкращий спосіб позбутися від усіх непотрібних речей - це запустити git-filter&, git gcа потім підштовхнути майстра до нового голого репо. Нове голе репо матиме очищене дерево.

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