Клонирование: shallow, partial clone, bundle и backfill

Рейтинг: 80.6% · 16 голосов
Самый подробный курс по Git - от первого коммита до объектной базы и packfiles. Терминал и первый репозиторий, ветки и слияние, конфликты, удалённая работа и форджи (GitHub, GitLab), rebase и переписывание истории, спасение кода через reflog и fsck, stash и worktree, bisect и blame, подмодули и монорепо, хуки и подпись коммитов, внутреннее устройство и масштаб. Для новичков и профи. Актуально на 2026 (Git 2.54, дорожная карта 3.0).
Ответить
Аватара пользователя
Pavel_Git
Сообщения: 52
Зарегистрирован: 11 май 2026, 05:31

Клонирование: shallow, partial clone, bundle и backfill

Сообщение Pavel_Git »

Оглавление курса (52)
  1. Что такое контроль версий и почему победил Git
  2. Установка Git на Windows, macOS и Linux и первая настройка
  3. Терминал и как спрашивать у Git: выживание в командной строке
  4. Ментальная модель Git: три состояния, коммит-снимок и граф
  5. Первый репозиторий: init, add, commit, status
  6. Просмотр изменений: status, diff и индексация по частям
  7. История проекта: git log, диапазоны и поиск
  8. .gitignore: что не должно попасть в репозиторий
  9. .gitattributes: окончания строк, бинарники и атрибуты
  10. Ветки как указатели: branch, switch и detached HEAD
  11. Слияние веток: fast-forward против трёхстороннего merge
  12. Конфликты слияния: анатомия и уверенное разрешение
  13. Практикум: твой первый день с Git от нуля до push
  14. Удалённые репозитории: remote, fetch, базовый pull и refspec
  15. git push: безопасная отправка и защита от перезаписи
  16. Клонирование: shallow, partial clone, bundle и backfill (вы здесь)
  17. GitHub, GitLab и форки: pull request, rulesets и защита веток
  18. Рабочие процессы команды: GitFlow, GitHub Flow, trunk-based
  19. Merge queue и merge trains: безопасное вливание в trunk
  20. Культура code review и Conventional Commits
  21. git reset: три дерева и отмена индексации и коммитов
  22. git revert: безопасная отмена в общей истории
  23. Правка последнего коммита: git commit --amend
  24. git rebase: пересадка истории и золотое правило
  25. Интерактивный rebase: переписать серию коммитов
  26. git cherry-pick: перенос отдельных коммитов
  27. Stacked diffs: цепочка зависимых PR
  28. Массовое переписывание истории: git filter-repo и git replace
  29. git reflog: журнал, который помнит всё
  30. Восстановление утерянных коммитов, веток и файлов
  31. Откат опасных операций: reset --hard, сломанный rebase, плохой merge
  32. git stash: тайник для незавершённой работы
  33. git worktree: несколько рабочих деревьев одного репозитория
  34. git bisect: бинарный поиск регрессии
  35. git blame, mailmap и археология кода
  36. Теги, релизы, archive и bundle
  37. Подмодули: внешние репозитории внутри проекта
  38. Subtree, монорепозитории и sparse-checkout
  39. Git LFS и большие файлы
  40. Вспомогательные команды: clean, mv, rm, restore
  41. Псевдонимы и кастомизация .gitconfig
  42. Хуки Git: автоматизация на клиенте и сервере
  43. Подпись коммитов и тегов: SSH, GPG и Sigstore
  44. Внутреннее устройство I: объектная база Git
  45. Внутреннее устройство II: ссылки, HEAD, notes и reftable
  46. Внутреннее устройство III: индекс, packfiles, gc и обслуживание
  47. От SHA-1 к SHA-256: переход на новый хеш
  48. Огромные репозитории: partial clone, FSMonitor, Scalar
  49. Git в CI/CD: shallow, кеш, merge_group и безопасность
  50. Куда движется Git: дорожная карта 3.0 и экосистема
  51. Профессиональный setup: собираем рабочее окружение Git
  52. 30+ типичных ошибок Git и как из них выбираться
