вторник, 17 февраля 2015 г.

Ветвление в Git - Основы ветвления и слияния

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

  1. Поработаем над веб-сайтом.
  2. Создадим ветку для работы над новой задачей.
  3. Выполним некоторую работу на этой ветке.

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

  1. Вернёмся на ветку для версии в производстве.
  2. Создадим ветку для исправления ошибки.
  3. После тестирования ветки с исправлением сольём её обратно и отправим в продакшн.
  4. Вернёмся к своей исходной задаче и продолжим работать над ней.

Для начала представим, что вы работаете над своим проектом и уже имеете коммиты C0, C1 и C2

basic-branching-1

 

W0001

Командами

$ git log --pretty=format:"%h - %s"

$ git log --oneline --decorate

Мы посмотрели историю коммитов и то на какой коммит в какой ветке указывает HEAD. Все как видим соответствует диаграмме.

Вы решили, что вы будете работать над проблемой №53 из системы отслеживания ошибок, используемой вашей компанией. Разумеется, Git не привязан к какой-то определенной системе отслеживания ошибок. Так как проблема №53 является обособленной задачей, над которой вы собираетесь работать, мы создадим новую ветку и будем работать на ней. Чтобы создать ветку и сразу же перейти на неё, вы можете выполнить команду git checkout с ключом -b:

$ git checkout -b iss53
Switched to a new branch "iss53"

Это сокращение для комманд:

$ git branch iss53
$ git checkout iss53

W0002

На диаграмме это выглядит вот так:

basic-branching-2

Во время работы над своим веб-сайтом мы делаем коммит. Это действие сдвигает ветку iss53 вперёд потому, что вы на неё перешли (то есть ваш HEAD указывает на неё; см. рис. и скрины):

W0003

W0004

Теперь тоже самое на диаграмме

basic-branching-3

Теперь вы получаете звонок о том, что есть проблема с веб-сайтом, которую необходимо немедленно устранить. С Git'ом вам нет нужды делать исправления для неё поверх тех изменений, которые вы уже сделали в iss53, и нет необходимости прикладывать много усилий для отмены этих изменений перед тем, как вы сможете начать работать над решением срочной проблемы. Всё, что вам нужно сделать, это перейти на ветку master.

Однако, прежде чем сделать это, учтите, что если в вашем рабочем каталоге или индексе имеются незафиксированные изменения, которые конфликтуют с веткой, на которую вы переходите, Git не позволит переключить ветки. Лучше всего при переключении веток иметь чистое рабочее состояние. Существует несколько способов добиться этого (а именно, прятанье (stash) работы и правка (amend) коммита), которые мы рассмотрим позже. А на данный момент представим, что все изменения были добавлены в коммит, и теперь вы можете переключиться обратно на ветку master:

W0005

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

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

$ git checkout -b hotfix

Подправим наш файлик index.html и закоммитим изменения

W0006

Из скрина видно что HEAD теперь указывает на наш коммит (7995847) C4 в ветке hotfix. На диаграмме это можно отобразить так:

basic-branching-4

А на псевдографе это выглядит так

W0007

То есть ветка для решения срочной проблемы базируется на ветке master.

После тестирования нашего хотфикса, убеждаемся, что решение работает, и решаем слить (merge) изменения назад в ветку master, чтобы включить их в продукт. Это делается с помощью команды git merge:

$ git checkout master
$ git merge hotfix

W0008

Наверное, вы заметили фразу "Fast forward" в этом слиянии. Так как ветка, которую мы слили, указывала на коммит, являющийся прямым родителем коммита, на котором мы сейчас находимся, Git просто сдвинул её указатель вперёд. Иными словами, когда вы пытаетесь слить один коммит с другим таким, которого можно достигнуть, проследовав по истории первого коммита, Git поступает проще, перемещая указатель вперёд, так как нет расходящихся изменений, которые нужно было бы сливать воедино. Это называется "перемотка" (fast forward).

Теперь наш граф коммитов и веток выглядит так:

basic-branching-5

Посмотрим то же самое в Git

W0009

После слияния ветка master указывает туда же, куда и ветка hotfix. То есть на коммит С4 (7995847).

