1. Введение
Хотя легко воспринимать Git-ветвление как инструмент "разделения по функциям", на самом деле стратегия ветвления в деятельности напрямую связана с методами совместной работы, развертыванием и тестированием в среде разработки, проверкой качества, развертыванием в эксплуатации и реагированием на hotfix. В среде, где несколько человек разрабатывают одновременно и должны выполнять развертывание в соответствии с установленным графиком, стратегия ветвления особенно становится способом работы команды, выходящим за рамки простого использования Git.
Сначала я пытался понять структуру ветвления на основе известных стратегий, таких как Git Flow и GitHub Flow. Но стратегия ветвления, с которой я столкнулся в реальных проектах, не совпадала полностью с учебниками. Даже если существуют ветки, такие как feature, develop, stage, prod, release, фактический способ их использования изменялся в зависимости от цикла развертывания, методов проверки качества, численности команды, среды CI/CD и процедур утверждения.
В этой статье я кратко изложу типичные стратегии ветвления, а затем сосредоточу внимание на структуре ветвления окружающей среды и ветвления кандидатов на развертывание, основанной на реальном опыте. Хотя эту стратегию я не разрабатывал сам, опыт создания feature-веток, объединения их с веткой разработки для тестирования и сбора функций в ветку кандидатов на развертывание для развертывания в эксплуатации позволил мне понять ее преимущества и моменты, на которые следует обратить внимание.
Из этого опыта я больше всего почувствовал, что гораздо важнее, чем ярлык "использует ли наша команда Git Flow или GitHub Flow", четкость роли каждой ветки, последовательность направлений слияния и общее понимание командой критериев развертывания и процесса синхронизации.
2. Типичные стратегии ветвления Git
2.1 Git Flow
Git Flow - это стратегия, сосредоточенная на четком разделении ролей ветвлений вокруг релиза. main или ранее называемая master управляет историей релизов, пригодных для развертывания в эксплуатации, а develop служит интеграционной веткой для следующего развертывания. feature/* используется для разработки функций, release/* - для стабилизации перед развертыванием и подготовки версий, а hotfix/* - для экстренных правок в эксплуатации.
feature/* -> develop -> release/* -> main
hotfix/* -> main, develop 또는 현재 release/*
Хорошо подходит для проектов, которые требуют явной версии релиза, или когда необходим отдельный этап стабилизации и проверки перед релизом, или когда нужно поддерживать несколько версий. Однако наличие большого количества веток и определенное направление слияния делает важным обмен правилами внутри команды, защиту веток и автоматизацию. Прежде чем применять это как стандарт для всех проектов, необходимо сначала оценить, соответствует ли это способу релиза и размерам команды.
2.2 GitHub Flow
GitHub Flow - это простая стратегия, сосредоточенная вокруг главной ветки. Разработка функций осуществляется в краткосрочных feature-ветках, после чего код проходит через процесс рецензирования и тестирования с помощью Pull Request перед слиянием в main. Ключевым моментом является то, что код, вошедший в main, всегда должен быть в состоянии, пригодном для развертывания.
feature/* -> Pull Request / CI / review -> main -> deploy
На практике команды могут автоматически развертывать код сразу после слияния с main, или проходить ручное утверждение и поэтапное развертывание. Поэтому правильнее было бы понимать это как "код, объединенный в main, всегда должен быть готов к развертыванию", а не "объединение с main обязательно приводит к немедленному развертыванию".
Простая стратегия хорошо подходит для совместной работы на основе PR, но функции, которые еще не должны быть доступны пользователям, должны контролироваться с помощью feature flag, управления правами, настроек и поэтапного rollout. Поскольку сбой в main может блокировать весь процесс развертывания, автоматическое тестирование и правила защиты веток также имеют важное значение.
2.3 GitLab Flow
GitLab Flow основывается на.simple feature branch, проходящей через Merge Request к main, и сочетает production-ветку, staging-ветку, release-ветку, теги и CI/CD-среду в зависимости от способа развертывания проекта, чтобы выразить рабочий процесс.
예시 1: feature/* -> main -> staging -> production
예시 2: feature/* -> main -> release/x.y -> production deploy
Git Flow проще, но при этом более четко выражает этапы развертывания и проверки, чем GitHub Flow. Это полезно в проектах, где важны этапы проверки для каждого окружения, такие как QA, staging, UAT и production.
Однако это не означает, что GitLab Flow всегда должен иметь ветки staging или production. С помощью функции окружения GitLab CI/CD можно управлять средами QA, staging и production, и можно выбрать развертывание на основе веток релиза или тегов. То есть ветка окружения — это один из характерных методов GitLab Flow, но не обязательное условие.
2.4 Разработка, основанная на главной ветке
Разработка, основанная на главной ветке, — это стратегия, при которой изменения часто объединяются в одну центральную ветку, обычно main или trunk. Это не означает, что ветки вообще не используются, а суть заключается в том, чтобы избегать длительного существования длинных веток и быстро объединять мелкие изменения через короткие ветки или прямые коммиты.
main 또는 trunk
└─ short-lived branch -> main 또는 trunk
trunk всегда должен поддерживать состояние, готовое для сборки и развертывания, поэтому важны быстрая CI, автоматическое тестирование, маленькие рабочие единицы и быстрая проверка кода. Вместо того чтобы скрывать большие функции на долгое время, управляют недоработанными функциями так, чтобы они не были видимы пользователям, используя feature flag, branch by abstraction и управление параметрами.
Это хорошо подходит для организаций с хорошо организованным CI/CD и надежным автоматическим тестированием. Напротив, в организациях с медленным тестированием, высоким уровнем зависимости от ручной проверки и длительным сохранением функциональных веток трудно поддерживать trunk в стабильном состоянии.
3. Причины, по которым стратегия не всегда применяется в реальной работе
В реальных проектах одна стратегия часто применяется не так четко, как ожидалось, а элементы нескольких стратегий смешиваются. Стратегия веток не является просто вопросом использования Git, она также связана с циклом развертывания, методами QA, реагированием на сбои в эксплуатации, численностью команды, уровнем CI/CD и процессом одобрения задач.
Например, наличие веток develop, stage и prod не означает автоматического использования Git Flow. Также наличие веток staging или production не всегда означает, что мы имеем дело с GitLab Flow. Просто потому, что ветка feature создается от main и затем сливается обратно в main, это не гарантирует, что мы имеем дело с Trunk-Based Development. Использование Pull Request не делает это GitHub Flow. PR — это не сама стратегия веток, а способ сотрудничества для проверки кода и слияния.
При анализе стратегии веток в реальной работе важнее было отвечать на следующие вопросы, чем просто обращать внимание на названия.
- Необходимо убедиться, какая ветка является основной для операционного развертывания.
- Необходимо проверить, какие ветки разворачиваются в средах разработки и проверки.
- Необходимо выяснить, где создаются ветки feature и куда они сначала объединяются.
- Необходимо выяснить, каким образом выбираются функции для этого развертывания.
- Необходимо проверить, как синхронизируются ветки разработки и проверки после операционного развертывания.
- необходимо проверить, из какой ветки начинается hotfix и в какую ветку он возвращается.
Когда ответы на эти вопросы были ясны, команда могла действовать по единым стандартам. Напротив, если названия веток знакомы, но их реальная роль неясна, могут возникнуть недоразумения в таких вопросах, как: все ли функции, вошедшие в ветку разработки, развертываются, является ли ветка проверки кандидатом на эксплуатацию или она предназначена только для тестирования.
4. Практический опыт: структура веток среды и веток кандидатов на развертывание
В проектах, с которыми я имел дело, существовали эксплуатационная ветка, ветка разработки и ветка проверки. Реальные названия веток могут различаться в зависимости от проекта, поэтому в этом тексте я объясню по ролям, а не по названиям.
- Эксплуатационная ветка — это ветка, которая служит основой для фактического развертывания в эксплуатации. Обычно она защищается и прямые пуши ограничиваются.
- Ветка разработки — это ветка, предназначенная для развертывания в среде разработки и интеграционного тестирования. Она не означает, что все, что развернуто, предназначено для эксплуатации.
- Ветка проверки — это ветка, которая развертывается в среду для QA, стадиирования, предпродакшна и т.д. перед эксплуатацией.
- Ветка feature — это ветка для индивидуальных задач. Если учитывать выборочное развертывание, она также играет роль безопасного хранения первоисточника работы.
- Ветка кандидатов на развертывание — это временная ветка, в которую собираются только функции, которые пойдут в эксплуатацию, для окончательной проверки.
Ключевым моментом является то, что перед эксплуатационным развертыванием создается отдельная ветка кандидатов на развертывание, и собрано только то, что будет развернуто в этой версии. Поскольку не все функции, вошедшие в ветку разработки, были включены в данное развертывание в эксплуатацию, ветка кандидатов на развертывание создавалась на основе эксплуатационной ветки, и диапазон развертывания контролировался явно.
Эта структура не обязательно может быть определена как стандартный Git Flow. Есть элементы использования веток среды, как в GitLab Flow, и моменты, когда есть ветки стабилизации перед эксплуатацией, как в ветке release Git Flow. Однако, в отличие от типичного Git Flow, когда ветка release снимается из develop, здесь ветка кандидатов создается на основе эксплуатационной ветки, и собрано только выбранные функции, что приближает эту структуру к практическому гибридному подходу.
4.1 Общий поток разработки
Каждый разработчик создает ветку feature из эксплуатационной ветки или самой последней эталонной ветки, определенной командой, и после завершения разработки сливает в ветку разработки для тестирования в среде разработки. При этом эталонная ветка может различаться в зависимости от политики команды. Важно, чтобы вся команда одинаково знала, из какой ветки начинать и в какую ветку сначала сливаться.
feature/A -> 개발 브랜치 -> 개발계 배포 및 테스트
feature/B -> 개발 브랜치 -> 개발계 배포 및 테스트
feature/C -> 개발 브랜치 -> 개발계 배포 및 테스트
Здесь важно отметить, что ветка разработки не является основой для развертывания в эксплуатации. Не все функции, вошедшие в ветку разработки, были частью данного развертывания в эксплуатации.
개발 브랜치 = 운영 코드 + feature/A + feature/B + feature/C
이번 배포 대상 = feature/A + feature/B
В ситуации, похожей на описанную выше, если ветка разработки будет слита в эксплуатацию напрямую, то также выйдет feature/C. Поэтому ветка feature должна управляться не просто как рабочее пространство, но и как единица, позволяющая выбирать диапазон развертывания. Удаление или потеря истории работы сразу после слияния в ветку разработки может затруднить сбор обратно во время выборочного развертывания.
4.2 Создание branch для кандидата на выпуск
Когда дата выпуска приближается, создается branch для кандидата на выпуск на основе рабочей ветки, и функции, которые должны быть включены в этот выпуск, сливаются по одной. Команда может использовать merge или отобрать только необходимые коммиты. В любом случае, ключевым моментом является то, что только изменения, которые пойдут в производство, должны попасть в branch кандидата на выпуск.
개발 중: feature/A, feature/B, feature/C -> 개발 브랜치 -> 개발계 테스트
배포 준비: 운영 브랜치 -> 배포 후보 브랜치 <- feature/A, feature/B
최종 흐름: 충돌 해결 -> 의존성 확인 -> 검증 환경 테스트 -> 운영 PR -> 운영 배포
Основным преимуществом этой структуры является четкое управление диапазоном выпуска. Даже если в разработческой ветке смешаны различные функции, только те функции, которые пойдут в производство, могут быть собраны в branch кандидата на выпуск.
Тем не менее, если между функциями есть зависимости, нужно быть осторожным. Например, если feature/B внутренне зависит от кода feature/C, то включение только B в кандидаты на выпуск и исключение C может быть невозможно или рискованно. В структурах, требующих выборочного выпуска, необходимо проектирование функций как независимых, подтверждение зависимостей и стратегия feature flag.
4.3 Интеграция, верификация, внедрение в эксплуатацию
Наиболее важными аспектами, которые нужно было учитывать в branch кандидата на выпуск, были конфликты и окончательная верификация. Branch функции только отделяет рабочее пространство, но не минимизирует затраты на интеграцию. Если несколько функций одновременно изменяют общие компоненты, файлы маршрутизации, определения типов, конфигурационные файлы и т.д., это может привести к конфликтам на этапе сбора в branch кандидата на выпуск.
Кроме того, комбинации, протестированные в разработческой ветке, могут отличаться от комбинации в branch кандидата на выпуск. Например, в разработческой ветке тестировалась комбинация A+B+C, но в кандидате на выпуск могут быть только A+B. Поэтому окончательная верификация должна проводиться не на основе разработческой ветки, а на основании branch кандидата на выпуск, который действительно будет введен в эксплуатацию.
После завершения верификации был создан Pull Request для слияния branch кандидата на выпуск с рабочей веткой. Этот PR не является простой процедурой слияния, а служит контрольной точкой для проверки того, что входит в текущий выпуск. Если информация о включенных функциях, исключенных функциях, решении конфликтов, результате верификации, изменениях в настройках выпуска и критериях отката собрана в одном месте, стабильность выпуска повышается.
Подводя итог, branch кандидата на выпуск является «временной веткой для сбора функций, подлежащих выпуску», одновременно являясь «финальной контрольной точкой верификации перед производственным выпуском». Поэтому лучше предоставить достаточно времени на верификацию, чем спешить с его созданием непосредственно перед выпуском.
4.4 Синхронизация и hotfix после выпуска
После завершения рабочего выпуска происходил процесс синхронизации branch разработки и branch верификации на основе рабочей ветки. Это важно для четкого понимания контрольной точки сразу после выпуска и уменьшения накопления различий между ветками. В это время изменения в рабочей ветке могут быть возвращены и объединены с branch разработки и верификации, или в зависимости от политики команды определенные branch окружения могут быть снова синхронизированы с рабочей веткой.
Однако методы force push или сброса для инициализации общей ветки должны использоваться только в исключительных случаях, поскольку это может быть опасно. Это связано с тем, что не только удаление удаленной ветки должно быть завершено, но и локальные ветки членов команды также должны быть синхронизированы заново. Важнее, чем сама команда, являются политика команды и уведомления. Если критерий нечеткий, код, удаленный в процессе синхронизации после выпуска, может снова вернуться, или, наоборот, необходимая работа может исчезнуть.
Поток hotfix также необходимо определить отдельно. Операционные сбои или экстренные исправления следует рассматривать отдельно от обычного потока функций, и обычно branch hotfix создается в рабочей ветке, и после завершения исправлений и верификации сначала интегрируется в рабочую ветку. Затем те же исправления должны быть возвращены в branch разработки, branch верификации и текущие branch кандидата на выпуск, чтобы они не исчезли в следующем цикле разработки. Для hotfix лучше ограничить объем, избегая миксования рефакторинга или изменения других функций.
4.5 Как можно рассматривать эту структуру в стратегии
Структура, с которой я столкнулся, не может быть четко отнесена к одной из следующих: чистый Git Flow, GitHub Flow, GitLab Flow или Trunk-Based Development. То, что разработческая ветка разделена с рабочей веткой, похоже на Git Flow, а то, что верификационная ветка и рабочая ветка представляют окружающую среду через ветку, похоже на GitLab Flow. Создание branch кандидата на выпуск для стабилизации и верификации перед производством также похоже на стратегию release branch.
Однако это отличается от типичного Git Flow тем, что речь идет не о том, чтобы отправить всю ветку разработки в release, а о том, что мы собрали только выбранные функции на основе рабочей ветки. Также это отличается от Trunk-Based Development тем, что ветка функций сохраняется до выпуска и интегрируется в финальную ветку кандидата на релиз.
5. Преимущества и предостережения
5.1 Преимущества
- Можно явно контролировать диапазон развертывания. Даже если в ветке разработки смешано несколько функций, можно собрать только целевые для данного развертывания в ветке кандидата на релиз.
- Можно разделить тестирование в разработке и развертывание в рабочей среде. Сначала мы тестируем ветку функций, объединяя её с веткой разработки, а перед выпуском отдельно проверяем фактическую комбинацию развертывания в ветке кандидата на релиз.
- Ясно определяются единицы развертывания в рабочей среде. Поскольку одна ветка кандидата на релиз представляет собой диапазон данного развертывания, легко отслеживать, какие функции включены.
- Можно поддерживать рабочую ветку как надежную опорную точку. Если создать ветку кандидата на релиз из рабочей ветки, это снижает риск неожиданного включения неразвернутых функций, смешанных с веткой разработки, в рабочую среду.
- После развертывания точки отсчета становятся ясными. Согласовав ветки разработки/проверки с рабочей веткой, становится проще начать следующую работу с текущего рабочего кода.
5.2 Предостережения
- Затраты на интеграцию могут сосредоточиться непосредственно перед развертыванием. Если несколько функций модифицируют общие файлы, конфликты сосредоточиваются в момент создания ветки кандидата на релиз.
- Комбинации тестирования ветки разработки и кандидата на релиз могут отличаться. Финальная проверка должна обязательно осуществляться на основе ветки кандидата на релиз.
- Если между функциями есть скрытые зависимости, выборочное развертывание может нарушиться. Вы можете считать, что только A будет развернуто, но на самом деле это может зависеть от кода B или C.
- Если смешиваются методы отражения, такие как merge и cherry-pick, отслеживание истории может стать сложным. Команде необходимо четко определить, какой метод использовать.
- Если после инициализации общей ветки пропустить локальную синхронизацию, удаленный код может снова появиться. Управление общей веткой требует предварительного уведомления и команды по процедуре.
- Незавершенные задачи должны оставаться в ветке функций в безопасности. Оставлять исходные материалы только в ветке разработки, если структура позволяет инициализацию ветки разработки, является рискованным.
- Лучше не управлять функциями, которые нужно скрыть, только с помощью веток. Функции с долгосрочным отсутствием отображения должны рассматривать возможность использования feature flag или контроля доступа.
5.3 Элементы, которые должны оставаться в правилах команды
Стратегия веток должна оставаться в виде операционных правил, а не только в виде картинок или названий, чтобы снизить вероятность ошибок. По моему опыту, лучше установить короткие и четкие критерии, которые легко запутать, вместо того, чтобы документировать все элементы слишком подробно.
- Роль и степень защиты каждой ветки
- Целевой веткой для слияния с базовой веткой, в которой создается feature ветка
- Критерии развертывания для среды разработки, проверки и эксплуатации
- Время создания ветки-кандидата на развертывание и способ выбора включаемых функций
- Условия одобрения PR для рабочей ветки и контрольный список развертывания
- Способ синхронизации веток разработки/проверки после развертывания в производство
- Начальная ветка для hotfix, целевая ветка слияния, процесс последующей синхронизации
- Критерии использования feature flag и управление неразворачиваемыми функциями
6. Заключение
У стратегии веток Git нет единственно правильного ответа. Git Flow, GitHub Flow, GitLab Flow и Trunk-Based Development — это хорошие ориентиры, каждый из которых имеет свои преимущества, но в реальных проектах они адаптируются в зависимости от циклов развертывания, методов QA, операционных рисков, уровня CI/CD и размера команды.
Я не могу сказать, что структура, с которой я работал, является классическим Git Flow. Использовались ветки feature, разработки, проверки, эксплуатации и кандидат на развертывание, а также имелась политика по приведению ветки окружения в соответствие с рабочими критериями после развертывания. Это было ближе к практической структуре, созданной для повышения стабильности развертывания и эффективности совместной работы, чем к теоретической стратегии.
Два аспекта этого опыта запомнились мне лучше всего. Во-первых, feature ветки не устраняют затраты на интеграцию. Даже если работа ведется в отдельных ветках, перед развертыванием они должны быть объединены в одну ветку, и в этот момент могут возникнуть конфликты. Так же важно, как «где работать», «когда интегрировать» и «где проводить окончательную проверку».
Другая сторона заключается в том, что суть стратегии ветвления заключается не в названии, а в ясности правил. Роли каждой ветки, направление слияния, критерии развертывания и процедуры синхронизации после развертывания должны быть одинаково доступны всем членам команды. Хорошая стратегия ветвления не должна просто следовать известным стратегиям, а должна последовательно адаптироваться к ситуации нашей команды.
Ссылка
- Git Flow: Vincent Driessen, nvie.com
- GitHub Flow: GitHub Docs
- GitLab Flow: Официальный сайт GitLab, GitLab Docs
- Разработка на основе основы: TrunkBasedDevelopment.com
- Разработка на основе основы: Atlassian
green