Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Git: ветки и стратегии слияния

Ветвление в Git: trunk-based, Git Flow и выбор стратегии команды

Сравнение моделей ветвления: trunk-based с короткоживущими ветками, Git Flow для релизных циклов, GitHub Flow; merge/squash/rebase и правила, снижающие конфликты.

Прямой ответ

Стратегия ветвления выбирается по одному критерию: как часто вы релизитесь. Trunk-based (короткоживущие ветки 1–2 дня → main, релиз с main хоть ежедневно) — стандарт для продуктовых команд с CI/CD: незавершённые фичи прячутся feature-флагами, не долгими ветками; интеграционный ад отсутствует по построению (ветка живёт день — конфликтовать не успевает). Git Flow (develop + feature + release + hotfix ветки) — для релизных циклов с версиями (десктоп/мобайл/коробка: релиз 1.2 готовится неделями, параллельно идёт разработка 1.3 — отдельные линии обоснованы); ценой — сложный процесс и отложенная интеграция. GitHub Flow (main + feature-ветки + PR, деплой с main) — упрощённый trunk-based без обязательных флагов — частый дефолт веб-команд. Правило, которое важнее выбора: короткая жизнь ветки и частая интеграция — слияние раз в день в main дешевле любого «правильного» графа с месячными ветками (merge-конфликты и «большой взрыв интеграции» растут нелинейно со временем жизни ветки). Технически: merge vs squash vs rebase — вопрос истории: squash в main — читаемый лог PR-ами, merge-коммиты — полная картина (для релизных моделей), rebase личных веток — гигиена перед PR (публичные ветки не ребейзятся).

Trunk-based: механика коротких веток

Цикл: ветка от свежего main → работа 0.5–2 дня → PR с ревью и CI → слияние (squash) → деплой с main (или по расписанию). Незавершённое в main попадает под флагом (код мёртв до включения — флаги: их отсутствие — главная причина, по которой команды «не могут» в trunk-based); большие фичи режутся на вертикальные slices, каждый из которых мержится рабочим (не «полгода в ветке, потом полгода интеграции»). Обязательная обвязка: CI на каждый PR (Actions-матрица — тесты обязательны: без зелёных тестов частая интеграция превращается в частые поломки main), pre-commit-гейты (хуки — локальная проверка до PR), защита main (required reviews/checks — force push запрещён). Выигрыши: интеграция непрерывна (конфликты малы и часты — решаются сразу), релиз — обычное событие (main всегда релизен), откат прост; издержки: дисциплина маленьких PR и флаги — навык, который команда нарабатывает месяцами. Для параллельной работы над несколькими задачами локально — git worktree (ветки без переключений контекста).

Git Flow и релизные модели: когда линии обоснованы

Git Flow: main (только релизы, теги), develop (интеграция), feature/* (от develop), release/* (стабилизация версии: только багфиксы — фичи уже в develop для следующей), hotfix/* (от main — критичные правки релиза, назад в develop). Когда это правильно: продукт версионируется и отгружается (мобильное приложение через сторы с ревью неделями, on-premise поставки клиентам с патчами к старым версиям — несколько поддерживаемых релизных линий живут одновременно); команда большая с выделенными релиз-инженерами; регрессионная стабилизация — отдельная фаза. Цена: двойная интеграция (feature→develop→release — конфликты дважды), отложенная обратная связь (баг интеграции фичи находится через недели), «develop сломан» как штатное состояние большой команды. Облегчённая версия для «релизы раз в месяц» — release-ветки от main без develop (GitHub Flow + release/1.2 на стабилизацию) — большинство команд, считающих, что им нужен Git Flow, на деле нуждаются в этом. Долгоживущие поддерживаемые версии ( LTS-патчи) — maintenance-ветки от тегов, cherry-pick фиксов — единственная часть Git Flow, переживающая упрощения.

Merge, squash, rebase: техника без войн

Squash-merge в main: PR становится одним коммитом (лог main = история фич — читабильно, бисект по фичам; теряется детализация внутри PR — приемлема при хороших сообщениях squash-коммита). Merge-коммит: полная история ветки в main (видна последовательность работы; граф сложнее; бисект точнее на уровне коммитов) — осмыслен в релизных моделях. Rebase: линейная история (ветка пересаживается на свежий main — конфликты решаются по коммиту, история без «шумовых» merge — но переписывает хеши: только личные ветки, публичный rebase ломает коллегам историю — force-push в общие ветки запрещён политикой репо). Гибрид-практика: локально git pull --rebase (свои коммиты поверх апстрима — без merge-мусора), в main — squash через PR (политика платформы: GitHub/GitLab настраивают разрешённые методы на репозиторий — единый выбор команды, не импровизация по настроению). Конфликт-гигиена: маленькими PR (дифф 400 строк ревьюится и мержится за день — 4000 строк живут неделю и конфликтуют), частый rebase/merge main в долгоживущую ветку (каждый день — конфликты порциями), форматирование до ветвления (конфликты «пробелы против кода» убиваются pre-commit и .gitattributes/linguist-настройками).

Практический сценарий: команда переходит с Git Flow на trunk-based

Исходник: 6 разработчиков, Git Flow «потому что начали так»; боли: feature-ветки по 2–3 недели, develop красный половину времени, релиз-ветка собирает конфликты месяца работы, хотфиксы забывают мержить в develop (расхождение версий — баг возвращается). Переход за месяц: (1) инвентаризация — что держит долгую интеграцию: две фичи «неудобно показывать недоделанными» — в них вводят флаги (неделя — флаги-скелеты: if flag: new else old), остальное режется на slices; (2) процесс: ветки ≤2 дней, PR ≤400 строк, squash-merge, CI required (тесты уже были, но «иногда скипали» — required checks без исключений); (3) релизы: деплой с main по кнопке (раньше — «релиз» раз в месяц из release-ветки) — первые две недели деплоят осторожно, к концу месяца — ежедневно; (4) хотфиксы — сразу в main (ветки maintenance — только для отданных клиентам версий — их две, патчи cherry-pick'ом). Итоги квартала: merge-конфликты — с «еженедельных часовых сессий» до минутных в PR; «develop сломан» исчез как явление (сломанный main чинится немедленно — на нём все); время от коммита до прода — месяц→день. Что не вернули обратно: Git Flow-линии не понадобились — «релизные» страхи закрылись флагами и canary-деплоем (стратегии).

FAQ

Trunk-based без feature-флагов возможен? Да, с оговорками: если всё в PR по-настоящему маленькое и завершённое (фиксы, мелкие улучшения) — флаги не нужны; продуктовые фичи больше пары дней без флагов возвращают команду к долгим веткам — флаг дешевле, чем ветка-долгожитель.

Какой метод слияния выбрать команде? Squash-merge в main — дефолт для веб-продуктов (читаемая история фич); rebase-merge — если ценятся атомарные коммиты и их бисект; обычный merge — релизные модели с историей веток; важно выбрать один и зафиксировать настройкой репозитория — смешение методов хуже любого одного.

Что делать с горящими конфликтами в большом рефакторинге? Разрезать: рефакторинг-ветка живёт долго — значит, режется на этапы, каждый мержится (переименования — отдельными атомарными коммитами/скриптами, поведение — после); git rerere (переиспользование решений конфликтов) помогает при повторяющихся пересечениях; худший вариант — «доделать всё и смержить» — конфликт к тому времени охватывает весь репозиторий.

Нужны ли отдельный develop и release-ветки веб-продукту с CI/CD? Почти никогда: staging-окружение проверяет main (деплой по мержу — стейдж как ветка, не как git-линия), релиз — тег/кнопка с main; develop-ветка дублирует main с задержкой и расхождениями — реликт моделей без непрерывной интеграции.

Источники: trunkbaseddevelopment.com, Git Flow (nvie), документация git-merge — ссылки в шапке.