Форж - это не 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)
Синхронизация форка с upstream: самая частая боль контрибьютора
Форк протухает мгновенно. Пока ты пилил фичу, в оригинал влили двадцать чужих PR, и твой main отстал. Если открыть PR из устаревшей ветки, ревьюер увидит конфликты и кучу чужих коммитов. Синхронизация - обязательный ритуал.
Код: Выделить всё
# подтянуть свежак из оригинала
git fetch upstream
# встать на свой main и подтянуть его под оригинал
git switch main
git merge upstream/main # или git rebase upstream/main для линейной истории
# вернуть свежий main в свой форк на форже
git push origin main
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
- Merge commit - создаёт merge-коммит, сохраняет всю историю ветки как есть.
- Squash and merge - схлопывает все коммиты ветки в один. Любимый способ команд, которые хотят чистый линейный main, где один PR = один коммит.
- Rebase and merge - переносит коммиты ветки поверх main по одному, без merge-коммита.
CODEOWNERS: кто обязан смотреть твой PR
В большом проекте ты не знаешь, кто отвечает за каждый кусок. Для этого есть файл CODEOWNERS (кладётся в корень, .github/ или docs/). Он сопоставляет пути с владельцами, и форж автоматически запрашивает у них ревью, когда PR трогает их файлы.
Код: Выделить всё
# .github/CODEOWNERS
# по умолчанию - вся команда платформы
* @org/platform-team
# фронт смотрят фронтендеры
/src/frontend/ @org/frontend @alice
# инфра и CI - только девопсы, последнее совпадение побеждает
/.github/workflows/ @org/devops
/infra/ @org/devops
Защита веток: 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 - все коммиты должны быть с верифицированной подписью (об этом отдельный урок дальше).
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.