Зачем вообще думать о том, как клонировать

Команда git clone выглядит как кнопка скачать. Набрал адрес, подождал, получил папку. Пока репозиторий маленький, так и есть - думать не о чем. Боль начинается, когда история разрослась до сотен тысяч коммитов и гигабайтов бинарей, когда CI поднимает чистый клон на каждый пуш по двадцать раз в час, когда у тебя в дороге мобильный интернет и пять минут до посадки в самолёт. Вот тут полный клон становится дорогим: ты тащишь всю историю всех файлов от первого коммита, хотя собрать проект можно из одного снимка.

Git за последние годы оброс инструментами, чтобы скачивать ровно столько, сколько нужно прямо сейчас, и дозагружать остальное лениво. Поверхностное клонирование (shallow), частичное (partial), перенос репозитория одним файлом (bundle) и дозагрузка пропущенного (backfill). Разберёмся, что именно скачивается при каждой опции, чем платишь за экономию и где грабли, на которых спотыкаются даже опытные.

Важно сразу зафиксировать факты, потому что в интернете полно мифов. Partial clone стабилен с Git 2.27 - это 2020 год, а не свежак 2026. bundle-uri в ядре с 2.38 (конец 2022). А вот git backfill - действительно молодой, появился в Git 2.49 (2025) и помечен как экспериментальный. Примеры ниже калибрую на Git 2.5x.

Изображение

Полный клон, git clone branch и --single-branch

Базовый случай - клонировать репозиторий git целиком:

Код: Выделить всё

