Что делает git, когда мы делаем: git gc - git prune
Что происходит в фоновом режиме при запуске,
git gc
git prune
Вывод git gc:
Counting objects: 945490, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (334718/334718), done.
Writing objects: 100% (945490/945490), done.
Total 945490 (delta 483105), reused 944529 (delta 482309)
Checking connectivity: 948048, done.
Вывод git prune:
Checking connectivity: 945490, done.
В чем разница между этими двумя вариантами?
Спасибо
2 ответа
TL;DR
git prune
удаляет только свободные, недостижимые, устаревшие объекты (объекты должны иметь все три свойства для удаления). Недоступные упакованные объекты остаются в файлах их пакетов. Доступные незакрепленные предметы остаются доступными и незакрепленными. Объекты, которые недоступны, но еще не устарели, также остаются нетронутыми. Определение устаревшего немного сложно (см. Подробности ниже).
git gc
делает больше: упаковывает ссылки, упаковывает полезные объекты, истекает записи reflog, удаляет незакрепленные объекты, удаляет удаленные рабочие деревья и чернослив / старые файлы gc git rerere
данные.
Долго
Я не уверен, что вы подразумеваете под "на заднем плане" выше (фон имеет техническое значение в оболочках, и вся деятельность здесь происходит на переднем плане оболочки, но я подозреваю, что вы не имели в виду эти термины).
Какие git gc
делает это организовать целый ряд коллекционных мероприятий, в том числе, но не ограничиваясь git prune
, Список ниже представляет собой набор команд, выполняемых передним планом gc
без --auto
(опуская свои аргументы, которые в некоторой степени зависят от git gc
аргументы):
git pack-refs
: компактные ссылки.git/refs/heads/...
а также.git/refs/tags/...
записи в записи в.git/packed-refs
, исключая отдельные файлы)git reflog expire
: истекают старые записи refloggit repack
: упаковать свободные объекты в формат упакованного объектаgit prune
: удалить ненужные незакрепленные предметыgit worktree prune
: удалить данные рабочего дерева для добавленных рабочих деревьев, которые пользователь удалилgit rerere gc
: удалить старые записи
Есть еще несколько отдельных файловых операций git gc
делает сам по себе, но выше, это основная последовательность. Обратите внимание, что git prune
происходит после (1) истечения срока действия повторных флагов и (2) запуска git repack
Это связано с тем, что удаленная запись reflog с истекшим сроком действия может привести к тому, что объект станет не связанным, и, следовательно, не будет упакован, а затем удален, чтобы полностью исчезнуть.
Вещи, чтобы знать, прежде чем мы смотрим на упаковать и подрезать
Прежде чем углубляться в детали, полезно определить в Git, что такое объект, и что означает, что объект может быть потерян или упакован. Нам также необходимо понять, что означает достижение объекта.
Каждый объект имеет хеш-идентификатор - один из тех больших уродливых идентификаторов, которые вы видели в git log
например, это имя этого объекта, для целей поиска. Git хранит все объекты в базе данных значений ключей, где имя является ключом, а сам объект является значением. Поэтому объекты Git - это то, как Git хранит файлы и коммиты, и на самом деле существует четыре типа объектов: объект коммитов содержит фактический коммит. Объект дерева содержит наборы пар, 1 удобочитаемое имя, например README
или же subdir
вместе с идентификатором хэша другого объекта. Этот другой объект является объектом BLOB- объекта, если имя в дереве является именем файла, или это другой объект дерева, если имя является именем подкаталога. Объекты BLOB-объектов содержат фактическое содержимое файла (но обратите внимание, что имя файла находится в дереве, ссылающемся на BLOB-объект!). Последний тип объекта - аннотированный тег, используемый для аннотированных тегов, которые здесь не особенно интересны.
После создания ни один объект не может быть изменен. Это потому, что имя объекта - его хэш-идентификатор - вычисляется путем просмотра каждого бита содержимого объекта. Измените любой бит с нуля на единицу или наоборот, и идентификатор хеша изменится: теперь у вас есть другой объект с другим именем. Вот как Git проверяет, что ни один файл никогда не был испорчен: если содержимое файла было изменено, хеш-идентификатор объекта изменится. Идентификатор объекта сохраняется в записи дерева, и если бы объект дерева был изменен, идентификатор дерева изменился бы. Идентификатор дерева сохраняется в коммите, и если идентификатор дерева был изменен, хэш коммита изменился бы. Так что, если вы знаете, что хеш коммита a234b67...
и содержание коммита по-прежнему хэши a234b67...
в коммите ничего не изменилось, а идентификатор дерева все еще действителен. Если дерево все еще хеширует свое собственное имя, его содержимое все еще допустимо, поэтому идентификатор BLOB-объекта является правильным; так что, пока содержимое BLOB-объекта хэшируется на собственное имя, BLOB-объект также является правильным.
Объекты могут быть свободными, что означает, что они хранятся в виде файлов. Имя файла - это просто хэш-идентификатор. 2 Содержимое незакрепленного предмета сплющено от zlib. Или, объекты могут быть упакованы, что означает, что многие объекты хранятся в одном файле пакета. В этом случае содержимое не просто дефлируется, оно сначала дельта-сжимается. Git выбирает базовый объект - часто последнюю версию некоторого большого двоичного объекта (файла) - и затем находит дополнительные объекты, которые могут быть представлены в виде серии команд: взять базовый файл, удалить один текст с этим смещением, добавить другой текст с другим смещение и так далее. Фактический формат файлов пакета задокументирован здесь, хотя и слегка. Обратите внимание, что в отличие от большинства систем управления версиями, дельта-сжатие происходит на уровне ниже абстракции хранимого объекта: Git сохраняет целые снимки, а затем выполняет дельта-сжатие на базовых объектах. Git по-прежнему обращается к объекту по имени хеш-идентификатора; просто чтение этого объекта включает в себя чтение файла пакета, поиск объекта и лежащих в его основе дельта-баз и восстановление всего объекта на лету.
Существует общее правило для файлов пакета, которое гласит, что любой дельта-сжатый объект в файле пакета должен иметь все свои базы в одном и том же файле пакета. Это означает, что файл пакета является автономным: никогда не требуется открывать несколько дополнительных файлов пакета, чтобы получить объект из пакета, в котором есть объект. (Это конкретное правило может быть намеренно нарушено, создавая то, что Git называет тонким пакетом, но они предназначены для использования только для отправки объектов по сетевому соединению другому Git, у которого уже есть базовые объекты. Другой Git должен "исправить" или "откормить" тонкий пакет, чтобы создать обычный файл пакета, прежде чем оставить его для остальной части Git.)
Достижимость объекта немного сложнее. Давайте сначала посмотрим на достижимость коммитов.
Обратите внимание, что когда у нас есть объект фиксации, этот объект фиксации сам содержит несколько хеш-идентификаторов. У него есть один хэш-идентификатор для дерева, которое содержит снимок, который идет с этим коммитом. Он также имеет один или несколько хеш-идентификаторов для родительских коммитов, если только этот конкретный коммит не является корневым. Корневой коммит определяется как коммит без родителей, так что это немного циклично: коммит имеет родителей, если у него нет родителей. Это достаточно ясно: учитывая некоторый коммит, мы можем нарисовать этот коммит как узел на графике со стрелками, выходящими из узла, по одной на каждого родителя:
<--o
|
v
Эти родительские стрелки указывают на родителя или родителей коммита. Учитывая последовательность коммитов с одним родителем, мы получаем простую линейную цепочку:
... <--o <--o <--o ...
Один из этих коммитов должен быть началом цепочки: это корневой коммит. Один из них должен быть концом, и это коммит чаевых. Все внутренние стрелки указывают назад (влево), поэтому мы можем нарисовать это без наконечников стрел, зная, что корень находится слева, а наконечник справа:
o--o--o--o--o
Теперь мы можем добавить название ветви, как master
, Имя просто указывает на коммит наконечника:
o--o--o--o--o <--master
Ни одна из стрелок, встроенных в коммит, не может измениться, потому что ничто в любом объекте не может измениться. Стрелка в названии ветки master
однако на самом деле это просто хэш-идентификатор некоторого коммита, и это может измениться. Давайте использовать буквы для представления хэшей коммитов:
A--B--C--D--E <-- master
имя master
теперь просто хранит хеш коммита коммита E
, Если мы добавим новый коммит в master
мы делаем это, выписывая коммит, чей родитель E
и чье дерево является нашим снимком, давая нам совершенно новый хеш, который мы можем назвать F
, совершить F
указывает на E
, У нас есть Git написать F
хэш ID в master
и теперь у нас есть:
A--B--C--D--E--F <-- master
Мы добавили один коммит и изменили одно имя, master
, Все предыдущие коммиты достижимы, начиная с имени master
, Мы считываем хэш-идентификатор F
и читать коммит F
, Это имеет хэш-идентификатор E
итак мы достигли коммита E
, Мы читаем E
чтобы получить хэш-идентификатор D
и, таким образом, достичь D
, Мы повторяем, пока не прочитаем A
, найдите, что у него нет родителя, и все готово.
Если есть ветви, это просто означает, что у нас есть коммиты, найденные под другим именем, чьи родители являются одним из коммитов, также найденных по имени master
:
A--B--C--D--E--F <-- master
\
G--H <-- develop
Имя develop
находит коммит H
; H
находки G
; а также G
относится к E
, Так что все эти коммиты достижимы.
Коммиты с более чем одним родителем - то есть коммиты слияния - делают всех своих родителей достижимыми, если сам коммит достижим. Поэтому, как только вы сделаете коммит слияния, вы можете (но не обязаны) удалить имя ветви, которое идентифицирует коммит, который был объединен: теперь он доступен из кончика ветки, в которой вы находились, когда выполняли операцию слияния, То есть:
...--o--o---o <-- name
\ /
o--o <-- delete-able
коммиты в нижнем ряду доступны name
через слияние, так как коммиты в верхнем ряду всегда были доступны из name
, Удаление имени delete-able
оставляет их по-прежнему достижимыми. Если коммит слияния отсутствует, как в этом случае:
...--o--o <-- name2
\
o--o <-- not-delete-able
затем удаление not-delete-able
фактически отказывается от двух коммитов в нижнем ряду: они становятся недоступными и, следовательно, могут быть использованы для сбора мусора.
Это же свойство достижимости применяется к объектам дерева и блоба. совершить G
имеет tree
в этом, например, и это tree
имеет <имя, ID> пары:
A--B--C--D--E--F <-- master
\
G--H <-- develop
|
tree=d097...
/ \
README=9fa3... Makefile=0b41...
Так из коммита G
, объект дерева d097...
достижимо; из этого дерева, объект BLOB 9fa3...
достижим, как и объект BLOB 0b41...
, совершить H
может иметь то же самое README
объект под тем же именем (хотя и другое дерево): это хорошо, это просто делает 9fa3
вдвойне достижимо, что не интересно для Git: Git заботится только о том, что он вообще доступен.
Внешние ссылки - имена ветвей и тегов, а также другие ссылки, найденные в репозиториях Git (включая записи в индексе Git и любые ссылки через связанные добавленные рабочие деревья), обеспечивают точки входа в граф объектов. Из этих точек входа любой объект является либо достижимым, либо имеет одно или несколько имен, которые могут привести к нему, либо недоступным, то есть не существует имен, по которым можно найти сам объект. Я исключил аннотированные теги из этого описания, но они обычно находятся через имена тегов, и у аннотированного объекта тега есть одна ссылка на объект (произвольного типа объекта), которую он находит, что делает этот один объект достижимым, если сам объект тега доступен,
Поскольку ссылки ссылаются только на один объект, но иногда мы делаем что-то с именем ветви, которое мы хотим отменить позже, Git ведет журнал каждого значения, которое имела ссылка, и когда. Эти справочные журналы или журналы позволяют нам узнать, что master
было в нем вчера, или что было в develop
прошлая неделя. В конце концов, эти записи reflog устарели и устарели и вряд ли пригодятся больше, и git reflog expire
откажусь от них.
Перепаковать и подрезать
Какие git repack
на высоком уровне теперь должно быть достаточно ясным: он превращает коллекцию множества незакрепленных объектов в файл, полный всех этих объектов. Однако он может сделать больше: он может включать все объекты из предыдущего пакета. Предыдущая упаковка становится лишней и может быть удалена позже. Он также может опускать любые недоступные объекты из пакета, превращая их вместо этого в незакрепленные объекты. когда git gc
работает git repack
это происходит с опциями, которые зависят от git gc
варианты, поэтому точная семантика здесь варьируется, но по умолчанию для переднего плана git gc
это использовать git repack -d -l
, у которого есть git repack
удалить лишние пакеты и запустить git prune-packed
, prune-packed
Программа удаляет незакрепленные объектные файлы, которые также появляются в пакетных файлах, поэтому удаляет незакрепленные объекты, которые вошли в пакет. repack
Программа проходит -l
вариант на git pack-objects
(которая является реальной рабочей лошадкой, которая создает файл пакета), где это означает, что нужно пропускать объекты, заимствованные из других репозиториев. (Этот последний вариант не важен для большинства обычных Git-приложений.)
В любом случае это git repack
Или технически, git pack-objects
- который печатает подсчет, сжатие и запись сообщений. Когда это сделано, у вас есть новый файл пакета и старые файлы пакета исчезли. Новый файл пакета содержит все достижимые объекты, включая старые достижимые упакованные объекты и старые достижимые незакрепленные объекты. Если незакрепленные объекты были извлечены из одного из старых (теперь уничтоженных и удаленных) файлов пакета, они присоединяются к другим незакрепленным (и недоступным) объектам, загромождающим ваше хранилище. Если они были уничтожены во время демонтажа, остаются только существующие незакрепленные объекты.
Сейчас время для git prune
: это находит свободные, недоступные объекты и удаляет их. Тем не менее, он имеет предохранительный выключатель, --expire 2.weeks.ago
: по умолчанию, под управлением git gc
, он не удаляет такие объекты, если им не менее двух недель. Это означает, что любая Git-программа, которая находится в процессе создания новых объектов и еще не подключила их, имеет льготный период. Новые объекты могут быть свободными и недоступными (по умолчанию) за четырнадцать дней до git prune
удаляю их. Таким образом, у программы Git, которая занята созданием объектов, есть четырнадцать дней, в течение которых она может завершить подключение этих объектов в граф. Если он решает, что эти объекты не стоит подключать, он может просто оставить их; 14 дней с этого момента, будущее git prune
удалит их.
Если вы бежите git prune
вручную, вы должны выбрать свой --expire
аргумент. По умолчанию без --expire
не является 2.weeks.ago
но вместо этого просто now
,
1 Объекты дерева на самом деле содержат тройки: имя, режим, хэш. Режим 100644
или же 100755
для объекта BLOB-объекта, 004000
для поддерева, 120000
для символической ссылки и так далее.
2 Для скорости поиска в Linux хеш разделяется после первых двух символов: имени хеша ab34ef56...
становится ab/34e567...
в .git/objects
каталог. Это сохраняет размер каждого подкаталога в пределах .git/objects
small-ish, который укрощает O (n 2) поведение некоторых операций с каталогами. Это связано с git gc --auto
который автоматически перепаковывает, когда один каталог объектов становится достаточно большим. Git предполагает, что каждый подкаталог имеет примерно одинаковый размер, так как хеши в основном должны быть равномерно распределены, поэтому ему нужно только сосчитать один подкаталог.
После недавнего добавления команды (Git 2.29 (Q4 2020)) замена для
git gc -prune
было бы:
git maintenance pack-refs
# for
git pack-refs --all --prune
С Git 2.31 (первый квартал 2021 г.) " " ( человек ) инструмент изучил новую задачу обслуживания.
См. Commit acc1c4d , commit 41abfe1 (09 февраля 2021 г.) Деррик Столи (
<tcode id="774767"></tcode>) .
(Слияние Junio C Hamano -
<tcode id="774768"></tcode>- в коммите d494433, 17 февраля 2021 г.)
: добавить задачу pack-refs
Подписано: Деррик Столи
Рецензент: Тейлор Блау
Полезно собрать отдельные ссылки в более сжатую форму.
Обычно это файл с упакованными ссылками, хотя в будущем его можно будет восстановить.
Упакованные ссылки могут быть чрезвычайно полезны в репозиториях со многими тегами или удаленными ветвями, которые не изменяются локальным пользователем, но все же необходимы для других запросов.Например, со многими разнесенными ссылками такие команды, как
git describe --tags --exact-match HEAD
может быть очень медленным (несколько секунд).
Эта команда, в частности, используется в запросах терминала, чтобы показать, когда отсоединенный HEAD указывает на существующий тег, поэтому медленная работа вызывает значительные задержки для пользователей.Добавить новую »задачу обслуживания.
Это работает ' <tcode id="774772"></tcode>' ( человек ), чтобы переместить свободных ссылок в упакованную форму.
На данный момент это файл pack-refs, но в будущем его можно будет адаптировать к другим форматам файлов.Это первая из нескольких подзадач задачи «gc», которые можно выделить в их собственные задачи.
В этом процессе мы не должны изменять поведение задачи «gc», поскольку это остается способом по умолчанию для поддержания репозиториев в рабочем состоянии.
Создание новой задачи для одной из этих подзадач предоставляет только дополнительные параметры настройки для тех, кто предпочитает не использовать задачу «gc».
Конечно, возможно, чтобы задачи «gc» и «pack-refs» выполнялись регулярно.
Хотя они могут повторять усилия, они не противоречат разрушительным образом.'
auto_condition
'указатель на функцию остаетсяNULL
сейчас.
Мы могли бы расширить это в будущем, чтобы иметь возможность проверки состояния, следует ли запускать pack-refs во время ' <tcode id="774775"></tcode>' ( мужчина ) .
git maintenance
теперь включает в свою справочную страницу :
Задача собирает отдельные справочные файлы и собирает их в один файл. Это ускоряет операции, которые требуют перебора множества ссылок.
И он может работать по расписанию в рамках своей новой задачи pack-refs:
<tcode id="774769"></tcode>: инкрементальная стратегия запускает pack-refs еженедельно
Подписано: Деррик Столи
Рецензент: Тейлор Блау
Когда '
maintenance.strategy
'опция конфигурации установлена в'incremental
', включен график обслуживания по умолчанию.
Добавьте к этой стратегии задачу «pack-refs» еженедельно.
git config
теперь включает в свою справочную страницу :
задача, но запускает
prefetch
иcommit-graph
задачи ежечасно,loose-objects
иincremental-repack
задачи ежедневно, аpack-refs
задача еженедельно.