Увидев в очередной раз базворд GitFlow я психанул и решил перевести описание более простой и менее проблемной схемы работы с ветками под названием GitHub Flow. Именно её имеет смысл использовать по умолчанию, переходя к какой-то другой лишь в случае непреодолимых обстоятельств.
Создайте ветвь
Пока вы работаете над одним проектом, у вас может быть куча различных реализуемх параллельно улучшений. Некоторые из них готовы к работе, а другие — нет. Ветвление позволяет вам управлять этим рабочим процессом.
При создании ветви (branch) в проекте создается среда, в которой можно опробовать новые идеи. Изменения, вносимые в отдельной ветке, не влияют на ствол (master branch). Это позволяет вам экспериментировать и фиксировать изменения безопасно, зная, что ваша ветвь никак не повлияет на другие, пока не будет действительно готова.
Ветвление — это основное понятие в git. Весь GitHub Flow основанн именно на нем и солгасно ему есть только одно правило: всё, что находится в стволе — гарантированно стабильно и готово к деплою в любой момент.
Поэтому чрезвычайно важно, чтобы любая ваша новая ветвь создавалась именно от ствола. А имя ветви было быть описательным, чтобы другим было понятно над чем в ней идёт работа. Несколько примеров:refactor-authentication
,user-content-cache-key
,make-retina-avatars
.
Фиксируйте изменения
Создав ветвь, начинайте вносить в неё изменения. Добавляя, редактируя или удаляя файлы, не забывайте делать новые фиксации (commits) в ветви. Последовательность фиксаций образует в конечном счёте прозрачную историю работы над вашей задачей, по которой остальные смогут понять что вы делали и почему.
У каждой фиксации есть связанное сообщение, являющееся объяснением, почему было сделано то или иное изменение. Также каждая фиксация считается отдельной единицей изменения. Это позволяет откатить изменения, если обнаружилась ошибка, или если вы решите пойти в другом направлении.
Внятное описание фиксации очень важно, так как позволяет остальным разработчикам сразу понять ваши намерения и оценить насколько внесённые изменения им соответствуют. А значит обратная связь от них придёт быстрее и окажется полезней.
Вливайте изменения из ствола в свою ветвь как можно чаще, чтобы она всегда оставалась актуальной и готовой к обратному слиянию. Разрешение возможных конфликтов слияния — право и обязанность разработчика ветви, так как именно он лучше всего знает зафиксированные в ней изменения.
Откройте запрос на слияние
Запросы на слияние (pull request) инициируют обсуждение ваших коммитов. Поскольку они тесно интегрированы с базовым git-репозиторием, любой может однозначно понять, какие изменения будут внесены в ствол, если они примут ваш запрос.
Вы можете открыть запрос на слияние в любой момент процесса разработки:
- когда у вас мало или вообще нет кода, но вы хотите поделиться некоторыми скриншотами или общими идеями
- когда вы застряли и нуждаетесь в помощи или совете
- когда вы готовы к тому, чтобы кто-либо проверил вашу работу
Используя систему @упоминаний GitHub в сообщении запроса на слияние, вы можете запросить обратную связь от конкретных людей или целых команд, будь то сосед по офису или кто-то в десяти часовых поясах от вас.
Запросы на слияние полезны не только в рамках одного репозитория, но и как инструмент переноса кода между форками. Просто создайте запрос на слияние ветви из одного репозитория в ветвь из другого и действуйте дальше.
Проверьте и обсудите код
После открытия запроса на слияние команда рассматривает изменения, задавая вопросы и оставляя комментарии. Возможно стиль кодирования не соответствует принятому соглашению. Возможно отсутствуют модульные тесты. А возможно все выглядит хорошо и не вызывает нареканий. Запросы предназначены фокусировки обсуждения именно на предлагаемых изменениях и группировки вместе с ними.
Конечно вы можете продолжать пополнять ветку обновлениями в свете возникшего обсуждения. Если вам указывают, что вы забыли что-то сделать или в коде есть ошибка, вы можете исправить это в своей ветке и протолкнуть (push) на сервер. GitHub покажет ваши новые фиксации и любую дополнительную обратную связь на них всё в том же унифицированном представлении запроса на слияние.
В комментариях к запросу на слияние можно использовать markdown размету, что позволяет вставлять изображения и смайлики, использовать предварительно отформатированные текстовые блоки и другое облегченное форматирование.
Проверьте в бою
После проверки запроса на слияние и прохождения тестов ветвь можно развернуть в боевом окружении, чтобы окончательно убедиться в её работоспособности. Если ветвь вызывает какие-либо проблемы, то ее можно быстро откатить, развернув вместо неё гарантированно работоспособный основной ствол. Так как ветвь ещё не была никуда влита, то всякое её влияние на другие ветви по прежнему исключено и проблемный код не сможет поломать другие ветви. так что можно продолжить работу над задачей, пока она не будет действительно готова.
Вливайте
Теперь, когда изменения были проверены в бою, пришло время влить код в основной ствол.
При слиянии в стволе создаётся фиксация со всеми изменениями из ветки. Как и любые другие фиксации, она доступна для поиска и "перемещения во времени".
Путем включения определенных ключевых слов в текст запроса на слияние, вы можете связать проблемы (issues) с кодом. При вливании ветви в ствол связанные проблемы также закрываются. Например, ввод фразы closes #32
закрывает проблему номер 32 в репозитории. Для получения более подробной информации, ознакомьтесь с соответствующей статьей.
А если частые релизы невозможны?
Если вы не практикуете непрерывную поставку (Continous Delivery), то вам может быть сложно доводить до ствола каждую ветвь по отдельности. В этом случае просто создавайте интеграционный ветви, куда вливайте лишь те ветви, что считаете готовыми. Если изменения одной из ветвей вызовут проблемы, то интеграционную ветвь всегда можно будет пересобрать заново, но уже не включая проблемную. Это позволит вам не срывать график релизов, даже если какие-либо из планируемых задач оказались не до конца готовы к запланированной дате.
В чём отличие от GitFlow?
В GitFlow у вас есть дополнительная ветвь develop
куда сливаются все разрабатываемые в текущий момент ветви. develop
необходимо "стабилизировать" перед релизом, что часто приводит либо к переносу релиза, либо "релизу с замечаниями".
В чём отличие от GitLab Flow?
В GitLab Flow вы сначала вливаете ветвь в основной ствол и лишь потом разворачиваете в тестовом, боевом и других окружениях. Если релиз окажется проблемным и его потребуется откатить, то потребуется порой весьма проблемный "reverse merge" либо "стабилизация" ствола, как в случае GitFlow.
Комментарии (22)
Areso
05.01.2018 05:46Как мне кажется, возможно, связанный с небольшим опытом работы с Git, у длинных ветвей разработки есть недостаток. Когда между форком и принятым PR проходит значительно времени и мастер в этих двух точках отличается (другие программисты работают над другими ветвями). Если вы не владелец мастера, то этот недостаток не будет стоять перед вами, а перед тем, кто проводит Merge, но он все равно есть. Или потихоньку доливать в свою ветку код из мастера и готовить свой PR к Merge самостоятельно.
vershinin
05.01.2018 09:43В таких случаях периодический rebase может спасти отца русской демократии. Не смотря на то, что многие боятся rebase как огня, попробуйте с ним разобраться, в особенности с его интерактивной составляющей. Оно того стоит.
ZyXI
05.01.2018 10:54Как я уже говорил в обсуждении ThreeFlow (начало здесь) просто разрешите merge, и никогда не делайте rebase для больших ветвей, за исключением редактирования истории (в смысле
rebase --interactive
, но без собственноrebase
; у mercurial на это есть histedit, для git’а пришлось написать длинный alias, который определяет, с каким аргументами нужно вызватьrebase --interactive
, чтобы ничего никуда не переносить и, к тому же, не уничтожить случайно слияния). «Периодический rebase» с лёгкостью незаметно переведёт пачку коммитов в ещё менее рабочее состояние, чем они были до этого, что отрицательно скажется на точность bisect, если в ветке найдутся ошибки.t-nick
06.01.2018 14:05+1А зачем мерджить пачку коммитов? Как в этом случае вообще гарантировать работоспособность каждого коммита в отдельности? CI обычно запускается на ветку, то есть на последний коммит.
Сливание пачки в один коммит решает эту проблему.
Что касается rebase, его делает автор изменений, убедившись, что все работает на свежем мастере. Разрешение конфликтов мало чем отличается от такового в случае merge, но в последнем мы получаем два коммита на одно изменение, что добавляет хаоса в проект.ZyXI
06.01.2018 14:28Куда сливать пачку коммитов? Предполагается сливать master в длинную ветку, пока она не готова, если такое слияние зачем?то требуется (возникли конфликты, был проведён рефакторинг, …), а не длинную ветку в master. Гарантировать работоспособность коммитов не нужно, но не нужно и делать их менее рабочими, чем они есть. Полный squash — это та же самая потеря точности bisect. А при rebase без squash будут цепочки конфликтов.
Относительно хаоса: а зачем вы используете историю, что оно вас волнует? Я вижу основные три применения: annotate, bisect при проблемах и создание списка изменений. Для bisect чем больше коммитов, тем лучше (если они, конечно, достаточно рабочие). Для annotate тоже. Для списка изменений git вполне позволяет вам пройтись только по «первым родителям», которыми будут слияния веток в master, а не слияния master в ветки, так что это только вопрос «насколько адекватны описания слияний».
t-nick
06.01.2018 14:39+1На мой взгляд, слияния — бесполезные коммиты, которые представляют собой скрещивание бульдога с носорогом, смысловой нагрузки в таких изменениях нет, невозможно понять мотивацию автора мердж коммита при разрешении конфликтов.
Инкрементальную, одномерную последовательность коммитов намного легче анализировать.ZyXI
06.01.2018 14:57Может и кажется, что легче, но если вы не делаете ещё и squash, то это абсолютно бесполезно, потому как вы анализируете не то, что реально было, а конфликты никуда не деваются, просто теперь размазаны и логику их разрешений опять никто не объснит. А если вы делаете, то вы получаете последовательность огромных патчей, из описания которой значительная часть логики автора пропала при squash; и, дополнительно, логику автора для разрешения конфликтов никто не объясняет и тут, просто теперь все притворяются, что никаких конфликтов не было.
И, самое весёлое, последовательность сплюснутых огромных коммитов в виде патчей соответствует последовательности разниц (в виде патчей) между первыми родителями при использовании слияний. Т.е. вы для «лёгкости анализа» зазря выкидываете кучу информации, ничего взамен реально не получая.
t-nick
06.01.2018 15:13+1Конфликты в случае rebase просто изчезают, превращаются в осмысленные изменения, как если бы автор начал разработку с коммита, на который был сделан rebase. У нас есть описание коммита, в котором указано, что было сделано в этом коммите, это намного лучше, чем изменения поверх устаревшего мастера с последующим мердж с конфликтами, где нам нужно сначала понять изменения (при этом вспомнить как выглядел мастер неделю назад), потом понять как эти изменения были влиты в мастер.
Коммиты нужно стараться минимизировать, это упростит код ревью и сделает историю более подробной и понятной (для аннотаций и bisect в том числе). Конечно такая организация требует дополнительных усилий, как и поддержание чистоты кода.ZyXI
06.01.2018 15:29На review идёт уже результат слияния, он ничем не отличается от результата rebase+squash, кроме того, что вместо одного коммита с одним описанием есть последовательность. История со слияниями более подробна, понятность также ограничена авторами изменений и не всегда сильно страдает, а усилий нужно делать меньше. Понять «о чём думал автор» в случае спорных решений легче при последовательности изменений с описаниями, а сама модель более лояльна к большим изменениям, которые нельзя минимизировать.
t-nick
06.01.2018 15:57Если работать с большими изменениями, то я пожалуй соглашусь, что мердж будет менее трудозатратным (у меня был такой опыт).
Однако большие изменения возникают не сами по себе, а из подхода к разработке. Введение feature-toggle'ов позволяет коммитить частично готовый функционал (например набор оттестированных классов/функций), что способствует работе со свежим мастером и быть в курсе изменений в нем. Также большие изменения почти неизбежны при работе с legacy (читай «говно-») кодом, который требует обширного рефакторинга, но после некоторого времени, при правильном подходе, эта проблем уходит.
Тут конечно дело вкуса. Для большого, динамично развивающегося проекта я выбираю trunk-based с feature-toggle'ми.ZyXI
06.01.2018 17:09Feature-toggl’ы не всегда возможны и увеличивают затраты на разработку — их нужно создавать, их нужно убирать, с ними нужно научить работать CI. Review тоже придётся делать чаще, при этом не обладая всей картиной: уже готовые классы и описание задачи reviewer увидит сразу, а насколько готовые классы оптимальны для задачи — нет, нужно выспрашивать у автора, какая картина у него в голове; к тому же я не удивлюсь, если в ходе работы над сложной задачей разработчик часто сначала будет допиливать что может до включения в master, а потом выпиливать это обратно. И это в дополнение к затратам на поддержание «чистоты» истории. А refactoring требуется не только legacy, хотя обширный, может, и реже — смотря что не учли при разработке архитектуры. В trunk-based с флагами я пока вижу только «давайте увеличим трудозатраты программистов и reviewer’ов (часто — других программистов) на то, чтобы тимлид смог читать „чистую“ историю».
t-nick
06.01.2018 19:31Возможно у вас другая практика ревью и структура команды, размер проекта, время жизни проекта…
У нас плоская структура (Scrum), тимлида как такового нет, есть более опытные разработчики и менее опытные. Историю читают все. Знания шарятся между всеми разработчиками проекта (уменьшаем bus-factor) в том числе посредством код ревью. Для эффективности этого процесса нужно минимизировать изменения, иначе код ревью превращается в проверку код-стайла, так как понять суть изменений слишком сложно.
Для долгоживущего проекта время разработки не критично, критично время внесения изменений. И feature toggl'ы — позволяют поддерживать это время на приемлемом уровне, так же как и SOLID, юнит тесты, TDD.
CI можно гонять для всех юнит тестов и для интеграционных тестов продакшн конфигурации. Но это отдельная тема.
t-nick
06.01.2018 14:59Относительно хаоса: а зачем вы используете историю, что оно вас волнует?
Да просто посмотреть лог за неделю, что было сделано. Приятнее видеть список, а не гордиев узел из мердж коммитов.
Коммит должен представлять законченный набор изменений. Если это так, то имеет смысл влить его в мастер, а не держать в ветке.
Я собственно веду к подходу trunk-based разработки, к которому в итоге пришел за много лет, так как этот подход дает стабильный мастер (с возможностью релиза с любого коммита) при большом количестве ежедневных изменений, простоту код-ревью, список изменений понятный каждому участнику проекта.
amarao
05.01.2018 12:58+9У автора фиксация на переводе фиксаций. Фиксация такое русское слово, что назвать коммит коммитом никак нельзя.
Zav
05.01.2018 13:43Т.е. вы считаете PR в dev не делается?
Там ровно такое же код ревью, ровно такой же PR с обсуждением и так далее.
Master — продакшен
Dev — стейдж
GitFlow на хабре
В случае GitFlow проблема возникает только в dev'е, а мастер всегда рабочий (ну, во всяком случае чаще).
В вашем случае если ошибку упустили на ревью, если не обнаружили на тестах ДО слияния, то оно вполне может попасть на прод.
therhino
А что, есть те, кто не осилили оригинал и им надо перевод?
zelenin
Здесь переводят не для тех, кто не осилил перевод.