Объяснение расширенного Git Flow

Опубликовано: 2022-03-11

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

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

Как и многие разработчики, вы хотели что-то, что «просто работало бы», чтобы вы могли приступить к реальной разработке программного обеспечения. Итак, вы выбрали Git flow — модель ветвления, которую часто рекомендуют пользователям Git. Возможно, вы сначала были согласны с логикой Gitflow, пока не столкнулись с некоторыми трудностями на практике. Или, может быть, Gitflow не кажется вам достаточно подходящим для его принятия. В конце концов, в игре участвует бесчисленное множество переменных, и ни одна модель ветвления не будет работать хорошо во всех ситуациях.

Хорошие новости! Вариант классической модели потока Git, улучшенный поток Git упрощает наиболее распространенные маневры рабочего процесса потока Git, сохраняя при этом основные преимущества.

Великолепие и нищета классической модели Git Flow

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

Значительное приращение стоимости требует значительного времени для завершения, например, двухнедельные спринты, обычно используемые в разработке на основе Scrum. Если команда разработчиков уже выполнила развертывание в рабочей среде, могут возникнуть проблемы, если объем следующего выпуска будет накапливаться в том же месте, где находится производственный код — например, в основной ветке репозитория Git, который они используют.

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

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

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

Поток Git обрабатывает эти фундаментальные сценарии, разделяя «основную» (производственная или «текущая версия» ветку) и «разрабатывать» (разрабатываемую или «следующую выпускную» ветку) и предоставляя все правила использования веток функций/выпусков/исправлений. . Это эффективно решает множество головных болей, связанных с рабочими процессами разработки продуктов на основе релизов.

Но даже с проектами, хорошо подходящими для классической модели потока Git, я столкнулся с типичными проблемами, которые она может принести:

  • Поток Git сложен, с двумя долгоживущими ветвями, тремя типами временных ветвей и строгими правилами взаимодействия ветвей друг с другом. Такая сложность делает ошибки более вероятными и увеличивает усилия, необходимые для их исправления.
  • Ветки релиза и исправления требуют «двойного слияния» — один раз в основную, а затем в разработку. Иногда вы можете забыть сделать и то, и другое. Вы можете упростить ветвление потока Git с помощью скриптов или клиентских плагинов VCS GUI, но вы должны сначала настроить их для каждой машины каждого разработчика, участвующего в данном проекте.
  • В рабочих процессах CI/CD вы обычно получаете две окончательные сборки для выпуска — одну из последней фиксации самой ветки выпуска, а другую — из фиксации слияния с основной. Строго говоря, вы должны использовать один из основных, но они обычно идентичны, что может привести к путанице.

Введите «Расширенный поток Git»

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

Примечание. Вплоть до первого публичного релиза продукта имеет смысл фиксировать все изменения непосредственно в основной ветке — даже если вы являетесь сторонником Git-потока — ради скорости и простоты рабочего процесса разработки. Поскольку производства еще нет, нет возможности, чтобы производственная ошибка была исправлена ​​командой как можно скорее. Таким образом, выполнение всей магии ветвления, которую подразумевает классический поток Git, на данном этапе является излишним.

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

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

Сначала я оттолкнулся. Мне показалось, что некоторые из предложенных «заплаток» к классическому потоку Git были чересчур революционными. Я думал, что они могут нарушить основную идею, и весь подход не оправдает ожиданий. Но после дальнейших размышлений я понял, что эти настройки на самом деле не нарушают работу Git. Между тем, они делают его лучшей моделью ветвления Git, решая все упомянутые выше болевые точки.

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

Типичный график коммитов Git при использовании расширенного потока Git. На графике показаны несколько коммитов на development и main, а перед их общим коммитом — несколько тегов на основе даты.

Мой общий опыт работы с этим новым подходом к ветвлению был настолько положительным, что я захотел поделиться им со своими коллегами-разработчиками, чтобы помочь им обойти недостатки классического потока Git.

Сходства с классическим Git Flow: изоляция разработки

