GitHub, GitLab и форки: pull request, rulesets и защита веток

Рейтинг: 72.2% · 10 голосов
Самый подробный курс по Git - от первого коммита до объектной базы и packfiles. Терминал и первый репозиторий, ветки и слияние, конфликты, удалённая работа и форджи (GitHub, GitLab), rebase и переписывание истории, спасение кода через reflog и fsck, stash и worktree, bisect и blame, подмодули и монорепо, хуки и подпись коммитов, внутреннее устройство и масштаб. Для новичков и профи. Актуально на 2026 (Git 2.54, дорожная карта 3.0).
Ответить
Аватара пользователя
Pavel_Git
Сообщения: 52
Зарегистрирован: 11 май 2026, 05:31

GitHub, GitLab и форки: pull request, rulesets и защита веток

Сообщение Pavel_Git »

Оглавление курса (52)
  1. Что такое контроль версий и почему победил Git
  2. Установка Git на Windows, macOS и Linux и первая настройка
  3. Терминал и как спрашивать у Git: выживание в командной строке
  4. Ментальная модель Git: три состояния, коммит-снимок и граф
  5. Первый репозиторий: init, add, commit, status
  6. Просмотр изменений: status, diff и индексация по частям
  7. История проекта: git log, диапазоны и поиск
  8. .gitignore: что не должно попасть в репозиторий
  9. .gitattributes: окончания строк, бинарники и атрибуты
  10. Ветки как указатели: branch, switch и detached HEAD
  11. Слияние веток: fast-forward против трёхстороннего merge
  12. Конфликты слияния: анатомия и уверенное разрешение
  13. Практикум: твой первый день с Git от нуля до push
  14. Удалённые репозитории: remote, fetch, базовый pull и refspec
  15. git push: безопасная отправка и защита от перезаписи
  16. Клонирование: shallow, partial clone, bundle и backfill
  17. GitHub, GitLab и форки: pull request, rulesets и защита веток (вы здесь)
  18. Рабочие процессы команды: GitFlow, GitHub Flow, trunk-based
  19. Merge queue и merge trains: безопасное вливание в trunk
  20. Культура code review и Conventional Commits
  21. git reset: три дерева и отмена индексации и коммитов
  22. git revert: безопасная отмена в общей истории
  23. Правка последнего коммита: git commit --amend
  24. git rebase: пересадка истории и золотое правило
  25. Интерактивный rebase: переписать серию коммитов
  26. git cherry-pick: перенос отдельных коммитов
  27. Stacked diffs: цепочка зависимых PR
  28. Массовое переписывание истории: git filter-repo и git replace
  29. git reflog: журнал, который помнит всё
  30. Восстановление утерянных коммитов, веток и файлов
  31. Откат опасных операций: reset --hard, сломанный rebase, плохой merge
  32. git stash: тайник для незавершённой работы
  33. git worktree: несколько рабочих деревьев одного репозитория
  34. git bisect: бинарный поиск регрессии
  35. git blame, mailmap и археология кода
  36. Теги, релизы, archive и bundle
  37. Подмодули: внешние репозитории внутри проекта
  38. Subtree, монорепозитории и sparse-checkout
  39. Git LFS и большие файлы
  40. Вспомогательные команды: clean, mv, rm, restore
  41. Псевдонимы и кастомизация .gitconfig
  42. Хуки Git: автоматизация на клиенте и сервере
  43. Подпись коммитов и тегов: SSH, GPG и Sigstore
  44. Внутреннее устройство I: объектная база Git
  45. Внутреннее устройство II: ссылки, HEAD, notes и reftable
  46. Внутреннее устройство III: индекс, packfiles, gc и обслуживание
  47. От SHA-1 к SHA-256: переход на новый хеш
  48. Огромные репозитории: partial clone, FSMonitor, Scalar
  49. Git в CI/CD: shallow, кеш, merge_group и безопасность
  50. Куда движется Git: дорожная карта 3.0 и экосистема
  51. Профессиональный setup: собираем рабочее окружение Git
  52. 30+ типичных ошибок Git и как из них выбираться
