2010-09-26

A successful Git branching model

Добрейшего.

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

Не удивительно, что стали появляться и вменяемые руководства по практикам ветвления для новоиспеченных лидеров рынка - в первую очередь git и Mercurial. На одно руководство я уже ссылался. Не прошло и года, как я наткнулся на ещё одно руководство: A successful Git branching model.



Что сказать - по наглядности переплюнуло всё, что я видел. Можно просто глянуть на картинки и посмотреть на нужные команды - и всё. Комментарии автора лишь связывают всё воедино. Читать/смотреть в обязательном порядке.

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

В общем, enjoy.

Update 1. PDF-версия.

Update 2. Обсуждение на Хабе закинутой ссылки, есть пара занятных ссылок.

Update 3. Хороший перевод статьи.

29 комментариев:

  1. Приветствую,

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

    Ветвление оно же не само по себе нужно, а для достижения какой-то цели.

    Рассмотрим типичные сценарии в одном из моих проектов. Характериcтики проекта - шесть разработчиков, SVN, Scrum, Test Driven Development.

    В trunk у меня сейчас B84 ( следующая версия), B83 - в branches ( текущая версия, предрелизное состояние), B82, B81 - в tags.

    1) Новая задача для B84. Разработка, unit-тесты проходят - commit в trunk. И так НЕСКОЛЬКО раз. Тесты и функциональность наращиваются постепенно, однако в любой момент времени тесты проходят.
    2) Фикс для B83. Разработка, unit-тесты проходят - commit в branches\B83 + merge с trunk
    3) Фикс для B82. Ветка в branches\B82.2, Разработка, unit-тесты проходят - commit в branches\B82.2 + merge с trunk и branches\B83

    Сценарий типа (3) редок, и в 99% запускается только серьезных дефектов - мы выпускаем версию раз в 1-2 месяца, так что клиент в большинстве случаев может дождаться следующей версии (а раньше выпускали версию вообще раз в две недели, отсюда такие номерки).

    Я тут пока не вижу необходимости использовать подход одна ветка - одна задача.

    Другое дело, если
    a) Версии редки
    б) Не покрыты unit-тестами
    в) Существуют масса параллельных разношерстных версий

    Здесь ситуация другая - сommit в trunk потенциально приводит к проблемам, тестов-то нет. Ковыряться с кучей коммитов по одной задаче чтобы сделать merge в другую версию системы - удовольствие ниже среднего. И вот "одна ветка - одна задача" работает хорошо.

    ОтветитьУдалить
  2. Добрейшего.

    В общем-то, если убрать из приведенного в комменте описания branches и tags, то остальная разработка на транке как раз в статье про ветку-на-задачу и описывается. И поскольку используется TDD, то в статье можно убирать куски текста про поломку отстройку и функционала (таких больше половина). Однако остальные возражения остаются в силе - "стрельба по движущейся мишени", "независимость задач", "ветки как единица изменения" и "улучшенное отслеживание".
    Общий смысл - в большем контроле нам теми изменениями, которые вносятся. Самый простой пример - нельзя сказать по отдельно взятому коммиту на транке, зачем он был сделан. А имея ветку, привязанную к задаче, подобный вопрос вообще не ставится.

    Однако, соглашусь, что всё хорошо в меру. Я, к примеру, точно также разрабатываю на транке :) Просто потому что code owner на конкретном проекте - это только я один, я могу позволить себе коммитить непрерывно по мере выкладывания проекта на живой работающий сайт. Но как только один файл начинает правиль больше, чем один человек - ветки неизбежны.

    Кстати, по описанной организации работы - вопрос небольшой. Зачем разделять бэйзлайны между branches и tags? Ведь в SVN меток в традиционном понимании вообще нет и то, что называется метками - по сути те же самые ветки. Так не проще ли на каждый релиз (он же и бэйзлайн) почковать только ветки, без переноса в tags?

    ОтветитьУдалить
  3. >Самый простой пример - нельзя сказать по отдельно взятому коммиту на транке, зачем он был сделан.

    У нас к каждому commit прилагается комментарий типа:

    view Importance & Budget
    [url задачи в трекере]

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

    >Я, к примеру, точно также разрабатываю на транке :)

    Дык :) Смысла особого нет в таком частом бранчевании. Я вот на текущую версии сгенерировал 40 задач при ручном тестировании сборки, всего в трекере 160 задач на версию. Обычно их поменьше, конечно, у нас просто система управления проектами позволяет делать задачи неограниченной вложенности, декомпозиция увлекает. Но те 40 задач, о которых я сказал, все равно имели бы место.

    >Зачем разделять бэйзлайны между branches и tags?

    Э-э, я вот написал это а потом сам долго думал. Ночью :)

    В tag делается копия по выпуску чтобы знать точный состав выпущенной версии. Копия делается автоматическим построителем сборки.

    >Так не проще ли на каждый релиз (он же и бэйзлайн) почковать только ветки, без переноса в tags?

    Ну вот у меня был выпущен B82, при выпуске сделана копия в tags. Были бы метки - поставили бы, конечно, метку. Теперь мне надо задним числом пофиксить что-то. tags\B82 - это уже своего рода "эталон", его трогать не нужно. Делается копия в branches и с нее начинается работа.

    ОтветитьУдалить
  4. Прокомментирую чуть позже в комментах к http://scm-notes.blogspot.com/2010/09/branch-per-task-workflow-explained.html
    потому как обсуждение касается веток, а не конкретно git :)

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

    с

    $ git remote add bob /home/bob/myrepo

    все понятно. Но это если они все на одной машине работают. А если нет ?

    ОтветитьУдалить
  6. посмотрим, может быть комменты читает кто-то из гитофанатов :)

    ОтветитьУдалить
  7. В результате чтения различных материалов у меня сложилось ощущение, что "ветка-на-задачу" vs "все в trunk" это священная война типа Windows/Linux, Canon/Nikon.

    См. к примеру
    http://martinfowler.com/bliki/FeatureBranch.html

    Тут Мартин приводит аргументацию в пользу работы на trunk при условии CI. И я с ним совершенно согласен. Внедряю, кстати, у себя CI.

    Ну т.е. все зависит от условий, для одного проекта выгодна практика Feature Branches + интегратор, для другого - CI/TDD + работа trunk + ветки по мере необходимости.

    ОтветитьУдалить
  8. Спасибо за наводку - прочитаю, обязательно отпостю в бложик с комментами.

    Однако, после его предыдущих высказываний про контроль версий:
    http://scm-notes.blogspot.com/2010/02/versioncontroltools.html
    отношусь к нему с настороженностью :)

    ОтветитьУдалить
  9. > сложилось ощущение, что "ветка-на-задачу" vs "все в trunk" это священная война

    да, местячковый такой холиварчик :)

    ОтветитьУдалить
  10. > Однако, после его предыдущих высказываний про контроль версий:
    http://scm-notes.blogspot.com/2010/02/versioncontroltools.html

    Я там оставил комментарий :)

    > да, местячковый такой холиварчик :)

    Почему, вполне себе планетарного масштаба.

    ОтветитьУдалить
  11. > Почему, вполне себе планетарного масштаба.

    Ну в том смысле, что узконаправленный :)

    ОтветитьУдалить
  12. >Общий смысл - в большем контроле нам теми изменениями, которые вносятся. Самый простой пример - нельзя сказать по отдельно взятому коммиту на транке, зачем он был сделан. А имея ветку, привязанную к задаче, подобный вопрос вообще не ставится.

    Вот тут надо понимать: зачем оно нам в истории? Т.е. нужна ли в СКВ более гранулярная история изменений чем в багтрекере?

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

    ИМХО более мелкая история нужна только девелоперу. Да и как к ней просто достучаться? Все топик бранчи оставлять?

    ОтветитьУдалить
  13. > Т.е. нужна ли в СКВ более гранулярная история изменений чем в багтрекере?

    Я скажу так - чем больше народу изменяет один элемент за единицу времени, тем нужнее.

    При мер из моей практики - 20-30 человек интенсивно работают с 20-30 файлами. Каждый файл изменяется ежедневно на пиках активности по десятку раз. Иногда функционал в отдельных файлах ломался - и тогда было делом пары команд describe (речь про ClearCase) для отдельных версий, чтобы восстановить последовательность действий - кто поломал, по какому запросу на изменение, какую дельту он внёс и на каком бэйзлайне базировал свои изменения.

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

    > Все топик бранчи оставлять?

    не совсем понял - что такое "топик ветка"?

    ОтветитьУдалить
  14. > когда гранулярность коммитов надо отслеживать.

    пардон, "когда гранулярность коммитов важна".

    ОтветитьУдалить
  15. >При мер из моей практики - 20-30 человек интенсивно работают с 20-30 файлами. Каждый файл изменяется ежедневно на пиках активности по десятку раз.

    Интересно, а что за файлы такие ?

    ОтветитьУдалить
  16. Например, исходники медиаприложений для мотороловской платформы P2K в пору её расцвета. Я как раз на направлении Multimedia и работал СМ-инженером. Одна из самых больших компонент/команд была, отсюда и СМщиков было в лучшие времена - около десятка.

    ОтветитьУдалить
  17. >Я скажу так - чем больше народу изменяет один элемент за единицу времени, тем нужнее.

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

    >В общем, есть целый класс случаев, когда гранулярность коммитов надо отслеживать.

    Только 1 коммит - 1 таска в трекере.

    > Все топик бранчи оставлять?

    не совсем понял - что такое "топик ветка"?

    Feachure branch, ветка в которой разраб изолированно от других решает свою небольшую задачу.

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

    Вот тут не соглашусь по принципиальному соображению - коммититься, т.е. создавать новые версии, надо тогда, когда для этого есть необходимость. Т.е. я начал работу над задачей, расчитаной на 12 человеко-часов и работа моя разобьется на 2 дня. Значит ли это, что мне надо "терпеть" и копить изменения, чтобы потом всё закоммитить одним движением? Никак нет - это просто опасно с точки зрения сохранности данных, не говоря уже о рабочих моментах, когда нужно откатить код на версию, написаную 3 часа назад - такое бывает сплошь и рядом.

    Особенно это актуально для той самой топик-ветки - ведь не всегда получается сделать фичу работающей сразу и без ошибок.

    ОтветитьУдалить
  19. > Значит ли это, что мне надо "терпеть" и копить изменения, чтобы потом всё закоммитить одним движением?

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

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

    ОтветитьУдалить
  20. > ветка задачи вливается в интеграционную ветку и после этого удаляется.

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

    > в дальнейшую работу пойдет одиночный коммит - результат слияния

    Ну это понятно. Но, тем не менее, вся история важна. А то, что место занимает - так сейчас место на HDD стоит дешевле воды :)

    ОтветитьУдалить
  21. Aquary комментирует...
    >Плохая практика. Если всплывет ошибка через неделю или две и надо будет искать концы - не найдешь.

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

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

    С другой стороны, удаление ветки после вливания в trunk служит надежным маячком, что ветка влита :) Т.е. если есть ветка это значит, над ней ведется работа.

    ОтветитьУдалить
  22. > Какая еще нужна информация ?

    На транке остается *результат мёржа* дельты разработчика и тоо, тчо было на транке.
    При этом *дельта разработчика* - становится неизвестной.

    То есть нельзя будет с уверенностью сказать - ошибка в коде появилась в результате ошибки слияния или ошибки разработчика.

    Конечно, зачастую всё на потом поставлено и быстрее поправить ошибку, чем искать её причину. Однако - такие ситуации возможны.

    > Их тогда именовать как, интересно, чтобы было и уникально и информативно ?

    Например, схема
    ___
    покрывает собой бОльшую часть ситуаций. Здесь branch type может быть ввведен, чтобы показать, что ветка - интеграционная, отладочная, для работы над задачей и т.п. task_id в случае интеграции может быть пропущен. В общем, соглашение об именовании веток и меток - оно в любом случае полезно.

    ОтветитьУдалить
  23. Хы... форматирование обрезало имя бранча :))
    Попробую по-другому
    [branch_type]_[developer_id]_[task_id]_[comment]

    ОтветитьУдалить
  24. >На транке остается *результат мёржа* дельты разработчика и тоо, тчо было на транке.
    При этом *дельта разработчика* - становится неизвестной.

    Я тут посмотрел, как будет в меркуриале - в trunk после слияния получается вся история коммитов разработчика + интеграционный коммит.

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

    В subversion все вроде гораздо печальнее.

    Почему они слияние-то не могут сделать, интересно, это архитектурные ограничения какие-то ?

    ОтветитьУдалить
  25. > Почему они слияние-то не могут сделать, интересно, это архитектурные ограничения какие-то ?

    Как не могут? В СВН есть слияние, что бы там ни говорили. Сам пользуюсь :) Да, где-то оно сделано лучше, однако - в СВН оно вполне себе есть. :)

    ОтветитьУдалить
  26. Ну, понятно, слияние есть, я тоже пользуюсь :) но по сравнению с другими системами, та же Plastic SCM :) функциональность крайне хромает.

    ОтветитьУдалить
  27. Заметь - все плачут, колются, "но продолжают жрать кактус" :)

    ОтветитьУдалить
  28. Цена перехода на другую систему довольно высока, вот и потребляем кактус.

    К тому же, у subversion есть плюсики типа хранения нескольких проектов в одном репо и система безопасности. К примеру, можно запретить модифицировать trunk "кому попало".

    ОтветитьУдалить