Урок 04 · Одно понятие

origin, push и pull: две истории и обмен между ними

На твоём компьютере лежит полная история проекта. На GitHub лежит полная история проекта. Между ними нет ни одной автоматической связи.

~15 минутнужен урок 2вводить в терминал ничего не надо

Зачем это в твоей миссии

PR стоит в целях курса, но подступиться к нему нельзя, пока работа лежит только у тебя на диске. Этот урок — про то, как она туда попадает и почему иногда отказывается попадать.

Облачный диск и Git

Начать стоит с привычки, которая здесь мешает. Облачные диски приучили к одной модели: положил файл в папку — он сам уехал на сервер, сам появился на других устройствах, сам обновился, когда его изменили. Ты в этом не участвуешь.

Git устроен наоборот, и это не недоделка, а замысел. Его придумали для работы без постоянной связи: в поезде, в самолёте, на даче с одной палочкой сигнала. Поэтому вся история проекта целиком лежит у тебя на диске, и работать с ней можно, ни с кем не соединяясь.

Отсюда главное следствие, из-за которого потом возникают все недоразумения:

Если запомнить одно

Ничего никуда не уезжает само. Коммит остаётся на твоём компьютере до тех пор, пока его вручную не отправят. Агент может сделать десять коммитов за час — на GitHub при этом не появится ни одного.

Две полные копии

Слово «сервер» сбивает с толку: кажется, что там лежит что-то главное, а у тебя — рабочая копия. На самом деле обе стороны устроены одинаково.

У тебя на дискеНа GitHub
История коммитовполнаяполная
Все веткидада
Работает без второй стороныдада
Файлы развёрнуты в папкуданет, только история

Единственное реальное отличие — в последней строке. У тебя проект развёрнут в папку, которую видно в Finder. На GitHub развёрнутых файлов нет, там только хранилище; то, что ты видишь в браузере, — это отрисовка содержимого по запросу.

«Главный» репозиторий главный не технически, а по договорённости. Команда просто условилась считать копию на GitHub точкой сборки. Git об этой договорённости ничего не знает.

origin: кличка, а не место

У удалённого хранилища есть адрес — что-то вроде git@github.com:ykurilov/myapp.git. Писать его в каждой команде неудобно, поэтому адресу дают короткое имя. По традиции — origin.

Слово ничего не означает само по себе. Это не «главный», не «облако» и не «сервер» вообще. Это кличка конкретного адреса, записанная в настройках твоего проекта. Её можно поменять, а хранилищ можно подключить несколько: скажем, origin — твоя копия, upstream — чужая, из которой ты её сделал.

Откуда кличка берётся: когда проект скачивают командой git clone, Git сам записывает адрес, откуда качал, под именем origin. Отсюда и название — «источник», то место, откуда всё пришло.

Обмен по шагам

Дальше проще смотреть, чем читать. Пройди историю по шагам: слева твой компьютер, справа GitHub. Зелёным подсвечены коммиты, которых нет на другой стороне.

push и pull

Две команды, и обе всегда запускают вручную.

КомандаЧто делаетКуда двигаются коммиты
git pushотправляет твои коммиты в удалённое хранилищеот тебя → на GitHub
git pullзабирает оттуда чужие коммиты и вливает в твою веткус GitHub → к тебе

Запомнить направление помогает буквальный перевод: push — толкнуть от себя, pull — потянуть к себе.

Важная деталь про push: он отправляет коммиты, а не файлы из папки. Всё, что не закоммичено, не уедет никуда, сколько ни отправляй. Если агент сказал «запушил», а на GitHub пусто — почти наверняка он не сделал коммит.

origin/main: твоя память о сервере

Вот место, где ломаются почти все — потому что сущностей оказывается три, а не две.

В твоей истории живут две метки с похожими именами:

МеткаЧто означает
mainгде находится твоя работа
origin/mainгде ветка main была на сервере в момент последней связи

Обрати внимание на формулировку: не «где она сейчас», а «где была». origin/main лежит на твоём диске и обновляется только тогда, когда ты соединяешься с сервером. Это не сама удалённая ветка, а твоя запись о ней.

Как фотография доски в переговорке. Ты сфотографировал — снимок лежит в телефоне. Пока не зайдёшь в переговорку снова, ты не узнаешь, что на доске дописали ещё три строчки. Снимок при этом не врёт: он честно показывает, что было в момент съёмки.

Поэтому фраза «на GitHub этого нет, я только что смотрел» ничего не доказывает: смотреть надо было не в свою память, а на сервер.

fetch: разведка без последствий

Сходить и посмотреть, ничего у себя не меняя, — это git fetch. Он идёт на сервер, обновляет метку origin/main и на этом останавливается. Твоя ветка, твои файлы, твоя папка — нетронуты.

А git pull — это два действия подряд:

git pull  =  git fetch        ← сходить и обновить память
           + слить с твоей веткой  ← вот тут меняются файлы

Разница практическая. fetch безопасен всегда: он только приносит сведения. pull лезет в твою работу и может упереться в конфликт, если вы с кем-то правили одно и то же место.

Отсюда безопасный порядок

Сначала посмотреть, что появилось на сервере, и только потом решать, забирать ли. Агенту это формулируется одной фразой: «сначала fetch, покажи, что прилетело, потом решим». Он всё равно почти всегда предложит сразу pull — это не ошибка, просто привычная короткая дорога.

Отклонённый push

Рано или поздно агент упрётся в это и напишет что-то вроде ! [rejected] main -> main (fetch first).

Читается так: на сервере есть коммиты, которых нет у тебя. Кто-то — коллега, второй агент, ты сам из другого worktree — успел отправить свою работу раньше.

Git отказывается не из вредности. Если бы он молча принял твою отправку, чужие коммиты пришлось бы куда-то деть, а деть их можно только выбросив. Отказ — это защита чужой работы.

Правильный выход всегда один и тот же: сначала забрать чужое к себе, разобраться, как оно уживается с твоим, и только потом отправлять. Как именно Git соединяет две разошедшиеся линии — тема следующего урока.

Force push и стёртая работа

У отказа есть способ обхода: git push --force. Он говорит серверу «выброси то, что у тебя, и запиши то, что у меня».

Работает безотказно. Проблема ровно в слове «выброси»: чужие коммиты исчезают с сервера, и человек, который их отправил, узнаёт об этом не сразу.

СитуацияForce
Твоя личная ветка, над которой больше никто не работаетобычное дело, особенно после переписывания истории
Ветка, где сидит второй агент или коллегачужая работа пропадёт
main или любая общая ветканикогда

Практический вывод для тебя: это первый кандидат в список запретов для агента. Не потому, что агент злонамеренный, а потому, что --force — самый короткий способ убрать препятствие, и модель на него натыкается естественным образом.

Формулировка, которая экономит нервы

В правилах проекта (CLAUDE.md, AGENTS.md) стоит прописать прямо: «push --force запрещён на main и на любых общих ветках. Если push отклонён — остановись и скажи мне, ничего не форсируй». Без этой строчки запрет существует только у тебя в голове.

Как сказать агенту

Работа сделана, надо выложить

Закоммить изменения и запушь ветку feature/pay в origin. Ветку main не трогай.

Два действия названы отдельно, потому что это и есть два действия. И явно сказано, куда не надо — иначе агент может решить, что работу пора вливать в основную ветку.

Долго не заходил в проект

Сделай fetch и покажи, что появилось на origin с моего последнего захода. Ничего пока не сливай.

Первая фраза приносит сведения, вторая удерживает агента от слияния. Без неё он почти наверняка сделает pull сразу.

Push отклонён

Не форсируй. Покажи, какие коммиты есть на origin и нет у меня, и кто их автор.

Отказ — это сигнал, что рядом кто-то работал. Сначала выясняем кто и что, потом решаем. Слово «не форсируй» стоит говорить вслух каждый раз, пока оно не прописано в правилах проекта.

Кажется, работа пропала

Проверь, есть ли мои коммиты на origin. Если их там нет — они остались только у меня локально?

Половина паник про «всё пропало» — это работа, которая просто не уехала. Вопрос ставит проверку раньше выводов.

Чего просить не стоит

Формулировка «синхронизируй с гитхабом» кажется удобной, но у неё нет однозначного смысла: это может быть push, может быть pull, а может быть и то и другое с попыткой всё склеить. Агент выберет сам, и не всегда так, как ты думал. Говори направление: отправь туда или забери оттуда.

Проверь себя

Первоисточники к этому уроку

Pro Git → Работа с удалёнными репозиториями — на русском, официальная книга по Git. Там же про несколько удалённых хранилищ и переименование.

Справка git-push — раздел про отклонённую отправку и про то, чем --force-with-lease отличается от простого --force.

GitHub → Основы Git — короткое введение от самих GitHub, с их терминологией.

Если где-то сложно — скажи, на каком именно шаге. Например: «не понял, чем origin/main отличается от main» или «не понял, почему push отклоняется». Разберу это место отдельно, с другим примером.