Управление ветками Git на базе меток или GitTag Flow
Теория и практика | создано: 05.03.2024 | опубликовано: 07.03.2024 | обновлено: 18.03.2024 | просмотров: 1097 | всего комментариев: 14
Если вам не подходит GitFlow или если в GitHub Flow не хватает каких-либо важных моментов, то просто ознакомьтесь с GitTag Flow. В системе контроля версий GIT есть такое понятие как метки (tags). Моя задача показать, как соотнести метки git с семантическим версионированием.
О GitTag Flow
GitTag Flow - это некий симбиоз GitHub Flow и Semantic Versioning 2.0.0. Данный подход был создан по той простой причине, что GitHub Flow не имел возможность решить некоторые вопросы при управлении веток по части публикации не релизных версий. Итак, при управлении ветками репозитория за основу принимается GitHub Flow. При этом есть некоторые дополнения, связанные со спецификой публикации веток (версий проекта) для компаний, которые имеют большое количество филиалов в разных регионах. То есть, публикация новых версий ПО должна происходить "не сразу всё и везде", а с учем тестирования какого-то функционала на каких-то конкретных регионах или структурных единицах другого типа. Суть данного подхода - "обкатка" нового на боевых серверах с малой загрузкой.
Почему симбиоз состоит именно из этих компонентов? GitHub Flow значительно проще и гибче в отличии, например от GitFlow, особенно для небольших команд. А управление версиями решений, где за основу принимается международный стандарт Semantic Versioning 2.0.0 делает подход универсальным и знакомым для большенства разработчиков (и не только). Рекомендую ознакомиться с Semantic Versioning 2.0.0 более подробно, перед тем как продолжить чтение, чтобы термины МАЖОРНАЯ, МИНОРНАЯ и ПАТЧ не резали слух. Ниже описаны основные правила работы по конвейеру GitTag Flow.
Описание стандарта Semver
В мире управления процессом разработки есть понятие «ад зависимостей» (dependency hell). В системе с множественными зависимостями выпуск новой версии может быстро превратиться в кошмар. В качестве решения данной проблемы я предлагаю простой набор правил и требований, которые определяют, как назначаются и увеличиваются номера версий. Слова «ДОЛЖЕН» (MUST), «НЕ ДОЛЖЕН» (MUST NOT), «ОБЯЗАТЕЛЬНО» (REQUIRED), «СЛЕДУЕТ» (SHOULD), «НЕ СЛЕДУЕТ» (SHOULD NOT), «РЕКОМЕНДОВАННЫЙ» (RECOMMENDED), «МОЖЕТ» (MAY) и «НЕОБЯЗАТЕЛЬНЫЙ» (OPTIONAL) в этом документе должны быть интерпретированы в соответствии с RFC 2119. Это краткая версия описания, более подробно смотри Semantic Versioning 2.0.0.
Правила ведения веток
Тут всё просто, основа взята из принципов GitHub Flow, поэтому проговорим их еще раз, чтобы было понятно.
Правило 1
Ветка main
всегда содержит "правильную" версию продукта. "Правильная" версия - это всегда рабочая, на ней пройдены все unit-тесты, в ней проверена вся функциональность (all features), в ней проверены все интеграции функциональности даже при вливании одной feature в другую feature.
Правило 2
Вливание любой дочерней ветки в свою родительскую всегда происходит только через Pull Request (PR), который создается с привязкой к номеру задачи, на основании которой была реализация. При получении ветки изменений через PR, речь идет о main и develop, минорная версия ее метки должна быть инкрементирована. При этом PATCH должен быть сброшен в 0 (правило 8).
Пример: 5.4.132 → 5.5.0
Правило 3
Ветка develop
предназначена для тестирования функциональности, содержится по максимуму в рабочем состоянии, от нее разработчики делают ветки для реализации нового функционала (features).
Правило 4
При создании ветки для разработки нового функционала (new feature) разработчик должен обязательно включить в название ветки номер задачи (task или backlog), на основании которой будет реализации.
Правило 5
В ветке main
после получения изменений через Pull Request (PR) создается метка (tag) для однозначного определения заложенного в нее функционала. Именование меток смотри в разделе "Версионирование Semver". Создается "обратный" Pull Request (PR) в ветку develop
, чтобы изменения и новая метка (tags) "появилась" в develop
. При этом метка в develop
не создается.
Правило 6
В ветке с реализацией нового функционала (feature) можно быть сколько угодно фиксаций (commits). Обязательно наличие сообщения к фиксации с указанием проделанной работы. Чем точнее и лаконичнее комментарий к коммиту, тем проще будет формирование истории изменений (используя, например, какие-нибудь помощники для генерации).
Правило 7
Все вливания изменений (commits) из "дочерних" веток в "родительские" обязательно инкрементируют минорную версию метки (tag). Изменения от "родительских" в "дочерние" ветки синхронизируют изменения без инкрементирования каких-либо версий меток.
Правило 8
При инкрементировании минорной версии - версия патча обнуляется.
Правило 9
При инкрементировании мажорной версии - минорной версия обнуляется.
Метки в GIT
В системе контроля версий GIT есть такое понятие как метки (tags). Моя задача показать, как соотнести метки git с семантическим версионированием (SemVer).
Именование меток (tags)
Давайте введём понятие "регион" и "группа регионов", чтобы определить область действия конкретной версии ПО. Тут как раз речь о том, что нужно постепенно вводить новый функционал в всеобщее использование для клиентов компании. Например, регионом может считаться конкретный город, или обрасть, или район города, или часть страны. Другими словами, регион - это всего лишь область действия конкретной версии ПО. Как помогут метки в версионировании ПО для конкретных регионов? Очень просто!
Для каждого региона или группы регионов следует использовать именование метки (tag) в соответствии со стандартом Semantic Versioning 2.0.0. Без соответствия формальной спецификации, номера версий практически бесполезны для управления зависимостями. Ясно определив и сформулировав идею версионирования, становится легче сообщать о намерениях пользователям вашего ПО. Когда эти намерения ясны, гибки (но не слишком), спецификации зависимостей наконец могут быть созданы.
Основные правила стандарта
Учитывая номер версии МАЖОРНАЯ.МИНОРНАЯ.ПАТЧ, следует увеличивать:
- МАЖОРНУЮ версию, когда сделаны обратно несовместимые изменения API.
- МИНОРНУЮ версию, когда вы добавляете новую функциональность, не нарушая обратной совместимости.
- ПАТЧ-версию, когда вы делаете обратно совместимые исправления.
Дополнительные обозначения для пред-релизных и билд-метаданных возможны как дополнения к МАЖОРНАЯ.МИНОРНАЯ.ПАТЧ формату.
Спецификация SemVer (выписка)
- Обычный номер версии ДОЛЖЕН иметь формат X.Y.Z, где X, Y и Z — неотрицательные целые числа и НЕ ДОЛЖНЫ начинаться с нуля. X — мажорная версия, Y — минорная версия и Z — патч-версия. Каждый элемент ДОЛЖЕН увеличиваться численно. Например: 1.9.0 ->1.10.0 -> 1.11.0.
- После релиза новой версии пакета содержание этой версии НЕ ДОЛЖНО быть модифицировано. Любые изменения ДОЛЖНЫ быть выпущены как новая версия.
- Патч-версия Z (x.y.Z | x > 0) ДОЛЖНА быть увеличена только если содержит обратно совместимые баг-фиксы. Определение баг-фикс означает внутренние изменения, которые исправляют некорректное поведение.
- Минорная версия (x.Y.z | x > 0) ДОЛЖНА быть увеличена, если в публичном API представлена новая обратно совместимая функциональность. Версия ДОЛЖНА быть увеличена, если какая-либо функциональность публичного API помечена как устаревшая (deprecated). Версия МОЖЕТ быть увеличена в случае реализации новой функциональности или существенного усовершенствования в приватном коде. Версия МОЖЕТ включать в себя изменения, характерные для патчей. Патч-версия ДОЛЖНА быть обнулена, когда увеличивается минорная версия.
- Мажорная версия X (X.y.z | X > 0) ДОЛЖНА быть увеличена, если в публичном API представлены какие-либо обратно несовместимые изменения. Она МОЖЕТ включать в себя изменения, характерные для уровня минорных версий и патчей. Когда увеличивается мажорная версия, минорная и патч-версия ДОЛЖНЫ быть обнулены.
- Приоритет определяет, как версии соотносятся друг с другом, когда упорядочиваются. Приоритет версий ДОЛЖЕН рассчитываться путём разделения номеров версий на мажорную, минорную, патч и пред-релизные идентификаторы. Именно в такой последовательности. Приоритет определяется по первому отличию при сравнении каждого из этих идентификаторов слева направо: Мажорная, минорная и патч-версия всегда сравниваются численно. Пример: 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1. Когда мажорная, минорная и патч-версия равны, пред-релизная версия имеет более низкий приоритет, чем нормальная версия. Пример: 1.0.0-alpha < 1.0.0. Приоритет двух пред-релизных версий с одинаковыми мажорной, минорной и патч-версией ДОЛЖНЫ быть определены сравнением каждого разделённого точкой идентификатора слева направо до тех пор, пока различие не будет найдено следующим образом: идентификаторы, состоящие только из цифр, сравниваются численно; буквенные идентификаторы или дефисы сравниваются лексически в ASCII-порядке. Численные идентификаторы всегда имеют низший приоритет, чем символьные. Больший набор пред-релизных символов имеет больший приоритет, чем меньший набор, если сравниваемые идентификаторы равны. Пример: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.
- Сборочные метаданные МОГУТ быть обозначены добавлением знака плюс и ряда разделённых точкой идентификаторов, следующих сразу за патчем или пред-релизной версией. Идентификаторы ДОЛЖНЫ содержать только ASCII буквенно-цифровые символы и дефис [0-9A-Za-z-]. Идентификаторы НЕ ДОЛЖНЫ быть пустыми. Сборочные метаданные СЛЕДУЕТ игнорировать, когда определяется старшинство версий. Поэтому два пакета с одинаковой версией, но разными сборочными метаданными, рассматриваются как одна и та же версия. Примеры: 1.0.0-alpha+ea001, 1.0.0+sp20130313144700, 1.0.0-beta+akr.exp.sha.5114f85.
Примеры ветвления с версионированием
Примеры, которые будут показаны ниже, больше подходят для разработчиков, потому что именно им придется создавать ветки, удалять, вливать и т.д. и т.п. Главное, что нужно понять, что GitTag Flow очень прост. И на это есть две причины:
- Для разработчиков, которые занимаются публикацией ПО на серверы какие-то пред-релизные версии будет чётко понятно, какая версия из какой ветки, каким разработчиком была опуликована на тот или иной сервер.
- Для разработчиков, которые не занимаются публикацией - вообще, по сути, знание меток (tags) и их предназначение не обязательно.
Базовые принципы
Слияние в каждом конкретном случае следует производить в соответствии с предназначением каждой из веток репозитория. Обратите внимание на минорные версии меток. Мажорная версия устанавливается при крупном обновлении, которое обратно несовместимое и/или когда количество реализованного нового функционал достаточно большое и/или была одна реализация очень важного функционала, влияющего на бизнес-процессы и работу приложения в целом.
Слияние в каждом конкретном случае следует производить в соответствии с предназначением каждой из веток репозитория. Обратите внимание на минорные версии меток. Мажорная версия устанавливается при крупном обновлении, которое обратно несовместимое и/или когда количество реализованного нового функционал достаточно большое и/или была одна реализация очень важного функционала, влияющего на бизнес-процессы и работу приложения в целом. Названия веток, которые используются в данном подходе:
- main - главная ветка, в которой могут быть только метки с версией релиза (например: 1.0.1, 5.25.21, 90.1.200)
- develop - ветка разработки, всегда имеет в наименовании дополнительный суффикс -RC в дополнение к версии ветки main (например: 1.0.1-rc.0, 5.25.21-rc.54, 90.1.200-rc.32). Создается от main всегда есть в репозитории, пока ведется разработки.
- hotfix - ветка критических правок, которая создается от ветки main и вливается только в ветку main. Пример наименования: 1.0.1-hotfix.0, 5.25.21-hotfix.1.1, 90.1.200-hotfix.2. Правки в нижние уровни иерархии передаются простой загрузкой изменений.
- fix - ветка некритических правок, создается из ветки develop и вливается в ветку develop. Правки передаются в верхние уровни иерархии через PR. Пример наименования: 1.199.1-rc.0, 5.25.21-rc.1.4, 90.1.200-rc.32
- feature - верка реализации конкретного функционала. Такой ветки не существую, потому что названия принимаются по правилам:
- содержит номер задачи (task) или истории (backlog), например, 5403-lead-update. Пример наименования меток для данной ветки: 1.0.1-beta.0+akr.1.0, 4.2.101-beta.0+clb.0, 6.0.6-beta.0+ae.11
- содержит краткое описание с типом изменений, например, 5403-lead-update. Еще пример наименования меток для данной ветки: 1.0.1-beta.1.3+akr.22, 4.0.101-beta.2+clb.22.0, 3.3.1-beta.11+ae.11.12
Управление публикациями осуществляется с использованием меток (tags), функциональность которых реализована в GIT. Метки используются как инструмент именования конкретных релизов для конкретных целей: боевой сервер, тестовый сервер, публикация для группы регионов и т.д. и т.п. Другими словами, для разработки ведение полной иерархи не обязательно, но при публикации требуется подставить правильную версию метки. Построение "правильной" иерархии для определения имени метки строиться от ветки main.
Публикация только с меткой
Метки устанавливаются для конкретных коммитов на конкретных ветках, только при условии, если эти коммиты должны будут опубликованы на сервер для тестирования или на боевой сервер для работы в Production. То есть, если не требуется публикация какой-нибудь из версий на какое-либо окружение (сервере) метки в принципе не требуются, но могу быть установлены.
Иерархия версий меток для публикации
Если потребовалось опубликовать дочерние ветки от главной ветки (develop, main), то вся иерархия меток от главной ветки должна быть соблюдена, при этом установка меток не обязательно. Главное требование - соблюдение именования для дочерних веток, которые будут опубликованы.
Новый функционал (Feature branches)
Каждая новая задача для реализации нового функционала получает свою ветку для ее реализации. Если feature может реализовываться обособленно от других, то она должна реализовываться отдельно от других реализаций. Новый функционал вливается в ветку разработки отдельно через PR, при этом обязательно обновляет минорную версию метки (tag).
Схема 1. Пример создания веток для нового функционала (feature) из ветки develop.
Срочные исправления (Hotfix branches)
Из главной ветки создается ветка в которой делаются правки кода. Далее фиксация (commit) вливается через PR в главную ветку, после этого инкрементируется метка (tag). Далее полученные изменения также через PR возвращаются в ветку разработки develop
. При этом метка не создается. Все ветки наследники от develop
должны также "притянуть" последние изменения из ветки разработки к себе, при этом метки (tags) не создаются.
Схема 2. Пример создания срочной правки (hotfix) в главную ветку (main)
Несрочные исправления (Fix branches)
Если исправление не имеет должной срочности (!), то есть не влияет на работу приложения (или влияет не критично) - такое исправления можно и нужно вносить в ветку разработки (develop
). Это значит, что ветка для реализации исправления (fix) следует создавать из develop
. После реализации правок изменения через PR вносятся обратно в ветку разработки, при этом метка в develop инкрементируется. Далее дочерние ветки от develop, должны "притянуть" последние изменения из ветки разработки к себе.
Схема 3. Пример создания несрочной правки (fix) в главную ветку (develop).
Равнозначные ветки с новых функционалом (Developer branches 1)
От ветки разработчика были созданы две ветки для реализации двух независимых задач, причем планируется, что каждая из них будет опубликована для тестирования на разные сервера. На схеме 4-1 две ветки: 7887-send-sms
и 7878-reg-lead
созданы из develop
и поэтому к названию метки добавлены суффиксы "-beta.0
". А так как их реализацию ведут разные разработчики добавлены еще соответствующие суффиксы для идентификации этих разработчиков: "+akr.X" и "+ae.X", где X - это номер версии опубликованной на сервере для тестирования. То есть, и разработчик akr
и разработчик ae
опубликовали сначала версию 1, а после внесения правок (по две фиксации - commits) обновили версию на 2 и снова опубликовали.
Схема 4-1. Пример версионирования разнозначных веток для публикации для теста (разработка)
Равнозначные ветки с новых функционалом (Developer branches 2)
После того, как один из разработчиков завершил тестирование своего функционала на сервере и влил свою реализацию в develop
, минорная версия метки на develop
инкрементируется (см. Схема 4-2).
Схема 4-2. Пример версионирования разнозначных веток для публикации для теста (вливание)
Равнозначные ветки с новых функционалом (Developer branches 3)
Другой разработчик может подлить (см. Схема 4-3) в свою ветку изменения, которые сделать первый разработчик, при этом, обязательно изменив минорную версию своей метки в соответствии с версией, которую вливает в свою ветку.
Схема 4-3. Пример версионирования разнозначных веток для публикации для теста (обновление)
Равнозначные ветки с новых функционалом (Developer branches 4)
На схема 4-4 готовится новая ГЛОБАЛЬНАЯ версия для публикации. Изменение из develop
вливаются в main
через PR, при этом обязательно обновляется минорная версия метки (tag). На схеме новая версия 1.11.0.
Схема 4-4. Пример версионирования разнозначных веток для публикации для теста (global release)
Подготовка Release Candidate
Более сложный вариант использования меток при подготовке публикации для нескольких городов: Новосибирск (+nsk) и Хабаровск (+khv).
О конкретных способах наименований для смежных публикация разных городов (других типов группировки) можно договориться на базе создания более сложных суффиксов. Например: 1.134.0-rc.4+dv (Дальний Восток), 1.134.0-rc.4+region1, 1.134.0-rc.4+east, , 1.134.0-rc.4+russia
Заключение
Я надеюсь, что мне удалось донести основную мысль и специфику используемую в подходе GitTag Flow, а также надеюсь, что вы оставите комментарий, в котором напишите своё мнение отностительно простоты данного подхода. Замечания, предложения, дополнения - также приветствуются.
Комментарии к статье (14)
Мне не понравилось...
Максим,
Спасибо конечно, но так не работает. Может быть напишите что именно не нравится? Или может напишите, как вы отслеживаете версии на разных серверах?
Написана полная хрень.
Видно, что автору не хватает системных знаний. Для того, чтобы управлять большой системой, в том числе и выпуском не релизных версий, нужно хотя бы понимать, что такое Системная Декомпозиция (System Decomposition), которая представляет собой подход, при котором система декомпозируется на более мелкие и понятные элементы для облегчения понимания ее структуры и функционирования.
Цель системной декомпозиции заключается в разбиении сложных систем на более простые компоненты, что позволяет проще анализировать и проектировать систему в целом.
Например, крупное приложение может быть разделено на модули, подсистемы и компоненты для упрощения разработки, сопровождения и масштабирования.
Если вам нужно развернуть и протестировать некую функциональность не релизной версии, то вы должны понимать, что не тестируете код в ветках, а тестируете модули, компоненты, подсистемы или что-то там ещё в зависимости от вашей структурной декомпозиции.
Далее идет процесс интерграции, при котором вы объединяете отдельные программные компоненты в единую систему. Если вы объединяете программные компоненты в единую систему путем слияния веток, то я сочуствую вашим коллегам, которые работают под вашим руководством. Если у вас есть молоток, то все проблемы будут казаться гвоздями. Также и тут - у вас есть Git и все проблемы вам кажутся ветками, мержами, тегами... Бред.
а где вы такие картинки рисовали?
Серёга,
в редакторе Online FlowChart & Diagrams Editor - Mermaid Live Editor
Mike, Спасибо комментарий.
Оставил один из четырех, потому что они все одинаковые. Кстати, неужели не видно было, что на модерацию отправляются комментарии? Очень странно.
Касательно вашего комментария. Очущение, что вы не читали статью, или читали между строк.... Без фактов, без ссылок, без примеров - сплошные абстракции. Вообще о чем речь? Причем тут "системная декомпозиция"? Да и вообще, что значит "системная" в данном контексте? Откуда это? Как "системная декомпозиния" вам поможет управлять ветками? Научите меня пожалуйста, этой самой "системной декомпозиции", очень интересно! Многое видел за 25-летний опыт, но такое впервые. Причем, четыре попытки отправить на модерацию... Жесть! :)
Написана полная хрень.
Видно, что автору не хватает системных знаний. Для того, чтобы управлять большой системой, в том числе и выпуском не релизных версий, нужно хотя бы понимать, что такое Системная Декомпозиция (System Decomposition), которая представляет собой подход, при котором система декомпозируется на более мелкие и понятные элементы для облегчения понимания ее структуры и функционирования.
Цель системной декомпозиции заключается в разбиении сложных систем на более простые компоненты, что позволяет проще анализировать и проектировать систему в целом.
Например, крупное приложение может быть разделено на модули, подсистемы и компоненты для упрощения разработки, сопровождения и масштабирования.
Если вам нужно развернуть и протестировать некую функциональность не релизной версии, то вы должны понимать, что не тестируете код в ветках, а тестируете модули, компоненты, подсистемы или что-то там ещё в зависимости от вашей структурной декомпозиции.
Далее идет процесс интерграции, при котором вы объединяете отдельные программные компоненты в единую систему. Если вы объединяете программные компоненты в единую систему путем слияния веток, то я сочуствую вашим коллегам, которые работают под вашим руководством. Если у вас есть молоток, то все проблемы будут казаться гвоздями. Также и тут - у вас есть Git и все проблемы вам кажутся ветками, мержами, тегами... Бред.
При отправке комментария была ошибка: ошибка 502 bad gateway, а по факту оказалось, что все разы были добавлены.
Сергей, если вам нетрудно, то расскажите пожалуйста, как вы понимаете "интеграцию"? Слияние веток - это интеграция? Какие виды интеграции вы знаете и используете на практике?
Mike,
интеграция обычно подразумевает два объекта. "ЧТО" с "ЧЕМ". Если в контексте статьи, то о чем идёт речь? Что вы имеете в виду?
По-русски же вроде написал: слияние веток - это интеграция? В вашей статье это встречается?
Mike,
1. Слияние веток по-русски, - это "слияние". А по-английски, - это "merging".
2. До сих пор не получается прочитать статью? Может тогда поиск браузера по тексту поможет?
П.С. Херню можно и по-руски писать, потому и уточнил, что по-вашему означает "интеграция".
> По-русски же вроде написал: слияние веток - это интеграция? В вашей статье это встречается?
Как вы считаете, ваша статья полезна кому-либо? Если да, то кому? Знаете ли вы реальных людей и реальные проекты, где это всё применялось?
Mike,
Догадываюсь, почему вы задаете такой вопрос, но отвечу без комментариев, просто фактами: минимум в трех компаниях, где порядка 50 разработчиков используют такой подход с благодарностью. Сколько сейчас у них проектов, я не могу сказать.
> Как вы считаете, ваша статья полезна кому-либо? Если да, то кому? Знаете ли вы реальных людей и реальные проекты, где это всё применялось?
Если такой подход действительно успешно используется на практике такой большой командой, то я возьму это на заметку и, может когда-нибудь ваша статья пригодится мне или команде. Не буду давать категорическую оценку статье, ни хорошую, ни плохую. Но видно, что вы трудились над ней, и это вызывает отдельное уважение.