Git gc -aggressive vs git repack

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

Почему? что я должен знать, если я запускаю gc --aggressive?

git repack -a -d --depth=250 --window=250 рекомендуется по gc --aggressive. Зачем? Как repack уменьшить размер репозитория? Кроме того, я не совсем понимаю флаги --depth и --window.

Что мне выбрать между gc и repack? Когда следует использовать gc и repack?

Ответ 1

В настоящее время нет никакой разницы: 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 -f ilter-branch может использоваться, чтобы избавиться от подмножества файлов, обычно с некоторой комбинацией --index-filter --subdirectory-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 -f ilter-branch: say

    git for-each-ref --format="%(refname)" refs/original/ |
      xargs -n 1 git update-ref -d
    
  • git reflog expire --expire=now --all всех reflogs с git reflog expire --expire=now --all.

  • git gc --prune=now мусора собирает все объекты без git gc --prune=now с помощью git gc --prune=now (или если ваш git gc недостаточно нов, чтобы поддерживать аргументы для --prune, используйте git repack -ad; git 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: <[email protected]>
Message-ID: <[email protected]>
References: <[email protected]>
            <[email protected]>
            <[email protected]>
            <[email protected]>
            <[email protected]>

В четверг, 6 декабря 2007 года, Даниэль Берлин написал:

На самом деле, оказывается, что git-gc --aggressive делает эту git-gc --aggressive вещь, чтобы упаковать файлы иногда независимо от того, конвертировали ли вы из репозитория SVN или нет.

Абсолютно. git --aggressive в основном тупой. Это действительно полезно только для случая "Я знаю, что у меня действительно плохая упаковка, и я хочу отбросить все плохие решения, которые я принял".

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

В других SCM дельта-цепь обычно является фиксированной. Это может быть "вперед" или "назад", и он может немного эволюционировать, когда вы работаете с репозиторием, но, как правило, представляет собой цепочку изменений в одном файле, представленном в виде некой единой сущности SCM. В CVS, очевидно *,v файл *,v и многие другие системы делают довольно похожие вещи.

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

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

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

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

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

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

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

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

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

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

Таким образом, эквивалент git gc --aggressive - но выполненный правильно - это сделать (в одночасье) что-то вроде

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

где эта глубина только о том, насколько глубокими могут быть дельта-цепочки (сделать их более длинными для старой истории - это стоит пространства), а суть окна в том, насколько велико окно объекта, которое мы хотим, чтобы каждый кандидат-дельта сканировал.

И здесь вы, возможно, захотите добавить флаг -f (то есть "отбросить все старые дельты"), поскольку вы сейчас действительно пытаетесь убедиться, что этот действительно найдет хороших кандидатов.

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

          Linus

Ответ 2

Когда я должен использовать gc & repack?

Как я упоминал в " Сборке мусора Git, похоже, не работает полностью ", git gc --aggressive является ни достаточным, ни даже недостаточным.
И, как я объясню ниже, часто не нужны.

Наиболее эффективной комбинацией будет добавление 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

Смотрите коммит 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, скорее всего, не возможна.


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

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

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

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

Ответ 3

Проблема с git gc --aggressive заключается в том, что имя параметра и документация вводят в заблуждение.

Как Линус сам объясняет в этом письме, что git gc --aggressive в основном делает это:

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

Обычно нет необходимости пересчитывать дельта в 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 (который является "отбрасывать все старые дельта", так как вы сейчас пытаетесь убедиться, что этот на самом деле находит хороших кандидатов.

Ответ 4

Внимание. Не запускайте git gc --agressive с хранилищем, которое не синхронизировано с удаленным, если у вас нет резервных копий.

Эта операция воссоздает дельты с нуля и может привести к потере данных в случае изящного прерывания.

Для моего 8GB компьютера агрессивный gc исчерпал память в 1Gb репозитории с 10k небольших коммитов. Когда OOM killer прекратил процесс git - он оставил мне почти пустой репозиторий, сохранилось только рабочее дерево и несколько дельт.

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

Ответ 5

Примечание: остерегайтесь использования git gc --aggressive, как git gc --aggressive Git 2.22 (Q2 2019).

См совершают 0044f77, совершает daecbf2, совершает 7384504, совершают 22d4e3b, совершает 080a448, совершает 54d56f5, совершают d257e0f, совершает b6a8d09 (07 Apr 2019), а также совершать fc559fb, совершает cf9cd77, совершает b11e856 (22 марта 2019) путем Эвар Arnfjord Bjarmason (avar)
(Объединено Junio C Hamano - gitster - в коммите ac70c53, 25 апреля 2019 г.)

gc docs: преуменьшить полезность --aggressive

Существующие документы " gc --aggressive " не рекомендуют пользователям регулярно запускать его.
Я лично говорил со многими пользователями, которые восприняли эти документы как совет, чтобы использовать эту опцию, и, как правило, это (в основном) трата времени.

Итак, позвольте уточнить, что он на самом деле делает, и пусть пользователь делает свои собственные выводы.

Позвольте также разъяснить "Эффекты [...] постоянны", чтобы перефразировать краткую версию объяснения Джеффа Кинга.

Это означает, что документация git-gc теперь включает в себя:

Агрессивное

Когда --aggressive опция --aggressive, git-repack будет вызываться с флагом -f, который, в свою очередь, будет передавать --no-reuse-delta объектам git-pack-pack.
Это отбросит любые существующие дельты и пересчитает их, за счет того, что вы потратите гораздо больше времени на переупаковку.

Эффекты этого в основном постоянны, например, когда пакеты и незакрепленные объекты объединяются в один пакет, существующие дельты в этом пакете могут быть повторно использованы, но есть также различные случаи, когда мы можем выбрать субоптимальную дельту из более новой упаковать вместо.

Кроме того, при поставке --aggressive будут --window опции --depth и --window, переданные в git-repack.
См. gc.aggressiveDepth и gc.aggressiveWindow ниже.
Используя больший размер окна, мы с большей вероятностью найдем более оптимальные дельты.

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

И (совершить 080a448):

gc docs: обратите внимание, как --aggressive влияет на --window и --depth

Начиная с 07e7dbf (gc: по умолчанию агрессивная глубина до 50, 2016-08-11, Git v2.10.1) мы несколько смущающе используем ту же глубину в --aggressive что и по умолчанию.

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