До этого урока ты работал с Git наедине с собой: коммиты, ветки, rebase, история. Но Git задумывался как распределённая система, и вся его сила раскрывается, когда над одним кодом работают десятки людей, часть из которых ты в глаза не видел. Тут на сцену выходят форджи - GitHub, GitLab, Gitea/Forgejo, Bitbucket. Они не часть Git. Это веб-надстройки вокруг голого git, которые добавляют то, чего в самом Git нет: pull request, ревью, права доступа, CI, защита веток. И вот тут новичка ждёт первая боль: ты сделал коммит, нажал push, а в ответ "remote: error: GH006: Protected branch update failed". Кажется, что Git сломался. Нет. Это форж сказал тебе "в main напрямую нельзя, иди через pull request". Этот урок - про то, как устроен этот слой и как с ним жить, не воюя.

Форж - это не Git, и почему это важно

Запомни главное разделение. Git - это движок: объекты, ссылки, протокол передачи pack-файлов. Форж - это сервис поверх него. GitHub, GitLab и остальные говорят с твоим Git по тому же протоколу, что и любой другой remote: ты делаешь fetch и push, а на той стороне крутится тот же git receive-pack. Но форж навешивает свои правила на момент приёма: проверяет, кто ты, можно ли тебе писать в эту ветку, прошёл ли CI. Поэтому защита веток, pull request и CODEOWNERS - это НЕ команды Git. Их нет в git --help, ты не найдёшь их в .git. Это политики форжа, которые исполняются на сервере во время push и merge.

Из этого следует практический вывод: одни и те же концепции в разных форжах называются по-разному. На GitHub - pull request (PR), на GitLab и Gitea/Forgejo - merge request (MR), смысл идентичный: "прошу влить мою ветку в твою". Forgejo - это форк Gitea (после смены лицензии Gitea в 2022), полностью self-hosted и в моде у тех, кто не хочет зависеть от Microsoft. Bitbucket - корпоративный мир Atlassian, тесно сцеплен с Jira. Команды git одинаковы для всех, отличается только веб-морда и API.

Изображение

Fork git и модель pull request: как живёт open-source

Теперь центральная идея, на которой стоит весь публичный open-source. У тебя нет прав писать в чужой репозиторий - и хорошо, что нет. Как тогда предложить свою правку в проект Linux или Vue? Через fork. Fork git на форже - это серверная копия чужого репозитория под твоим аккаунтом. Технически это обычный клон, который форж пометил как форк (хранит связь parent). Дальше схема такая: ты клонируешь СВОЙ форк локально, делаешь ветку, пушишь в свой форк, а потом через веб открываешь pull request из своего форка в оригинал (его принято называть upstream).

Код: Выделить всё

# 1. форкнул через кнопку Fork на github.com, теперь клонирую СВОЙ форк
git clone git@github.com:tym=/awesome-lib.git
cd awesome-lib

# 2. добавляю оригинал вторым remote под именем upstream
git remote add upstream git@github.com:original-org/awesome-lib.git

git remote -v
# origin    git@github.com:tym=/awesome-lib.git (fetch)
# origin    git@github.com:tym=/awesome-lib.git (push)
# upstream  git@github.com:original-org/awesome-lib.git (fetch)
# upstream  git@github.com:original-org/awesome-lib.git (push)
Разбор вывода: origin - это твой форк, в него ты пушишь. upstream - оригинал, из него ты только тянешь обновления (писать туда права нет, и push туда упрётся в отказ доступа). Это классическая схема "fork + upstream", без неё в open-source не живут. Дальше работаешь как обычно: ветка от свежего main, коммиты, push в origin. Форж увидит новую ветку и подскажет ссылку для открытия PR.

Синхронизация форка с upstream: самая частая боль контрибьютора

