Команда 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 clone branch через флаг --branch (короткий -b):
Код: Выделить всё
git clone -b release-2.54 https://github.com/git/git.git
Код: Выделить всё
git clone -b release-2.54 --single-branch https://github.com/git/git.git
Shallow clone: git clone depth и чем платим
Поверхностное клонирование - это shallow clone. Идея: взять не всю историю, а последние N коммитов. Флаг git clone depth:
Код: Выделить всё
git clone --depth=1 https://github.com/git/git.git
Под капотом Git создаёт файл .git/shallow со списком SHA коммитов, у которых история искусственно обрезана. Эти коммиты выглядят как корневые, хотя на сервере у них есть родители. Посмотреть границу:
Код: Выделить всё
$ git log --oneline
e83c516 (HEAD -> master, origin/master) Sync with maint
$ cat .git/shallow
e83c516331d4...
Теперь чем платим - и это место, где ломаются вещи. Без истории не работает то, что эту историю обходит:
- git blame показывает дичь. Команде нужно дойти до коммита, который последним менял строку. Если он за линией отреза, blame упирается в shallow-границу и припишет авторство граничному коммиту. Сидишь и не понимаешь, почему весь файл написал один человек в один день.
- git describe врёт или падает. Он считает расстояние до ближайшего тега по предкам. Тегов за отрезом нет - либо ошибка, либо номер версии из пальца.
- git merge-base не находит общего предка между ветками, если он остался по ту сторону отреза. Отсюда раздутые или просто неверные диффы при сравнении и слиянии.
Код: Выделить всё
git fetch --depth=50
Код: Выделить всё
git fetch --unshallow
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
Более агрессивный вариант - tree:0, не качать ни деревья, ни blob (treeless clone):
Код: Выделить всё
git clone --filter=tree:0 https://github.com/git/git.git
Чем blobless лучше shallow: ты сохраняешь полный граф коммитов. Поэтому blame, describe, merge-base корректны - им нужны коммиты, а не содержимое старых blob. За правильность платишь редкими подгрузками blob вместо вранья в истории. Для рабочей копии большого монорепо blobless clone почти всегда лучше, чем shallow.
Проверить, что репозиторий частичный, можно через конфиг и promisor remote:
Код: Выделить всё
$ git config remote.origin.partialclonefilter
blob:none
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
Связка получается такая: клонируешь blobless (быстро, мало трафика на старте), сразу работаешь с полным графом коммитов, а в фоне или перед тяжёлой операцией прогреваешь нужные blob через backfill несколькими сетевыми вызовами. Это золотая середина между полным клоном и постоянными ленивыми дёргами. Помни: команда экспериментальная, флаги могут поменяться - держи Git свежим и сверяйся с git help backfill.
Подмодули: --recurse-submodules
Если в репозитории есть подмодули (вложенные репозитории, привязанные к конкретным коммитам через .gitmodules), обычный clone их не наполняет - папки будут пустыми. Чтобы сразу клонировать и проинициализировать всё дерево:
Код: Выделить всё
git clone --recurse-submodules https://example.com/super.git
Код: Выделить всё
git submodule update --init --recursive
Иногда сети между двумя машинами просто нет. Закрытый контур, перенос на флешке, бэкап истории, отправка репозитория по почте. Для этого есть git bundle - упаковка репозитория (или его части) в один файл, который переносится как угодно и из которого можно клонировать и фетчить, как с обычного remote.
Создать bundle со всей историей всех веток:
Код: Выделить всё
git bundle create repo.bundle --all
Код: Выделить всё
git clone repo.bundle myrepo
Код: Выделить всё
git bundle create update.bundle main~20..main
Код: Выделить всё
$ 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
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
Что и сколько скачивается: сводка
- Полный 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
Контрольные вопросы
- Почему 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 решают офлайн-перенос и разгрузку сервера. Выбирай осознанно по тому, что именно и сколько тебе нужно скачать.