Для изоляции работы в расширенном потоке Git по-прежнему есть две долгоживущие ветки: main и develop. (Пользователи по-прежнему имеют возможности исправления и выпуска — с акцентом на «возможности», поскольку это больше не ветки. Мы подробно рассмотрим в разделе различий.)

Официальной схемы именования для классических веток функций потока Git не существует. Вы просто отделяетесь от разработки и возвращаетесь к разработке, когда функция будет готова. Команды могут использовать любое соглашение об именах, которое им нравится, или просто надеяться, что разработчики будут использовать более описательные имена, чем «моя ветка». То же самое верно и для расширенного потока Git.

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

Сквош слияния

Я настоятельно рекомендую использовать сквош-слияние для веток функций, чтобы большую часть времени история оставалась красивой и линейной. Без него графики коммитов (из GUI-инструментов или git log --graph ) начинают выглядеть неряшливо, когда команда жонглирует даже несколькими ветвями функций:

Сравнение графика коммитов, полученного в результате стратегии слияния сквоша, с графиком, полученным в результате стратегии коммитов слияния. Начальный граф коммитов Git имеет свою основную ветвь, помеченную как «разработка», с более ранним коммитом, имеющим ответвления «функция-D» и «функция-C», а еще более ранний коммит имеет «функция-B» и «функция-A». "ответвление от него. Результат коммита слияния выглядит аналогично, но каждая ветвь функции вызывает новую фиксацию при разработке в той точке, где она связана с ней. Результат слияния сквоша получился как прямая линия коммитов, без других ветвей.

Но даже если у вас все в порядке с визуальными эффектами в этом сценарии, есть еще одна причина для раздавливания. Представления истории коммитов без раздавливания — среди них как простой git log (без --graph ), так и GitHub — рассказывают довольно бессвязные истории даже в самых простых сценариях слияния:

Сравнение просмотра истории коммитов, полученного в результате применения тактики слияния, с тактикой фиксации слияния. Исходное состояние репозитория представлено в виде временной шкалы, показывающей хронологию коммитов в две ветки функций, исходящих из общего коммита «x» при разработке, с чередованием коммитов между ветвями, а именно в порядке 1a, 2a, 1b и 2b. Результаты фиксации слияния показаны в двух вариантах. В истории коммитов, полученной в результате слияния второй ветки, слияния первой ветки, а затем удаления обеих ветвей, история является хронологической, но не связной и включает дополнительную фиксацию слияния для первой ветки. В истории, полученной в результате слияния веток по порядку перед их удалением, коммиты упорядочены: «x, 2a, 1a, 2b, 1b», за которыми следует коммит слияния для второй ветки, что даже не в хронологическом порядке. История коммитов от слияния сквоша просто имеет один коммит для каждой функциональной ветки с историей каждой ветки, рассказанной коммиттером.

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

Отличия от классического Git Flow: выпуски и исправления

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

Графики коммитов Git по мере их изменения при выполнении обычного выпуска в расширенном потоке Git. Исходный график имеет основное отклонение от разработки на несколько коммитов за верхушкой и на один коммит. После пометки main и vYYYY-MM-DD совпадают друг с другом. После удаления локального файла main, создания его в конце разработки, принудительной отправки, развертывания, тестирования и т. д. main отображается даже при разработке, оставляя vYYYY-MM-DD там, где изначально был main. После цикла развертывания/тестирования промежуточные исправления на основном (в конечном итоге сквош сливаются с разработкой), а между тем, несвязанные изменения на разработке, окончательный граф имеет расхождения разработки и основного, каждый с несколькими коммитами, от того места, где они были даже друг с другом в предыдущий график.

Релизы в Enhanced Git Flow

Каждый шаг создания релиза с расширенным потоком Git отличается от классического процесса потока Git:

  1. Релизы основаны на основном, а не на разработке. Пометьте текущую вершину основной ветки чем-то значимым. Я принял теги на основе текущей даты в формате ISO 8601 с префиксом «v», например, v2020-09-09 .
    • Если случилось так, что в день было несколько выпусков — например, исправлений — формат мог иметь порядковый номер или букву, прикрепленную к нему по мере необходимости.
    • Имейте в виду, что теги, как правило, не соответствуют датам выпуска. Они нужны исключительно для того, чтобы заставить Git сохранять ссылку на то, как выглядела основная ветка на момент начала процесса следующего выпуска.
  2. Нажмите тег, используя git push origin <the new tag name> .
  3. После этого небольшой сюрприз: удалите свою локальную основную ветку . Не волнуйтесь, потому что мы собираемся восстановить его в ближайшее время.
    • Все коммиты в main по-прежнему безопасны — мы защитили их от сборки мусора, пометив main на предыдущем шаге. Каждая из этих коммитов — даже исправления, о чем мы вскоре расскажем, — также является частью разработки.
    • Просто убедитесь, что только один человек в команде делает это для каждого выпуска; это так называемая роль «менеджера релизов». Менеджер релиза обычно является самым опытным и/или самым старшим членом команды, но команде было бы разумно избегать того, чтобы какой-либо конкретный член команды брал на себя эту роль на постоянной основе. Имеет больше смысла распространять знания среди команды, чтобы увеличить коэффициент печально известного автобуса.
  4. Создайте новую локальную основную ветку в конце фиксации вашей ветки разработки .
  5. Нажмите эту новую структуру, используя git push --force , поскольку удаленное репо не примет такое «радикальное изменение» так легко. Опять же, это не так небезопасно, как может показаться в данном контексте, потому что:
    • Мы просто перемещаем указатель главной ветки из одного коммита в другой.
    • Только один конкретный член команды делает это изменение одновременно.
    • Повседневная работа по разработке происходит в ветке разработки, поэтому вы не нарушите чью-либо работу, переместив main таким образом.
  6. У вас новый релиз! Разверните его в промежуточной среде и протестируйте. (Удобные шаблоны CI/CD мы обсудим ниже.) Любые исправления попадают сразу в основную ветку, и из-за этого она начинает расходиться с веткой разработки.
    • В то же время вы можете начать работу над новым релизом в ветке разработки, то же преимущество, что и в классическом потоке Git.
    • В том случае, если, к сожалению, требуется исправление для того, что в настоящее время находится в производстве (а не для предстоящего промежуточного выпуска) на данный момент, более подробная информация об этом сценарии приведена в разделе «Работа с исправлениями во время активного выпуска…» ниже.
  7. Когда ваш новый выпуск будет признан достаточно стабильным, разверните окончательную версию в производственной среде и выполните однократное слияние main для разработки , чтобы получить все исправления.

Исправления в Enhanced Git Flow

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

В качестве последнего шага выберите свой коммит из основного для разработки, чтобы гарантировать, что следующий выпуск будет содержать все исправления. В случае, если вы в конечном итоге сделаете несколько коммитов исправлений, вы сэкономите усилия — особенно если ваша среда IDE или другой инструмент Git может облегчить это — создав и применив патч, а не выбирая вишни несколько раз. Попытка раздавить слияние main для разработки после первоначального выпуска, вероятно, приведет к конфликтам с независимым прогрессом, достигнутым в ветке разработки, поэтому я не рекомендую этого.

Работа с исправлениями во время активного выпуска — т. е. когда вы просто принудительно отправили main и все еще готовите новый выпуск — является самой слабой частью расширенного потока Git. В зависимости от продолжительности вашего цикла выпуска и серьезности проблемы, которую вы должны решить, всегда стремитесь включать исправления в сам новый выпуск — это самый простой способ сделать это, и он вообще не нарушит общий рабочий процесс.

В случае, если это невозможно — вам нужно быстро ввести исправление, и вы не можете ждать, пока будет готов новый выпуск, — тогда приготовьтесь к довольно сложной процедуре Git:

  1. Создайте ветку — мы назовем ее «новый выпуск», но ваша команда может принять здесь любое соглашение об именах — в той же фиксации, что и текущая вершина main. Нажмите новый релиз.
  2. Удалите и заново создайте локальную основную ветку при фиксации тега, который вы создали ранее для текущего активного выпуска. Силовой толчок основной.
  3. Внесите необходимые исправления в main, разверните в промежуточной среде и протестируйте. Когда это будет готово, разверните его в рабочей среде.
  4. Распространяйте изменения с текущего основного на новый выпуск либо путем выбора вишни, либо с помощью исправления.
  5. После этого повторите процедуру выпуска: пометьте конец текущей основной ветки и нажмите на нее, удалите и заново создайте локальную главную в конце ветки нового выпуска и принудительно нажмите главную.
    1. Скорее всего, вам не понадобится предыдущий тег, поэтому вы можете удалить его.
    2. Ветка нового выпуска теперь избыточна, поэтому вы также можете удалить ее.
  6. Теперь вы должны быть в порядке, как обычно, с новым выпуском. Завершите распространением экстренных исправлений с основного на разработку путем выбора вишни или патча.

Графики фиксации Git по мере их изменения при выполнении исправления во время активного выпуска в рамках расширенного потока Git. Стартовый график развивался как самая длинная линия коммитов, при этом main расходится на два коммита ранее на один коммит, а на три коммита до этого расходится ветвь a на один коммит с тегом v2020-09-18. После первых двух шагов, описанных выше, на графике появляется новая версия вместо main, а main нет даже с v2020-09-18. Затем выполняются остальные шаги, в результате чего получается окончательный график, где main — это фиксация перед новой версией, а v2020-09-20 — фиксация перед v2020-09-18.

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

Настройка CI/CD поверх Enhanced Git Flow

Не каждый проект требует специальной среды разработки. Достаточно просто настроить сложную локальную среду разработки на каждой машине разработчика.

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

Я обнаружил, что некоторые шаблоны CI/CD особенно полезны в сочетании с расширенным потоком Git:

  • Если вам нужна среда разработки, настройте CI для сборки, тестирования и развертывания в ней при каждой фиксации в ветке разработки. Сюда также подходит E2E-тестирование, если оно у вас есть и если оно имеет смысл в вашем случае.
  • Настройте CI для сборки, тестирования и развертывания в промежуточной среде при каждой фиксации в основной ветке. E2E-тестирование также весьма полезно на этом этапе.
    • Использование E2E-тестирования в обоих случаях может показаться излишним, но помните, что исправлений в процессе разработки не будет. Запуск E2E при коммитах в main позволит тестировать исправления и ежедневные изменения до того, как они будут выпущены, но также запуск при коммитах для разработки позволит выявить ошибки раньше.
  • Настройте CI таким образом, чтобы ваша команда могла развертывать сборки из основной в рабочей среде по запросу вручную.

Рекомендуемая настройка CI/CD с расширенным потоком Git при наличии среды разработки ("dev") в дополнение к промежуточной ("stage") и рабочей ("prod"). Все коммиты в ветке разработки приводят к развертыванию сборки в dev. Аналогично, все коммиты в main приводят к развертыванию сборки на этапе. Ручной запрос конкретной фиксации из main приводит к развертыванию сборки в рабочей среде.

Такие шаблоны относительно просты, но предоставляют мощный механизм для поддержки повседневных операций разработки.

Модель Enhanced Git Flow: улучшения и возможные ограничения

Расширенный поток Git не для всех. Он использует противоречивую тактику принудительного продвижения основной ветки, поэтому пуристы могут возмутиться. Хотя с практической точки зрения в этом нет ничего плохого.

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

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

Особая благодарность коллеге Toptal Антуану Фаму за его ключевую роль в разработке идеи улучшенного потока Git.


Дальнейшее чтение в блоге Toptal Engineering:

  • Разработка на основе магистрали против Git Flow
  • Git Workflows для профессионалов: хорошее руководство по Git

Значок Золотого партнера Microsoft.

Будучи золотым партнером Microsoft, Toptal — это ваша элитная сеть экспертов Microsoft. Создавайте высокопроизводительные команды с нужными вам экспертами - где угодно и именно тогда, когда они вам нужны!