Сбор мусора в Git с помощью git gc

17 декабря 2025
Автор

Олег Марков

Введение

Сбор мусора в Git — это процесс очистки репозитория от ненужных данных и оптимизации хранения объектов. Команда git gc (gc — garbage collection) отвечает именно за это. Без нее репозиторий со временем начинает разрастаться, операции замедляются, а в .git копятся старые ненужные объекты.

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

В этой статье мы разберем:

  • какие объекты очищает git gc и какие сжимает;
  • как именно устроена работа git gc внутри;
  • когда стоит запускать сборку мусора вручную, а когда — не трогать;
  • как настроить параметры, чтобы не сломать историю;
  • как диагностировать проблемы с производительностью и размером репозитория.

Смотрите, я покажу вам на реальных примерах, что делает git gc, и как это влияет на ваш репозиторий.


Что такое сбор мусора в Git и зачем нужен git gc

Объектная модель Git в контексте git gc

Чтобы понять, что именно “убирает” git gc, полезно освежить, как Git хранит данные.

Внутри каталога .git/objects находятся объекты нескольких типов:

  • commit — коммиты;
  • tree — состояния каталогов (деревья);
  • blob — содержимое файлов;
  • tag — аннотированные теги.

Каждый объект имеет:

  • хеш-идентификатор (SHA-1 или SHA-256 в новых конфигурациях);
  • тип;
  • размер;
  • содержимое.

Git различает:

  • Loose-объекты — хранятся по отдельности в виде отдельных файлов в .git/objects/<2 символа>/<38 символов>;
  • Пакеты объектов (pack files) — сжатые коллекции объектов в .git/objects/pack.

git gc:

  • удаляет недостижимые объекты (сбор мусора в прямом смысле);
  • сжимает большое количество loose-объектов в pack-файлы;
  • перепаковывает уже существующие pack-файлы для лучшего сжатия;
  • чистит вспомогательные файлы вроде старых reflog.

По сути, git gc — это:

  • уборщик (удаление мусора);
  • архиватор (упаковка и перепаковка объектов).

Как git gc ищет “мусор” и что он удаляет

Понятие достижимого и недостижимого объекта

Git считает объект достижимым, если до него можно “дойти” от одного из корневых указателей:

  • ссылок в .git/refs (ветки, теги, HEAD);
  • объектов, упомянутых в reflog;
  • специального файла .git/ORIG_HEAD и некоторых временных ссылок.

Недостижимый объект — это объект, до которого нельзя дойти по этим ссылкам. Например:

  • вы сделали коммит, а затем сделали git reset --hard HEAD~1 — старый коммит перестал быть привязан к веткам;
  • вы переписали историю через git rebase, и старые коммиты заменились новыми;
  • вы сделали git commit --amend, и старый коммит оказался “висящим”.

Вот таких “висящих” коммитов, деревьев и blob-объектов со временем скапливается довольно много, особенно в активных репозиториях с частыми rebases и amend.

git gc:

  • проходит от всех корней (refs, reflog) и помечает достижимые объекты;
  • все остальные объекты считает кандидатами на удаление;
  • удаляет недостижимые объекты, которые “протухли” (старше определенного срока).

Гарантии “безопасности” — grace period

Git по умолчанию не удаляет недостижимые объекты мгновенно. Он использует время жизни (grace period). Это нужно, чтобы вы могли “откатить” неудачный reset или rebase, пока старые объекты еще доступны через reflog.

Основные параметры:

  • gc.pruneExpire — по умолчанию “2 weeks ago”
    • недостижимый объект моложе двух недель — сохраняется;
    • старше — удаляется при следующем git gc (или git prune);
  • gc.reflogExpire и gc.reflogExpireUnreachable — регулируют, сколько хранятся записи reflog.

Важно: пока запись о коммите есть в reflog, он считается достижимым, и git gc его не тронет.


Основные режимы работы git gc

Обычный запуск git gc

Базовая команда:

git gc
# Запуск сборщика мусора с настройками по умолчанию

При обычном запуске git gc:

  • собирает loose-объекты в pack-файлы;
  • может перепаковать существующие пакеты;
  • чистит старые записи reflog;
  • удаляет недостижимые объекты старше gc.pruneExpire.

В Git обычно настроен авто-запуск сборки мусора после определенного количества коммитов, fetch или изменений. Но на больших проектах разработчики иногда отключают авто-gc и запускают его вручную в “окно обслуживания”.

Принудительный запуск git gc --aggressive

Режим --aggressive делает более глубинную перепаковку объектов:

git gc --aggressive
# Более агрессивная оптимизация репозитория

Что он делает:

  • использует более сложные и дорогие алгоритмы поиска дельт между объектами;
  • может существенно уменьшить размер pack-файлов;
  • может занять намного больше времени и ресурсов (особенно CPU).

Когда использовать:

  • огромный репозиторий, который давно не чистили;
  • вы только что импортировали большой монорепозиторий;
  • вы сделали массовую миграцию истории (переписали много коммитов).

Когда лучше не использовать:

  • на CI при каждом билде;
  • на слабых машинах;
  • на активных прод-репозиториях в рабочее время (процесс может “съесть” CPU).

Просушка объектов — git gc --prune и git prune

Иногда вам нужно более строго удалить мусор, чем это делает обычный git gc. Для этого есть:

git gc --prune=now
# Удалить все недостижимые объекты сразу, без grace period

Или отдельная команда:

git prune
# Низкоуровневая команда для удаления недостижимых объектов

Разница:

  • git gc — высокоуровневая обертка, делает prune, repack и уборку;
  • git prune — только удаление недостижимых объектов, без перепаковки.

Используйте --prune=now аккуратно. Обратите внимание: если вы удалили недостижимые объекты сразу, шанс вернуть состояние через reflog резко снижается.


Что именно делает git gc внутри

Сжатие loose-объектов в pack-файлы

При активной работе с репозиторием появляются тысячи loose-объектов. Каждый такой объект — отдельный файл. Это плохо по нескольким причинам:

  • файловая система не любит огромное число маленьких файлов;
  • чтение и запись по одному объекту замедляет операции;
  • резервное копирование и перенос репозитория становятся тяжелее.

git gc объединяет loose-объекты в pack-файл:

git gc
# Внутри будет, по сути, вызвано что-то вроде git repack

Результат:

  • в .git/objects/pack появляется один или несколько файлов:
    • pack-XXXX.pack — сами данные;
    • pack-XXXX.idx — индекс для быстрого поиска объекта по хешу;
  • в .git/objects становится намного меньше отдельных файлов.

Git использует дельта-сжатие: он не хранит каждый blob целиком, а строит отличия (дельты) от похожего содержимого. Например, между версиями одного файла.

Перепаковка уже существующих pack-файлов

Со временем pack-файлов может стать несколько, особенно в долгоживущих репозиториях. Это тоже не очень удобно: поиск может работать медленнее, хранение становится менее оптимальным.

git gc умеет:

  • перепаковывать несколько pack-файлов в один крупный;
  • избавляться от дубликатов;
  • строить более выгодные цепочки дельт.

Пример ручной перепаковки (обычно не нужно, git gc сделает сам):

git repack -Ad
# -A  перепаковать все объекты
# -d  удалить старые pack-файлы

Очистка reflog и влияние на сбор мусора

Reflog — это журнал изменений ссылок, например HEAD и веток. Он сохраняет историю:

  • перемещений веток;
  • ребейзов;
  • hard reset;
  • merge и cherry-pick.

Пока запись о коммите есть в reflog, объект считается достижимым, даже если его больше нет в ветках и тегах.

git gc:

  • удаляет старые записи reflog;
  • настраивается параметрами:
    • gc.reflogExpire — по умолчанию хранит записи 90 дней;
    • gc.reflogExpireUnreachable — для недостижимых коммитов, по умолчанию 30 дней.

Как только запись из reflog исчезает и объект становится недостижимым, он может быть собран (удален) при следующем git gc.


