Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як Git зберігає гілки й теги: файли в refs, packed-refs і reftable?

Ref - ім'я, що вказує на об'єкт (зазвичай коміт). Класичне зберігання - один файл на ref:

.git/refs/heads/main            → 23933defbcb6...
.git/refs/remotes/origin/main   → 23933defbcb6...
.git/refs/tags/v2.4.0           → 9fceb02d0ae5...

Просто й наочно, але з тисячами гілок і тегів (типово для великих проєктів і серверів) з'являються проблеми: тисячі дрібних файлів, повільний перелік, а на файлових системах без урахування регістру (macOS, Windows) гілки Feature і feature конфліктують.

packed-refs - багато ref-ів одним текстовим файлом:

git pack-refs --all
cat .git/packed-refs
# # pack-refs with: peeled fully-peeled sorted
# 84ba0356e619... refs/heads/f
# 23933defbcb6... refs/heads/main
# 9fceb02d0ae5... refs/tags/v2.4.0
# ^4a1f2c9b7e3d...    ← «peeled»: коміт, на який вказує анотований тег

Як Git шукає ref: спершу окремий файл у refs/, потім packed-refs. Окремий файл має пріоритет - тому оновлення гілки просто створює новий файл, не переписуючи packed-refs. git gc періодично пакує ref-и автоматично.

Наслідок для скриптів: cat .git/refs/heads/main може не знайти файл - гілка є, але упакована. Правильно - через команди:

git rev-parse refs/heads/main
git for-each-ref --format='%(refname) %(objectname)' refs/tags/
git update-ref refs/heads/main <хеш>     # атомарно, із записом у reflog

Атомарність: оновлення ref-а йде через lock-файл (main.lock) і перейменування. Звідси повідомлення Unable to create '.git/refs/heads/main.lock': File exists - залишок після аварійно завершеного процесу Git; якщо інших процесів Git немає, файл можна видалити.

Reftable - новий бінарний формат зберігання ref-ів (з Git 2.45), створений у JGit для серверів з мільйонами ref-ів:

  • атомарні транзакції для багатьох ref-ів одночасно;
  • швидкий пошук і перелік, без проблем з регістром імен;
  • reflog у тому самому сховищі.
git init --ref-format=reftable
git refs migrate --ref-format=reftable    # перетворити наявний репозиторій

Для звичайного проєкту різниця непомітна, але у великих монорепозиторіях і на серверах reftable прибирає вузьке місце з тисячами ref-ів.

Докладніше в документації: git pack-refs

Схожі питання