Skip to content

Instantly share code, notes, and snippets.

@nimdraugsael
Created June 20, 2013 06:11
Show Gist options
  • Save nimdraugsael/5820632 to your computer and use it in GitHub Desktop.
Save nimdraugsael/5820632 to your computer and use it in GitHub Desktop.

Workflow, описанный в данном документе в явном виде используется на данный момент в проектах jetradar и ifeiso, геме cms_engine, частично в nano_api. Так же возможно использование приёмов, описанных в документе, в других workflow.

Trivia

Описываемый workflow в общем случае выглядит следующим образом:

  1. Актуализируем локальный master.
  2. Создаём feature ветку от мастера.
  3. Делаем необходимые изменения и коммиты.
  4. Содержим ветку актуальной относительно master при помощи rebase.
  5. Пуши готовую фичу на сервер.
  6. Самостоятельно в 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 ниже.

Работа в feature ветке

Вся работа по изменению исходного кода должна производиться в отдельной ветке. За редким исключением ветка создаётся из мастера:

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

Push ветки на сервер

Пуш ветки на сервер осуществляется командой:

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 пути:

  1. Локальных коммитов в эту ветку со времён обновления не делалось. Проверяетмя это примерно так: если 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
    

    Продолжаем работать в перерождённой веточке.

  2. Второй вариант немного посложнее — у нас были локальные изменения. Суть действий в том, чтобы сохранить локальные коммиты. Поэтому мы переименовываем локальный 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

Для работы с конфликтами во время rebase лучше всего использовать git mergetool. Под Linux лучшие программы: для kde — kdiff3, для gnome — meld. Под MacOS - ????? Работа с mergetool сильно облегчает резолвинг конфликтов. Настоятельно рекомендуется учиться и использовать именно этот инструмент вместо ручного резрешения конфликтов.

Почитать

Почитать про все подробнее с примерами и картинками можно в книге Pro Git. Для начала прочитать можно Git Basics для освоения осноных команд и их полезных ключей и Git Branching для понимания, что такое fast-forward, что происходит во время rebase и почему надо бранчиться.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment