open atlas
↑ К треку
Git: от нуля до сеньора GIT · 05 · 02

Push и pull

git push отправляет коммиты на удалёнку; git pull — это fetch плюс интеграция. Задай upstream через push -u origin branch, потом голые push и pull работают. Push отклоняется как non-fast-forward, когда на удалёнке есть коммиты, которых нет у тебя, — сначала pull.

GIT Junior ◷ 16 min
Уровень
ОсновыJuniorMiddleSenior

Ты коммитишь работу, набираешь 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 это решает.

1

git push загружает коммиты с твоей локальной ветки на ветку удалёнки. Он берёт коммиты, существующие только в твоём репозитории, копирует их на сервер, затем продвигает ветку удалёнки (и твою локальную origin/main) под это состояние. Полная форма называет remote и ветку:

git push origin main      # запушить локальную main в main удалёнки origin

Push — зеркальное отражение fetch: fetch тянет коммиты сервера вниз, push отправляет твои вверх. Ни тот, ни другой не трогают твои рабочие файлы.

2

Задай 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.

3

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».

4

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.

5

Запушить новую ветку — это просто создать её на удалёнке. Удалёнке не нужно, чтобы ветка существовала заранее; твой 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-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.