Рабочий процесс Git | Сравнение рабочих процессов

Сравнение процессов

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

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

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

Что такое успешный рабочий процесс Git?

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

  • Можно ли масштабировать рабочий процесс по мере роста команды?
  • Насколько легко можно устранять недоработки и ошибки в рамках рабочего процесса?
  • Сколько избыточных умственных усилий требует рабочий процесс от команды?

Централизованный рабочий процесс

Рабочий процесс Git | Центральный и локальный репозитории

Централизованный рабочий процесс Git отлично подходит для команд, которые переходят в Git из SVN. Как и в системе Subversion, централизованный рабочий процесс Git использует центральный репозиторий в качестве единой точки входа для всех изменений в проекте. Ветка разработки по умолчанию называется не стволом (trunk), а главной веткой (main). В нее отправляются коммиты всех изменений. В этом рабочем процессе необязательно создавать другие ветки кроме main.

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

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

Во-вторых, этот процесс открывает доступ к надежной модели ветвления и слияния, принятой в Git. В отличие от SVN, ветки Git представляют собой отказоустойчивый механизм для интеграции кода и передачи изменений между репозиториями. Централизованный рабочий процесс похож на другие рабочие процессы тем, что в нем используется удаленный репозиторий. Репозиторий размещается на сервере, и разработчики помещают туда и извлекают оттуда содержимое. В отличие от других рабочих процессов, в централизованном рабочем процессе не определены шаблоны запросов pull или ветвления (форков). Централизованный рабочий процесс обычно больше подходит для команд, которые переходят из SVN в Git, а также для небольших команд.

Порядок действий

Разработчики начинают с клонирования центрального репозитория. Как и в SVN, они редактируют файлы и делают коммиты изменений в собственных локальных копиях проекта. Однако эти новые коммиты хранятся локально и полностью изолированы от центрального репозитория. Синхронизацию с вышестоящими изменениями разработчики могут отложить до более удобного момента.

Для публикации изменений в официальном проекте разработчик «помещает» свою локальную ветку main в центральный репозиторий. Для этого также можно использовать команду svn commit, однако в этом случае добавляются все локальные коммиты, которых нет в ветке main центрального репозитория.

Инициализация центрального репозитория

Рабочий процесс Git: инициализация чистого центрального репозитория

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

Централизованные репозитории всегда должны быть чистыми (т. е. не должны содержать рабочего каталога). Такой репозиторий можно создать командой

ssh user@host git init --bare /path/to/repo.git

Здесь user — действительное имя пользователя SSH, host — доменный или IP-адрес сервера, /path/to/repo.git — место, где будет храниться репозиторий. Обратите внимание, что к имени чистых репозиториев обычно добавляется расширение .git, чтобы их можно было отличить.

Размещение центральных репозиториев

Часто центральные репозитории создаются через сторонние сервисы хостинга Git, такие как Bitbucket Cloud или Bitbucket Server. В этом случае процедуру инициализации чистого репозитория, рассмотренную выше, выполняет сам сервис хостинга. После этого сервис хостинга предоставляет разработчикам адрес центрального репозитория для доступа к нему из локальных репозиториев.

Клонирование центрального репозитория

Затем каждый разработчик создает локальную копию всего проекта с помощью команды git clone:

git clone ssh://user@host/path/to/repo.git

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

Внесение изменений и выполнение коммита

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

git status # View the state of the repo
git add <some-file> # Stage a file
git commit # Commit a file</some-file>

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

Отправка новых коммитов в центральный репозиторий

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

 git push origin main

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

Управление конфликтами

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

Рабочий процесс Git |: управление конфликтами

Прежде чем публиковать свою функцию, разработчик должен извлечь из центрального репозитория обновленные коммиты и перебазировать свои изменения поверх них. Этим он как бы заявляет: «Я хочу, чтобы мои изменения были добавлены после всех остальных». В результате получится абсолютно линейная история, как в классических рабочих процессах SVN.

Если локальные изменения прямо противоречат предыдущим коммитам, Git приостанавливает процесс перебазирования и дает возможность разработчику разрешить конфликты вручную. Очень удобно, что в Git и для создания коммитов, и для разрешения конфликтов слияния используются одни и те же команды: git status и git add. Благодаря этому новые разработчики могут без труда разрешать конфликты при слиянии. Кроме того, если у них возникнут проблемы, Git позволит легко прервать процесс перебазирования и повторить попытку (или обратиться за помощью).

Пример

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

Джон работает над своей функцией

Рабочие процессы Git: процесс редактирования, индексирования и выполнения коммитов при работе над функцией

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

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

Мэри работает над своей функцией

Рабочие процессы Git: редактирование, индексирование и выполнение коммитов при работе над функцией

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

Джон публикует свою функцию

Рабочие процессы Git: публикация функции

