Удалённые репозитории: remote, fetch, базовый pull и refspec

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

Удалённые репозитории: remote, fetch, базовый pull и refspec

Сообщение 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

Локальный репозиторий - это вся история проекта у тебя на диске. Он самодостаточен: коммить, ветви, ходи по истории без интернета. Но как только над проектом работает больше одного человека (или хотя бы ты с двух машин), нужна общая точка, куда сходятся изменения. Эта точка - удалённый репозиторий git: ровно такой же репозиторий, как твой, только живёт он на сервере (GitHub, GitLab, Gitea, голый сервер по SSH) и доступен по сети.

Главная боль, которую решают remote-команды: как обмениваться коммитами между репозиториями не теряя контроль. Новичок часто живёт в режиме "нажал кнопку - что-то синхронизировалось", а потом ловит конфликты, перезатёртые чужие коммиты и панику. После этого урока ты будешь понимать, что именно скачивается, куда оно ложится и почему git fetch безопасен, а git pull - уже нет.

Заранее договоримся о легенде графа: кружок-узел - это коммит, стрелка идёт от коммита к его родителю, а ярлык вроде main, HEAD или origin/main - это указатель (ref), который просто показывает на конкретный коммит.

Изображение

Что такое remote и откуда берётся origin

Remote - это именованный URL другого репозитория. Никакой магии: Git хранит короткое имя (например, origin) и адрес, по которому к этому репозиторию ходить. Имя нужно, чтобы не писать длинный URL руками каждый раз.

Когда ты делаешь git clone, Git автоматически заводит remote с именем origin - это просто соглашение, "место, откуда я клонировал". Имя origin ничем не священно, его можно переименовать или удалить, но трогать без нужды не стоит: на него завязаны скрипты, привычки и документация.

Посмотрим, что есть:

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

$ git remote -v
origin  git@github.com:acme/webapp.git (fetch)
origin  git@github.com:acme/webapp.git (push)
Две строки на один remote - это не дубликат. У remote может быть разный URL для скачивания (fetch) и для отправки (push). На практике они почти всегда одинаковые, но Git показывает оба явно. Разными их делают, например, когда читают по https, а пишут по ssh.

Базовый набор команд для управления remote:

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

# добавить новый remote по имени
$ git remote add upstream git@github.com:original/webapp.git

# подробности про конкретный remote
$ git remote show origin

# переименовать
$ git remote rename origin gh

# удалить
$ git remote remove upstream

# сменить адрес (например, переехали с https на ssh)
$ git remote set-url origin git@github.com:acme/webapp.git
Команда git remote add - твой основной инструмент. Классический сценарий с upstream: ты форкнул чужой проект, твой форк стал origin, а оригинальный проект добавляешь как upstream, чтобы подтягивать оттуда свежак.

Разберём вывод git remote show origin - он недооценён, а пользы много:

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

$ git remote show origin
* remote origin
  Fetch URL: git@github.com:acme/webapp.git
  Push  URL: git@github.com:acme/webapp.git
  HEAD branch: main
  Remote branches:
    main     tracked
    feature/auth tracked
    stale/old stale (use 'git remote prune' to remove)
  Local branch configured for 'git pull':
    main merges with remote main
  Local ref configured for 'git push':
    main pushes to main (up to date)
Здесь сразу видно: какая ветка на сервере главная (HEAD branch), какие удалённые ветки Git знает, кто из них stale (уже удалена на сервере, а у тебя ссылка осталась), и как настроена связка локальной main с удалённой для pull и push. Это первое, что стоит смотреть, когда "что-то не туда пушится".

git fetch против git pull - ключевое различие

Это сердце урока, запомни намертво.

git fetch - скачивает из remote новые коммиты и обновляет remote-tracking ссылки (вроде origin/main), но не трогает твои рабочие ветки и рабочий каталог. После fetch твоя main осталась там же, где была. Ты просто узнал, что есть нового на сервере. Поэтому fetch абсолютно безопасен: его можно делать сколько угодно раз, он ничего не ломает и не порождает конфликтов.

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

$ git fetch origin
remote: Enumerating objects: 12, done.
remote: Counting objects: 100% (12/12), done.
Unpacking objects: 100% (8/8), done.
From github.com:acme/webapp
   a1b2c3d..f4e5d6c  main       -> origin/main
 * [new branch]      feature/pay -> origin/feature/pay
Читаем вывод. Строка

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

a1b2c3d..f4e5d6c  main -> origin/main
говорит: на сервере ветка main уехала вперёд, и теперь твой указатель origin/main показывает на f4e5d6c. Стрелка main -> origin/main - это куда легло скачанное: в remote-tracking ref, не в твою локальную main. Появилась и новая ветка feature/pay - Git завёл origin/feature/pay.

git pull - это не отдельная операция, а две подряд:

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

git fetch
, а затем

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

git merge
(или

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

git rebase
, если так настроено) скачанного в твою текущую ветку. То есть pull не только узнаёт новости, но и сразу вливает их в твою рабочую ветку и трогает рабочий каталог. Отсюда и риск: pull может породить merge-конфликт, создать merge-коммит или (при rebase) переписать твои локальные коммиты.

Формула простая:

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

git pull = git fetch + git merge origin/<ветка>
Практический вывод: если не уверен, что происходит на сервере - сначала git fetch, потом спокойно посмотри

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

git log --oneline main..origin/main
(что приехало нового), и только потом решай, мёржить или ребейзить. Тонкости pull --rebase, --ff-only и разбор конфликтов мы разбираем отдельно в уроке про rebase и историю - здесь нам важен сам принцип и базовый цикл.

Базовый ежедневный цикл: clone, commit, push, pull

Теперь собёрем рутину одиночки и небольшой команды. Это тот минимум, который ты будешь делать каждый день.

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

# 1. один раз: получить проект
$ git clone git@github.com:acme/webapp.git
$ cd webapp

# 2. работаешь: правишь файлы, потом фиксируешь
$ git add src/login.php
$ git commit -m "Fix login redirect"

# 3. отправляешь своё на сервер
$ git push

# 4. перед началом нового куска работы - забираешь чужое
$ git pull
Порядок commit -> push -> pull для одиночки часто схлопывается, но в команде правило золотое: перед push сделай pull (или хотя бы fetch), чтобы влить чужие изменения у себя локально, разрулить конфликты на своей машине, а уже потом пушить чистый результат. Если попробуешь запушить поверх чужих коммитов, которых у тебя нет, Git честно откажет:

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

$ git push
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'github.com:acme/webapp.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
Это не ошибка в твоих руках - это защита. Сервер говорит: "у меня есть коммиты, которых у тебя нет, сначала забери их". Делаешь git pull, разбираешься с возможным конфликтом, и push проходит. Никогда не лечи это через вслепую - так затирают чужую работу.

Отслеживаемые ветки, upstream и origin/main

Откуда git pull без аргументов знает, что и откуда тянуть? За это отвечает tracking branch (отслеживаемая ветка) и её upstream.

Сначала про природу origin/main. Это remote-tracking ref - локальный снимок того, где была ветка main на remote в момент последнего fetch. Он лежит в

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

.git/refs/remotes/origin/main
и обновляется только при fetch/pull/push. Двигать его руками ты не должен - это "память Git о сервере". Твоя локальная main лежит отдельно, в

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

.git/refs/heads/main
.

Связь "локальная main -> следит за origin/main" называется upstream. Благодаря ей работают

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

git pull
/

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

git push
без аргументов и удобный статус:

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

$ git status
On branch main
Your branch is ahead of 'origin/main' by 2 commits.
  (use "git push" to publish your local commits)
Git сравнил твою main с origin/main и сказал: у тебя на 2 коммита больше. Это и есть работа upstream-связи. При git clone upstream для основной ветки настраивается автоматически. А вот когда ты создаёшь новую ветку и пушишь её, upstream надо задать. Варианты:

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

# задать upstream существующей ветке
$ git branch -u origin/main
# или с явными именами
$ git branch --set-upstream-to=origin/feature/pay feature/pay

# задать upstream прямо при первом push
$ git push -u origin feature/pay
Флаг -u (он же

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

--set-upstream
) при первом push - самый частый способ. Если надоело каждый раз писать для новых веток, включи современную опцию:

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

$ git config --global push.autoSetupRemote true
Она доступна с Git 2.37, но по умолчанию в 2026 ещё выключена, так что включай руками - после этого простой

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

git push
на новой ветке сам заведёт upstream.