После того как очень важная проблема решена, вы готовы вернуться обратно к тому, над чем вы работали перед тем, как вас прервали. Однако, сначала удалите ветку hotfix, так как она больше не нужна — ветка master уже указывает на то же место. Помните, что в данном случае, удаляя ветку вы удаляете всего лишь указатель на нее. Вы можете удалить ветку с помощью опции -d к git branch:

$ git branch -d hotfix

W0010

Сравните два последних скриншота. Теперь на коммит 7995847 указывает только ветка master.

Теперь мы можем вернуться обратно к рабочей ветке для проблемы №53 и продолжить работать над ней. Переключаемся в ветку iss53, правим файлик index.html и коммитим изменения.

W0011

Теперь HEAD указывает на коммит (f219655) C5 ветки iss53. На графе это можно изобразить так:

basic-branching-6

Стоит напомнить, что работа, сделанная на ветке hotfix, не включена в файлы на ветке iss53. Если вам это необходимо, вы можете слить ветку master в ветку iss53 посредством команды git merge master. Или же вы можете подождать с интеграцией изменений до тех пор, пока не решите включить изменения на iss53 в продуктовую ветку master.

Основы слияния

Допустим, вы разобрались с проблемой №53 и готовы объединить эту ветку и свой master. Чтобы сделать это, мы сольём ветку iss53 в ветку master точно так же, как мы делали это ранее с веткой hotfix. Всё, что вам нужно сделать, — перейти на ту ветку, в которую вы хотите слить свои изменения, и выполнить команду git merge:

W0012

Но в этом нашем случае слияние отличается от того, что мы делали раньше. У нас есть конфликт слияния. Это вполне нормальное и допустимое явление.

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

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

basic-merging-1

Иногда процесс слияния не идёт гладко (как в нашем случае). Если вы изменили одну и ту же часть файла по-разному в двух ветках, которые собираетесь слить, Git не сможет сделать это чисто. Если ваше решение проблемы №53 изменяет ту же часть файла, что и hotfix, вы получите конфликт слияния, точно так же как на скрине сверху.

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

basic-merging-2

Но это только в том случае если нет конфликтов слияния. Но это не наш случай. У нас конфликт есть. Поэтому Git не создал новый коммит для слияния. Он приостановил этот процесс до тех пор, пока вы не разрешите конфликт. Если вы хотите посмотреть, какие файлы не прошли слияние (на любом этапе после возникновения конфликта), выполните команду git status:

W0013

Всё, что имеет отношение к конфликту слияния и что не было разрешено, отмечено как unmerged. Git добавляет стандартные маркеры к файлам, которые имеют конфликт, так что вы можете открыть их вручную и разрешить эти конфликты. То есть в нашем случае Git поменял содержимое файла index.html и теперь он содержит секцию изменений вызвавших конфликт слияни, которая выглядит так в моем случае так:

W0014

В верхней части блока (всё что выше =======) это версия из HEAD (вашей ветки master, так как именно на неё вы перешли перед выполнением команды merge), всё, что находится в нижней части — версия в iss53. Чтобы разрешить конфликт, вы должны либо выбрать одну из этих частей, либо как-то объединить содержимое по своему усмотрению. Например, вы можете разрешить этот конфликт заменой всего блока, показанного выше, следующим блоком:

W0015

Я полностью удалил строки <<<<<<<, ======= и >>>>>>>. После того как вы разобрались с каждой из таких секций в каждом из конфликтных файлов, выполните git add для каждого конфликтного файла. Индексирование будет означать для Git'а, что все конфликты в файле теперь разрешены.

Можете выполнить git status ещё раз, чтобы убедиться, что все конфликты были разрешены. И после этого дать команду git commit.

W0016

После этого слияния можно удалить уже не нужную ветку iss53

$ git branch -d iss53

W0017

Если вы хотите использовать графические инструменты для разрешения конфликтов, можете выполнить команду git mergetool, которая запустит соответствующий графический инструмент (если он у вас настроен) и покажет конфликтные ситуации:

W0018

W0019

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

Комментариев нет:

Отправить комментарий