git gc --aggressive vs 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 (4 квартал 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 изо всех сил старается не потерять ваши объекты, пока вы не скажете об этом. Сначала убедитесь, что:

  • Вы действительно удалили все варианты имени файла, если большой двоичный объект был перемещен за время своего существования. 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: say

    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 дельта-цепочка обычно фиксируется. Это может быть «вперед» или «назад», и оно может немного развиваться по мере того, как вы работаете с репозиторием, но, как правило, это цепочка изменений одного файла, представленного как некая единая сущность SCM. В CVS, очевидно, это *,vфайл, и многие другие системы делают аналогичные вещи.

Git также создает дельта-цепочки, но делает их гораздо более «вольно». Нет фиксированной сущности. Дельты генерируются для любой другой случайной версии, которую git считает хорошим кандидатом в дельта (с различными довольно успешными эвристиками), и здесь нет абсолютно никаких правил жесткой группировки.

Это вообще очень хорошо. Это хорошо по разным концептуальным причинам ( например , git внутри никогда даже не нужно заботиться обо всей цепочке ревизий - он вообще не мыслит в терминах дельт), но это также здорово, потому что избавление от негибких правил дельты означает у этого git вообще нет проблем с объединением двух файлов, например - просто не существует произвольных *,v«файлов ревизий», которые имеют какое-то скрытое значение.

Это также означает, что выбор дельт - гораздо более открытый вопрос. Если вы ограничите дельта-цепочку одним файлом, у вас действительно не будет большого выбора, что делать с дельтами, но в git это действительно может быть совершенно другой проблемой.

И вот --aggressiveтут-то и появляется действительно плохо названный . Хотя git обычно пытается повторно использовать дельта-информацию (потому что это хорошая идея, и он не тратит время процессора на повторное нахождение всех хороших дельт, которые мы нашли ранее), иногда вы хочу сказать: «Давайте начнем все сначала, с чистого листа, и проигнорируем всю предыдущую информацию о дельтах и ​​попытаемся создать новый набор дельт».

Так --aggressiveчто на самом деле речь идет не об агрессивности, а о том, чтобы тратить время процессора на повторное принятие решения, которое мы уже приняли ранее!

Иногда это хорошо. В частности, некоторые инструменты импорта могут создавать действительно ужасно плохие дельты. git fast-importНапример, все, что использует , скорее всего, не имеет хорошего дельта-макета, поэтому, возможно, стоит сказать: «Я хочу начать с чистого листа».

Но почти всегда, в других случаях, это действительно плохой поступок. Это приведет к потере процессорного времени, и особенно если вы действительно хорошо поработали с дельтами ранее, в конечном результате не будут повторно использоваться все те хорошие дельты, которые вы уже нашли, так что вы фактически получите много худший конечный результат тоже!

Я пришлю патч Юнио, чтобы просто удалить git gc --aggressive документацию. Это может быть полезно, но обычно полезно только тогда, когда вы действительно очень глубоко понимаете, что он делает, и эта документация вам не поможет.

Как правило, постепенное выполнение git gc- правильный подход, и он лучше, чем делать это git gc --aggressive. Он будет повторно использовать старые дельты, и когда эти старые дельты не могут быть найдены (в первую очередь, причина для выполнения инкрементного сборщика мусора!), Он создаст новые.

С другой стороны, определенно верно, что «начальный импорт долгой и сложной истории» - это момент, когда стоит потратить много времени на поиск действительно хороших дельт. Тогда каждый пользователь когда-либо после (если он не использует его 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, похоже, не работает полностью », одного git gc --aggressiveэлемента a недостаточно и даже недостаточно.
И, как я объясню ниже , часто не нужны.

Самым эффективным сочетанием было бы добавление git repack, но также git prune:

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

Примечание. Git 2.11 (4 квартал 2016 г.) установит gc aggressiveглубину по умолчанию 50.

См. Commit 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) хорошо подходят для «агрессивной» переупаковки.
Они просят переупаковку проделать больше вычислений в надежде получить лучший пакет. Вы оплачиваете расходы во время переупаковки, а другие операции видят только выгоду.

Пункт (3) не так понятен.
Разрешение более длинных цепочек означает меньшие ограничения на дельты, что означает потенциально поиск лучших и экономию места.
Но это также означает, что операции, которые обращаются к дельтам, должны следовать более длинным цепочкам, что влияет на их производительность.
Так что это компромисс, и неясно, является ли он даже хорошим.

(См. Коммит для изучения )

Вы можете видеть, что экономия ЦП для обычных операций улучшается по мере уменьшения глубины.
Но мы также можем видеть, что экономия места не так велика, когда глубина увеличивается. Экономия 5-10% между 10 и 50, вероятно, стоит компромисса с процессором. Экономия 1% для перехода от 50 до 100 или еще 0,5% для перехода от 100 до 250, вероятно, нет.


Говоря об экономии CPU, " git repack" научилась принимать --threads=<n>опцию и передавать ее pack-объектам.

См. Commit 40bcf31 (26 апреля 2017 г.), автор Junio ​​C Hamano ( gitster) .
(Объединено Junio ​​C Hamano - gitster- в коммите 31fb6f4 , 29 мая 2017 г.)

repack: принять --threads=<n>и передать егоpack-objects

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

VonC
источник
3
Я упомянул ветку Линуса в ссылке «Сборщик мусора Git не работает полностью»
VonC
1
Спасибо за это современное обновление! Все остальные ответы здесь старые. Теперь мы видим, что git gc --aggressiveэто было исправлено дважды: во-первых, сделать то, что Линус предложил в 2007 году как «лучший метод упаковки». А затем в Git 2.11, чтобы избежать чрезмерной глубины объекта, которую предложил Линус, но которая оказалась вредной (замедляет все будущие операции Git и не экономит место, о котором стоит говорить).
gw0
git gc, за которым следует git repack -Ad и git prune увеличивает размер моего репозитория ... почему?
DevOps 05
@devops Не уверен: какую версию Git вы используете? Вы можете задать для этого новый вопрос (с более подробной информацией, такой как ОС, общий размер вашего репо, ...)
VonC
man git-repackговорит для -d: «Также запустите git prune-Pack, чтобы удалить лишние свободные объектные файлы». Или 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 ГБ с небольшими коммитами 10k. Когда OOM killer завершил процесс git - он оставил у меня почти пустой репозиторий, сохранилось только рабочее дерево и несколько дельт.

Конечно, это была не единственная копия репозитория, поэтому я просто воссоздал ее и вытащил с удаленного (выборка не работала на сломанном репо и заходила в тупик на этапе разрешения дельт несколько раз, я пытался это сделать), но если ваше репо локальное репозиторий одного разработчика без пультов - сначала сделайте резервную копию.

Указатель мудреца
источник
5

Примечание: остерегайтесь использования git gc --aggressive , как поясняет Git 2.22 (второй квартал 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 документы: преуменьшить полезность --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, 2016-08-11, Git v2.10.1), мы несколько сбиваем с толку, используя ту же глубину, --aggressiveчто и по умолчанию.

Как отмечалось в этом коммите, который имеет смысл, было неправильно делать большую глубину по умолчанию для «агрессивного» и, таким образом, экономить дисковое пространство за счет производительности во время выполнения, что обычно противоположно тому, кто хотел бы «агрессивный gc». хочет.

VonC
источник