Почему бы не запушить прямо в main? Потому что тогда сломанная сборка и спорное решение попадают ко всей команде раньше, чем их кто-то увидел, а откатывать дороже, чем не пускать. Pull request (в GitLab — merge request) — это предложение влить вашу ветку в основную, оформленное на сервере: с описанием, обсуждением, автопроверками и кнопкой слияния. Вокруг PR построен командный контроль качества: чужие глаза (код-ревью) и автоматика (CI-конвейер) смотрят на изменение до того, как оно попадёт в общий код.
На схеме кнопку слияния держат две проверки сразу: зелёный конвейер и одобрение человека. Так настроена ветка main в этом репозитории — сам по себе pull request ничего не запрещает. Замечание не отменяет PR — правка приезжает в ту же ветку новым коммитом и попадает в то же обсуждение. При squash три коммита ветки уходят в main одним.
Жизненный цикл PR
- Запушили ветку → открыли PR «моя ветка → main».
- Автоматика гоняет сборку, тесты, линтеры; коллеги читают код и оставляют замечания.
- Вы дорабатываете: новые коммиты в ту же ветку автоматически попадают в PR.
- Одобрение (approve) → слияние → ветка удаляется.
Оговорка, которая экономит нервы: сам по себе pull request ничего не запрещает. В репозитории без настроек кнопка слияния активна сразу — можно слить собственную ветку, не дожидаясь ни ревьюеров, ни тестов. Требования «сначала зелёный конвейер, потом два одобрения» задаёт защита ветки (branch protection) — настройка репозитория, которую включает владелец. Поэтому в одной команде кнопка серая до утверждения, а в соседней активна всегда: дело не в PR, а в том, как настроен main.
git push -u origin feature-export-csv # ветка появилась на сервере
# в ответе push — ссылка «create a pull request», по ней открывается форма
git push # доработки едут в тот же PR
Ещё не готово, но нужен ранний взгляд — откройте draft PR: обсуждение уже возможно, кнопка слияния заблокирована.
Самый частый затык случается на третьем дне ревью: пока вы дорабатывали, main ушёл вперёд, и сервер пишет «ветка устарела» или требует обновить её перед слиянием. Кнопка «Update branch» делает git merge main в вашу ветку на сервере: появляется merge-коммит, ваши коммиты остаются как были, комментарии ревьюеров на местах. Вариант «Update with rebase» и ручной git rebase main дают прямую историю, но переписывают ваши коммиты, а значит потребуют push --force-with-lease и отвяжут построчные комментарии от строк. Правило то же, что в статье про rebase: пока PR не обсуждали построчно, rebase; когда обсуждают, кнопка с merge. Если конфликт, сервер предложит решить его в браузере, но надёжнее сделать это локально: git fetch && git merge origin/main, разобрать, запушить.
Оформление: уважение к времени читателя
Ревьюеру предстоит понять ваше изменение без вашего контекста. Заголовок говорит, что делает PR, в одну строку и часто с номером задачи: «PROJ-142: экспорт заказов в CSV». Описание говорит, что и зачем изменено и как проверить, со скриншотами для UI и обязательной ссылкой на задачу.
Главный фактор качества ревью — размер. PR на 200 строк читают внимательно, на 2000 «одобряют глазами», поэтому большую задачу режут на цепочку маленьких PR. И перед назначением ревьюеров сами пройдитесь по вкладке изменений: забытый отладочный вывод, закомменченный код, случайный файл дешевле поймать самому. В терминале та же вкладка это git diff main...feature-export с тремя точками: сравнение не с текущим main, а с точкой, где ветка от него отошла, то есть ровно ваши изменения без чужих, приехавших в main позже; две точки показали бы и их.
Большую задачу режут на цепочку не только в голове, но и в Git: вторую ветку отводят от первой, а не от main, и открывают PR «вторая → первая». Ревьюер второго PR видит только его изменения, а не всё накопленное. Сливают цепочку с начала: после слияния первой ветки цель второго PR переключают на main (GitHub делает это сам) и при необходимости перебазируют. Так задача на две тысячи строк превращается в пять PR по четыреста, и каждый читают внимательно.
Форму описания фиксируют шаблоном: файл .github/pull_request_template.md (в GitLab .gitlab/merge_request_templates/) подставляется в каждое новое описание. В него кладут ровно то, о чём этот раздел: что и зачем изменено, как проверить, ссылка на задачу, отметки «тесты добавлены», «миграция есть». Шаблон не заставляет думать, но не даёт забыть.
Как проходить ревью
Замечания к коду — не оценка вас; это второй контур того же контроля качества, что и тесты. На каждое замечание отвечают: поправили — «сделано» и коммит, не согласны — аргументируйте, потому что молча проигнорированное замечание это потерянное доверие. Если не согласны, обсуждайте, а не продавливайте: два раунда переписки без сближения — повод созвониться, голосом это решается за минуты.
У ревьюера три способа завершить просмотр, и они значат разное. Комментарий это мнение без последствий, кнопку слияния он не трогает. Запросить изменения блокирует слияние, пока автор не доработает и ревьюер не снимет блок: так помечают то, без чего вливать нельзя. Одобрить это голос «за», и защита ветки считает именно такие голоса. Отсюда правило автора: замечание в виде комментария можно закрыть аргументом, запрошенное изменение только правкой или явным согласием ревьюера. Кого звать в ревьюеры, часто решает не автор, а файл CODEOWNERS: в нём по путям записано, чья это область, и сервер сам назначает владельцев затронутых каталогов, а защита ветки может требовать одобрения именно от них.
Доработки идут новыми коммитами, не перезаписью: ревьюеру важно видеть, что изменилось после его замечаний, а прибраться в истории можно перед самым слиянием. И конвейер должен быть зелёным до того, как вы позовёте ревьюеров: просить людей читать код, который не собирается, невежливо.
Ревью в обратную сторону — тоже навык: комментируйте код, а не автора («здесь потеряется ошибка», а не «ты потерял ошибку»), предлагайте, а не приказывайте, отмечайте хорошее.
Три способа слитьспросят на собеседовании
Кнопка слияния обычно предлагает выбор, и разница в том, что останется в истории main. Merge commit вливает историю ветки как есть и добавляет merge-коммит: полная летопись, но main зарастает мелкими «wip»-коммитами. Squash and merge схлопывает все коммиты ветки в один аккуратный: одна задача — один коммит, частый выбор в командах, которым важна читаемая лента main. Rebase and merge переносит коммиты ветки в main по одному, без merge-коммита, прямой линией; как и при обычном rebase, переносятся не сами коммиты, а их копии с новыми идентификаторами, так что ваша локальная ветка после слияния расходится с main, и её проще удалить и взять свежий main, чем синхронизировать.
Что выбирать — командное соглашение из модели ветвления. Практическое следствие squash: не полируйте промежуточные коммиты ветки сверх разумного — в main всё равно уедет один. Второе следствие всплывает при уборке: после squash-слияния Git не считает вашу ветку влитой — в main лежит один новый коммит, ваших там формально нет. git branch -d feature-export ответит error: the branch 'feature-export' is not fully merged, и удалять придётся через git branch -D.
Одна и та же ветка из трёх коммитов даёт три разные ленты main: merge сохраняет все коммиты и добавляет коммит слияния, squash сводит их в один, rebase кладёт копии в линию.
Коротко
- PR = ветка + описание + обсуждение + автопроверки + кнопка слияния. Draft — для раннего взгляда.
- Маленький PR с внятным описанием «что и зачем» — половина качества ревью. Самопроверка диффа перед назначением ревьюеров.
- Кнопку слияния держат зелёный конвейер и одобрение человека — но только если так настроена защита ветки; без настроек PR можно слить самому себе.
- На замечания отвечать все; доработки — новыми коммитами в ту же ветку и то же обсуждение, нового PR не нужно; споры дольше двух раундов — голосом.
- Слияние: merge (летопись), squash (задача = коммит), rebase (прямая линия) — по соглашению команды.
mainушёл вперёд: кнопка «Update branch» делает merge и сохраняет комментарии, rebase даёт прямую историю, но требует force-push и отвязывает построчные замечания.- Свой дифф в терминале это
git diff main...featureс тремя точками; большую задачу режут цепочкой PR «ветка от ветки»; форму описания задаётpull_request_template.md. - Комментарий не блокирует, «запросить изменения» блокирует до правки, «одобрить» считает защита ветки; ревьюеров по путям назначает
CODEOWNERS.
Что почитать дальше
- Удалённые репозитории — push ветки на сервер, с которого начинается PR.
- Модели ветвления — как PR встраивается в процесс команды.
- Rebase и cherry-pick — приборка истории ветки перед слиянием.
- CI/CD-конвейер — автопроверки, которые гоняются на каждый PR.