git gc - агрессивный против 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?

4 ответа

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

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

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

Тогда есть два способа получить меньший репозиторий. Более безопасный способ - клонировать, который сохранит ваш оригинал без изменений.

  • Клонировать его с git clone file:///path/to/repo, У клона не будет удаленных объектов. Смотри мерзавец. (Обратите внимание, что клонирование с простым путем просто жестко связывает все!)

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

  • Удалите оригинальные ссылки, сохраненные git-filter-branch: say

    git for-each-ref --format="%(refname)" refs/original/ |
      xargs -n 1 git update-ref -d
    
  • Удалите все reflogs с 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. Хотя 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

Когда я должен использовать 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 года) установит для агрессивной глубины по умолчанию значение 50

Смотрите коммит 07e7dbf (11 августа 2016 г.) Джеффа Кинга ( peff )
(Объединено Юнио С Хамано - 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> вариант и передать его в pack-объекты.

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

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

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

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

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

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

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

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

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

Смотрите коммит 0044f77, коммит daecbf2, коммит 7384504, коммит 22d4e3b, коммит 080a448, коммит 54d56f5, коммит d257e0f, коммит b6a8d09 (07 апреля 2019 г.) и коммит fc559fb, коммит cf9cd77, коммит b11e856 (22 мар. 201jf) by avar )
(Объединено Юнио С Хамано - gitster - в комитете ac70c53, 25 апреля 2019 г.)

gc документы: преуменьшайте полезность --aggressive

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

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

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

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

Агрессивное

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

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

Кроме того, поставка --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" хочет.

Другие вопросы по тегам