Форк протухает мгновенно. Пока ты пилил фичу, в оригинал влили двадцать чужих PR, и твой main отстал. Если открыть PR из устаревшей ветки, ревьюер увидит конфликты и кучу чужих коммитов. Синхронизация - обязательный ритуал.

Код: Выделить всё

# подтянуть свежак из оригинала
git fetch upstream

# встать на свой main и подтянуть его под оригинал
git switch main
git merge upstream/main        # или git rebase upstream/main для линейной истории

# вернуть свежий main в свой форк на форже
git push origin main
Здесь git switch - стабильная замена git checkout для переключения веток (вышла из эксперимента в Git 2.51); checkout тоже работает, его в сети полно, но switch читается яснее и не делает десяти разных вещей сразу. Рабочую ветку с фичей синхронизируют так же, но через rebase, чтобы твои коммиты легли поверх свежего main: git switch my-feature && git rebase upstream/main. После rebase история переписана, и push в свой форк потребует git push --force-with-lease (force-with-lease вместо --force, чтобы не затереть чужой push, если кто-то трогал твою ветку - про это был отдельный разговор в уроке о rebase). На GitHub есть кнопка Sync fork и команда gh repo sync, но понимать ручной путь обязательно: кнопка делает ровно этот fetch+merge.

GitHub Flow на практике: ветка, PR, ревью, merge

GitHub Flow - это простой рабочий цикл, который победил тяжёлый git-flow в большинстве команд. Суть: main всегда деплоится, любая работа идёт в короткой ветке, изменения попадают в main только через pull request с ревью и зелёным CI. Полный цикл:

Код: Выделить всё

git switch -c feature/add-cache
# ... правишь, коммитишь маленькими осмысленными коммитами ...
git push -u origin feature/add-cache
# remote: Create a pull request for 'feature/add-cache' on GitHub by visiting:
# remote:   https://github.com/org/repo/pull/new/feature/add-cache
Сервер сам печатает ссылку на создание PR - удобно. Дальше через веб (или gh pr create) открываешь PR: база (target) - main, источник (head) - твоя ветка. В PR начинается ревью: коллеги оставляют комментарии на конкретных строках, ставят апрув (Approve) или просят изменения (Request changes). Ты дописываешь коммиты и пушишь в ту же ветку - PR обновляется автоматически, никаких пересозданий. Когда апрувы собраны и CI зелёный, PR можно влить. Три способа влить на GitHub:
  • Merge commit - создаёт merge-коммит, сохраняет всю историю ветки как есть.
  • Squash and merge - схлопывает все коммиты ветки в один. Любимый способ команд, которые хотят чистый линейный main, где один PR = один коммит.
  • Rebase and merge - переносит коммиты ветки поверх main по одному, без merge-коммита.
push.autoSetupRemote (доступен с 2.37, но по умолчанию в 2026 ВЫКЛЮЧЕН) стоит включить локально командой git config --global push.autoSetupRemote true - тогда первый push новой ветки не требует -u, Git сам заведёт upstream. Мелочь, а экономит нервы.

CODEOWNERS: кто обязан смотреть твой PR

В большом проекте ты не знаешь, кто отвечает за каждый кусок. Для этого есть файл CODEOWNERS (кладётся в корень, .github/ или docs/). Он сопоставляет пути с владельцами, и форж автоматически запрашивает у них ревью, когда PR трогает их файлы.

Код: Выделить всё

# .github/CODEOWNERS
# по умолчанию - вся команда платформы
*                       @org/platform-team

# фронт смотрят фронтендеры
/src/frontend/          @org/frontend @alice

# инфра и CI - только девопсы, последнее совпадение побеждает
/.github/workflows/     @org/devops
/infra/                 @org/devops
Ключевое правило, на котором спотыкаются: побеждает ПОСЛЕДНЕЕ совпадение, а не самое специфичное. Если строка для /infra/ стоит ниже общей звёздочки - владельцем будет devops, как и задумано. Поставишь звёздочку последней - она перебьёт всё. Если CODEOWNERS сделан обязательным правилом (а так обычно и делают), PR нельзя влить без апрува нужного владельца. Это не вежливая просьба, а блокировка на стороне форжа.

Защита веток: rulesets вместо classic branch protection

Вот сердце урока. Защита веток - это набор условий, которые форж проверяет, прежде чем пустить изменение в важную ветку (main, release). Исторически на GitHub это были classic branch protection rules - по одному правилу на паттерн ветки, и работало только одно правило за раз. В 2026 GitHub активно мигрирует на repository rulesets (общая доступность, GA в 2026). Rulesets гибче по трём причинам: несколько rulesets применяются ОДНОВРЕМЕННО и складываются (а не "побеждает одно"), у них есть статусы (active/evaluate/disabled - можно гонять правило в режиме "только лог" перед включением), и они задаются на уровне организации, накрывая разом много репозиториев. Classic protection пока работает, но новые проекты заводи сразу на rulesets.

Что включают в ruleset для main на практике:
  • Require a pull request before merging - прямой push в main запрещён, только через PR. Можно требовать N апрувов и сброс апрувов при новом коммите.
  • Require status checks to pass (required status checks) - PR нельзя влить, пока не позеленели указанные CI-проверки (тесты, линтер, сборка). Имена проверок должны совпадать с именами джоб в CI точь-в-точь.
  • Require branches to be up to date - ветку надо синхронизировать с main перед вливанием, чтобы CI гонялся на актуальном коде.
  • Require linear history - запрет merge-коммитов, в main только линейная история (вынуждает squash или rebase).
  • Block force pushes - запрет force-push в защищённую ветку, чтобы никто не переписал общую историю.
  • Require signed commits - все коммиты должны быть с верифицированной подписью (об этом отдельный урок дальше).
Когда ты упёрся в "remote rejected ... protected branch", это сработал именно такой ruleset. Лечится не силой, а правильным путём: ветка -> PR -> зелёный CI -> апрув -> merge. GitLab устроен аналогично: там Protected branches задают, кто может пушить и мёржить, плюс есть push rules (отдельный механизм) - регэкспы на имена коммитов, запрет коммитов без подписи, ограничение размера файлов, запрет менять чужие коммиты. Терминология разная, идея одна: сервер - последний рубеж, который не пускает мусор в главную ветку.

Verified-подписи и связь с локальным Git

В списке правил мелькнуло "require signed commits". Коротко: форж умеет показывать бейдж Verified рядом с коммитом, если коммит подписан ключом, который форж знает. В 2026 рекомендуемый простой дефолт - SSH-подпись (gpg.format=ssh плюс allowedSignersFile), а не громоздкий GPG; для CI и ботов есть keyless-вариант через Sigstore/gitsign. Зачем это в уроке про форжи: ruleset может ТРЕБОВАТЬ подпись, и тогда неподписанный коммит просто не влить. Это не косметика, а защита от подделки авторства. Полноценно подпись разберём в отдельном уроке - сейчас держи в голове, что галочка Verified рождается из локальной настройки твоего git + ключа, загруженного в профиль на форже.

Типичные грабли
  • Пушишь в чужой репозиторий, а не в форк. Клонировал оригинал вместо своего форка - и упёрся в отказ доступа. Всегда клонируй origin = свой форк, upstream = оригинал.
  • PR из устаревшей ветки. Не синхронизировался с upstream - ревьюер видит конфликты и чужие коммиты. Перед открытием PR: fetch upstream + rebase.
  • Имя CI-проверки не совпало с required status check. Переименовал джобу в workflow - и ветка вечно "ожидает проверку, которой нет". Имена должны быть идентичны.
  • CODEOWNERS - порядок строк. Думаешь, что выигрывает самое точное правило. Нет, выигрывает последнее совпадение. Специфичные пути - ниже общих.
  • force-push в защищённую ветку. Block force pushes отбреет даже админа, если правило это требует. И правильно - общую историю не переписывают.
  • Merge Queue и "PR прошёл, а main красный". В 2026 команды включают GitHub Merge Queue (GA), где CI гоняется на событии merge_group уже после очереди. Если твой workflow не слушает merge_group - очередь будет считать его непройденным.
