Workflow, описанный в данном документе в явном виде используется на данный момент в проектах jetradar и ifeiso, геме cms_engine, частично в nano_api. Так же возможно использование приёмов, описанных в документе, в других workflow.
Описываемый workflow в общем случае выглядит следующим образом:
- Актуализируем локальный master.
- Создаём feature ветку от мастера.
- Делаем необходимые изменения и коммиты.
- Содержим ветку актуальной относительно master при помощи rebase.
- Пуши готовую фичу на сервер.
- Самостоятельно в master ветку мерджить не следует. Фичи для мерджинга в master выбираются по мере необходимости.
Стоит заметить, что каждая фича должна быть протестирована, а спеки должны быть полностью зелёными.
Repo-master сам занимается выбором фич, которые попадут в master.
Таким образом, master будет содержать только необходимые в данный момент фичи. При этом, в любой момент от мастера можно отвести release ветку для деплоя на продакшен сервер.
Данный workflow хорошо подходит для работы с системами CI.
git clone [email protected]:KosyanMedia/jetradar.git
Команда создаёт локальную копию репозитория.
После того, как репозиторий склонирован — начинается работа.
git fetch -p
Эта команда получает последние данные о состоянии удалённого репозитория.
В штатном режиме возможны 2 варианта вывода: пустой или информацию о состоянии репозитория. Если мы не получили информацию — пропускаем следующий шаг. Если же вывод выглядит примерно так:
remote: Counting objects: 21, done.
remote: Compressing objects: 100% (8/8), done.
remote: Total 14 (delta 6), reused 12 (delta 4)
Unpacking objects: 100% (14/14), done.
From github.com:KosyanMedia/jetradar
e3554b2..9f1d45a master -> origin/master
то нас интересует нижняя строчка. В таком виде она означает, что ветка master была изменена на сервере. В свою очередь, это означает, что нам необходимо обновить локальный master до удалённого. Делается это следующим образом:
git rebase origin/master
Чаще всего rebase проходит в автоматическом режиме, не вызвая конфликтов. Но если конфликты всё-таки случились, то смотри раздел Rebase ниже.
Вся работа по изменению исходного кода должна производиться в отдельной ветке. За редким исключением ветка создаётся из мастера:
git checkout -b prefix_123345679_feature_name
Где prefix — это тип тикета (bug, feature), а 123345679 — это номер тикета в crm, если таковой существует. Дальше делаются необходимые изменения исходного кода, при этом коммиты происходят как можно атомарнее и чаще.
Так же происходит регулярный rebase рабочей ветки относительно мастера, чтобы не терять актуальность кода. Стандартный процесс — обновление, актуализация master ветки (описана выше) и rebase:
git fetch -p
git checkout master
git rebase origin/master
git checkout feature_branch_name
git rebase master
Пуш ветки на сервер осуществляется командой:
git push origin feature_branch_name
Всё предельно просто, если пуш осуществляется один раз после завершения работы над фичей. Но если пуш был сделан один раз, затем локально был сделан rebase относительно мастера, а затем понадобилось сделать ещё раз push — не получится. При rebase относительно мастера меняются хеши коммитов, и получается, что наша ветка состоит из совершенно иных комитов, нежели наша ветка на сервере. Когда сервер отказывает в пуше — мы видим примерно такое сообщение:
To [email protected]:KosyanMedia/jetradar.git
! [rejected] feature_branch_name -> feature_branch_name (non-fast-forward)
Решение проблемы — force push.
git push -f origin feature_branch_name
Увага. Сервер может отказать при пуше не только в случае локального ребейза, но и в случае, если другой участник совместной работы над фичей запушил в ветку свои изменения. В этом случае надо актуализировать локальную feature_branch относительно серверной.
git fetch -p
git rebase feature_branch_name
Итого. Для того, чтобы не наломать дров, стоит взять за правило перед пушем делать git fetch -p
, который покажет, произошли ли изменения в feature_branch удалённого репозитория. Если изменения есть — то rebase. Если изменений нет, либо если rebase прошёл, а push не проходит — то force push ветки на сервер.
Многие разработчики небезосновательно считают форс пуш злом. Он и есть зло и пользоваться им необходимо обдуманно и осторожно. Только в случае работы над своей веткой. Но если знать некоторые особенности работы гита — всё становится намного проще и понятнее. Например, если при git fetch
приходит что-то типа:
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 2), reused 3 (delta 2)
Unpacking objects: 100% (3/3), done.
From github.com:KosyanMedia/jetradar
+ 60a1bc2...0a3de2d feature_branch_name -> origin/feature_branch_name (forced update)
Значит, ветка была пушнута на сервер с форсом, а это, в свою очередь, означает, что необходимо вернуться к редактированию этой ветки. Тут у нас есть 2 пути:
-
Локальных коммитов в эту ветку со времён обновления не делалось. Проверяетмя это примерно так: если
git log feature_branch_name
иgit log origin/feature_branch_name
показывают один и тот же последний коммит (одинаковый коммит-message) — значит локальных изменений не было. Порядок действий следующий:git checkout master git branch -D feature_branch_name git checkout feature_branch_name origin/feature_branch_name
Продолжаем работать в перерождённой веточке.
-
Второй вариант немного посложнее — у нас были локальные изменения. Суть действий в том, чтобы сохранить локальные коммиты. Поэтому мы переименовываем локальный feature_branch, чекаутим feature_branch с сервера и черри-пикаем наши коммиты из переименованного в новый feature_branch:
git branch -m feature_branch_name1 git checkout -b feature_branch_name origin/feature_branch_name git log feature_branch_name1 --oneline git cherry-pick 60a1bc2 # черри-пикаем все нужные коммиты, выведенные логом до победного git branch -D feature_branch_name1 # не нужна нам она больше
Продолжаем работать в перерождённой веточке.
Если git fetch -p
возвращает что-то вроде:
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 2), reused 3 (delta 2)
Unpacking objects: 100% (3/3), done.
From github.com:KosyanMedia/jetradar
- [deleted] feature_branch_name
Это значит, что ветка была удалена. Кстати, удаляется ветка на сервере git push origin :feature_branch_name
.
Если у нас не было коммитов в эту ветку, которые хотелось бы запушить — то счастливо забываем про неё. Если же были коммиты, то лучше эти коммиты нанизать на мастер:
git checkout master
git checkout -b feature_branch_name_additional
git log feature_branch_name --oneline
git cherry-pick 60a1bc2
git branch -D feature_branch_name
git push origin feature_branch_name_additional
Продолжаем работать в перерождённой веточке, либо забываем про неё.
Для работы с конфликтами во время rebase лучше всего использовать git mergetool
. Под Linux лучшие программы: для kde — kdiff3, для gnome — meld. Под MacOS - ?????
Работа с mergetool сильно облегчает резолвинг конфликтов. Настоятельно рекомендуется учиться и использовать именно этот инструмент вместо ручного резрешения конфликтов.
Почитать про все подробнее с примерами и картинками можно в книге Pro Git. Для начала прочитать можно Git Basics для освоения осноных команд и их полезных ключей и Git Branching для понимания, что такое fast-forward, что происходит во время rebase и почему надо бранчиться.