Медленный git clone и push: что проверить

, 7 мин чтения

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, а не сетью.

Стабильный канал для работы

Один ключ, 20 стран, до 5 устройств на Standard. Возврат в течение 7 дней.

Все тарифы