company_banner

Как генерировать осмысленные коммиты. Применяем стандарт Conventional Commits



    Привычный хаос в названиях коммитов. Знакомая картина?

    Наверняка вы знаете git-flow. Это отличный набор соглашений по упорядочиванию работы с ветками в Git. Он хорошо документирован и широко распространен. Обычно мы знакомы с правильным ветвлением и много говорим об этом, но, к сожалению, уделяем слишком мало внимания вопросу наименования коммитов, поэтому часто сообщения в Git пишутся бессистемно.

    Меня зовут Ержан Ташбенбетов, я работаю в одной из команд Яндекс.Маркета. И сегодня я расскажу читателям Хабра, какие инструменты для создания осмысленных коммитов мы используем в команде. Приглашаю присоединиться к обсуждению этой темы.


    Отсутствие договоренностей при наименования коммитов затрудняет работу с историей в Git. Такое было в нашей команде. До использования общего для всех регламента и внедрения автоматизации типичные коммиты выглядели следующим образом:

    SECRETMRKT-700: пропали логотипы партнеров
    Приложение падает, поправил.
    SECRETMRKT-701, SECRETMRKT-702: Отцентрировал картинки на всех ...

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

    По этой причине мы внедрили в команде стандарт Conventional Commits, стали генерировать коммиты в консольной утилите commitizen и проверять результат с помощью commitlint. В результате коммиты изменились и стали выглядит так:

    refactor(tutorial): оптимизировать работу эпиков в тултипах
    feat(products): добавить банер с новогодними скидками
    fix(products): исправить в банере формат даты

    Читать историю и распознавать внесенные изменения стало проще. Мы не отказались от указания номеров задач, всё аккуратно перенесено внутрь коммитов согласно конвенции Conventional Commits.

    Дальше я расскажу, как добиться схожего порядка в Git.



    Лучшие практики, рекомендации и распространенные решения при наименовании коммитов


    Если попробовать разобраться в том, какие практики применяются в индустрии, то можно обнаружить следующие варианты:

    • Статьи с общими советами по написанию коммитов. По большей части они вполне логичны и неплохо раскрывают тему, но чувствуется беспорядочность и отсутствие комплексного решения вопроса.
    • Стандарты по написанию коммитов. Их немного. Они представляют собой документы с четким перечнем правил, довольно часто написанных специально для крупной библиотеки или фреймворка. Эти стандарты подкупают системным подходом, популярностью и поддержкой в open-source сообществе.

    Нам нужно больше порядка в коммитах!

    Методология Conventional Commits выделяется на фоне других стандартов и заслуживает пристального изучения по ряду причин:

    1. Она хорошо документирована и проработана. В её спецификации даны ответы на наиболее распространенные вопросы.
    2. Создатели конвенции вдохновились требованиями к написанию коммитов, которые используются в популярном и проверенном временем фреймворке AngularJS.
    3. Правил конвенции придерживаются несколько крупных и популярных open-source библиотек (таких как yargs и lerna).
    4. К плюсам отнесу подготовку к автоматическому формированию Release Notes и Change Log.

    Пример коммита по этому стандарту:

    fix(products): поправить длину строки с ценой
    
    Часть заголовков неправильно отображается в мобильной версии из-за ошибок
    в проектировании универсальных компонентов.
    
    МЕТА ДАННЫЕ: SECRETMRKT-578, SECRETMRKT-602



    Основные тезисы Conventional Commits


    • Разработчик должен придерживаться следующей структуры коммитов:
      <type>(<scope>): <subject>

      <body>

      <footer>
    • У коммита должен быть заголовок, может быть тело и нижний колонтитул.
    • Заголовок коммита должен начинаться с типа (type), указывающего на специфику внесенных в кодовую базу изменений, и завершаться описанием.
    • Наряду с обязательными feat, fix (использование которых строго регламентировано), допускаются и другие типы.
    • У коммита может быть область (scope). Она характеризует фрагмент кода, которую затронули изменения. Область следует за типом коммита. Стандарт не регламентирует четкий список областей. Примеры областей: eslint, git, analytics и т.д.
    • Описание коммита должно быть сразу после типа/области.
    • Тело коммита может быть использовано для детализации изменений. Тело должно быть отделено от описания пустой строкой.
    • Нижний колонтитул следует использовать для указания внешних ссылок, контекста коммита или другой мета информации. Нижний колонтитул должен быть отделен от тела пустой строкой.


    Кроме перечисленых в конвенции правил мы используем следущие популярные рекомендации:


    • В теле коммита пишем что было изменено и почему.
    • Используем следующие типы коммитов:
      build Сборка проекта или изменения внешних зависимостей
      ci Настройка CI и работа со скриптами
      docs Обновление документации
      feat Добавление нового функционала
      fix Исправление ошибок
      perf Изменения направленные на улучшение производительности
      refactor Правки кода без исправления ошибок или добавления новых функций
      revert Откат на предыдущие коммиты
      style Правки по кодстайлу (табы, отступы, точки, запятые и т.д.)
      test Добавление тестов
    • Пишем описание в повелительном наклонении (imperative mood), точно также как сам Git.
      Merge branch 'fix/SECRETMRKT-749-fix-typos-in-titles'
    • Не закачиваем описание коммита знаками препинания.




    Стандарт коммитов Conventional Commits используют котрибьюторы lerna



    Как просто перейти на правильное наименование коммитов?


    Нужно добавить автоматизации и удобства. Для решения этого вопроса нам потребуется два инструмента: генератор коммитов и линтер коммитов, настроенный на проверку перед пушем в репозиторий.



    Настроим утилиту commitizen


    Этот инструмент позволяет генерировать коммиты при помощи встроенного визарда. Кроме того, commitizen хорошо поддерживается сообществом и, благодаря дополнительным модулям, отлично настраивается.

    1. Установим утилиту commitizen глобально (вам могут потребоваться права администратора).

      npm i -g commitizen
    2. Следом установим адаптер cz-customizable. Он нужен для настройки шаблона с вопросами, которым пользуется утилита commitizen.

      npm i -D cz-customizable
    3. Создадим файл commitizen.js, он нужен для настройки cz-customizable. Поместим созданный файл в директорию ./config/git. Рекомендую не захламлять корень проекта конфигурационными файлами и стараться группировать файлы в подготовленной для этого папке. Содержимое:

      Показать commitizen.js
      "use strict";
      
      module.exports = {
        // Добавим описание на русском языке ко всем типам
        types: [
          {
            value: "build",
            name: "build:     Сборка проекта или изменения внешних зависимостей"
          },
          { value: "ci", name: "ci:        Настройка CI и работа со скриптами" },
          { value: "docs", name: "docs:      Обновление документации" },
          { value: "feat", name: "feat:      Добавление нового функционала" },
          { value: "fix", name: "fix:       Исправление ошибок" },
          {
            value: "perf",
            name: "perf:      Изменения направленные на улучшение производительности"
          },
          {
            value: "refactor",
            name:
              "refactor:  Правки кода без исправления ошибок или добавления новых функций"
          },
          { value: "revert", name: "revert:    Откат на предыдущие коммиты" },
          {
            value: "style",
            name:
              "style:     Правки по кодстайлу (табы, отступы, точки, запятые и т.д.)"
          },
          { value: "test", name: "test:      Добавление тестов" }
        ],
      
        // Область. Она характеризует фрагмент кода, которую затронули изменения
        scopes: [
          { name: "components" },
          { name: "tutorial" },
          { name: "catalog" },
          { name: "product" }
        ],
      
        // Возможность задать спец ОБЛАСТЬ для определенного типа коммита (пример для 'fix')
        /*
        scopeOverrides: {
          fix: [
            {name: 'style'},
            {name: 'e2eTest'},
            {name: 'unitTest'}
          ]
        },
        */
      
        // Поменяем дефолтные вопросы
        messages: {
          type: "Какие изменения вы вносите?",
          scope: "\nВыберите ОБЛАСТЬ, которую вы изменили (опционально):",
          // Спросим если allowCustomScopes в true
          customScope: "Укажите свою ОБЛАСТЬ:",
          subject: "Напишите КОРОТКОЕ описание в ПОВЕЛИТЕЛЬНОМ наклонении:\n",
          body:
            'Напишите ПОДРОБНОЕ описание (опционально). Используйте "|" для новой строки:\n',
          breaking: "Список BREAKING CHANGES (опционально):\n",
          footer:
            "Место для мета данных (тикетов, ссылок и остального). Например: SECRETMRKT-700, SECRETMRKT-800:\n",
          confirmCommit: "Вас устраивает получившийся коммит?"
        },
      
        // Разрешим собственную ОБЛАСТЬ
        allowCustomScopes: true,
      
        // Запрет на Breaking Changes
        allowBreakingChanges: false,
      
        // Префикс для нижнего колонтитула
        footerPrefix: "МЕТА ДАННЫЕ:",
      
        // limit subject length
        subjectLimit: 72
      };


    4. Добавим в package.json ссылки на cz-customizable и созданный ранее конфигурационный файл:

      Показать часть package.json
      {
        "config": {
          "commitizen": {
            "path": "node_modules/cz-customizable"
          },
          "cz-customizable": {
            "config": "config/git/commitizen.js"
          }
        },
      }

    5. Давайте проверим получившийся результат. Наберите в терминале следующую команду:

      git cz

    Визард commitizen сначала соберет информацию о типе, области коммита, затем последовательно запросит текст, который будет в описании, в теле, в нижнем колонтитуле и после вашего согласия создаст коммит.

    Обязательно посмотрите на пример работы настроенной утилиты commitizen и подключенного к нему адаптера cz-cusomizable



    Настроим утилиты husky и commitlint


    1. Установим в проект husky и commitlint:

      npm i -D husky @commitlint/cli
    2. С помощью husky добавим проверку коммитов. Для этого в package.json сразу после скриптов добавим следующий хук и укажем в нем ссылку на файл commitlint.js:

      Показать часть package.json
      {
        "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1"
        },
        "husky": {
          "hooks": {
            "commit-msg": "commitlint -E HUSKY_GIT_PARAMS -g './config/git/commitlint.js'"
          }
        },
        "devDependencies": {
          "@commitlint/cli": "^7.2.1",
          "husky": "^1.1.3",
      }


    3. Создадим файл commitlint.js, необходимый для корректной работы линтера. Поместим созданный файл в директорию ./config/git. Содержимое файла:

      Показать commitlint.js
      // Файл создан на основе @commitlint/config-conventional
      
      module.exports = {
        rules: {
          // Тело коммита должно начинаться с пустой строки
          "body-leading-blank": [2, "always"],
      
          // Нижний колонтитул коммита должен начинаться с пустой строки
          "footer-leading-blank": [2, "always"],
      
          // Максимальная длина заголовка 72 символа
          "header-max-length": [2, "always", 72],
      
          // Область всегда только в нижнем регистре
          "scope-case": [2, "always", "lower-case"],
      
          // Описание не может быть пустым
          "subject-empty": [2, "never"],
      
          // Описание не должно заканчиваться '.'
          "subject-full-stop": [2, "never", "."],
      
          // Тип всегда только в нижнем регистре
          "type-case": [2, "always", "lower-case"],
      
          // Тип не может быть пустым
          "type-empty": [2, "never"],
      
          // Перечислим все возможные варианты коммитов
          "type-enum": [
            2,
            "always",
            [
              "build",
              "ci",
              "docs",
              "feat",
              "fix",
              "perf",
              "refactor",
              "revert",
              "style",
              "test"
            ]
          ]
        }
      };



    Всё. Теперь все коммиты будут проверяться перед отправкой в репозиторий :)

    Обязательно посмотрите на пример работы настроенной утилиты commitlint



    Так что выбрать commitizen или commitlint?


    И то, и другое! В связке они приносят отличный результат: первый генерирует коммиты, второй их проверяет.


    Почему стандарты рекомендуют использовать повелительное наклонение?


    Это крайне интересный вопрос. Коммит это изменение кода, сообщение в коммите можно расценивать как инструкцию по изменению этого кода. Сделать, изменить, добавить, обновить, поправить  — всё это конкретные инструкции для разработчика.

    Кстати, повелительное наклонение рекомендовано в самой системе версионирования Git:

    [[imperative-mood]]
    Describe your changes in imperative mood, e.g. "make xyzzy do frotz"
    instead of "[This patch] makes xyzzy do frotz" or "[I] changed xyzzy
    to do frotz", as if you are giving orders to the codebase to change
    its behavior.


    Зачем придерживаться каких-либо конвенций? Стоит ли тратить на это время? Какой в этом профит?


    Стоит. В целом я заметил, что мы стали охотнее детализировать изменения, внесенные в кодовую базу. В теле коммита мы подробно расписываем почему пришлось использовать те или другие решения. Разбираться в истории стало объективно проще. Плюс наш продукт развивается, и мы ожидаем пополнения в команде. Уверен, что благодаря внедрению стандарта и автоматизации новичкам будет легче встроиться в процесс разработки.

    Попробуйте и поделитесь результатом.


    Полезные ссылки:


    Яндекс

    786,00

    Как мы делаем Яндекс

    Поделиться публикацией
    Комментарии 74
      +4
      Отличный материал по теме chris.beams.io/posts/git-commit
      +11
      image
        +8
        По-моему, если у вас большинство комментариев типа «поправил баг» и «небольшое изменение», то у вас куда более серьёзные проблемы. Нужно начинать с них, требуя у разрабов понятного описания (возможно, с обязательной ссылкой на задачу в багтрекере), а потом уже возиться с оформлением. Или делать это одновременно, но описание должно быть приоритетней.
          0
          Спасибо за комментарий. Скриншот в начале статьи немного приукрашивает действительность, но спешка и отсутствие договоренностей действительно приводят к подобному результату. Правила и автоматизация помогают избегать подобного.
          +9
          fix(products): поправить длину строки с ценой

          Часть заголовков неправильно отображается в мобильной версии из-за ошибок
          в проектировании универсальных компонентов.

          МЕТА ДАННЫЕ: SECRETMRKT-578, SECRETMRKT-602

          Мне кажется, что тут возникла небольшая ошибка. Коммит — не то, что должно быть сделано, а то, что уже сделано. Что нужно сделать должно быть написано в таске где-то или в требованиях. Как я понял, в этом примере такая информация находится в SECRETMRKT-578, SECRETMRKT-602. Т.е. верный вариант будет
          fix(products): поправлена длина строки с ценой

          Часть заголовков неправильно отображались в мобильной версии из-за ошибок
          в проектировании универсальных компонентов.

          МЕТАДАННЫЕ: SECRETMRKT-578, SECRETMRKT-602

          И тогда автоформирование Change Log уже взлетит само собой.

          UPD: Да, не дочитал. В статье об этом есть в конце. Но мне всё равно кажется это несколько странным.
            +3
            В статье несколько раз упоминается про повелительное наклонение которое рекомендуется использовать самим гитом, поэтому и получается «сделать, исправить» и т.д. Сам я если честно тоже плохо воспринимаю в таком формате коммиты, как будто задача не завершена этим коммитом, а наоборот коммит породил задачу.
              0
              А мне норм: коммит — указание VCS сделать какие-то изменения в кодовой базе. Особенно хорошо, если сообщение коммита копируется из названия задачи — типа я делегирую VCS задачу, назначенную на меня, детализируя какие конкретно изменения сделать, чтобы задача была решена
                +2
                Предположу, что так сделано из-за идеи в гите с переписыванием истории коммитов. В английском варианте:
                If I (merge|cherry-pick|rebase|use|...) this commit it will (commit message):
                If I use this commit it will «Add logout button to home page».

                Это позволяет мейнтейнерам буквально по кусочкам собирать нужную цепочку из сотен коммитов и веток.

                По-русски, наверное, стоит перефразирвать, т.к. слова меняются из-за склонения и времени.
                  +3
                  Проблему решает написание текста коммита на английском языке. И конвеншн соблюдаем и выглядит единообразно, и язык тренируем, и вообще одни плюсы.
                    +2

                    На русском повелительное не приживётся в коммитах — просто потому, что сами мы в разговоре никогда так коммит не опишем. Коммит это выполненная работа, и всегда описывается как "что было сделано", "что изменилось". Я сам за следование какому-то соглашению, но оно должно выбираться так, чтобы вызывать минимальное сопротивление, быть достаточно близко к тому, что люди склонны использовать и без явных соглашений — потому что наличие соглашения намного важнее его конкретики. (И под "на русском" я имею в виду не язык, на котором пишется сообщение коммита, а язык, на котором думает программист.)

                      0
                      Так никто так коммит в разговоре не описывает, ни по-русски, ни по английски. Но соглашение уже как бы выбрано, так что или иму следуем, или все переделываем.
                        0
                        Инфинитив удобно использовать в названиях pull/merge реквестов, как отражение самих задач для разработки (сделать, добавить, исправить, ...). Они в свою очередь порождают рабочие ветки в git, а по завершении (squash & merge) сопровождаются комментарием в нужном стиле и аккуратно укладываются в master.

                        Правки внутри рабочих веток удобно именовать в стиле бортового журнала (т.е. как угодно), т.к. они отражают ваш путь решения задачи. Чем сложнее задача, тем путь может быть более удивительными и непредсказуемым. Здесь главное не мешать творческому процессу — чем меньше церимоний тем лучше. Во время squash все эти шаги будут удалены или переписаны.

                        Если вдруг покажется, что вливаемые в master изменения слишком крупные или слишком мелкие, или неочевидные, то значит херня творится на уровне постановки задач. Может в работу под видом задачи на разработку попадают сразу стори или целые эпики, или какие-то невнятные хотели. В таком случае дрючить разработчиков за именоваание комитов бесполезно.
                        0
                        Мы пару лет в студии писали коммиты на английском (без использования методологий). В итоге получилось так что когда пришлось читать историю одного проекта все осознали что больше половины коммитов непонятно о чем. В итоге пришли к тому что лучше для коммитов оставить только русский.

                        Я понимаю что проблема тут кроется во владении английским языком, у всех разный уровень и язык надо учить, но все таки на родном языке стали получаться более осознанные описания изменений.
                      +2

                      Думаю это целая тема для обсуждения, вот мои соображения, которые являются результатом многократных споров и обсуждений с моими коллегами.


                      Для коммитов на русском языке просто следовать этому правилу: "Используйте повелительное наклонение", будет не благозвучно и думаю не правильно с точки зрения русского языка, тут говорится: "Use the imperative mood in the subject line" и поясняется, что заголовок должен быть продолжением фразы: "If applied, this commit will your subject line here", пример:


                      If applied, this commit will update getting started documentation
                      В случае применения этого коммита будет обновлено руководство по началу работы

                      Без начала предложения т.е. без глагола will в английском языке получается imperative mood:


                      update getting started documentation
                      обновить руководство по началу работы

                      а в русском языке, если не ошибаюсь, это инфинитив:


                      обновить руководство по началу работы
                      обновить конфигурацию commitizen
                      добавить хук забытый в прошло коммите

                      а повелительное наклонение образуется иначе:


                      обновите руководство по началу работы
                      обновите конфигурацию commitizen
                      добавьте хук забытый в прошло коммите

                      Если следовать трюку с продолжением фразы, то данное правило можно свести до:


                      Заголовок комментария должен формулироваться, как продолжение фразы: В случае применения этого коммита будет...

                      В таком случае комментарии получаю такой вид:


                      обновлено руководство по началу работы
                      обновлена конфигурация commitizen
                      добавлен хук забытый в прошло коммите
                      исправлена ошибка…
                      добавлена возможность…
                      улучшена возможность...

                      Я далеко не эксперт в русском и тем более в английском, но определенно такой подход более логичный во всех смыслах история лаконично читается и на ее основе можно генерировать список изменений.


                      Мне будут очень интересны другие мнения на эту тему.

                        0
                        Логика есть, но вот не уверен что русский вариант правилен. «If applied, this commit will update getting started documentation» скорее переводится как «Будучи примененным этот коммит будет обновлять руководство ...» или более благозвучно без изменения грамматики основной части «При применении этот коммит будет обновлять руководство ...». То есть тот же инфинитив по сути, неопределнная форма глагола в изоляции и (простое) будущее в сочетании с будет/will

                        ЗЫ

                        обновлять руководство по началу работы
                        обновлять конфигурация commitizen
                        добавлять хук забытый в прошло коммите
                        исправлять ошибку
                        добавлять возможность
                        улучшать возможность
                          0
                          При применении этот коммит будет обновлять руководство

                          Так обновит в итоге или нет? Несовершенный вид, как мне кажется, тут вообще не подходит.
                            0
                            Возможно, тогда «не будет обновлять», а «При применении этот коммит обновит, добавит, исправит, улучшит...» В принципе при чтении коммитов перед мержем/пуллом очень даже логично выглядит — какой эффект от применения коммита будет
                      0
                      Как по мне, на английском языке коммиты в повелительном наклонении воспринимаются хорошо. Само собой получается так их писать.
                      А на русском выглядит очень странно. Да.
                      +3
                      Погуглил — есть тулзы и на других языках (питон например), если кому не охота `npm` юзать по причинам частых проблем с безопасностью или отсутствием необходимости иметь npm.
                        +1
                        Отличный материал, спасибо! Есть вопросы:
                        1. Список изменений (changelog.md) генерируете на основе истории или нет (есть мнение, что это не лучший способ), какой подход у вас к созданию и поддержке истории изменений?
                        2. Применяете git-flow или свой поход? Расскажите подробнее.
                        3. Интересно как относитесь к линейной истории это полезно или нет? Мое мнение, что линейная или частично линейная история дает массу профитов и даже применяя git-flow история может быть линейная, если приложить немного усилий, но мало кто видит в этом преимущества, давно хочу понять почему. На текущий момент гипотиреоза в том, что мало кто использует историю как инструмент анализа или может быть есть иной способ сохранить блейм или анализировать историю в незавидности от ее линейности.
                          +2
                          По поводу истории изменений, можно я вклинюсь с отличным описанием того, почему её следует поддерживать вручную?

                          keepachangelog.com/en/1.0.0
                            +1
                            Спасибо. Могу поделиться опытом нашей команды:

                            1. Change Log не ведем, есть Release Notes. Перед каждым Pull Request-ом разработчик заполняет в файле ReleaseNotes.md следующие пункты:
                            — New (Что нового? (фичи, новая функциональность));
                            — Improvements (Какие улучшения мы добавили в существующие фичи);
                            — Fixes (Список исправленных багов);
                            — Operations (Eslint, обновление библиотек и т.д.).

                            На основе этого файла формируются Release Notes каждой версии. Этот документ смотрят QA специалисты. После публикации версии содержимое файла ReleaseNotes.md обнуляется.

                            2. Выбрали классический git-flow (хотя сам я сторонник другого ветвления). Это правильно по многим причинам, но для меня главная — открытость к ротациям в команде. Отраслевые стандарты позволяют свести начальный инструктаж к минимуму.

                            3. Не задумывался линейна или не линейная история. Использую в работе удобный GitKraken, всё наглядно. За ссылки благодарю, изучу внимательно этот вопрос.
                              0
                              1. Перед каждым Pull Request-ом разработчик заполняет в файле ReleaseNotes.md

                              Это один файл на проект? Учитывая конвенцию и генерацию коммитов что мешает это файл формировать автоматически?

                              2. Выбрали классический git-flow (хотя сам я сторонник другого ветвления)

                              Другого ветвления, это какого?
                              Учитывая Ваш опыт применения git-flow, представите, что будет с историей, если каждый разработчик будет синхронизировать свою рабочую ветку не слиянием с develop, а через перемещение git pull --rebase origin develop?
                                –1
                                Ай ай ай, бесплатный GitKraken запрещён для коммерческого использования
                                0
                                0
                                мы стараемся придерживаться строго линейной истории, но вот недавно перешли со строгого --no-ff на строгий --ff, что бы были видны мерж коммиты.

                                Ребейз перед мержем в мастер у нас прям обязателен, потому что лучше разрешить все конфликты до мержа, но получаются force пуши в ветки.
                                  +1

                                  --ff делает так, чтобы были видны мёрж коммиты?

                                0
                                И какой смысл? Огромное количество изменений в коммите просто убивает всю историю, никто не сможет понять что там изменилось. Практика микрокоммитов и проблемы в неймниге отпадет само собой.
                                  0
                                  Мой опыт говорит обратное, если вести историю по правилам, то как минимум можно воспользоватся git changelog, дает вполне адекватный материал, который можно обработать в ручном или полу-автоматическом режиме. Например применить агрегацию и фильтрацию на основе типов. Мы это делаем руками, надеюсь есть инструмент о котором я еще не знаю.
                                    0
                                    Я немного о другом.
                                    Типичный случай. Нужно пофиксить сложный баг — в первую очередь иду смотреть историю гита, анотации. Если микрокомиты с тегами — то все легко, сразу видно коммит в имени которого сразу прикреплен номер таски по которому эти изменения были сделаны и почему, даже если они были сделаны 5 лет назад. А уже в таске все прописано, аналитика, какие кейсы для воспроизведения, и почему конкретно было принято такое решение.
                                    А если это левый мердж, в котором под сотню ченджей, без тикета и тегов — то и смотреть бесполезно.
                                    А список ченджей он никому то и не нужен, написать можно всякое
                                      0
                                      Аннотации или git blame это отдельная история в теории за его формирование отвечает правила по оформлению кода и текущие конвенции не решают этот вопрос полностью, в общем это отдельная тема.

                                      Номер таска в комментарии полезен но не всегда, название бага и описание нежелательного поведения формирует тестировщик, а разработчик для решения может изменить совсем не связанный с описанием компонент или компоненты.

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

                                      Но это не решает вопроса аннотации тут нужен опыт, дополнительная конвенция или правила по оформлению кода которые это учитывают.
                                  +5

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

                                    0
                                    а часто делаете git bisect?
                                      0

                                      Очевидно что нет)

                                      +1
                                      Имхо написание хороших коммит сообщений важно не только для просмотра истории, но и для более чистого кода в репозитории. Когда я знаю что мне нужно описать коммит, я еще раз просматриваю все изменения, и порой начинаю дробить их на пару мелких коммитов, так как понимаю что сделал по факту несколько задач.
                                      –1
                                      Не хватает возможность описывать смешанные типы и области для коммитов.
                                      Области по своей природе могут смешанными при каких-то сложные и больших правках. Переход на новые библиотеки, утилиты сборки, какой-то глобальный рефакторинг и т.п.
                                      Смешанные типы это в общем случае плохо, но для маленьких проектов практически неизбежно, т.к. строгое следование лучшим практикам может трудоёмкость в небеса и сделать такие проекты бессмысленными.
                                      В таких случаях лучше предусмотреть возможность.
                                      Кстати, в какой-то момент времени оказалось, что комментарии коммитов этого хорошо, но нужен внятный независимый ченжлог. Прикрутил его формирование по коммитам. А потом оказалось, что удобнее сделать наоборот — генерировать комментарии по изменение ченжлога. Нужно сказать, что некоторое время было прямо счастье. И в больших и в маленьких и даже в микро-проектах дописывает правишь текстовый ченжлог (по правилам -с разделением на фичи/баги и т.п.), потом делаешь коммит и комментарий генерируется из изменений ченж-лога. Одновременно переформатируется сам ченжлог — добавляется информации о дате коммита, номере сборки, статусе релез/дебаг и т.п. А при коммите в релиз всё это автоматически собирается (от прошлого релиза) ещё раз переформатируется, собирается в один большой список изменение, снабжается версией, генерируется комментарий в коммит. Очень удобно было! Имеешь текстовый файл с описанием основных изменений, имеешь список изменений для релизов и т.п.
                                      Но всё держалось на мега-скрипте для баша, который со временем превысил предел сложности, развалился не несколько версий и т.п. А духу и времени всё это облагородить и переписать на каком-нибудь пайтоне не находится. В результате в новых проектах снова обычные комментарии к коммитам :(
                                        +2

                                        Смешанные области просто не надо указывать вообще. Смысл указания области в том, чтобы можно было легко пропускать коммиты, которые явно не представляют интереса. Если сложно чётко описать, что затрагивает коммит, а что нет — просто не надо указывать ничего. Если коммит затрагивает пару областей — никто не запрещает указать обе через запятую: "fix(this,that): something".


                                        Смешанные типы никак не связаны с размером проекта, они связаны с внутренней дисциплиной разработчика. Следуя Вашей логике маленьким проектам и git не нужен.


                                        В таких случаях лучше предусмотреть возможность.

                                        Не лучше. Если стандарт изначально предусматривает возможность на него забить — от него вообще пользы не будет, он просто добавит бессмысленной бюрократии и всё.

                                          0
                                          Ну, почему же. Описать можно. Просто коммит может затрагивать две области.
                                          Что значит забить на стандарт? Никто не призывает «забить». Но под стандарт пуштся парсеры и утилиты. И если в реальном мире есть какая-то ситуация, а стандарт её не предусматривает, то это хреновый стандарт и от него будут в этой ситуации отступать. Или просто будут использовать какие-то другие соглашения.
                                          Стандарт, он не для того, чтобы разработчиков дрессировать. Он для того, чтобы можно было различные утилиты совместимым образом автоматизировать. А вопросы, что и когда можно, а что нельзя решают договорённостями в каждой конкретной команде под её конкретные потребности.
                                          Вот, я например, не могут делать один коммит на одну область/тип. Просто потому, что нет у меня таких ресурсов под такие небольшие проекты. И это популярная ситуация. На самом деле матёрый энтерпрайз это скорее исключение, чем правило и большинство команды вынуждено идти на компромисы. По крайней мере до тей пор, пока проекты не достигнут определённой степени зрелости и/или коммерческого успеха. Значит они (и я в их числе) будут городить свои костыли. Писать несколько областей или типов через запятую, скорее всего. И все утилиты я будут пилить под это. Но это рушит совместимость, обесценивает и ограничивает стандарт. А если окажется, что что-то стандартное я допилить не могу, то я просто не будут использовать этот стандарт и будут искать что-то другое.
                                          Меня всегда удивляет, когда разрабатывают стандарт, но не закладывают каких-то очевидных расширений в него.
                                        +3
                                        Стандарт допускает следующие типы коммитов:

                                        Это не совсем так. Стандарт (текущая версия v1.0.0-beta.2) определяет два особых типа (feat и fix) и отдельно все остальные. Для остальных приведены несколько примеров, но не более того.


                                        Описание коммита не должно закачиваться знаками препинания.

                                        IMHO здраво, но стандарт этого не требует, хотя примеры написаны в этом стиле.


                                        В описание необходимо использовать повелительное наклонение (imperative mood), точно такое же, какое использует сам Git.

                                        IMHO плохая идея, и стандарт этого даже не упоминает. Более того, многие примеры явно не в этом стиле: "feat(lang): added polish language", "fix: minor typos in code"…


                                        Я бы не придирался, если бы Вы сказали, что описанное — это ваше внутреннее соглашение, но ведь Вы поместили все эти пункты под заголовок "Основные тезисы Conventional Commits", что явно не соответствует истине.


                                        Нужно добавить автоматизации и удобства. Для решения этого вопроса нам потребуется два инструмента: генератор коммитов и линтер коммитов, настроенный на проверку перед пушем в репозиторий.

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


                                        Вообще, основная польза этой спецификации не в том, что она задаёт правила оформления коммитов, а в том, что она приводит к линейной истории, состоящей из осмысленных и цельных изменений, которую можно обрабатывать утилитами для автоматизации механических действий вроде подготовки changelog или выбора номера следующей версии для релиза. Иными словами, суть спецификации составляют 4 термина: squash, feat, fix, BREAKING CHANGE — именно они создают ценность. Всё остальное можно поменять, и ничего принципиально не изменится.


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

                                          0
                                          Спасибо за комментарий! Действительно часть советов и рекомендаций ошибочно попала в блок тезисов конвенции Conventional Commits. Внес корректировки. Благодарю за внимательное прочтение статьи. Ни в коем случае не хотел ввести кого-то в заблуждение. Сам люблю порядок и ценю точность формулировок.
                                          –1
                                          deleted
                                            +1
                                            А если просто <НОМЕР-ЗАДАЧИ-В-ТРЕКЕРЕ> и сквошить все комиты? 1 комит — 1 задача. Кому интересно, пойдет в джиру и посмотрит, не?
                                              +2

                                              Лично я против. Это смена контекста. К тому же, возможно, такое, что тикет переоткрывали не раз, и тогда придётся сравнивать время комментариев и коммита.
                                              Джира может быть и недоступна по разным причинам (работаю дома, интернет отвалился, etc).
                                              Ну, а кроме того, убеждён, что человек, пишущий осмысленный текст, чётче начинает понимать, что же он сделал, и, возможно, даже поймёт, что сделал не всё или не так

                                                0
                                                Или что надо то, что он сделал разбить на два коммита и, может даже, завести отдельную задачу на «лишнюю» часть.
                                              +2
                                              #номер_стори название стори
                                              Зачем придумывать велосипеды? (и да, сквошить все чейнджи в один коммит при PR, чтобы не мусорить в мейне, и нет, имена коммитов в фичабранче не интересуют вообще никого и никогда)
                                              +1

                                              Поддержу коллег выше. Коммит должен иметь форму: номер тикета: название тикета.
                                              Один коммит должен относиться к одному тикету. Если у вас тикеты связаны между собой то линкуйте их в Jira или что там у вас еще. Тикет — единственное место, куда кто-то еще смотрит. Коммит мессадж никто не читает.

                                                0
                                                Коммит должен иметь форму: номер тикета: название тикета.

                                                Название тикета может не отражать изменений которые были внесены для исправления этого тикета.


                                                Один коммит должен относиться к одному тикету.

                                                Если тикет маленький и локальный — конечно да.
                                                А что делать если тикет большой и по логике разбивается на несколько крупных независимых изменений?

                                                  0
                                                  Несколько коммитов на тикет не нарушают правила «Один коммит должен относиться к одному тикету»
                                                    0

                                                    Но нарушают предыдущее Коммит должен иметь форму: номер тикета: название тикета.
                                                    Или вы видите смысл в нескольких коммитах с одинаковым комментарием?


                                                    И, в реальности, бывает что один коммит может фиксить баги из разных тикетов.
                                                    Да, по хорошему, таки тикеты нужно свести в один закрыв остальные как дублирующие.


                                                    Но что делать когда каждый тикет описывает свою проблему часть которой фиксит твой коммит?

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

                                                      Если всё строго с форматом, то создать тикет, чётко описывающий что твой коммит частично фиксит в других тикетах и указать общий в сообщении коммита.
                                                    0
                                                    Так и не надо в названии отражать изменения. Отразите их внутри тикета. Если хотите подробно, прикрепите терйсы и т.п.
                                                    Если тикет большой, то сделайте из него epik и разбейте работу на небольшие тикеты.

                                                    Здесь важный момент, что если у вас в работе есть система тикетов, то это и есть первичный источник информации. В этом случае в сообщении коммита самое важное — это номер тикета т.к. основная цель, как можно скорее перебросить человека в систему, где хранятся знания.
                                                    Если тикетами не пользуетесь, то предложение автора имеет право на существование.

                                                    В описанном в статье виде получается часть информации в тикете, а часть в коммите.
                                                      0
                                                      Да, получается, и ничего плохого в целом в этом не вижу. И основная цель сообщения коммита не как можно быстрее перебросить человека куда-то, а дать человеку возможность получить нужную ему информацию прямо из первой строки сообщения и принять решение нужно ли дальше этот коммит рыть хоть в тиккет-системе, хоть смотреть список измененных файлов и собственно изменения, или пропустить его и смотреть другой.
                                                  0
                                                  «Крит пофикшен асапом»
                                                  Боже, что это за латынь?
                                                    +2
                                                    Глокая куздра штеко пофиксила крит и кудрячит асапа.
                                                    0
                                                    Раз git flow упомянут, может стоит упомянуть и trunk based подход? barro.github.io/2016/02/a-succesful-git-branching-model-considered-harmful
                                                    trunkbaseddevelopment.com
                                                      0
                                                      Если вы делаете agile/scrum в команде, то любая story/задача/баг whatever, должна быть на канбан борде с уникальным номером. Как правило, мы в команде указываем номер story с тайтлом при коммите. Так что никаких вопросов не возникает у членов команды. А если же у вас каждый разраб делает комиты и никто потом не может понять что именно правилось и для чего, то здесь пару проблем. 1-ая — вы не делаете agile и каждый разраб получает задачи индивидуально, 2-ая — вы не делает pair programming/mobbing/code review в команде. И как результат, очень слабая коммуникация внутри команды.
                                                        0
                                                        Канбан или скрам разве диктуют универсальный номер?
                                                          0
                                                          Нет, не диктует. Но так принято в любом скрам тиме иначе как потом искать таск в жире или любом другом багтрекере?
                                                            +1
                                                            Некоторые скрам команды довольствуются скрам доской с листочком и счастливы вполне без всякого треккера кроме лога гита.
                                                        0
                                                        Все правильно пишут про номера тикетов. Разумное именование коммитов — утопия, под которую можно подвести только процентов 50% изменений.

                                                        Мы в компании пользуемся гитхабом, и там метаинформация побогаче. PR линкуется к тикету или связанным тикетам. Тикет рассказывает, ЧТО мы делаем / фиксим. В описании PR я кратко описываю КАК мы это делаем (если это не очевидное устранение, допустим, опечатки). Таким образом остаются какие-то более-менее адекватные следы.

                                                          0
                                                          Под конкретный тикет определить каждый коммит тоже может быть затруднительно, особенно если треккер не поддерживает иерархии типа эпик-стори-таск-подтаск, а чисто плоский списко тасков
                                                          0
                                                          Спасибо, полезная статья. Честно говоря на работе уже кровь течет из глаз, когда в очередной раз видишь такие коммиты:
                                                          fix
                                                          fix
                                                          fix
                                                          done
                                                            0
                                                            Согласен, в таких случаях отправляю автора коммитов читать git rebase: порядок в локальных ветках.
                                                              0
                                                              rebase как любое переписівание истории — опасная штука.
                                                                0
                                                                Речь идет о перезаписи рабочей ветки, т.е. над которой работает один разработчик до ее вливания, исключительно в этом случае нулевая опасность.
                                                                  0
                                                                  1) при перезаписи всегда есть шанс что-то важное потерять. Перезапись делается чтобы что-то потерять, но всегда есть шанс, что потеряется не только то, что хотел, но и что-то ещё.

                                                                  2) если «вливание» — это мерж или типа того, то есть, например, кодревью перед ним при котором ветка стягивается на машину другого разработчика и он может попросить сделать какие-то изменения. Ревью этих изменений в перезаписанной ветке составляет проблему.

                                                                  3) даже если ведешь разработку оин, но с разных машин, всегда есть риск что-то потерять или испортить при актуализации одной из машин
                                                            0
                                                            Спасибо, очень полезная статья.
                                                              0
                                                              Не удалось подружиться из-за commit.gpgsign=true
                                                              Хуки запускаются до того, как будет введет пароль
                                                                0

                                                                А вы как-то правите комментарии при слиянии веток?


                                                                Я про вот это сообщение по умолчанию — Merge branch '<merged-branch>' into '<branch>'


                                                                P.S. С сообщения к коммитам в представленном репозитории очень толсто получилось.

                                                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                Самое читаемое