git fetch --prune - чистка устаревших remote-веток

Со временем ветки на сервере удаляют (смёржили фичу - стёрли feature/pay). Но твой локальный origin/feature/pay сам собой не исчезнет: fetch добавляет новое, а удалённое не убирает. Накапливается мусор из мёртвых remote-tracking ссылок - именно их git remote show помечал как stale.

Чистим:

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

$ git fetch --prune
From github.com:acme/webapp
 - [deleted]         (none)     -> origin/feature/pay
 - [deleted]         (none)     -> origin/stale/old
Строки

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

[deleted] (none) -> origin/feature/pay
- это удаление протухших указателей. Важно: --prune убирает только remote-tracking ссылки (твою память о сервере), он не трогает твои локальные ветки с теми же именами. Если у тебя была локальная feature/pay, она останется - её удаляй отдельно через

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

git branch -d
.

Чтобы не помнить про флаг, включи prune навсегда:

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

$ git config --global fetch.prune true
Теперь любой

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

git fetch
и

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

git pull
будет попутно подчищать устаревшее. Это безопасная и крайне рекомендуемая настройка.

Что такое refspec - механика под капотом

Мы говорили "fetch обновляет origin/main". Откуда Git знает это соответствие "ветка на сервере -> мой локальный ref"? Из refspec. Refspec - это правило отображения ссылок в формате:

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

[+]<src>:<dst>
где src - имя ссылки в remote, dst - куда её положить локально, а ведущий плюс означает "разрешить не-fast-forward обновление" (перезаписать, даже если история разошлась).

Загляни в

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

.git/config
после клонирования:

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

