Концепция remote: что это и зачем
До этого момента мы работали с локальным репозиторием — .git/ в твоей файловой системе. Но Git создан для распределённой работы. Remote — это именованный указатель на копию этого же репозитория где-то ещё: на GitHub, GitLab, корпоративном сервере или даже на соседнем диске.
В этом уроке мы разберёмся, что такое remote с точки зрения Git (это не сервер, это запись в конфиге), почему именно origin стал стандартом, как работает связка origin + upstream для форков, и что такое remote-tracking branches вида origin/main — те самые, которые ты постоянно видишь в git status.
Mental model: remote — это закладка
Когда ты слышишь “remote репозиторий”, в голове рисуется сервер. Это правда, но не полная. Для самого Git remote — это просто запись в .git/config: имя + URL + правила синхронизации refs.
То есть remote — это алиас. Вместо того чтобы каждый раз писать полный URL [email protected]:user/repo.git, ты используешь короткое имя origin. Git берёт это имя, лезет в .git/config, находит URL и идёт туда.
Remote можно потрогать руками. Открой .git/config в любом проекте, который ты клонировал — там увидишь блок [remote "origin"]. Это весь “remote” со стороны Git.
Команды: посмотреть и добавить
git remote -v
Самая частая команда — показать все remotes текущего репозитория с их URL.
$ git remote -v
origin [email protected]:acme/data-pipelines.git (fetch)
origin [email protected]:acme/data-pipelines.git (push)
Заметь: каждый remote показан дважды — для fetch (откуда читать) и push (куда писать). В 99% случаев это один и тот же URL. Но Git позволяет иметь разные — например, читать с GitHub-зеркала, а писать в основной GitLab.
git remote add
Добавить новый remote:
$ git remote add origin [email protected]:user/repo.git
$ git remote -v
origin [email protected]:user/repo.git (fetch)
origin [email protected]:user/repo.git (push)
git remote remove и git remote rename
$ git remote rename origin github
$ git remote remove old-server
Заметь: всё это локальные операции. Они меняют только твой .git/config. Никакого “удаления с сервера” не происходит.
Почему origin — стандарт
Когда ты пишешь git clone <url>, Git создаёт remote с именем origin автоматически. Это не магическая константа — это просто дефолт для команды clone.
$ git clone [email protected]:acme/repo.git
$ cd repo
$ git remote -v
origin [email protected]:acme/repo.git (fetch)
origin [email protected]:acme/repo.git (push)
Можно сменить дефолт через флаг -o:
$ git clone -o github [email protected]:acme/repo.git
$ git remote -v
github [email protected]:acme/repo.git (fetch)
github [email protected]:acme/repo.git (push)
Но не делай так. origin — конвенция, и ломать её — значит создавать когнитивную нагрузку всем, кто увидит твой репо. Все инструменты, туториалы и скрипты ожидают origin.
Multi-remote setup: origin + upstream при форке
Самый частый случай нескольких remote — работа с fork на GitHub/GitLab.
Сценарий: ты хочешь контрибьютить в open-source проект apache/airflow. Ты не можешь пушить туда напрямую — у тебя нет прав. Поэтому:
- Делаешь fork через UI GitHub -> получаешь
your-name/airflow. - Клонируешь свой fork локально.
- Добавляешь оригинальный репо как второй remote — обычно с именем
upstream.
Команды:
# Клонировал свой fork
$ git clone [email protected]:your-name/airflow.git
$ cd airflow
# Добавил оригинал как upstream
$ git remote add upstream [email protected]:apache/airflow.git
$ git remote -v
origin [email protected]:your-name/airflow.git (fetch)
origin [email protected]:your-name/airflow.git (push)
upstream [email protected]:apache/airflow.git (fetch)
upstream [email protected]:apache/airflow.git (push)
Теперь ты:
- Получаешь обновления из оригинала:
git fetch upstream->git merge upstream/main(илиrebase). - Пушишь свою feature ветку в свой fork:
git push origin feature/my-fix. - Открываешь PR из
your-name/airflow:feature/my-fix->apache/airflow:mainчерез UI.
Часто бывает: пушнул сразу в upstream (или попытался). Получил 403 Permission denied. Это потому что у тебя нет прав в оригинальном репо. Пушить в open-source — это про fork + PR, а не про прямой push в apache/*.
Remote-tracking branches: origin/main
Самая частая путаница у джунов — что такое origin/main. Это не remote-ветка. Это локальная ссылка вида “что я в последний раз видел в main на origin при fetch”.
Где они лежат физически?
$ ls .git/refs/heads/
main feature-x
$ ls .git/refs/remotes/origin/
HEAD main develop
Видишь? refs/heads/main — твоя локальная ветка. refs/remotes/origin/main — снимок того, что ты видел на сервере при последнем fetch. Это два разных ref, указывающих на разные коммиты.
Когда git status пишет:
Your branch is ahead of 'origin/main' by 3 commits.
Это значит: твоя локальная main ушла на 3 коммита вперёд относительно того, что ты последний раз скачал с сервера. Может быть, на сервере уже 5 новых коммитов — Git не знает, пока ты не сделаешь git fetch.
Ключевая мысль: origin/main обновляется только командами fetch, pull или push. Это не “живой указатель на сервер”. Это закладка. Поэтому привычка делать git fetch перед git status — здоровая.
Refspec: как ветки маппятся
В блоке [remote "origin"] ты видишь строчку:
fetch = +refs/heads/*:refs/remotes/origin/*
Это refspec — правило: “когда я делаю git fetch origin, забери все ветки с сервера (refs/heads/*) и сохрани их локально под именами refs/remotes/origin/*”.
Звёздочка раскрывается как glob: ветка main на сервере -> origin/main локально, develop -> origin/develop и так далее.
Знак + означает “разрешить force-update” — то есть если на сервере историю переписали через force-push, твой origin/main обновится без ошибки. Без + Git ругался бы на non-fast-forward.
В быту тебе не нужно править refspec вручную. Просто знай: вот так Git понимает, что чему соответствует.
Практика: посмотри свой remote
Открой любой склонированный репо и пройди по чек-листу:
# 1. Что есть в remote?
git remote -v
# 2. Какие remote-tracking branches у меня?
git branch -r
# 3. Все ветки сразу — локальные + remote
git branch -a
# 4. Полная инфо про remote
git remote show origin
Команда git remote show origin сходит на сервер и покажет очень полезное:
$ git remote show origin
* remote origin
Fetch URL: [email protected]:acme/data-pipelines.git
Push URL: [email protected]:acme/data-pipelines.git
HEAD branch: main
Remote branches:
main tracked
develop tracked
feature/x tracked
Local branches configured for 'git pull':
main merges with remote main
develop merges with remote develop
Local refs configured for 'git push':
main pushes to main (up to date)
Это полный отчёт: какие ветки есть на сервере, какие из них ты трекаешь, какая HEAD branch (default branch проекта).
Попробуй сам
# Создай локальный репо
mkdir remote-demo && cd remote-demo
git init
echo "hello" > README.md
git add . && git commit -m "init"
# Добавь несуществующий remote (Git не проверяет — это просто запись в config)
git remote add origin [email protected]:fake/repo.git
git remote -v
# Посмотри config
cat .git/config
# Удали remote
git remote remove origin
cat .git/config
Заметь: git remote add не делает запросов в сеть. Ты можешь добавить remote с любым URL — Git проверит его, только когда ты сделаешь fetch/push.
curl, wget: HTTP-запросы из командной строки