Несколько дней назад David Demaree, главный по Typekit в Adobe, издал крутую книжку "git для людей". Чтобы привлечь к ней внимание, он опубликовал выжимку самой, на мой взгляд, интересной главы — как оформлять коммиты чтобы и волки были целы, и овцы сыты, и песец не пришел. А я за эти выходные подготовил выжимку из выжимки — сокращенный и адаптированный перевод, чтобы можно было быстро прочитать и добавить в копилку своего опыта самое ценное.

Искусство Коммитов



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

— из комментов

Хороший комментарий к коммиту — короткий. И не только потому что краткость — сестра. Сообщения к коммитам чаще всего читают в логе изменений, где их будет много. Рассматривайте каждое сообщение как заголовок новости в интернете: оно должно быть достаточно коротким чтобы вы могли быстро пролистывать новости и содержать ровно столько информации чтобы вы могли найти важное лично для вас. Если у вас в команде больше пары программистов, то лог коммитов позволяет быть в курсе того, что происходит в проекте. Git не накладывает ограничений на длину сообщения, после краткого анонса вы можете добавить несколько параграфов текста:

Updated Ruby on Rails version because security

Bumped Rails version to 3.2.11 to fix JSON security bug. 
See also http://weblog.rubyonrails.org/2013/1/8/Rails-3-2-11-3-1-10-3-0-19-and-2-3-15-have-been-released/


Обратите внимание, что сообщение целиком содержит довольно много информации, а первая строка — краткую выжимку. Потому что в логе вы увидите только первую строку:

commit f0c8f185e677026f0832a9c13ab72322773ad9cf
Author: David Demaree 
Date:   Sat Jan 3 15:49:03 2013 -0500

Updated Ruby on Rails version because security


Ваш любимый текстовый редактор


git интегрируется как с консольными редакторами (vim, emacs), так и с графическими (Atom, Sublime, TextMate). Вызванный без --message git передаст заготовку текста настроенному редактору. После редактирования сообщения достаточно сохранить открытый файл и закрыть редактор, git определит что сообщение было изменено и использует его. Пример интеграции git с Atom:

$: git config --global core.editor "atom --wait"


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

Комментарий к коммиту


Кода вы пишите комментарий к коммиту, я рекомендую придерживаться следующих правил:

  • Несите пользу

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

  • Остерегайтесь графоманства

    Часто нам хочется написать «дурацкие баги» или «исправил проблему». Не поддавайтесь этой слабости, старайтесь максимально четко и ясно напсать что и зачем было сделано.

  • Линкуйте информацию

    Если коммит связан с какой-то внешней информацией — багрепортом, статьей в вики или чем-нибудь еще, то очень хорошей идеей будет явно указать это в комментарии к коммиту,
    например так
    Replace jQuery onReady listener with plain JS; fixes #1357
    
    Многие багтрекеры (включая встроенный в github) интегрируются с git и автоматически помечают баги как исправленные, если встречают в комментарии к коммиту номер этого бага вместе с зарезервированным словом, таким как fixes в этом примере.

  • Будьте (достаточно) подробны

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

  • Будьте последовательны

    Чтобы ваш лог изменений читался как единая новостная лента — уделите немного времени, чтобы объяснить команде как лучше писать комментарии для коммитов. Хорошей идеей будет создать страницу в вашей вики (у вас ведь есть внутренняя вики?) с примерами хороших и плохих комментариев.

  • Используйте глаголы

    Часто возникает соблазн написать комментарий к коммиту вида «исправления». Но помните, что лог изменений — это история ваших действий над проектом, а действия лучше всего описывать глаголами. Если этого не делать, то очень легко деградировать до таких вот
    комментариев
    # Making the last homepage update before releasing the new site
    $: git commit -m "Version 1.0"
    
    # Ten minutes later, after discovering a typo in your CSS
    $: git commit -m "Version 1.0 (really)"
    
    # Forty minutes later, after discovering another typo
    $: git commit -m "Version 1.0 (oh FFS)"
    
    Кроме того, этого очень простое правило, которому легко следовать: «я только что поменял код. Что и зачем я сделал?»,
    например
    $: git commit -m "Update homepage for launch"
    $: git commit -m "Fix typo in screen.scss"
    $: git commit -m "Fix misspelled name on about page"
    


Ваши комментарии?


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

