Push и pull
git push отправляет коммиты на удалёнку; git pull — это fetch плюс интеграция. Задай upstream через push -u origin branch, потом голые push и pull работают. Push отклоняется как non-fast-forward, когда на удалёнке есть коммиты, которых нет у тебя, — сначала pull.
Ты коммитишь работу, набираешь git push, а git огрызается: fatal: The current branch feature has no upstream branch. Ты добавляешь -u origin feature, срабатывает, и с этого момента достаточно голого git push. Позже push падает с ! [rejected] ... (non-fast-forward), и тебе велят сначала сделать pull. Эти два момента — установка upstream и отклонение — и есть весь ежедневный ритм работы с удалёнкой.
К концу этого урока ты сможешь уверенно пушить и пуллить, настраивать отслеживание ветки и объяснять, почему push отклоняется и что с этим делает pull.
После этого урока ты сможешь использовать git push и git pull, задавать upstream через git push -u, читать отслеживание через git branch -vv и объяснять, почему non-fast-forward push отклоняется и как предварительный pull это решает.
git push загружает коммиты с твоей локальной ветки на ветку удалёнки. Он берёт коммиты, существующие только в твоём репозитории, копирует их на сервер, затем продвигает ветку удалёнки (и твою локальную origin/main) под это состояние. Полная форма называет remote и ветку:
git push origin main # запушить локальную main в main удалёнки originPush — зеркальное отражение fetch: fetch тянет коммиты сервера вниз, push отправляет твои вверх. Ни тот, ни другой не трогают твои рабочие файлы.
Задай upstream один раз через -u, и тогда push и pull не требуют аргументов. Набирать origin main каждый раз — шум. Первый push ветки может записать постоянную связь — её upstream (также называемый отношением отслеживания, tracking):
git push -u origin feature # запушить И запомнить origin/feature как upstream
git push # отныне этого достаточно
git pull # и этого тоже-u — сокращение от --set-upstream. После него твоя локальная feature отслеживает origin/feature, и git знает, куда должны идти голые push/pull.
git branch -vv показывает, что отслеживает каждая ветка и насколько она разошлась. Когда push и pull перестают вести себя ожидаемо, это первая команда, к которой стоит тянуться:
git branch -vv
# * feature 9af1c2b [origin/feature: ahead 2] add retry logic
# main 1d4e7f0 [origin/main] release prep[origin/feature: ahead 2] означает, что feature отслеживает origin/feature и имеет 2 ещё не запушенных коммита. Ветка без [...] не имеет upstream — это ровно то состояние, что вызывает ошибку «no upstream branch».
git pull — это git fetch, за которым следует шаг интеграции, по умолчанию merge. Pull не примитив; это две операции в одной упаковке:
git pull origin main
# = git fetch origin (обновить origin/main)
# затем git merge origin/main в твою текущую веткуТак что pull одновременно обновляет твою удалённо-отслеживающую ветку и вносит новые коммиты удалёнки в твою локальную ветку. Интеграция по умолчанию — merge; следующий урок показывает альтернативы и зачем они могут понадобиться.
▸Почему это работает
Почему свежий push отклоняется как non-fast-forward? Git позволяет ветке на удалёнке двигаться только вперёд — на коммит, у которого текущая вершина является предком («fast-forward»). Если тем временем кто-то запушил, на удалёнке у main есть коммит, которого нет в твоей истории, поэтому замена её твоей вершиной выкинула бы их коммит. Git отказывается. Лекарство — сделать pull (получить их коммит и слить или ребейзнуть его в свой), чтобы твоя ветка теперь строилась поверх их, — тогда твой push становится чистым fast-forward.
Запушить новую ветку — это просто создать её на удалёнке. Удалёнке не нужно, чтобы ветка существовала заранее; твой push её создаёт:
git switch -c hotfix # новая локальная ветка
# ...коммитишь работу...
git push -u origin hotfix # создаёт origin/hotfix и настраивает отслеживаниеЭтот push новой ветки всегда fast-forward (на удалёнке там не было ничего конфликтующего), поэтому он никогда не отклоняется по причинам non-fast-forward.
Отклонённый push, разобранный по шагам.
Ты и коллега оба стартуете от origin/main на коммите c10. Ты коммитишь c11 локально. Тем временем коллега коммитит c12 и пушит его, так что main сервера теперь на c12. Ты запускаешь git push:
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to 'origin'
hint: Updates were rejected because the remote contains work that you
hint: do not have locally. ... integrate the remote changes (e.g. 'git pull')Git отказывается, потому что сдвиг main сервера с c12 на твой c11 молча стёр бы c12. Ты запускаешь git pull: он получает c12 (продвигая origin/main) и сливает его с твоим c11, создавая merge-коммит c13, у которого оба родителя. Теперь твоя локальная main (c13) содержит c12 как предка, поэтому git push — это fast-forward и проходит. Работа коллеги сохранена, и твоя тоже.
▸Частая ошибка
Когда push отклонён, опасный рефлекс — тянуться к git push --force, чтобы «продавить». Force перезаписывает удалёнку и реально удаляет c12 коллеги. На общей ветке это потеря данных. Правильный ответ на отклонение non-fast-forward почти всегда — сделать pull (или fetch + rebase) и запушить снова. У force-push есть законные применения — о них через два урока — но реакция на обычное отклонение не из их числа.
Твой git push отклонён с '(non-fast-forward)'. Что это говорит об удалёнке и какой правильный следующий шаг?
git push отправляет твои локальные коммиты вверх на ветку удалёнки; git pull — это git fetch плюс шаг интеграции (по умолчанию merge), который вносит коммиты удалёнки вниз в твою ветку. Первый push ветки стоит делать через git push -u origin <ветка>, чтобы записать связь upstream/tracking, после чего голые push и pull знают, куда идти; git branch -vv показывает это отслеживание и насколько ты впереди или позади. Push отклоняется как non-fast-forward, когда на удалёнке есть коммиты, которых нет у тебя, — лекарство в том, чтобы сначала сделать pull, а не force. Дальше ты отделишь получение от интеграции и будешь осознанно выбирать между merge и rebase.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.