Pull request — это предложение слить ветку с вашими изменениями в основную ветку проекта: на GitHub вокруг него собираются описание, обсуждение, ревью и автоматические проверки. Порядок такой: создать ветку, закоммитить, отправить её командой git push -u origin ветка, открыть pull request с понятным описанием, ответить на замечания новыми коммитами и после одобрения слить. Без прав записи в репозиторий сначала делают форк — свою копию проекта.
После материала вы сможете
- отправить ветку на удалённый репозиторий и открыть pull request с описанием, связанной задачей и чек-листом
- выбрать модель работы: ветка в общем репозитории или форк с remote upstream
- ответить на замечания ревью новыми коммитами и обновить ветку через merge или rebase с
--force-with-lease - объяснить, что дают защита ветки, обязательные проверки, SSH-ключи и fine-grained токены
Что знать заранее
Проверено на
Git 2.54 (Alpine Linux 3.24)OpenSSH 10.3markdownlint-cli2 0.23
Сверено с документацией: 11 октября 2026 г.
Содержание
- Удалённый репозиторий: remote, push, fetch, pull
- Форк или ветка в общем репозитории
- Ветка и pull request: описание, связанная задача, чек-лист
- Код-ревью: как давать и принимать замечания
- Обновление ветки: merge или rebase
- Слияние pull request и уборка
- Защита ветки и обязательные проверки
- SSH-ключ или токен: безопасный доступ
Удалённый репозиторий: remote, push, fetch, pull
Локальный репозиторий из урока о Git живёт только на вашем компьютере. Чтобы работать в команде, нужен удалённый репозиторий (remote) — копия на сервере, с которой все синхронизируются. Удалённым репозиториям дают короткие имена: основной по традиции называется origin. На GitHub адрес выглядит как https://github.com/user/repo.git или, для SSH, git@github.com:user/repo.git.
Ниже весь сценарий воспроизведён локально: роль GitHub играет «голый» репозиторий без рабочей папки (git init --bare), а вместо URL — путь к нему. Команды Git при этом те же, что и с GitHub. Сначала руководитель проекта публикует репозиторий, затем разработчица Айгерим клонирует его:
$ git remote add origin ../../server/coffee-site.git
$ git remote -v
origin ../../server/coffee-site.git (fetch)
origin ../../server/coffee-site.git (push)
$ git push -u origin main
To ../../server/coffee-site.git
* [new branch] main -> main
branch 'main' set up to track 'origin/main'.
$ cd ../../aigerim
$ git clone ../server/coffee-site.git
Cloning into 'coffee-site'...
done.
$ cd coffee-site
$ git branch -vv
* main c390cc0 [origin/main] Initial landing page-u связывает локальную ветку с удалённой: дальше хватает git push и git pull без аргументов.git clone URL
- Что делает
- Скачивает репозиторий целиком и настраивает remote
origin
git push -u origin ветка
- Что делает
- Отправляет коммиты ветки на сервер и запоминает связь
git fetch
- Что делает
- Забирает новые коммиты с сервера в
origin/…, не трогая ваши ветки
git pull
- Что делает
fetchплюс слияние в текущую ветку
git remote -v
- Что делает
- Показывает, куда смотрят удалённые репозитории
Сервер принимает push, только если ваша ветка продолжает его историю. Если кто-то успел отправить свои коммиты, push будет отклонён — сначала нужно забрать чужие изменения:
$ git switch main
Switched to branch 'main'
Your branch is behind 'origin/main' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)
$ git commit -am "Mention contacts in README"
[main f307c0e] Mention contacts in README
1 file changed, 1 insertion(+)
$ git push origin main
To /w2/aigerim/../server/coffee-site.git
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to '/w2/aigerim/../server/coffee-site.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.Форк или ветка в общем репозитории
Документация GitHub описывает две модели совместной работы. Выбор зависит от того, есть ли у вас права записи в репозиторий.
Общий репозиторий (shared repository)
- Все участники имеют право push в один репозиторий
- Каждая задача — отдельная ветка, pull request открывается внутри репозитория
- Типично для небольших команд и закрытых проектов
Форк и pull request (fork and pull)
- Вы создаёте форк — собственную копию чужого репозитория — и пушите туда
- Pull request открывается из ветки форка в исходный репозиторий (upstream)
- Типично для open source: права у владельца просить не нужно
Форк создаётся кнопкой Fork на странице репозитория или командой gh repo fork. Дальше вы клонируете свой форк, а исходный репозиторий добавляете вторым remote — по соглашению из документации GitHub он называется upstream. Синхронизация форка — это fetch из upstream, слияние и push в свой форк:
$ git clone ../server/aigerim-fork.git coffee-site
Cloning into 'coffee-site'...
done.
$ cd coffee-site
$ git remote add upstream ../../server/coffee-site.git
$ git remote -v
origin /w/forkwork/../server/aigerim-fork.git (fetch)
origin /w/forkwork/../server/aigerim-fork.git (push)
upstream ../../server/coffee-site.git (fetch)
upstream ../../server/coffee-site.git (push)
$ git fetch upstream
From ../../server/coffee-site
* [new branch] main -> upstream/main
$ git merge upstream/main
Updating 6cb64ae..ee2dd24
Fast-forward
README.md | 1 +
1 file changed, 1 insertion(+)
$ git push origin main
To /w/forkwork/../server/aigerim-fork.git
6cb64ae..ee2dd24 main -> maingit clone --bare); на GitHub его создаёт кнопка Fork.Ветка и pull request: описание, связанная задача, чек-лист
Работа начинается с ветки от свежего main. Одна ветка — одна задача: так pull request остаётся небольшим и его реально проверить.
$ git switch -c feature/contact-form
Switched to a new branch 'feature/contact-form'
$ git add contact.html
$ git commit -m "Add contact form page"
[feature/contact-form 76e59a8] Add contact form page
1 file changed, 7 insertions(+)
create mode 100644 contact.html
$ git push -u origin feature/contact-form
To /w/aigerim/../server/coffee-site.git
* [new branch] feature/contact-form -> feature/contact-form
branch 'feature/contact-form' set up to track 'origin/feature/contact-form'.
$ git status
On branch feature/contact-form
Your branch is up to date with 'origin/feature/contact-form'.
nothing to commit, working tree cleangit add).Откройте репозиторий на GitHub
После push GitHub показывает жёлтую плашку с кнопкой Compare & pull request для свежей ветки.
Выберите ветки
base — куда сливать (обычно
main), compare — ваша ветка. Для форка нажмите compare across forks и выберите head fork.Напишите заголовок и описание
Заголовок — что делает изменение. Описание — по шаблону ниже: что сделано, как проверить, скриншоты.
Создайте pull request или черновик
Create Pull Request — сразу на ревью. Create Draft Pull Request — черновик: его нельзя слить, и владельцы кода не получают автоматический запрос ревью.
Чтобы каждый pull request начинался с одинаковой структуры, положите шаблон в репозиторий — GitHub подставит его в описание автоматически. Строка Closes # с номером задачи свяжет PR с issue: по документации, при слиянии в ветку по умолчанию связанная задача закроется сама (ключевые слова работают, только если PR нацелен на ветку по умолчанию).
## Что сделано
Кратко: какую задачу решает pull request и что изменилось для пользователя.
Closes #
## Как проверить
1. Переключитесь на ветку и запустите проект.
2. Откройте страницу или выполните сценарий: …
3. Ожидаемый результат: …
## Скриншоты
Для изменений интерфейса — до и после (desktop и mobile).
## Чек-лист автора
- [ ] Ветка обновлена от `main`, конфликтов нет
- [ ] Проверки (сборка, тесты, линтер) проходят
- [ ] В коде нет ключей, паролей и персональных данных
- [ ] Документация и `.env.example` обновлены, если нужно
- [ ] Pull request решает одну задачуТот же pull request можно создать из терминала через GitHub CLI — флаги из официального руководства gh pr create:
gh pr create --base main --title "Add contact form page" \
--body-file .github/pull_request_template.md --draftКод-ревью: как давать и принимать замечания
Ревьюер смотрит вкладку Files changed, оставляет комментарии к строкам и завершает ревью одним из трёх решений. Пока ревью не отправлено, комментарии видны только автору ревью — их можно править.
Comment
- Значение по документации GitHub
- Общая обратная связь без явного одобрения или запроса изменений
Approve
- Значение по документации GitHub
- Изменения готовы к слиянию
Request changes
- Значение по документации GitHub
- Замечания, которые автор должен учесть до слияния
- Изменение решает задачу из описания и ничего лишнего
- Проверки (сборка, тесты, линтер) зелёные
- Понятны имена, нет мёртвого и закомментированного кода
- Обработаны ошибки и пустые состояния, а не только «счастливый путь»
- Нет секретов, паролей и персональных данных; ввод пользователя проверяется
- Для интерфейса: проверены мобильная версия и доступность с клавиатуры
- Каждое замечание объясняет «почему» и предлагает вариант
Автору важно помнить: замечание касается кода, а не человека. Исправления делаются новыми коммитами в ту же ветку — pull request обновится сам, а ревьюер увидит, что изменилось с прошлого раза. Мелкую правку ревьюер может прислать как suggestion: автор применяет её кнопкой Commit suggestion, и GitHub создаёт коммит в ветке PR.
$ git diff
diff --git a/contact.html b/contact.html
index d909d01..b60a3dc 100644
--- a/contact.html
+++ b/contact.html
@@ -2,6 +2,6 @@
<title>Contacts</title>
<form method="post" action="/api/lead">
<label>Name <input name="name"></label>
- <label>Phone <input name="phone" type="tel"></label>
+ <label>Phone <input name="phone" type="tel" required></label>
<button>Send</button>
</form>
$ git commit -am "Make phone field required after review"
[feature/contact-form f9d0d45] Make phone field required after review
1 file changed, 1 insertion(+), 1 deletion(-)
$ git push
To /w/aigerim/../server/coffee-site.git
76e59a8..f9d0d45 feature/contact-form -> feature/contact-formОбновление ветки: merge или rebase
Пока шло ревью, в main появились чужие коммиты. Ветку стоит обновить до слияния — так конфликты и падения тестов проявятся заранее. GitHub умеет сделать это кнопкой Update branch (слиянием или через Update with rebase), а локально есть два способа.
$ git fetch origin
From /w/aigerim/../server/coffee-site
c390cc0..08e9c80 main -> origin/main
$ git log --oneline --graph --all
* f9d0d45 Make phone field required after review
* 76e59a8 Add contact form page
| * 08e9c80 Update opening hours
|/
* c390cc0 Initial landing page
$ git merge origin/main -m "Merge origin/main into feature/contact-form"
Merge made by the 'ort' strategy.
index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git push
To /w/aigerim/../server/coffee-site.git
f9d0d45..4b8ff1d feature/contact-form -> feature/contact-form$ git fetch origin
From /w2/aigerim/../server/coffee-site
c390cc0..08e9c80 main -> origin/main
$ git rebase origin/main
Rebasing (1/1)Successfully rebased and updated refs/heads/feature/contact-form.
$ git push
To /w2/aigerim/../server/coffee-site.git
! [rejected] feature/contact-form -> feature/contact-form (non-fast-forward)
error: failed to push some refs to '/w2/aigerim/../server/coffee-site.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
$ git push --force-with-lease
To /w2/aigerim/../server/coffee-site.git
+ aea2fe5...0d33d81 feature/contact-form -> feature/contact-form (forced update)
$ git log --oneline --graph --all
* 0d33d81 Add contact form page
* 08e9c80 Update opening hours
* c390cc0 Initial landing pagemerge
- Историю не переписывает, обычный
git push - Появляется коммит слияния
- Безопасно, если в ветке работает несколько человек
rebase
- Линейная история без коммита слияния
- Коммиты пересоздаются — нужен
git push --force-with-lease - Только для своей ветки, на которую никто не опирается
Слияние pull request и уборка
Когда ревью одобрено и проверки прошли, человек с правом записи нажимает Merge. Способ слияния настраивается в репозитории:
Create a merge commit
- Что попадает в main
- Все коммиты ветки и коммит слияния; по документации — слияние с опцией
--no-ff
Squash and merge
- Что попадает в main
- Один коммит со всеми изменениями; промежуточные коммиты в main не видны
Rebase and merge
- Что попадает в main
- Коммиты ветки по одному поверх main, без коммита слияния; GitHub создаёт новые хеши
$ git fetch origin
From ../../server/coffee-site
* [new branch] feature/contact-form -> origin/feature/contact-form
$ git merge --no-ff origin/feature/contact-form -m "Merge feature/contact-form: contact form page"
Merge made by the 'ort' strategy.
contact.html | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 contact.html
$ git push
To ../../server/coffee-site.git
08e9c80..6cb64ae main -> mainПосле слияния автор обновляет свой main и удаляет ветку — локально и на сервере (на GitHub для удалённой ветки есть кнопка Delete branch прямо в pull request):
$ git switch main
Switched to branch 'main'
Your branch is behind 'origin/main' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)
$ git pull
From /w/aigerim/../server/coffee-site
08e9c80..6cb64ae main -> origin/main
Updating c390cc0..6cb64ae
Fast-forward
contact.html | 7 +++++++
index.html | 2 +-
2 files changed, 8 insertions(+), 1 deletion(-)
create mode 100644 contact.html
$ git branch -d feature/contact-form
Deleted branch feature/contact-form (was 4b8ff1d).
$ git push origin --delete feature/contact-form
To /w/aigerim/../server/coffee-site.git
- [deleted] feature/contact-form
$ git log --oneline --graph -7
* 6cb64ae Merge feature/contact-form: contact form page
|\
| * 4b8ff1d Merge origin/main into feature/contact-form
| |\
| |/
|/|
* | 08e9c80 Update opening hours
| * f9d0d45 Make phone field required after review
| * 76e59a8 Add contact form page
|/
* c390cc0 Initial landing pageЗащита ветки и обязательные проверки
Чтобы в main нельзя было попасть в обход ревью, владелец репозитория включает правило защиты ветки (Settings → Branches) или ruleset — набор правил; в отличие от классического правила, на одну ветку могут действовать несколько rulesets сразу. По документации GitHub, каждое правило по умолчанию запрещает force push в защищённую ветку и её удаление. Основные настройки:
- Require pull request reviews before merging — нужное число одобрений; опционально — снимать одобрение после новых коммитов и требовать ревью владельцев кода (CODEOWNERS).
- Require status checks before merging — сливать можно только при зелёных проверках CI; в строгом режиме ветка ещё и должна быть обновлена от base.
- Require conversation resolution before merging — все обсуждения в PR должны быть закрыты.
- Require linear history — запрет коммитов слияния: только squash или rebase.
- Do not allow bypassing the above settings — по умолчанию администраторы могут обходить правила; эта настройка распространяет их и на администраторов.
Если попытаться слить в защищённую ветку pull request с незавершённым или отклонённым ревью, документация приводит такой ответ сервера: remote: error: GH006: Protected branch update failed for refs/heads/main. Доступность защиты зависит от тарифа: в публичных репозиториях она есть на бесплатном плане, в приватных — начиная с GitHub Pro и Team.
SSH-ключ или токен: безопасный доступ
При push по HTTPS GitHub не принимает пароль от аккаунта: документация прямо говорит, что парольная аутентификация для Git удалена. Вместо пароля вводят персональный токен доступа (personal access token) или используют менеджер учётных данных, например Git Credential Manager. Второй путь — SSH-ключ: пара файлов, где закрытый ключ остаётся на компьютере, а открытый добавляется в настройки GitHub.
ssh-keygen -t ed25519 -C "your_email@example.com"$ git remote -v
origin https://github.com/YOUR-USERNAME/coffee-site.git (fetch)
origin https://github.com/YOUR-USERNAME/coffee-site.git (push)
$ git remote set-url origin git@github.com:YOUR-USERNAME/coffee-site.git
$ git remote -v
origin git@github.com:YOUR-USERNAME/coffee-site.git (fetch)
origin git@github.com:YOUR-USERNAME/coffee-site.git (push)- GitHub рекомендует fine-grained токены вместо classic: доступ только к нужным репозиториям и правам, со сроком действия.
- С токеном обращаются как с паролем: не вставляют в код, в URL репозитория и в сообщения.
- Закрытый SSH-ключ (
id_ed25519без.pub) никому не передают; на GitHub загружают только.pub. - Утёкший токен или ключ сразу отзывают в настройках GitHub и выпускают новый.
Как проверено4 · пройдено1 · частично2 · не запускалосьсверено 11 октября 2026 г.
- запущено, работает
Сценарий с remote: git init --bare как «GitHub», remote add, push -u, clone, branch -vv, ветка + push -u, правка после ревью + push, fetch и обновление ветки merge, слияние --no-ff (эквивалент Create a merge commit), pull, удаление ветки локально и на сервере, форк (clone --bare) + remote upstream + синхронизация
docker run --rm alpine/git:latest (Git 2.54.0, Alpine 3.24, bash 5.3) — транскрипт-раннер в /w, блоки кода вырезаны скриптом из транскрипта (code.mjs); пути вида /w/aigerim/../server/coffee-site.git — реальные пути внутри контейнера. Локальный Git Bash 2.55 не подошёл: push в bare-репозиторий во временной папке падал с Filename too long (MAX_PATH Windows)
- запущено, работает
Обновление ветки через rebase: отклонённый обычный push и git push --force-with-lease; отклонённый push в ушедший вперёд main
отдельный прогон того же сценария в alpine/git:latest (Git 2.54.0), реальный вывод
- запущено, работает
Шаблон .github/pull_request_template.md (RU и EN)
markdownlint-cli2 0.23.3 (markdownlint 0.41.1): 0 замечаний при отключённом MD041 (первая строка не H1 — заголовок PR задаётся отдельно). Подстановка шаблона на GitHub не проверялась (NOT_RUN — веб)
- запущено, работает
git remote set-url с HTTPS на SSH
alpine/git:latest, реальный вывод git remote -v до и после
- частично
ssh-keygen -t ed25519 -C "your_email@example.com"
OpenSSH 10.3p1 в alpine/git: проверен неинтерактивный эквивалент с -f/-N/-q (ключ создан во временном контейнере и уничтожен вместе с ним); интерактивный ввод парольной фразы не запускался; добавление ключа на GitHub и ssh -T git@github.com не выполнялись
- не запускалось
gh pr create --base main --title … --body-file … --draft
флаги сверены с cli.github.com/manual/gh_pr_create и docs.github.com (Creating a pull request); не запускалось, чтобы ничего не создавать на реальном GitHub (и gh недоступен в этом окружении)
- не запускалось
Веб-шаги GitHub: Fork, Compare & pull request, draft, ревью (Comment/Approve/Request changes, suggestions), Update branch, Merge/Squash/Rebase, Delete branch, защита веток и rulesets, GH006
описаны по docs.github.com на дату проверки; на реальном GitHub ничего не создавалось
«Запущено» — код выполнен во временном проекте или контейнере, вывод в статье взят из прогона. «Частично» — проверено с заглушками вместо внешних сервисов. «Не запускалось» — нужны устройство, аккаунт или ключ; шаги сверены с официальной документацией. Файлы проверочного проекта и логи запусков сохранены в архиве InterLAB.