Практические сценарии использования git gc

Сценарий 1. Репозиторий стал слишком большим

Вы замечаете, что .git занимает слишком много места:

du -sh .git
# Показывает общий размер каталога .git

Давайте разберемся, что можно сделать:

  1. Посмотреть статистику пакетов:

    git count-objects -vH
    # -v  подробный вывод
    # -H  человекочитаемые единицы (MB, GB)
    

    Пример вывода:

    count: 12034             # количество loose-объектов
    size: 59.54 MiB          # общий размер loose-объектов
    in-pack: 450123          # объектов в pack-файлах
    packs: 5                 # число pack-файлов
    size-pack: 1.23 GiB      # размер всех pack-файлов
    prune-packable: 234      # объекты, которые можно упаковать
    garbage: 0               # поврежденные или странные объекты
    

    Обратите внимание: если count и size большие — есть смысл вызвать git gc.

  2. Запустить обычную сборку мусора:

    git gc
    
  3. Если репозиторий очень большой и давно не чистился — попробовать агрессивный режим (желательно на отдельной машине или в “тихое время”):

    git gc --aggressive
    
  4. Еще раз проверить размер:

    git count-objects -vH
    

Здесь вы увидите, насколько уменьшился размер loose-объектов, и изменился ли размер pack-файлов.

Сценарий 2. После rebase и reset вы хотите убрать старые версии

Вы сделали много экспериментов с историей:

  • несколько rebase на разных ветках;
  • пару раз делали git reset --hard;
  • переписали историю через git filter-repo или BFG Repo-Cleaner.

В результате:

  • история в ветках выглядит аккуратно;
  • но внутри .git все еще лежат старые коммиты и blobs.

Покажу вам пример типичной последовательности:

# После успешной переписи истории
git reflog expire --expire=now --all
# Сразу же удалить все записи reflog

git gc --prune=now --aggressive
# Удалить все недостижимые объекты и глубоко перепаковать репозиторий

Комментарии к командам:

  • reflog expire — принудительно очищает reflog, чтобы старые коммиты не считались достижимыми;
  • git gc --prune=now — удаляет то, что теперь стало недостижимым, без grace period.

Используйте этот сценарий только тогда, когда вы уверены, что переписывание истории завершено, и вам не нужно будет “доставать” старые версии.

Сценарий 3. Репозиторий стал медленным — долгие fetch, status и log

Если операции Git начали работать заметно медленнее:

  • долгий git status;
  • медленный git log;
  • ощутимые задержки при git fetch и git pull.

Частая причина — огромное количество исторических объектов и плохая упаковка.

Шаги:

  1. Посмотреть количество loose-объектов:

    git count-objects -vH
    
  2. Если count большой — запустить git gc:

    git gc
    
  3. Для долговременной оптимизации настроить автоматический gc (если отключен):

    git config --global gc.auto 256
    # Запускать авто-gc после появления 256 loose-объектов
    
  4. При очень больших репозиториях — периодически, например раз в неделю, запускать:

    git gc --aggressive
    

    Лучше делать это на серверном зеркале или в maintenance-режиме.


Настройка и управление поведением git gc

Автоматический git gc и параметры gc.auto

Git запускает автоматический сборщик мусора после некоторых операций, когда количество loose-объектов превышает порог gc.auto.

Посмотреть текущие настройки:

git config --show-origin --get-all gc.auto
# Показывает значение gc.auto и откуда оно взято

Установить значение:

git config --global gc.auto 256
# Устанавливаем порог в 256 loose-объектов

Особенности:

  • gc.auto = 0 — отключает авто-gc;
  • слишком маленькое значение может приводить к частым запускам gc и задержкам;
  • слишком большое — к накоплению мусора и деградации производительности.

Если вы работаете с большими репозиториями, имеет смысл:

  • на локальных машинах оставить авто-gc включенным;
  • на CI и серверах часто авто-gc отключают и запускают его по расписанию.

Пример отключения авто-gc:

git config --global gc.auto 0
# Полное отключение автоматического git gc

Настройка сроков хранения reflog и недостижимых объектов

Параметры:

  • gc.reflogExpire — сколько хранить записи reflog для достижимых объектов;
  • gc.reflogExpireUnreachable — для недостижимых;
  • gc.pruneExpire — сколько хранить недостижимые объекты перед окончательным удалением.

Посмотрим пример настройки, если вы хотите более долго хранить историю:

git config --global gc.reflogExpire "180 days"
# Хранить reflog 180 дней

git config --global gc.reflogExpireUnreachable "60 days"
# Недостижимые коммиты держать 60 дней

git config --global gc.pruneExpire "60 days"
# Не удалять недостижимые объекты младше 60 дней

Если же, наоборот, вы хотите ускорить очистку:

git config --global gc.reflogExpire "30 days"
git config --global gc.reflogExpireUnreachable "7 days"
git config --global gc.pruneExpire "7 days"

Обратите внимание: слишком агрессивные значения уменьшают вашу способность “спасти” историю после ошибок.


Особенности и подводные камни git gc

Можно ли потерять данные из-за git gc

С точки зрения Git, данные “теряются” тогда, когда:

  • объект становится недостижимым (ни в ветках, ни в тегах, ни в reflog);
  • и выходит за пределы grace period (gc.pruneExpire);
  • и был запущен git gc (или git prune).

До этого момента вы еще можете:

  • найти объект через reflog;
  • восстановить ветку (git branch на нужный хеш);
  • отменить неудачный reset или rebase.

Поэтому основное правило:

  • git gc сам по себе редко становится причиной "потери" данных;
  • главная опасность — агрессивная очистка reflog и использование --prune=now.

Если вы сомневаетесь, лучше:

  • сначала выполнить обычный git gc без --prune=now;
  • не менять слишком резко gc.reflogExpire и gc.pruneExpire.

Почему git gc иногда запускается в самый неподходящий момент

Иногда, после commit или fetch, вы замечаете паузу — и в терминале видите:

Auto packing the repository in background for optimum performance

Это авто-gc:

  • он запускается, когда число loose-объектов превышает gc.auto;
  • может заметно нагружать процессор и диск.

Если это мешает во время CI или при сборке:

git config --global gc.auto 0
# Отключение авто-gc, чтобы запускать его вручную

Но для обычных локальных репозиториев авто-gc лучше оставить включенным.


Как диагностировать и контролировать работу git gc

Команда git count-objects

Это основной инструмент диагностики состояния хранилища:

git count-objects -vH

Покажу вам, на что смотреть:

  • count — количество loose-объектов. Если значение больше нескольких тысяч — есть смысл запускать git gc;
  • size — размер loose-объектов;
  • in-pack — сколько объектов уже упаковано;
  • size-pack — суммарный размер pack-файлов;
  • garbage — наличие “мусорных” объектов, возможно поврежденных.

Если garbage больше нуля, стоит проверить целостность:

git fsck
# Проверка целостности репозитория

Проверка и восстановление после ошибок

Если после git gc вы подозреваете проблемы:

  1. Проверить репозиторий:

    git fsck --full
    # Глубокая проверка, ищет недостающие объекты и повреждения
    
  2. Если git fsck сообщает о недостающих объектах:

    • попробуйте найти хеши в reflog: bash git reflog # Просмотр истории перемещений ссылок
    • если коммиты еще где-то есть (на удаленном сервере) — попробуйте сделать git fetch.
  3. Если у вас есть клон или зеркало, где проблема не проявляется:

    • можно пере-клонировать;
    • или восстановить поврежденные объекты из зеркала.

Рекомендации по использованию git gc в разных сценариях

Локальная разработка

Для обычных локальных репозиториев:

  • оставьте авто-gc включенным (значение gc.auto по умолчанию обычно разумно);
  • иногда (раз в несколько недель) можете вручную запускать: bash git gc
  • агрессивный режим используйте редко, например после больших чисток истории.

CI и билд-сервера