[remote "origin"]
    url = git@github.com:acme/webapp.git
    fetch = +refs/heads/*:refs/remotes/origin/*
Расшифруем

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

+refs/heads/*:refs/remotes/origin/*
: "возьми все ветки сервера из refs/heads/ и положи их под refs/remotes/origin/". Звёздочка - подстановка по имени. Плюс - потому что remote-tracking ссылки должны обновляться всегда, даже если на сервере историю переписали force-push'ем. Вот почему после fetch origin/main точно показывает на серверную main.

Refspec можно задать и в командной строке. Скачать только одну ветку в свой ref:

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

$ git fetch origin +refs/heads/main:refs/remotes/origin/main
Аналогично работает push:

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

git push origin main:production
значит "отправь мою локальную main в ветку production на сервере" (src слева - локальная, dst справа - удалённая). А

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

git push origin :feature/pay
с пустым src - это удаление ветки на сервере (отправить "ничто" в feature/pay).

Понимание refspec снимает кучу "магии": fetch, pull, push, prune - всё это операции над ссылками по правилам refspec.

git ls-remote - заглянуть на сервер не скачивая

Иногда нужно узнать, что есть на сервере, вообще ничего не скачивая в репозиторий. Для этого есть лёгкая команда git ls-remote - она просто спрашивает у сервера список его ссылок:

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

$ git ls-remote origin
f4e5d6c7...   HEAD
f4e5d6c7...   refs/heads/main
9a8b7c6d...   refs/heads/feature/auth
1122aabb...   refs/tags/v2.3.0
Слева - SHA коммита, справа - полное имя ссылки на сервере. Это удобно в скриптах и CI ("какой коммит сейчас в main на сервере?"), для проверки доступа (

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

git ls-remote git@host:repo.git
либо отвечает списком, либо падает на правах) и чтобы понять, существует ли ветка/тег, не делая полноценный fetch.

Типичные грабли
  • Думать, что fetch обновил рабочую ветку. Нет. После fetch твоя main на месте, новое лежит в origin/main. Чтобы влить -

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

    git merge origin/main
    или

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

    git pull
    .
  • Пытаться двигать origin/main руками. Это не твоя ветка, а память о сервере. Хочешь догнать сервер -

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

    git pull
    или

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

    git reset --hard origin/main
    на своей ветке, а не правка ref напрямую.
  • Не настроить upstream у новой ветки. Тогда голый

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

    git push
    ругается "no upstream". Лечится

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

    git push -u
    или опцией push.autoSetupRemote.
  • Удивляться "мёртвым" веткам в автодополнении. Это отсутствие prune. Включи fetch.prune.
  • Лечить "rejected (fetch first)" через --force. Так затирают чужие коммиты. Правильный путь -

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

    git pull
    , разрулить, потом push. Если переписывал историю осознанно -

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

    --force-with-lease
    , а не голый .
  • Путать локальную ветку и одноимённый origin/<имя>.

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

    git branch -d feature
    и

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

    git fetch --prune
    трогают разные вещи.
Мини-лаба: пройди руками

Сделаем "сервер" локально, без интернета, чтобы увидеть механику вживую.

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

# 1. создаём голый репозиторий - он сыграет роль сервера
$ git init --bare /tmp/server.git

# 2. клонируем его в рабочую копию
$ git clone /tmp/server.git /tmp/work && cd /tmp/work
$ git config init.defaultBranch main   # в 2026 ваниль ещё создаёт master

# 3. первый коммит и push
$ echo "hello" > readme.txt
$ git add readme.txt && git commit -m "init"
$ git branch -M main
$ git push -u origin main

# 4. смотрим remote и refspec
$ git remote -v
$ git config --get remote.origin.fetch

# 5. имитируем второго разработчика: второй клон
$ git clone /tmp/server.git /tmp/work2 && cd /tmp/work2
$ echo "from dev2" >> readme.txt
$ git commit -am "dev2 change" && git push

# 6. возвращаемся в первую копию и НЕ pull, а fetch
$ cd /tmp/work
$ git fetch
$ git status                       # ahead/behind относительно origin/main
$ git log --oneline main..origin/main   # что приехало, но ещё не влито

# 7. вливаем и проверяем
$ git pull
$ cat readme.txt                   # появилась строка from dev2

# 8. создаём и удаляем ветку на сервере, чистим prune
$ git push origin main:throwaway   # завели ветку throwaway
$ git push origin :throwaway       # удалили её
$ git fetch --prune                # видим [deleted] origin/throwaway
$ git ls-remote origin             # убеждаемся: на сервере её нет
После шага 6 обрати внимание: git status говорит "behind by 1", но readme.txt ещё старый - вот оно, доказательство что fetch не трогает рабочий каталог. После git pull файл меняется.

Контрольные вопросы
  • Чем git fetch отличается от git pull и почему fetch считают безопасным?
  • Что такое origin/main физически, где он лежит и когда обновляется?
  • Расшифруй refspec

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

    +refs/heads/*:refs/remotes/origin/*
    - что значат звёздочки и ведущий плюс?
  • Что именно удаляет git fetch --prune, а что он гарантированно не трогает?
Итог

Remote - это просто именованный URL чужого репозитория, origin - имя по умолчанию после clone. git fetch безопасно скачивает и обновляет remote-tracking ссылки вроде origin/main, не трогая твою работу; git pull - это fetch плюс merge/rebase, и он уже меняет твою ветку. Ежедневный цикл: clone, правки, commit, push, а перед push - pull. Tracking-ветка через upstream даёт магию pull/push без аргументов, fetch --prune чистит мёртвые ссылки, а refspec вида

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

+src:dst
- это правило, по которому весь обмен ссылками и работает. Понял refspec - понял remote целиком.
👍5 ❤️3 🔥3 😄 🤔
Аватара пользователя
svelte_lord
Сообщения: 1
Зарегистрирован: 14 май 2026, 09:18

Re: Удалённые репозитории: remote, fetch, базовый pull и refspec

Сообщение svelte_lord »

Наконец-то дошло почему после fetch ничего не поменялось в файлах. Я всегда думал что это баг и бежал делать pull. А оно просто в origin/main лежит, спасибо за лабу с /tmp/server.git - руками сразу видно.
👍1 ❤️1 🔥 😄 🤔
Аватара пользователя
ret321
Сообщения: 1
Зарегистрирован: 22 май 2026, 09:48

Re: Удалённые репозитории: remote, fetch, базовый pull и refspec

Сообщение ret321 »

Вопрос: если включить fetch.prune true глобально, оно может случайно снести мою локальную ветку с тем же именем что удалили на сервере? Или origin/feature и локальная feature это реально две независимые штуки?
👍 ❤️1 🔥 😄 🤔
Ответить
← Предыдущая глава
Практикум: твой первый день с Git от нуля до push
Следующая глава →
git push: безопасная отправка и защита от перезаписи

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

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

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

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

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