Завершив работу над функцией, Джон должен опубликовать свои локальные коммиты в центральном репозитории, чтобы к функции могли получить доступ другие участники команды. Он может сделать это следующим образом с помощью команды git push.

 git push origin main

Помните, что origin — это удаленное подключение к центральному репозиторию, который создала система Git, когда Джон клонировал этот репозиторий. Аргумент main дает системе Git команду попытаться привести ветку main в репозитории origin к текущему виду локальной ветки main. Поскольку центральный репозиторий не обновлялся с тех пор, когда Джон его клонировал, это действие пройдет без каких-либо конфликтов и команда push сработает должным образом.

Мэри пытается опубликовать свою функцию

Рабочие процессы Git: ошибка команды push

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

 git push origin main

Однако поскольку ее локальная история отличается от истории центрального репозитория, Git отклонит запрос, выдав довольно подробное сообщение об ошибке:

error: failed to push some refs to '/path/to/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

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

Мэри перебазирует свои изменения поверх коммитов Джона

Рабочие процессы Git: перебазирование с помощью команды git pull

Для добавления вышестоящих изменений в свой репозиторий Мэри может использовать команду git pull. Эта команда похожа на команду svn update: она собирает всю историю вышестоящих коммитов в локальном репозитории Мэри и пытается интегрировать ее с локальными коммитами.

 git pull --rebase origin main

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

Рабочие процессы Git: перебазирование на ветку master

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

Мэри разрешает конфликт слияния

Рабочие процессы Git: перебазирование коммитов

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

Если Мэри и Джон работают над не связанными друг с другом функциями, то вряд ли процесс перебазирования приведет к появлению конфликтов. Однако если это все же произойдет, Git приостановит перебазирование на текущем коммите и выведет вот такое сообщение с несколькими соответствующими инструкциями:

CONFLICT (content): Merge conflict in <some-file>
Рабочие процессы Git: разрешение конфликтов

Замечательная особенность Git состоит в том, что разрешить свои конфликты слияния может любой пользователь. В нашем примере Мэри может просто запустить команду git status и найти источник проблемы. Конфликтующие файлы отобразятся в разделе Unmerged paths (Пути без слияния).

# Unmerged paths:
# (use "git reset HEAD <some-file>..." to unstage)
# (use "git add/rm <some-file>..." as appropriate to mark resolution)
#
# both modified: <some-file>

Затем она может отредактировать файлы по своему усмотрению. После внесения нужных изменений можно проиндексировать файлы обычным способом и позволить команде git rebase сделать все остальное.

git add <some-file>
git rebase --continue

Вот и все. Git перейдет к следующему коммиту и повторит процесс для всех остальных коммитов, создающих конфликты.

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

git rebase --abort

Мэри опубликовала свою функцию

Рабочие процессы Git: синхронизация центрального репозитория

После выполнения синхронизации с центральным репозиторием Мэри может опубликовать свои изменения:

 git push origin main

Куда можно перейти отсюда

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

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

Другие распространенные рабочие процессы

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

Использование функциональных веток

Функциональное ветвление является логическим расширением централизованного рабочего процесса. Основная идея рабочего процесса с функциональными ветками в том, что разработка каждой функции ведется в отдельной ветке, а не в ветке main. Такое обособление позволяет нескольким разработчикам с легкостью создавать конкретную функцию, не затрагивая основную базу кода. Кроме того, благодаря этому ветка main ограждается от проблемного кода. Это важное преимущество для среды с непрерывной интеграцией.

Рабочий процесс Gitflow Workflow

Рабочий процесс Gitflow был впервые опубликован в 2010 году в блоге разработчика Винсента Дриссена, известного под псевдонимом nvie. Gitflow предполагает выстраивание строгой модели ветвления с учетом выпуска проекта. В этом рабочем процессе используются понятия и команды, которые были предложены в рамках рабочего процесса с функциональными ветками. Однако Gitflow привносит новые специфические роли для разных веток и определяет характер и частоту взаимодействия между ними.

Рабочий процесс Forking Workflow

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

Рекомендации

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

Краткосрочные ветки

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

Сокращение количества возвратов к предыдущей версии и упрощение этого процесса

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

Соответствие графику релизов

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

Резюме

В этом документе были рассмотрены рабочие процессы Git. Мы подробно изучили централизованный рабочий процесс и его практические примеры. Для более подробного освещения темы централизованного рабочего процесса были рассмотрены дополнительные специализированные рабочие процессы. Вот некоторые ключевые идеи этого документа.

  • Универсального рабочего процесса Git не существует.
  • Рабочий процесс должен быть простым и повышать продуктивность вашей команды.
  • Требования бизнеса должны оказывать непосредственное влияние на формирование рабочего процесса Git.

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

Готовы изучить Git?

Ознакомьтесь с этим интерактивным обучающим руководством.

Начните прямо сейчас