Мини-лаба: контрибьют через форк своими руками

Повтори по шагам на любом своём тестовом репозитории на GitHub (или Gitea/Forgejo - команды те же).
  • Создай публичный репозиторий repo-A под одним аккаунтом, положи README. Это будет upstream.
  • Со второго аккаунта (или попроси коллегу) нажми Fork - получится форк repo-A.
  • Локально склонируй ФОРК: git clone <url-форка>. Добавь оригинал: git remote add upstream <url-repo-A>. Проверь git remote -v - должно быть два remote.
  • Создай ветку: git switch -c fix/typo, поправь README, закоммить, git push -u origin fix/typo. Поймай в выводе ссылку на создание PR.
  • Открой PR из форка в upstream через веб. Посмотри, как форж показывает diff и статус.
  • В upstream (repo-A) зайди в Settings -> Rules -> Rulesets, создай ruleset на main: Require a pull request before merging + Block force pushes. Попробуй запушить прямо в main - получишь отказ. Разбери текст ошибки.
  • Слей чужой PR в main repo-A. Затем синхронизируй форк: git fetch upstream && git switch main && git merge upstream/main && git push origin main. Убедись, что форк догнал оригинал.
Контрольные вопросы
  • Почему защита веток, pull request и CODEOWNERS не являются командами Git, и где они реально исполняются?
  • Чем repository rulesets гибче classic branch protection - назови минимум два отличия.
  • Зачем нужен второй remote upstream и как одной последовательностью команд синхронизировать форк с оригиналом?
  • Какое правило ruleset запрещает merge-коммиты и к какому способу вливания PR это вынуждает?
Итог

Форж - это политический слой поверх голого Git: тот же fetch/push, но с правилами на приёме. Fork + pull/merge request - основа open-source: клонируешь свой форк, держишь upstream вторым remote, регулярно синхронизируешься. GitHub Flow гоняет любую работу через короткую ветку и PR с ревью и зелёным CI. Защита веток в 2026 - это rulesets (GA): required status checks, linear history, запрет force-push, обязательный PR и подпись. CODEOWNERS назначает обязательных ревьюеров по путям. Запомни: красный отказ на push - это не поломка, а сервер, который бережёт твой main.
👍3 ❤️4 🔥3 😄 🤔2
Аватара пользователя
isaack
Сообщения: 1
Зарегистрирован: 25 май 2026, 02:22

Re: GitHub, GitLab и форки: pull request, rulesets и защита веток

Сообщение isaack »

Вечно ловил GH006 и думал что у меня руки кривые. Оказалось это ruleset на main, а не Git сломался. Спасибо, дошло.
👍 ❤️ 🔥 😄 🤔
Аватара пользователя
Katiebec
Сообщения: 1
Зарегистрирован: 15 май 2026, 02:30

Re: GitHub, GitLab и форки: pull request, rulesets и защита веток

Сообщение Katiebec »

А подскажите: squash and merge vs rebase and merge - когда что выбирать на практике? У нас в команде спор, половина за squash.
👍1 ❤️ 🔥1 😄 🤔1
Ответить
← Предыдущая глава
Клонирование: shallow, partial clone, bundle и backfill
Следующая глава →
Рабочие процессы команды: GitFlow, GitHub Flow, trunk-based

Все главы курса «Git профессионально: от первого коммита до внутреннего устройства»

Поделиться темой: ✈ Telegram VK
Похожие запросы: GitHub и GitLab: pull request и защита ветокРазрешение конфликтов слияния в Gitgit worktree: несколько рабочих деревьевgit push и git pull: отправка и получение измененийgit branch: работа с ветками

Вернуться в «Git профессионально: от первого коммита до внутреннего устройства»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя