Медленный git clone и push: что проверить
Git редко тормозит сам по себе. Обычно время уходит на размер репозитория, на сеть или на конкретный протокол. Начните с измерений, а не с советов из чатов: так вы поймёте, что именно чинить.
Сначала измерьте
Посмотрите, на каком этапе задержка: разрешение имени, установка соединения, TLS или передача данных.
curl -o /dev/null -s -w 'dns %{time_namelookup} connect %{time_connect} tls %{time_appconnect} ttfb %{time_starttransfer}\n' https://github.comВ Windows вызывайте curl.exe: в Windows PowerShell слово curl может оказаться псевдонимом другой команды с иным синтаксисом.
Для доступа по SSH проверьте время рукопожатия: time ssh -T git@github.com. Если ответ приходит быстро, сеть до хостинга в порядке, и узкое место в объёме данных. Скорость показывает и сам git: строка Receiving objects при clone и fetch выводит текущую скорость загрузки.
Если clone долго висит сразу после строки Cloning into и прогресса нет, проблема в соединении или аутентификации. Если прогресс идёт, но медленно, дело в канале или в объёме репозитория. Сравните и с обычной загрузкой: если крупный файл через curl качается быстро, а git еле ползёт, узкое место не в канале, а в размере репозитория или на стороне сервера.
Подробный лог git
Если непонятно, где зависает операция, включите трассировку. Это обычные переменные окружения:
GIT_TRACE=1 GIT_CURL_VERBOSE=1 git fetch # HTTPS: запросы и ответы
GIT_SSH_COMMAND="ssh -v" git fetch # SSH: этапы соединения
GIT_TRACE2_PERF=1 git status # время каждого шагаВ PowerShell переменную задают отдельной командой: $env:GIT_TRACE="1", затем git. По логу видно, висит ли git на соединении, на согласовании недостающих объектов или на самой передаче. Перед тем как выкладывать лог в чат, проверьте, что в нём нет токенов.
Частые ошибки и что они значат
| Сообщение | Причина | Что делать |
|---|---|---|
| ssh: connect to host github.com port 22: Connection timed out | Порт 22 закрыт в этой сети | SSH через порт 443 или HTTPS |
| error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly | Сбой HTTP/2 на прокси или нестабильном канале | Временно переключиться на HTTP/1.1 |
| error: RPC failed; curl 56 Recv failure: Connection reset by peer | Соединение сброшено посреди передачи | Уменьшить объём загрузки, проверить канал |
| fatal: early EOF, fatal: index-pack failed | Поток данных оборвался до конца | Частичный или неглубокий клон, затем докачка |
| fetch-pack: unexpected disconnect while reading sideband packet | Разрыв во время передачи объектов | Меньше данных за раз и стабильный канал |
Если большой клон раз за разом обрывается на early EOF, скачайте его частями: сначала git clone --depth 1, затем докачивайте историю командой git fetch --deepen=1000 или сразу git fetch --unshallow. Каждый шаг короче, и обрыв не отменяет уже скачанное.
Переключение на HTTP/1.1 делается одной настройкой. Это диагностический шаг: если ошибка ушла, проблема в посреднике по пути, а не в git.
git config --global http.version HTTP/1.1
# вернуть как было:
git config --global --unset http.versionСкачивайте меньше
git clone --depth 1 <url>скачивает только последний коммит и по умолчанию только одну ветку. Подходит для CI и разовых сборок. Если позже понадобится история, выполнитеgit fetch --unshallow.git clone --filter=blob:none <url>получает все коммиты и деревья, а содержимое файлов докачивает по требованию. Удобно для больших репозиториев с долгой историей и для ежедневной работы.git clone --filter=tree:0 <url>экономит ещё больше, ноgit logпо отдельным файлам иgit blameбудут постоянно ходить в сеть. Этот режим лучше оставить для CI.- В монорепозитории добавьте к клону флаг
--sparseи выберите нужные каталоги:git sparse-checkout set <путь>. В рабочей копии будут только они. - Если в репозитории тысячи веток и тегов, ограничьте загрузку:
git clone --single-branch --branch main --no-tags <url>. В существующем клонеgit fetch origin mainскачает только одну ветку.
Частичный клон должен поддерживать сервер. GitHub и GitLab это умеют, на собственном сервере проверьте настройку uploadpack.allowFilter.
Проверьте и настройки CI. В GitHub Actions шаг actions/checkout по умолчанию делает клон глубиной 1, а fetch-depth: 0 включает полную историю и замедляет каждый запуск. В GitLab CI глубину задаёт переменная GIT_DEPTH. Полная история нужна только задачам, которые читают теги или считают версии по коммитам.
SSH через порт 443
В некоторых сетях порт 22 закрыт или работает медленно. GitHub и GitLab принимают SSH на порту 443, достаточно добавить в ~/.ssh/config:
Host github.com
HostName ssh.github.com
Port 443
User git
Host gitlab.com
HostName altssh.gitlab.com
Port 443
User gitПроверьте подключение: ssh -T -p 443 git@ssh.github.com. Адрес удалённого репозитория менять не нужно, ssh сам подставит нужный хост и порт.
Если вы много раз подряд делаете fetch и push, ускорит работу повторное использование одного SSH-соединения. Добавьте в тот же блок Host github.com:
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 10mПервая команда открывает соединение, следующие идут через него без нового рукопожатия. Встроенный OpenSSH в Windows этот режим не поддерживает.
Большие файлы и история
Посмотрите размер репозитория: git count-objects -vH, строка size-pack. Если в истории лежат бинарные файлы, новые выносите в Git LFS. Старые коммиты от этого не похудеют, но клоны с --filter перестанут тащить содержимое всех версий сразу. Самые тяжёлые объекты в истории найдёт такая команда:
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| sed -n 's/^blob //p' | sort -k2 -n -r | head -20Переписывать историю ради размера стоит только по договорённости со всей командой: у всех изменятся хеши коммитов.
Файлы Git LFS скачиваются отдельно от основного клона. Если в CI бинарники не нужны, задайте переменную GIT_LFS_SKIP_SMUDGE=1: в рабочей копии останутся только небольшие файлы-указатели.
Про http.postBuffer
Совет увеличить http.postBuffer встречается часто, но скорость он не меняет. Параметр влияет только на отправку по HTTPS: с большим буфером git отправляет данные одним запросом известной длины вместо потоковой передачи. Документация git прямо говорит, что это нужно лишь для серверов и прокси, которые не поддерживают потоковую передачу, а в остальных случаях только тратит память. Если push не падает с ошибкой вроде HTTP 411, оставьте значение по умолчанию.
Медленный push обычно означает много новых объектов. Если git подолгу стоит на Compressing objects или Writing objects, проверьте, не попали ли в коммит артефакты сборки, архивы или node_modules. Лучшее лечение здесь .gitignore, а не настройки сети.
Если тормозит не сеть, а рабочая копия
Бывает, что clone прошёл быстро, а git status и checkout идут долго. Это уже вопрос файловой системы:
- На macOS и Windows с Git 2.37 и новее включите встроенный монитор файловой системы:
git config core.fsmonitor trueиgit config core.untrackedCache true. Git перестанет заново сканировать все каталоги при каждомgit status. - Для очень больших рабочих копий есть
git config feature.manyFiles true: он включает набор настроек под репозитории с сотнями тысяч файлов. - На Windows заметную часть времени съедает антивирусная проверка каждого записанного файла. Для рабочих каталогов разработчика Microsoft предлагает Dev Drive.
git maintenance startвключает фоновое обслуживание: предварительную загрузку обновлений и упаковку объектов, после чегоgit fetchпроходит быстрее.
Если упирается в канал
Когда измерения показывают долгое соединение и низкую скорость при нормальном размере репозитория, дело в маршруте до хостинга. Для команды и CI поможет внутреннее зеркало: настройка insteadOf прозрачно перенаправит запросы на него.
git config --global url."https://git.example.internal/mirror/".insteadOf "https://github.com/"Если хостинг доступен и по IPv6, сравните curl -4 и curl -6 с теми же параметрами. Когда IPv6 заметно медленнее, проблема в его маршруте у провайдера. Для SSH можно оставить только IPv4 строкой AddressFamily inet в блоке Host.
Для личной работы выберите локацию подключения ближе к серверам хостинга и сравните результат той же командой curl.
Коротко
- Измерьте этапы через
curl -wи посмотрите скорость в строкеReceiving objects. - Для CI используйте
--depth 1, для ежедневной работы--filter=blob:none. - Порт 22 закрыт: SSH через
ssh.github.comилиaltssh.gitlab.comна порту 443. - Ошибки HTTP/2 проверьте переключением на
HTTP/1.1. http.postBufferскорость не повышает.- Медленный
git statusлечитсяcore.fsmonitorиcore.untrackedCache, а не сетью.