git gc --агресивно проти git repack


88

Я шукаю способи зменшити розмір gitсховища. Пошук веде мене до git gc --aggressiveбільшості випадків. Я також читав, що це не найкращий підхід.

Чому? що я повинен знати, якщо я біжу gc --aggressive?

git repack -a -d --depth=250 --window=250рекомендується більше gc --aggressive. Чому? Як repackзменшується розмір сховища? Крім того, я не зовсім зрозумілий щодо прапорів --depthта --window.

Що слід вибрати між gcі repack? Коли слід використовувати gcі repack?

Відповіді:


76

Нині різниці немає: git gc --aggressiveдіє згідно з пропозицією Лінуса, зробленою в 2007 році; Дивіться нижче. Починаючи з версії 2.11 (Q4 2016), за замовчуванням git встановлює глибину 50. Вікно розміром 250 - це добре, оскільки воно сканує більший ділянку кожного об’єкта, але глибина в 250 - погано, тому що кожен ланцюжок відноситься до дуже глибоких старих об'єктів, що уповільнює всі майбутні операції git для незначного зниження використання диска.


Історична довідка

Лінус запропонував (див. Нижче повний пост списку розсилки) використовувати git gc --aggressiveлише тоді, коли у вас є, за його словами, " дуже поганий пакет" або "дійсно жахливо погані дельти", однак "майже завжди, в інших випадках, це насправді дуже погано що робити ". Результат може навіть залишити ваше сховище в гіршому стані, ніж коли ви починали!

Команда, яку він пропонує зробити це належним чином після імпортування "довгої та задіяної історії", така

git repack -a -d -f --depth=250 --window=250

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

git-filter-branch можна використовувати для позбавлення від підмножини файлів, як правило, з деякою комбінацією --index-filterта --subdirectory-filter. Люди очікують, що отримане сховище буде менше оригіналу, але вам потрібно ще кілька кроків, щоб насправді зробити його меншим, тому що Git наполегливо намагається не втратити ваші об’єкти, поки ви цього не скажете. Спочатку переконайтеся, що:

  • Ви дійсно видалили всі варіанти імені файлу, якщо BLOB-файл переміщувався протягом усього життя. git log --name-only --follow --all -- filenameможе допомогти вам знайти перейменування.

  • Ви дійсно відфільтрували всі посилання: використовуйте --tag-name-filter cat -- --allпід час дзвінка git filter-branch.

Тоді є два способи отримати менше сховище. Більш безпечним способом є клонування, яке зберігає ваш оригінал цілим.

  • Клонуйте його за допомогою git clone file:///path/to/repo. Клон не матиме вилучених об’єктів. Дивіться git-clone. (Зверніть увагу, що клонування простим контуром просто зв’язує все!)

Якщо ви дійсно не хочете його клонувати, з якихось причин перевірте натомість наступні пункти (у такому порядку). Це дуже руйнівний підхід, тому зробіть резервну копію або поверніться до її клонування. Вас попередили.

  • Видаліть оригінальні посилання, створені за допомогою git-filter-branch: скажімо

    git for-each-ref --format="%(refname)" refs/original/ |
      xargs -n 1 git update-ref -d
    
  • Термін дії всіх помилок закінчується за допомогою git reflog expire --expire=now --all.

  • Сміття збирає всі об'єкти без посилань за допомогою git gc --prune=now(або якщо ваш git gcнедостатньо новий для підтримки аргументів --prune, використовуйте git repack -ad; git pruneзамість цього).


Date: Wed, 5 Dec 2007 22:09:12 -0800 (PST)
From: Linus Torvalds <torvalds at linux-foundation dot org>
To: Daniel Berlin <dberlin at dberlin dot org>
cc: David Miller <davem at davemloft dot net>,
    ismail at pardus dot org dot tr,
    gcc at gcc dot gnu dot org,
    git at vger dot kernel dot org
Subject: Re: Git and GCC
In-Reply-To: <4aca3dc20712052111o730f6fb6h7a329ee811a70f28@mail.gmail.com>
Message-ID: <alpine.LFD.0.9999.0712052132450.13796@woody.linux-foundation.org>
References: <4aca3dc20712051947t5fbbb383ua1727c652eb25d7e@mail.gmail.com>
            <20071205.202047.58135920.davem@davemloft.net>
            <4aca3dc20712052032n521c344cla07a5df1f2c26cb8@mail.gmail.com>
            <20071205.204848.227521641.davem@davemloft.net>
            <4aca3dc20712052111o730f6fb6h7a329ee811a70f28@mail.gmail.com>

У четвер, 6 грудня 2007 року, Даніель Берлін написав:

Насправді виявляється, що git-gc --aggressiveця німа штука іноді пакує файли, незалежно від того, конвертували ви з репозиторію SVN чи ні.

Абсолютно. git --aggressiveв основному німий. Це насправді корисно лише у випадку: "Я знаю, що у мене дуже поганий пакет, і я хочу відкинути всі погані рішення щодо упаковки, які я зробив".

Щоб пояснити це, варто пояснити (ви, мабуть, це вам відомо, але дозвольте мені все-таки пройти через основи), як працюють дельта-ланцюжки git і чим вони настільки відрізняються від більшості інших систем.

В інших СКМ дельта-ланцюг, як правило, фіксований. Це може бути "вперед" або "назад", і це може трохи еволюціонувати, коли ви працюєте зі сховищем, але, як правило, це ланцюжок змін до одного файлу, представленого як певна сутність SCM. У CVS, очевидно, це *,vфайл, і багато інших систем роблять досить подібні речі.

Git також робить дельта-ланцюги, але робить їх набагато "вільніше". Немає фіксованої сутності. Дельти створюються на основі будь-якої випадкової іншої версії, яку git вважає хорошим кандидатом у дельту (з різними досить успішними евристиками), і немає абсолютно жодних жорстких правил групування.

Це взагалі дуже гарна річ. Це добре з різних концептуальних причин ( тобто , git внутрішньо ніколи навіть не потребує турботи про весь ланцюжок ревізій - він насправді взагалі не думає про дельти), але це також чудово, оскільки позбавлення від негнучких правил дельти означає що git взагалі не має жодних проблем зі злиттям двох файлів, наприклад - просто немає довільних *,v"файлів ревізій", які мають якесь приховане значення.

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

І ось тут з’являється справді погано названий --aggressive. Хоча git, як правило, намагається повторно використовувати дельта-інформацію (оскільки це гарна ідея, і вона не витрачає час процесора на пошук усіх хороших дельт, які ми знайшли раніше), іноді ви хочу сказати "давайте почнемо все спочатку, з чистого аркуша, і проігноруємо всю попередню інформацію про дельту і спробуємо створити новий набір дельт."

Отже, --aggressiveсправа не в тому, щоб бути агресивними, а в тому, щоб витратити час процесора на повторне прийняття рішення, яке ми вже приймали раніше!

Іноді це добре. Деякі інструменти імпорту, зокрема, можуть генерувати дуже жахливі дельти. Все, що використовує git fast-import, наприклад, швидше за все, не має великого дельта-макета, тому, можливо, варто сказати "Я хочу почати з чистого аркуша".

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

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

Як правило, робити інкремент git gc- це правильний підхід, і краще, ніж робити git gc --aggressive. Він буде використовувати повторно старі дельти, і коли ці старі дельти не вдасться знайти (причина для поступового GC в першу чергу!), Він збирається створити нові.

З іншого боку, безумовно вірно, що «початковий імпорт довгої та залученої історії» - це момент, коли може бути варто витратити багато часу на пошук справді хороших дельт. Тоді кожен користувач, який коли-небудь пізніше (якщо він не git gc --aggressiveскасовує це!) Отримає перевагу цієї одноразової події. Тож особливо для великих проектів з давньою історією, мабуть, варто зробити додаткову роботу, сказавши коду пошуку дельти розійштися.

Тож еквівалент git gc --aggressive- але зроблено правильно - це робити (за ніч) щось на зразок

git repack -a -d --depth=250 --window=250

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

І тут, можливо, ви захочете додати -fпрапор (що є “скидання всіх старих дельт”, оскільки зараз ви насправді намагаєтесь переконатися, що цей насправді знаходить хороших кандидатів.

І тоді це займе вічно і цілий день ( тобто річ "зроби це за ніч"). Але кінцевий результат полягає в тому, що всі, хто перебуває з цього сховища, отримуватимуть набагато кращі пакети, не витрачаючи на це жодних зусиль.

          Linus

2
Ваш коментар щодо глибини трохи заплутаний. Спочатку я збирався скаржитися, що ти мертвий неправий, що агресивний може значно пришвидшити сховище git. Після агресивного збору сміття ВЕЛИЧЕЗНЕ репо, що зайняло п’ять хвилин, щоб зробити стан git зменшено до секунд. Але тоді я зрозумів, що ви мали на увазі не агресивний gc, який уповільнив репо, а просто надзвичайно великий розмір глибини.
user6856

57

Коли слід використовувати gc & repack?

Як я вже згадував у " Збір сміття Git, здається, не працює повністю ", a git gc --aggressiveне є ані достатнім, ані навіть самим собою достатнім.
І, як я поясню нижче , часто не потрібен.

Найефективнішою комбінацією буде додавання git repack, але також git prune:

git gc
git repack -Ad      # kills in-pack garbage
git prune           # kills loose garbage

Примітка: Git 2.11 (Q4 2016) встановить за замовчуванням gc aggressiveглибину 50

Див. Коміт 07e7dbf (11 серпня 2016 р.) Джеффа Кінга ( peff) .
(Об’єднано Junio ​​C Hamano - gitster- у комітеті 0952ca8 , 21 вересня 2016)

gc: агресивна глибина за замовчуванням до 50

" git gc --aggressive" використовується для обмеження довжини дельта-ланцюга до 250, що занадто глибоко, щоб отримати додаткову економію місця, і шкодить для продуктивності.
Обмеження зменшено до 50.

Підсумок: поточний за замовчуванням 250 не економить багато місця, а коштує процесор. Це не хороший компроміс.

Прапор " --aggressive" git-gcробить три дії:

  1. використовуйте " -f" для викидання існуючих дельт і переобчислення з нуля
  2. використовуйте "--window = 250", щоб важче шукати дельти
  3. використовуйте "--depth = 250", щоб зробити довші дельта-ланцюжки

Елементи (1) і (2) добре підходять для "агресивного" перепакування.
Вони просять prepack зробити більше обчислювальних робіт в надії отримати кращий пакет. Ви оплачуєте витрати під час перепакування, а інші операції бачать лише вигоду.

Пункт (3) не є настільки зрозумілим.
Дозвіл на довші ланцюги означає менше обмежень на дельтах, що означає потенційно пошук кращих та економію місця.
Але це також означає, що операції, що мають доступ до дельт, повинні йти довшими ланцюгами, що впливає на їх ефективність.
Отже, це компроміс, і незрозуміло, що компроміс навіть хороший.

(Див. Комісію для вивчення )

Ви бачите, що економія центрального процесора для регулярних операцій покращується із зменшенням глибини.
Але ми також можемо бачити, що економія місця не така велика, оскільки глибина стає вище. Заощадження 5-10% від 10 до 50, мабуть, варте компромісу процесора. Економія 1% для переходу від 50 до 100 або ще 0,5% від 100 до 250, мабуть, ні.


Говорячи про економію центрального процесора, " git repack" навчився приймати --threads=<n>опцію та передавати її об'єктам пакета.

Див. Коміт 40bcf31 (26 квітня 2017 р.) Хуніо С Хамано ( gitster) .
(Об’єднано Junio ​​C Hamano - gitster- у коміті 31fb6f4 , 29 травня 2017 р.)

prepack: прийміть --threads=<n>і передайтеpack-objects

Ми вже робимо це для --window=<n>і --depth=<n>; це допоможе, коли користувач хоче примусити --threads=1відтворити тестування, не зазнаючи впливу на перегонах декількох потоків.


3
Я згадував ланцюжок Linus у посиланні "Збір сміття Git, здається, не працює повністю"
VonC

1
Дякуємо за це сучасне оновлення! Кожна інша відповідь тут стара. Тепер ми можемо бачити, що git gc --aggressiveце було виправлено двічі: По-перше, зробити те, що Лінус запропонував у 2007 році як "кращий метод упаковки". А потім у Git 2.11, щоб уникнути надмірної глибини об’єкта, яку запропонував Лінус, але яка виявилася шкідливою (уповільнює всі майбутні операції Git і не економить місця, про яке варто говорити).
gw0

git gc, за яким слідує git repack -Ad та git prune збільшує розмір мого сховища ... чому?
Девопс

@devops Не впевнений: яку версію Git ви використовуєте? Ви можете задати нове запитання щодо цього (з додатковими деталями, такими як ОС, загальний розмір вашого репо, ...)
VonC

man git-repackкаже -d: "Також запустіть git prune-packed, щоб видалити зайві вільні об'єктні файли." Або це git pruneтеж робиться? man git-pruneкаже In most cases, users should run git gc, which calls git prune., то яка користь після git gc? Чи не краще чи достатньо використовувати лише git repack -Ad && git gc?
Якоб

14

Проблема в git gc --aggressiveтому, що назва опції та документація вводять в оману.

Як пояснює сам Лінус у цій пошті , git gc --aggressiveголовним чином є наступне:

Хоча git зазвичай намагається повторно використовувати дельта-інформацію (оскільки це гарна ідея, і вона не витрачає час процесора на повторне пошук усіх хороших дельт, які ми знайшли раніше), іноді ви хочете сказати "давайте почнемо все спочатку, з чистий аркуш, і проігноруйте всю попередню інформацію про дельту і спробуйте створити новий набір дельт ".

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

Здебільшого git робить досить гарну роботу при визначенні корисних дельт, а використання git gc --aggressiveзалишає вам дельти, які потенційно навіть гірші, витрачаючи багато часу на процесор.


Лінус закінчує свою пошту висновком, що git repackз великим --depthі --windowє кращим вибором у більшості випадків; особливо після того, як ви імпортували великий проект і хочете переконатися, що git знаходить хороші дельти.

Тож еквівалент git gc --aggressive- але зроблено правильно - це робити (за ніч) щось на зразок

git repack -a -d --depth=250 --window=250

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

І тут, можливо, ви захочете додати -fпрапор (а це «скинути всі старі дельти», оскільки зараз ви насправді намагаєтесь переконатися, що цей насправді знаходить хороших кандидатів.


8

Обережно. Не запускайте git gc --agressiveсховище, яке не синхронізується з віддаленим, якщо у вас немає резервних копій.

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

На моєму 8 Гб комп’ютерному агресивному gc не вистачало пам’яті у сховищі 1 Гб з 10 тис. Невеликих комітів. Коли вбивця OOM завершив git-процес, у мене залишилось майже порожнє сховище, вижило лише робоче дерево і кілька дельт.

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


5

Примітка: будьте обережні з використанням git gc --aggressive, як це пояснює Git 2.22 (Q2 2019).

Див здійснюють 0044f77 , здійснюють daecbf2 , роблять 7384504 , здійснюють 22d4e3b , здійснюють 080a448 , здійснюють 54d56f5 , здійснюють d257e0f , здійснюють b6a8d09 (7 квітня 2019), і роблять fc559fb , здійснюють cf9cd77 , здійснюють b11e856 (22 березень 2019) з допомогою Еварі Arnfjord Bjarmason ( avar) .
(Об’єднано Junio ​​C Hamano - gitster- у коміті ac70c53 , 25 квітня 2019)

gc docs: применшити корисність --aggressive

Існуючі " gc --aggressive" документи просто не рекомендують користувачам регулярно їх запускати.
Я особисто спілкувався з багатьма користувачами, які сприйняли ці документи як пораду користуватися цією опцією, і, як правило, це (переважно) втрата часу .

Тож давайте роз’яснимо, чим він насправді займається, і нехай користувач зробить власні висновки.

Давайте також пояснимо «Ефекти [...] стійкі», перефразовуючи коротку версію пояснення Джеффа Кінга .

Це означає, що документація git-gc тепер включає :

АГРЕСИВНИЙ

Коли --aggressiveопція надана, git-repackбуде викликаний -fпрапор, який, у свою чергу, перейде --no-reuse-deltaдо git-pack-objects .
Це викине будь-які існуючі дельти і перерахує їх, за рахунок витрати набагато більше часу на перепакування.

Наслідки цього здебільшого стійкі, наприклад, коли пакети та вільні предмети злиті в один пакет, існуючі дельти в цьому пакеті можуть бути використані повторно, але є також різні випадки, коли ми можемо вибрати деоптимальну дельту з нової замість цього.

Крім того, постачальник --aggressiveналаштує --depthі --windowпередані параметри git-repack.
Див. gc.aggressiveDepthІ gc.aggressiveWindowналаштування нижче.
Використовуючи більший розмір вікна, ми з більшою ймовірністю знаходимо більш оптимальні дельти.

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

І ( зафіксуйте 080a448 ):

gcдокументи: зверніть увагу на --aggressiveвплив --windowта--depth

З 07e7dbf ( gc: агресивна глибина за замовчуванням до 50, 11.08.2016, Git v2.10.1) ми дещо заплутано використовуємо ту саму глибину --aggressive, що і за замовчуванням.

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

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