git clone https://github.com/git/git.git
Git скачивает все объекты, доступные из всех веток сервера, разворачивает рабочее дерево из ветки по умолчанию и заводит remote с именем origin. Все удалённые ветки оседают в refs/remotes/origin/*, локально создаётся только одна - та, на которую указывал HEAD сервера.

Часто не нужна вся пачка веток. Если интересует конкретная, есть git clone branch через флаг --branch (короткий -b):

Код: Выделить всё

git clone -b release-2.54 https://github.com/git/git.git
Это всего лишь говорит, какую ветку выложить в рабочее дерево после клона. Объекты по-прежнему качаются для всех веток. Чтобы реально сузить трафик до одной ветки, добавляй --single-branch:

Код: Выделить всё

git clone -b release-2.54 --single-branch https://github.com/git/git.git
Теперь Git запрашивает у сервера историю только этой ветки. Конфиг remote.origin.fetch сужается до одной строки, и будущий git fetch тоже будет тянуть только её. Это уже экономия, но история всё ещё полная - от корня до вершины.

Shallow clone: git clone depth и чем платим

Поверхностное клонирование - это shallow clone. Идея: взять не всю историю, а последние N коммитов. Флаг git clone depth:

Код: Выделить всё

git clone --depth=1 https://github.com/git/git.git
--depth=1 значит один коммит на вершине каждой запрошенной ветки, без родителей. Для CI это золото: сборке обычно нужен снимок кода, а не родословная. Трафик и время падают в разы на больших репозиториях.

Под капотом Git создаёт файл .git/shallow со списком SHA коммитов, у которых история искусственно обрезана. Эти коммиты выглядят как корневые, хотя на сервере у них есть родители. Посмотреть границу:

Код: Выделить всё

$ git log --oneline
e83c516 (HEAD -> master, origin/master) Sync with maint
$ cat .git/shallow
e83c516331d4...
Видишь один коммит и одну строку в .git/shallow - это и есть линия отреза. Заметь: shallow почти всегда подразумевает --single-branch. Если нужны несколько веток мелко, бери --no-single-branch отдельно.

Теперь чем платим - и это место, где ломаются вещи. Без истории не работает то, что эту историю обходит:
  • git blame показывает дичь. Команде нужно дойти до коммита, который последним менял строку. Если он за линией отреза, blame упирается в shallow-границу и припишет авторство граничному коммиту. Сидишь и не понимаешь, почему весь файл написал один человек в один день.
  • git describe врёт или падает. Он считает расстояние до ближайшего тега по предкам. Тегов за отрезом нет - либо ошибка, либо номер версии из пальца.
  • git merge-base не находит общего предка между ветками, если он остался по ту сторону отреза. Отсюда раздутые или просто неверные диффы при сравнении и слиянии.
Лечится углублением. Дотянуть ещё коммитов:

Код: Выделить всё

git fetch --depth=50
Или развернуть до полной истории - превратить shallow в нормальный клон:

Код: Выделить всё

git fetch --unshallow
После --unshallow файл .git/shallow исчезает, blame и describe оживают. Практический вывод: shallow - инструмент эфемерных окружений (CI, одноразовая сборка, докер-слой). Для рабочей копии, где будешь копаться в истории, он плохой выбор.

Partial clone: --filter=blob:none и tree:0

У shallow есть фундаментальный минус: он режет историю целиком. А что если история нужна (blame, log, merge-base работают), но не нужны старые версии файлов? Для этого есть частичное клонирование - partial clone. Оно режет не по оси времени, а по оси содержимого.

Напомню объектную модель: коммит указывает на дерево (tree), дерево перечисляет файлы и подпапки, а содержимое файла лежит в blob. Тяжесть репозитория - это в основном blob, особенно их старые версии. Partial clone позволяет скачать все коммиты и деревья, но пропустить blob, договорившись с сервером тянуть их лениво по требованию.

Код: Выделить всё

git clone --filter=blob:none https://github.com/git/git.git
Фильтр blob:none - не качать ни одного blob при клоне. Это называют blobless clone. Граф коммитов и деревьев полный, поэтому git log, git blame по факту, merge-base - всё на месте. Когда тебе реально понадобится содержимое файла (checkout, git show, открыть файл), Git сходит на сервер и подтянет недостающие blob. Происходит это автоматически и прозрачно - заметишь только паузу и сетевой запрос.

Более агрессивный вариант - tree:0, не качать ни деревья, ни blob (treeless clone):

Код: Выделить всё

git clone --filter=tree:0 https://github.com/git/git.git
Тут экономия максимальная, но и история навигируется хуже: операции, которым нужны деревья (тот же blame по всей истории), будут постоянно дёргать сервер. tree:0 хорош для разовых вещей вроде git log по сообщениям без захода в файлы.

Чем blobless лучше shallow: ты сохраняешь полный граф коммитов. Поэтому blame, describe, merge-base корректны - им нужны коммиты, а не содержимое старых blob. За правильность платишь редкими подгрузками blob вместо вранья в истории. Для рабочей копии большого монорепо blobless clone почти всегда лучше, чем shallow.

Проверить, что репозиторий частичный, можно через конфиг и promisor remote:

Код: Выделить всё

$ git config remote.origin.partialclonefilter
blob:none
Сервер, который умеет отдавать пропущенные объекты потом, называется promisor remote, а ссылки на ещё не скачанные объекты - promisor objects. Поддержка нужна на стороне сервера (GitHub, GitLab умеют).

git backfill: дозагрузка пропущенных blob батчами

У ленивой подгрузки blob есть слабое место. Когда ты делаешь git checkout другой ветки или git grep по всем файлам, Git вдруг обнаруживает, что не хватает множества blob, и начинает тащить их по одному. Каждый запрос - отдельный round-trip к серверу. Сотни мелких запросов вместо одного большого - это медленно и больно бьёт по серверу.

Решение приехало в Git 2.49 - команда git backfill. Она дозагружает пропущенные blob заранее и пачками, а не по одному:

Код: Выделить всё

git clone --filter=blob:none https://github.com/git/git.git
cd git
git backfill
По умолчанию git backfill качает все blob, достижимые из HEAD, но делает это умно: группирует объекты по пути файла и шлёт небольшое число батч-запросов вместо лавины одиночных. Ключевая идея - сменить many one-blob requests на few batched requests. Если включён sparse-checkout, backfill подхватит это автоматически (или явно через --sparse) и подтянет только blob из твоего конуса путей.

Связка получается такая: клонируешь blobless (быстро, мало трафика на старте), сразу работаешь с полным графом коммитов, а в фоне или перед тяжёлой операцией прогреваешь нужные blob через backfill несколькими сетевыми вызовами. Это золотая середина между полным клоном и постоянными ленивыми дёргами. Помни: команда экспериментальная, флаги могут поменяться - держи Git свежим и сверяйся с git help backfill.

Подмодули: --recurse-submodules

Если в репозитории есть подмодули (вложенные репозитории, привязанные к конкретным коммитам через .gitmodules), обычный clone их не наполняет - папки будут пустыми. Чтобы сразу клонировать и проинициализировать всё дерево:

Код: Выделить всё

git clone --recurse-submodules https://example.com/super.git
Git склонирует супермодуль, прочитает .gitmodules и рекурсивно склонирует каждый подмодуль на зафиксированный коммит. Можно ускорить параллелизмом: --jobs=4 (или -j 4) клонирует несколько подмодулей одновременно. Фильтры комбинируются - blobless супермодуль с подмодулями вполне рабочая схема. Если забыл флаг при клоне, доинициализируешь потом:

Код: Выделить всё

git submodule update --init --recursive
git bundle: репозиторий в одном файле

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

Создать bundle со всей историей всех веток:

Код: Выделить всё

git bundle create repo.bundle --all
Перенёс repo.bundle любым способом - и на другой стороне клонируешь прямо из файла:

Код: Выделить всё

git clone repo.bundle myrepo
Bundle умеет быть инкрементальным - упаковать только то, чего нет на принимающей стороне, задав диапазон ревизий:

Код: Выделить всё

git bundle create update.bundle main~20..main
Перед применением чужой bundle стоит проверить, что у тебя есть все нужные базовые коммиты для него:

Код: Выделить всё

$ git bundle verify update.bundle
The bundle contains these 1 ref:
9f1c2a... refs/heads/main
The bundle requires these 1 ref:
a31be7... main~20
update.bundle is okay
Строка requires these refs - это базовые коммиты, которые должны уже быть в твоём репозитории. Если их нет, инкрементальный bundle не применится - сначала донеси полный или промежуточный.

bundle-uri: ускоряем клон предзагруженным bundle

bundle полезен и онлайн. Механизм bundle-uri (в ядре Git с 2.38) позволяет при клоне сначала скачать заранее посчитанный bundle со статического хранилища или CDN, а потом дотянуть с основного сервера только свежую разницу. Сервер делает тяжёлую работу один раз, клиенты качают bootstrap с дешёвого CDN, нагрузка на git-сервер падает.

Явно указать источник:

Код: Выделить всё

git clone --bundle-uri=https://cdn.example.com/git/repo.bundle https://example.com/repo.git
Либо сервер сам рекламирует список bundle, и клиент подхватывает его автоматически, если включён transfer.bundleURI. Это не новинка 2026, а зрелый механизм; особенно ценят CI-фермы и пользователи со слабой связью до origin.

Что и сколько скачивается: сводка
  • Полный clone - все коммиты, все деревья, все blob всех версий. Максимум трафика, всё работает офлайн.
  • --single-branch - объекты только одной ветки, история полная. Умеренная экономия.
  • --depth=1 (shallow) - один снимок на вершине, без истории. Минимум трафика, ломает blame, describe, merge-base.
  • --filter=blob:none (partial) - все коммиты и деревья, blob лениво. История корректна, blob подтягиваются по требованию.
  • --filter=tree:0 - только коммиты, деревья и blob лениво. Максимум экономии, навигация по файлам дёргает сервер.
  • git backfill - после blobless дозагрузить blob батчами, убрав лавину одиночных запросов.
  • bundle / bundle-uri - перенос офлайн одним файлом и bootstrap клона с CDN.
Типичные грабли
  • Поставил --depth=1 на рабочую копию и удивляешься, что git blame показывает одного автора и одну дату на весь файл. Это shallow-граница. Лечение - git fetch --unshallow.
  • Сделал shallow и пытаешься смержить ветку - merge-base не находит общего предка за отрезом, дифф огромный. Углубляйся или разворачивай историю.
  • Ждёшь, что blobless clone мгновенно работает офлайн. Нет: первый же checkout ветки без сети упрётся в отсутствие blob. Прогрей через git backfill, пока сеть есть.
  • Клонировал репозиторий с подмодулями без --recurse-submodules, собираешь - пусто. Добей git submodule update --init --recursive.
  • Инкрементальный bundle не применяется - забыл, что ему нужны базовые коммиты (requires these refs). Проверяй git bundle verify заранее.
  • Думаешь, что partial clone и bundle-uri - хайп 2026. Это 2020 и 2022. Свежее - именно git backfill (2.49) и его развитие.
Мини-лаба: пощупать руками

Возьми любой публичный репозиторий с историей. Пройди по шагам:

Код: Выделить всё

# 1. Поверхностный клон и граница истории
git clone --depth=1 https://github.com/git/git.git shallow-git
cd shallow-git
git log --oneline | wc -l        # ровно 1
cat .git/shallow                 # один SHA - линия отреза
git describe --tags 2>&1 | head  # увидишь проблему без истории
cd ..

# 2. Развернуть и сравнить
cd shallow-git
git fetch --unshallow
git log --oneline | wc -l        # теперь вся история
ls .git/shallow 2>&1             # файла больше нет
cd ..

# 3. Blobless clone и дозагрузка
git clone --filter=blob:none https://github.com/git/git.git partial-git
cd partial-git
git config remote.origin.partialclonefilter   # blob:none
git backfill                                   # прогреть blob батчами
cd ..

# 4. Bundle офлайн
cd partial-git
git bundle create ../all.bundle --all
git bundle verify ../all.bundle
cd ..
git clone all.bundle from-bundle
Сравни время и размер папок .git у shallow, partial и обычного клона - цифры закрепят разницу лучше любой таблицы.

Контрольные вопросы
  • Почему shallow clone ломает git blame и git describe, а blobless clone - нет? Что именно режет каждый?
  • Чем --single-branch отличается от --depth=1 по тому, что скачивается?
  • Какую проблему ленивой подгрузки решает git backfill и за счёт чего он быстрее, чем сами по себе ленивые дёрги?
  • В каком случае инкрементальный bundle откажется применяться и как это проверить заранее?
Итог

Клон - это не одна кнопка, а набор стратегий под задачу. shallow режет по времени и хорош только для эфемерного CI, потому что ломает всё, что ходит по истории. partial clone режет по содержимому, сохраняет корректный граф коммитов и тянет blob лениво - лучший выбор для больших рабочих копий. git backfill убирает боль одиночных подгрузок, качая blob батчами. bundle и bundle-uri решают офлайн-перенос и разгрузку сервера. Выбирай осознанно по тому, что именно и сколько тебе нужно скачать.
👍3 ❤️ 🔥1 😄 🤔2
Аватара пользователя
addict_denis
Сообщения: 1
Зарегистрирован: 14 май 2026, 05:00

Re: Клонирование: shallow, partial clone, bundle и backfill

Сообщение addict_denis »

Спасибо, наконец понял почему в CI blame показывал что весь файл написал один человек - это depth=1 виноват. Перешёл на blob:none и заработало.
👍3 ❤️1 🔥 😄 🤔
Аватара пользователя
meekoo
Сообщения: 1
Зарегистрирован: 14 май 2026, 20:42

Re: Клонирование: shallow, partial clone, bundle и backfill

Сообщение meekoo »

А git backfill можно гонять периодически по крону на рабочей машине, чтобы blob не тянулись по одному во время работы? Или он только сразу после клона имеет смысл?
👍 ❤️1 🔥 😄 🤔
Ответить
← Предыдущая глава
git push: безопасная отправка и защита от перезаписи
Следующая глава →
GitHub, GitLab и форки: pull request, rulesets и защита веток

Все главы курса «Git профессионально: от первого коммита до внутреннего устройства»

Поделиться темой: ✈ Telegram VK
  • Похожие темы
Похожие запросы: git clone: клонирование репозитория

Вернуться в «Git профессионально: от первого коммита до внутреннего устройства»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость