-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
80 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
--- | ||
title: "Упрощаем написание сообщений коммитов git" | ||
slug: "git-simple-commit-msg" | ||
date: 2024-01-29 | ||
tags: ["linux", "git"] | ||
description: "Как автоматизировать процесс написания сообщений для коммитов git" | ||
keywords: ["linux", "git", "коммиты", "автоматизация", "туториал"] | ||
--- | ||
В этой заметке я расскажу о том, как автоматизировать процесс написания сообщений для коммитов git. | ||
## Постановка задачи | ||
Во многих командах приняты различные соглашения об именовании веток и написании сообщений коммитов git, позволяющие упростить понимание истории изменений. | ||
|
||
В одном из репозиториев, c которым я взаимодействую по долгу службы, используется подход [GitHub Flow](https://habr.com/ru/articles/346066/) и приняты следующие соглашения: | ||
|
||
**Именование веток.** Для решения каждой задачи от master-ветки разработчиком создается отдельная feature-ветка. Название такой ветки должно иметь вид: `abc-xxx-component-feature_title` | ||
- `abc-xxx` - индекс задачи в таск-трекере. Состоит из: | ||
- `abc` - сокращенное название проекта | ||
- `xxx` - порядковый номер задачи | ||
- `component` - название компонента, в который вносятся изменения | ||
- `feature_title` - краткое описание задачи | ||
|
||
**Сообщения коммитов.** Первая строчка сообщения коммита должна иметь вид: `ABC-xxx: [?] component: some comment` | ||
- `ABC-xxx` - индекс задачи в таск-трекере | ||
- `?` - тип изменения: | ||
- `+` - добавление функциональности | ||
- `-` - удаление функциональности | ||
- `*` - изменение функциональности | ||
- `component` - название компонента, в который вносятся изменения | ||
- `some comment` - описание изменений | ||
|
||
Во время работы мне часто приходится переключаться между несколькими feature-ветками и создавать в них коммиты. Написание сообщений занимает много времени: после каждой смены ветки приходится либо писать сообщение коммита с нуля, либо рыскать по истории команд в поисках предыдущего коммита, относящегося к нужно фиче. | ||
### Что хочу | ||
Автоматизировать процесс создания коммита. Не прописывать индекс задачи и название компонента в сообщении вручную, а парсить их из названия текущей ветки. | ||
## Решение | ||
Для работы с git-ом я использую терминальные команды. Подробно о настройках своего терминала уже рассказывал в [этом посте](https://yu-leo.github.io/yu0dev/posts/my-terminal/). Кратко: [zsh](https://www.zsh.org/) + [oh-my-zsh](https://ohmyz.sh/) + плагины (в частности - плагин [git](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/git)). Поэтому я решил не изобретать велосипед, а использовать возможности, предоставляемые этим набором. | ||
|
||
Реализация задуманного умещается в две функции, которые нужно описать в конфиге zsh-а (`~/.zshrc`). По сути они являются простыми [unix pipeline](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BD%D0%B2%D0%B5%D0%B9%D0%B5%D1%80_(Unix))-ами: | ||
|
||
```zsh | ||
function task_index() { | ||
current_branch | grep -Po "^[a-zA-Z]*-\d*(?=-)" | tr "[:lower:]" "[:upper:]" | ||
} | ||
|
||
function component_name() { | ||
current_branch | grep -Po "(?<=\d-)\w*(?=-)" | head -n 1 | tr "[:upper:]" "[:lower:]" | ||
} | ||
``` | ||
### Объяснение | ||
- `current_branch` - функция, возвращающая название текущей git-ветки. Предоставляется плагином [git](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/git) для oh-my-zsh | ||
- `grep -Po "^[a-zA-Z]*-\d*(?=-)"` - парсинг индекса задачи | ||
- `tr "[:lower:]" "[:upper:]"` - преобразование строчных символов индекса задачи в заглавные | ||
- `grep -Po "(?<=\d-)\w*(?=-)"` - парсинг название компонента | ||
- `head -n 1` - взятие первой строки результата. Теоретически название ветки может содержать несколько подстрок, удовлетворяющих описанной выше регулярке. Например, из строки "*abc-123-component3-some-feature-2*" `grep` спарсит две подстроки: "*component3*" и "*some*". Нас интересует только первая из них | ||
- `tr "[:upper:]" "[:lower:]"` - преобразование заглавных символов названия компонента в строчные. На случай, если название ветки будет содержать заглавные буквы :) | ||
- [Документация по Perl-like регуляркам](https://perldoc.perl.org/perlre) | ||
|
||
❗ Для работы Perl-совместимых регулярных выражений (`-P`) **на MacOS** нужно использовать утилиту `ggrep` (GNU grep), вместо дефолтного `grep`. Поставить её можно командой `brew install grep` ([stackoverflow](https://stackoverflow.com/questions/16658333/grep-p-no-longer-works-how-can-i-rewrite-my-searches)) | ||
### Пример использования | ||
```shell | ||
git commit -m "$(task_index): [*] $(component_name): some feature" | ||
``` | ||
Типичная последовательность действий при разработке новой фичи. Помимо описанных выше функций используем [алиасы](https://github.com/Yu-Leo/knowledge-base/blob/main/git-tutorials/oh-my-zsh-git.md): | ||
```shell | ||
gcm # Переключается на master-ветку | ||
gpr # Пуллим изменения из удалённого репозитория | ||
gcb abc-1234-super_service-super_feature # Создаём feature-ветку | ||
# Кодим фичу... | ||
gaa # Добавляем изменения в индекс git-а | ||
gc -m "$(task_index): [*] $(component_name): super feature" # Коммитимся | ||
gpsup # Пушим ветку и изменения в удалённый репозиторий | ||
``` | ||
|
||
В итоге получаем коммит с сообщением "`ABC-1234: [*] super_service: super feature`". | ||
## Альтернатива. git hooks | ||
Для решения этой задачи можно использовать механизм [git-hooks](https://git-scm.com/docs/githooks). Процесс его настройки для этих целей подробно описан в [статье](https://habr.com/ru/companies/dins/articles/584562/). | ||
|
||
Однако мне не нравится данный способ из-за следующих **недостатков**: | ||
- Нужно помнить правила работы хуков в различных ситуациях. В противном случае хуки могут поломать ваши сообщения коммитов так, что вы даже не заметите, пока не посмотрите историю коммитов. Конечно, с последовательностью действий "*добавить в индекс -> закоммитить -> запушить*" вопросов не возникает. Однако в более сложных ситуациях - `git rebase`, `git rebase -i`, `git commit --amend` и проч. - хуки могут сработать не очевидным на первый взгляд образом | ||
- Написание сообщений коммитов становятся менее явными. При каждом использовании команды `git commit -m "..."` нужно помнить про, что написанный текст будет отредактирован хуком | ||
- Хуки неудобно использовать для части репозиториев. Как пишет сам автор, есть два способа включить хуки: "*Поместить их в специальную директорию .git/hooks в каждый (!) проект*." и "*более элегантный, если подразумевается, что ваши хуки применимы для всех проектов. Нужно добавить в глобальный конфиг Git параметр core.hooksPath, значение которого содержит путь до глобальной папки с хуками, откуда Git будет все тянуть в первую очередь*." |