Комментарии (32)


  1. Rathil
    08.02.2016 11:16

    Я с телефона, извините, не могу ЛС написать.
    Измените с " Как оформять коммиты, чтобы потом не было больно"
    На «Как оформлять коммиты, чтобы потом не было больно».


    1. AllRight88
      08.02.2016 11:31
      +1

      Спасибо, исправил! До заголовка спеллчекер не добрался :)


  1. Zapped
    08.02.2016 11:16
    +4

    очень мне нравятся в этом плане исходники самого Git:
    бывает,

    в файле изменена одна строка, а коммент - простыня на 20 строк
    git log -1 -p d69360c6b17d1693a60b9f723a3ef5129a62c2e5"

    commit d69360c6b17d1693a60b9f723a3ef5129a62c2e5
    Author: Ben Walton <bdwalton@gmail.com>
    Date:   Mon Dec 22 15:25:44 2014 -0800
    
        t0090: tweak awk statement for Solaris /usr/xpg4/bin/awk
        
        The awk statements previously used in this test weren't compatible
        with the native versions of awk on Solaris:
        
            echo "dir" | /bin/awk -v c=0 '$1 {++c} END {print c}'
            awk: syntax error near line 1
            awk: bailing out near line 1
        
            echo "dir" | /usr/xpg4/bin/awk -v c=0 '$1 {++c} END {print c}'
            0
        
        Even though we do not cater to tools in /usr/bin on Solaris that
        have and are overridden by corresponding ones in /usr/xpg?/bin,
        in this case, even the XPG version does not work correctly.
        
        With GNU awk for comparison:
        
            echo "dir" | /opt/csw/gnu/awk -v c=0 '$1 {++c} END {print c}'
            1
        
        which is what this test expects (and is in line with POSIX; non-empty
        string is true and an empty string is false).
        
        Work this issue around by using $1 != "" to state more explicitly
        that we are skipping empty lines.
        
        Helped-by: Jonathan Nieder <jrnieder@gmail.com>
        Signed-off-by: Ben Walton <bdwalton@gmail.com>
        Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>
        Signed-off-by: Junio C Hamano <gitster@pobox.com>
    
    diff --git t/t0090-cache-tree.sh t/t0090-cache-tree.sh
    index 067f4c6..601d02d 100755
    --- t/t0090-cache-tree.sh
    +++ t/t0090-cache-tree.sh
    @@ -22,7 +22,7 @@ generate_expected_cache_tree_rec () {
        # ls-files might have foo/bar, foo/bar/baz, and foo/bar/quux
        # We want to count only foo because it's the only direct child
        subtrees=$(git ls-files|grep /|cut -d / -f 1|uniq) &&
    -   subtree_count=$(echo "$subtrees"|awk -v c=0 '$1 {++c} END {print c}') &&
    +   subtree_count=$(echo "$subtrees"|awk -v c=0 '$1 != "" {++c} END {print c}') &&
        entries=$(git ls-files|wc -l) &&
        printf "SHA $dir (%d entries, %d subtrees)\n" "$entries" "$subtree_count" &&
        for subtree in $subtrees
    


    1. AllRight88
      08.02.2016 11:35
      +1

      Да, зря на вычитку понадеялся. Прогнал, все исправил, спасибо!


    1. Lol4t0
      08.02.2016 13:02
      +2

      Все зависит от изменений. Что вы напишете к коммиту с исправлением опечатки Nmae на Name?


      1. Zapped
        08.02.2016 13:07
        +2

        я-то так и напишу, но приводить для примера в статье, озаглавленной «Искусство коммита» («The Art of the Commit»), не стал бы :)


        1. Lol4t0
          08.02.2016 13:20
          +2

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


          1. Zapped
            08.02.2016 13:35

            Не думаю, что стоит впадать в крайности и описывать зачем исправлялась опечатка :) По-моему, это вещь очевидная и текста
            Исправить опечатку: имя локального параметра должно быть Name, а не Nmae
            кажется, вполне хватит.
            Но и я придерживаюсь мнения, что писать «сочинение» писать нужно не только по серьёзным багам. Стоит всё же думать о том, что то, что понятно сегодня, может быть (и будет) забыто и непонятно самому себе через месяц/полгода/год, а другим и то вовсе непонятно.


            1. Lol4t0
              08.02.2016 17:32
              +1

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


            1. Randl
              08.02.2016 20:36
              +3

              Хм, а зачем? Текста

              Исправил опечатку
              по-моему хватит. Если интересно какую именно (с чего бы?), смотрим диффы.


              1. Zapped
                09.02.2016 01:10
                +1

                ну, меня не убудет написать какая именно опечатка была исправлена

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


    1. Regis
      08.02.2016 17:17
      -2

      А по мне вся эта простыня — плохой пример.

      Этому тексту самое место в багтрекере, как мне кажется.


      1. Lol4t0
        08.02.2016 17:31
        +1

        Почему бы ему не быть и там и тут? Чтобы быстро оценить ситуацию все равно удобнее сомтреть git log --oneline


      1. Zapped
        08.02.2016 18:27
        +5

        не согласен

        метаданные к изменениям кода должны быть там, где они управляются — в SCM
        зачем в описании ошибки (в багтрекере) — описание того, как и зачем меняется код?
        кроме того, ошибка может затрагивать несколько версий продукта, несколько его платформ, etc

        развивая тему:
        при работе с историей кода, лезть ещё в багтрекер (и хорошо ещё, если с ним есть интеграция), сопоставлять даты комментариев (вы же в комментАриях будете писать как вы меняли код?) с датой самого коммита? нет, увольте…


  1. MANIAK_dobrii
    08.02.2016 12:36
    +1

    Тут помогает разумное использование трекера задач и чего-то вроде git-flow, когда на каждую таску есть ветка. Лень никто никогда не отменят, даже у самых занудных разработчиков, себя я спас от приступов лени, написав pre-commit скрипт, который из названия текущей ветки выдирает айди задачи в таск трекере и прилепляет перед сообщением.

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


  1. michael_vostrikov
    08.02.2016 12:44
    +4

    Не пишите что поменялось — это всегда видно по диффам

    Думаю, писать что поменялось тоже нужно. Это полезно, когда надо найти конкретный коммит из разряда «точно помню, что-то где-то я такое делал», или точную дату изменений. Смотреть дифф каждого коммита утомительно. А подробный ответ на вопрос зачем обычно есть в таск-трекере. Я обычно пишу как в последнем пункте — «что и зачем я сделал» в сжатом виде. А так со всем согласен, правильно написано.


    1. tyomitch
      08.02.2016 18:47

      Найти коммит, когда известно, что поменялось, помогает git blame.
      Так что ещё и в commit message это вписывать незачем.


      1. michael_vostrikov
        08.02.2016 19:02

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


        1. tyomitch
          08.02.2016 19:12

          Аргументом для git blame можно указать любую историческую версию.


          1. Zapped
            09.02.2016 01:02

            а вот как раз чтобы найти эту историческую версию, надо прошерстить git log
            )))

            и хорошо, если можно использовать git log -S/-G
            (если помнишь что именно изменялось)
            а при указанном подходе — почти точно поможет git log --grep


            впрочем, тогда git blame уже будет не нужен


  1. LeXuZZ
    08.02.2016 12:57
    +4

    Для плохих коммитов есть github.com/jayphelps/git-blame-someone-else :D


  1. cemick
    08.02.2016 13:00
    +5

    Не знаю, что в книге, но в выжимке капитанство. В принципе это давно стандарт, например, вот правила на которые мы ориентируемся kernel.org

    Хотел бы только добавить, что важно писать не только «зачем», но и «мотивацию». Между «зачем» и «мотивация» на самом деле большая разница. Из мотивации можно выяснить точку зрения автора коммита. Часто бывает неочевидно, почему для решения был выбран именно «вот такой» подход. Дам ссылку на наш опыт насаживания подобного подхода написания текста коммитов Что дал переход с SVN на Git или Git как ключ для синергии хороших практик


  1. seregamorph
    08.02.2016 18:05

    Небольшой оффтоп.
    Долгое время сидели на gerrit (обертка над git), его идеология отличается от github-подобных систем тем, что один смысловой changeset — это один коммит, если есть правки после ревью, делается amend/squash, а ревью идет между ревизиями одного changeset. После ревью в основную ветку попадает ровно один коммит.
    Так вот это я к тому, что когда изменения по одному тикету размазаны на несколько коммитов, очень неудобно смотреть лог, когда подряд идут коммиты с одним именем, скажем «PROJ-123 fixsmth».


    1. Zapped
      09.02.2016 00:55

      я в случаях, когда тикет размазан по нескольким коммитам, пишу
      issue #N: бла-бла
      issue #N: бла-бла-2
      и последний коммит уже
      fixed #N: <summary>
      (и если их больше двух, то обычно вливаю в master с --no-ff)


  1. BalinTomsk
    08.02.2016 19:28

    Пройдя через несколько крупных компаний пришел к такому темплейту:

    JIRA 3718: Service failure with error: 0x0023 — Add guard to check pointer — номер тикета, его заголовок и что сделано в сабмите.
    to CL2012 (main), 2021(release 1.1.3), 2023(accepted 1.1.3) — Если была интергация/merge в другие ветки, то номера всех изменений и номера изменений связанных с этим тикетом.

    Codeline: 1.1.5 — версия продукта, для которого было сделано изменение

    Code review: 4023 — номер преревью, где само изменение было обсуждено, с другими девелоперами и отвественными работниками

    Problem: Service failed because pointer was not verified.

    Fixed: Add guard to check pointer

    Tested:
    — Created database
    — Run Server
    — Pass all unit test

    Docs: www.company.com/KB45034


  1. berman
    08.02.2016 21:03
    +1

    Простите, снова не выдержал.


    1. Zapped
      09.02.2016 00:50
      +1

      удачи в командной разработке или вхождению в новый (для Вас) старый проект, который «оформлялся» подобным образом


      1. berman
        09.02.2016 00:59

        Так я не спорил с вашей статьей и поддерживаю вас, сам стараюсь писать полноценно. (Просто смешную пикчу запостить не выдержал)


        1. Zapped
          09.02.2016 01:04

          JFI: статья не моя ;)


  1. Myshov
    09.02.2016 19:35
    +1

    Еще есть такая забавная штука :)

    git commit -m '$(curl -s http://whatthecommit.com/index.txt)'