На CI часто используются временные клоны, которые живут недолго. Там можно:

  • отключить авто-gc: bash git config --global gc.auto 0
  • периодически чистить кеши или зеркала репозиториев, если они используются;

Если есть shared репозитории, которые кэшируются внутри CI, имеет смысл настроить плановый git gc в расписании (cron или встроенные механизмы CI).

Центральные репозитории и зеркала

На серверных зеркалах:

  • авто-gc нередко отключают, чтобы он не запускался в рабочее время;
  • делают плановый git gc (в том числе с --aggressive) по расписанию ночью или в выходные;
  • более аккуратно настраивают gc.reflogExpire и gc.pruneExpire, чтобы не удалять данные слишком рано.

Заключение

git gc — это ключевой механизм в Git, отвечающий за:

  • удаление недостижимых и устаревших объектов;
  • упаковку loose-объектов в эффективные pack-файлы;
  • оптимизацию размера и производительности репозитория.

Вы увидели, как:

  • Git отличает достижимые и недостижимые объекты;
  • reflog влияет на сбор мусора;
  • настраиваются параметры срока хранения объектов;
  • использовать обычный и агрессивный режимы git gc;
  • проверять состояние репозитория через git count-objects и git fsck.

Если вы понимаете, что именно делает git gc и как его настраивать, вы можете:

  • поддерживать репозитории компактными и быстрыми;
  • планировать обслуживание больших репозиториев;
  • уменьшать риск потери данных при переписывании истории.

Частозадаваемые технические вопросы по теме статьи и ответы на них

1. Как предотвратить запуск git gc в конкретной операции (например, при git fetch на CI)

Вы можете временно отключить авто-gc только для одной команды, переопределив конфиг через переменную окружения:

GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0=gc.auto \
GIT_CONFIG_VALUE_0=0 \
git fetch origin
# Здесь авто-gc не будет запущен

Так вы не меняете глобальную конфигурацию, но подавляете авто-gc именно для этого вызова.

2. Как посмотреть, запускался ли git gc и какие действия он выполнял

Логи git gc обычно попадают в общий лог Git, если вы включите трассировку:

GIT_TRACE=1 GIT_TRACE_PERFORMANCE=1 git gc
# В вывод попадет информация о действиях, времени и шагах

Также посмотрите файлы в .git/logs/ (это reflog) и системные лог-файлы вашей системы, если git gc запускается как часть серверного обслуживания.

3. Как полностью переупаковать репозиторий без удаления недостижимых объектов

Если вы хотите только оптимизировать pack-файлы, но не трогать недостижимые объекты:

git repack -Adf
# -A  перепаковать все объекты
# -d  удалить старые пакеты
# -f  пересоздать дельты даже для уже упакованных объектов

Эта команда не запускает prune и не удаляет недостижимые объекты.

4. Что делать, если git gc завершается с ошибкой из-за недостатка места на диске

Действия по шагам:

  1. Освободите немного места (хотя бы несколько процентов диска).
  2. Временное решение — отключить авто-gc: bash git config --global gc.auto 0
  3. Если репозиторий очень большой, перенесите его на диск с большим объемом и затем запустите: bash git gc --aggressive
  4. После успешного завершения gc можно снова включить авто-gc с разумным порогом.

5. Как ограничить использование памяти git gc на машине с небольшим объемом RAM

Git предоставляет параметры для ограничения памяти при упаковке:

git config --global pack.windowMemory "256m"
git config --global pack.packSizeLimit "512m"

Эти настройки подскажут Git не выходить за указанные пределы при создании pack-файлов. После этого можно запустить git gc с меньшим риском “съесть” всю доступную память.

Стрелочка влевоИнтерактивное перебазирование - git rebase -iПроверка связности и целостности репозитория в Git с помощью git fsckСтрелочка вправо

Постройте личный план изучения Git до уровня Middle — бесплатно!

Git — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Git

Открыть базу знаний

Лучшие курсы по теме

изображение курса

Основы Git

Антон Ларичев
AI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.9
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий