понедельник, 18 августа 2025 г.

[prog] Мои подходы по именованию веток в git-е

Git славен тем, что работа с ветками в нем возведена чуть ли не в абсолют. Насколько я могу судить, в более-менее вменяемо построенном процессе разработки использование веток -- это обязательная часть внесения изменений в проект: отпочковался от master, внес изменения, взял из master-а свежие правки из коллег, чтобы разрулить конфликты, затем влил свои правки в master.

Это ведет к тому, что при активной работе (особенно активной командной работе) одновременно живет несколько веток. А плюс к этому может быть еще сколько-то, скажем так, позабытых веток. Т.е. веток в которых работа велась, но которые не были по тем или иным причинам доведены до вливания в master. Например, пилили фичу, которую в итоге признали ненужной или преждевременной. Или начали какой-то эксперимент, получили неутешительные результаты и решили не продолжать.

Да, я понимаю, что наличие таких "позабытых и позаброшенных" веток не есть хорошо, но такова се ля ви. Во всех виденных мной проектах они были. Где-то "мусор" время от времени чистят. Но какие-то ветки просто так удалять и не хочется -- вот те же самые результаты неудачных экспериментов. В них могут оставаться какие-то наработки, которые могут оказаться тем или иным образом востребованы со временем. Хотя бы для того, чтобы подсмотреть как уже делали и что именно не получилось.

Так вот, ближе к теме разговора: когда в Git-репозитории одновременно существует несколько десятков веток, надо бы как-то в этом многообразии разбираться. А значит нужна какая-то система именования веток, чтобы сделав git branch можно было понять где, что и как.

С большим удивлением для себя регулярно обнаруживаю, что часто в именованиях веток нет вообще никакой системы. Обычно встречаются ветки с именами "bug-fix" или "memory-usage". Иногда к именам веток добавляются префиксы типа "bug/" (тогда получаются имена вида "bug/login-crach" или "bug/memory-leak") или "feature/" (тогда получаются имена вида "feature/open-auth-login" или "feature/sparse-table"), или "experimental/" (тогда получаются имена вида "experimental/simd-json"). Хотя, как по мне, эти префиксы не особо-то и полезны.

Но самое плохое, что лично мне такие имена ни о чем не говорят 🙁

Ситуация получше когда для всех задач проекта есть тикеты в условной JIRA и номер тикета. Особенно когда к номеру тикета добавляется еще что-то поясняющее, вроде "issue-1416-attempt-2". Но и это не идеал.

Наблюдая за подобным решил описать два своих подхода к именованию веток, которые я использую в несколько разных условиях. Ни в коем случае не как образец для подражания, а просто в рамках обмена опытом.


Первая схема используется при работе над нашими открытыми проектами, вроде SObjectizer и RESTinio.

Там имя ветки строится из трех компонентов:

  • имя мажорной версии продукта. Например, сейчас развивается версия 5.8 для SObjectizer. Поэтому первая составляющая будет иметь вид "5.8-dev", т.е. основная dev-ветка для версии 5.8;
  • имя конкретной версии продукта. Например, сейчас в работе версия 5.8.5, которая пока до релиза не дошла. Поэтому вторая составляющая будет иметь вид "5.8.5";
  • краткое именование того, над чем идет работа. Например, "reduce-state-size". Возможно, с указанием того, какая это итерация, например, "reduce-state-size-v2".

В результате получаются ветки с именами "5.8-dev-5.8.5-reduce-state-size-v2".

Чтобы не быть голословным, вот имена нескольких веток, которые можно сейчас увидеть в репозитории SObjectizer-а:

5.8-dev
5.8-dev-5.8.5
5.8-dev-5.8.5-cmake-files-update
5.8-dev-5.8.5-issue-96
5.8-dev-5.8.5-state-time-limit-v2
5.8-dev-5.8.5-issue-93
...

Можно обратить внимание, что имена образуют иерархичность и указывают что и куда затем должно вливаться. Так, изменения из "5.8-dev-5.8.5-cmake-files-update" будут влиты в "5.8-dev-5.8.5", а затем изменения из "5.8-dev-5.8.5" будут влиты в "5.8-dev".

Как-то у меня не прижился прямой слэш в качестве разделителя, поэтому мне удобнее работать с именами вида "5.8-dev-5.8.5-cmake-files-update", а не с "5.8-dev/5.8.5/cmake-files-update". Вкусовщина, конечно же, но у меня вот так.


Вторая схема используется мной когда я работаю консультантом на внешнем проекте и имею доступ к репозиторию заказчика. В этом случае имена создаваемых мной веток также состоят из трех составляющих:

  • никнейм основного автора кода в ветке, в моем случае это "eao197";
  • дата начала работы над веткой в формате YYYYMMDD;
  • краткое именование того, над чем идет работа. Например, "reduce-state-size". Возможно, с указанием того, какая это итерация, например, "reduce-state-size-v2".

Что в результате дает имена веток вида: "eao197-20250814-versioned-data-v2-tmp". Если для задачи есть номер тикета, то он включается в третью часть. Типа "eao197-20250818-issue-77889-attempt-1".

Такое именование лично для меня оказывается полезным тем, что:

  • сразу видно кто отвечает за ветку. В частности, мне легко понято что было сделано (и/или забыто) лично мной;
  • степень "свежести" изменений. Когда прямо в имени ветки видишь, что она относится к декабрю 2023-го года, то это сразу наводит на мысль, что код в ней довольно древний. Можно даже не делать git log 😉

К счастью, пока мне за такую схему по рукам не били. Видимо, она устраивает не только меня. По крайней мере хочется в это верить.


Интересно, а какие схемы именования веток в git-е вы считаете удобными и, может быть, даже практикуете?

3 комментария:

Ievgen комментирует...

Мой подход к именованию тикетов такой:
всегда есть префикс — имя разработчика и номер тикета, по которому можно понять детали разработки. Может, это и не так важно, но мы не делаем merge напрямую — только merge в чистую ветку после rebase.

Пример имени: `vp/JIRA-123`
Здесь `vp` — аббревиатура разработчика Vasya Pupkin.
Слэш удобен для:
- сворачивания по папкам, когда разработчиков много (в GitKraken можно скрыть чужие ветки или смотреть только свои);
- быстрого перехода в Jira, чтобы проверить состояние тикета, понять, какому разработчику принадлежит ветка, и попросить её удалить;
- очистки веток за уволившимися разработчиками.

Также можно использовать:
- `vp/feature-name/JIRA-123`
- `vp/topic/feature/JIRA-123`

Это полезно, если в одном тикете несколько задач, которые можно делать отдельно, или есть побочные задачи.

Ievgen комментирует...

В догонку про версии:
у нас стабильные версии хранятся в отдельных ветках. Например, ветка со стабильной версией называется `/stable/5.8`.

Фикс сначала делаем в `main`.
Если нужно сделать backport багфикса в стабильный бранч, создаём ветку вида:
- `vp/backport/JIRA-123`
- `vp/backport/5.8/JIRA-123` (если нужно несколько backport для конкретной версии).

Для тестирования деплоим релиз с именем ветки в приложении (по правилам SemVer v2):
- `5.8-vp-backport-JIRA-123.164+b7718` — ветка от stable branch

Нумерация:
- `5.8` — major и minor версия
- `164` — patch (порядковый номер коммита в ветке релиза)

eao197 комментирует...

Спасибо, очень интересно!