Команда Git Merge

В какой-то момент история репозитория ветвится. Например, в проекте требуется пофиксить баг —  обычно это выполняют в отдельной ветке, а потом сливают в основную ветку master.  Команда git merge как раз и служит для слияния веток.

Пример использования

Допустим, есть основная ветка master. И есть ветка hotfix, в которой мы фиксим баг. Мы его пофиксили и хотим слить изменения в ветку master. Для этого надо перейти на ветку master — ту ветку, в которую сливают изменения:

git checkout master

И залить изменения из ветки hotfix в текущую ветку master:

git merge hotfix

Это типичный пример использования команды.

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

Конфликт слияния

Допустим, файл index.html отредактировал кто-то еще в том же месте. Тогда при попытке выполнить команду merge мы получим сообщение:

$ git merge hotfix
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

Что же делать? Если открыть index.html, мы увидим, что Git его пометил примерно так:

<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
  please contact us at support@github.com
</div>
>>>>>>> hotfix:index.html

Надо удалить разметку в файле и оставить только один вариант спорной строки. После этого нужно добавить конфликтный файл в индекс:

git add index.html

И сделать снимок:

git commit

На этом все, конфликт будет разрешен.

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

git merge abort

Продвинутые сценарии git merge

Параметр —no-commit

Бывают ситуации, когда история снимков (ряд команд commit) в ветке была сделана не очень осмысленно, то есть вы делали снимки состояний, которые не стоит сохранять в истории на всеобщем обозрении. Здесь то и пригодится параметр —no-commit. Он используется нечасто — только в том случае, если хочется уничтожить историю снимков ветки, так чтоб никто их никто никогда не увидел и к ним нельзя было вернуться.

Но сначала вернемся к вышеприведенным командам и рассмотрим, как работает merge без параметра:

git checkout master
git merge hotfix

Первая команда (checkout) переведет нас на ветку master. Это значит, что наша папка на диске физически заполнятся версиями файлов из ветки master

А команда merge:

  • перенесет в master все изменения ветки hotfix. То есть содержимое текущей папки изменится с учетом залитых изменений
  • и сделает commit (если нет нет конфликтов). То есть сделает снимок полученного состояния и занесет его  в историю. При этом история коммитов ветки hotfix будет сохранена и ее можно будет посмотреть.

Теперь рассмотрим, как работает команда merge с параметром.

Первый шаг такой же — переходим на ветку master, в которую надо залить изменения:

git checkout master

Но в этот раз допустим, что снимки в ветке hotfix  были ненужными и мы хотим, чтоб их не было в истории. В этом случае надо добавить параметр —no-commit:

git merge --no-commit hotfix

Эта команда обновит текущие папки —  перенесет в них все изменения ветки hotfix, но финального снимка сделано не будет. Мы сможем еще кое-что поменять в файлах и потом выполнить команду commit самостоятельно. При этом в историю попадет только финальный снимок. Будет видно, что ветка hotfix слита с веткой master, и все. А снимки ветки hotfix видны не будут.

Параметр —squash

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

git checkout master

После выполнения команды:

git merge --squash hotfix

изменения  появятся в текущих папках ветки master, аналогично тому, как это было после выполнения предыдущей команды с аргументом —no-commit. При этом финального снимка (commit) так же не произойдет.

Но затем после самостоятельного выполнения команды commit мы увидим в истории этот снимок как самый обычный снимок в ветке master. То, что ветка hotfix сливалась в ветку master, отображено в истории не будет.

Параметр —no-ff

Параметр —no-ff запрещает перемотку.

ff означает fast forward, то есть перемотка.

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

На картинке мы ответвились от С и сделали снимки X и Y. После снимка C в ветке master никто ничего не делал, и при слиянии указатель просто переместится на снимок Y:

master: A - B - C
                 \
hotfix:           X - Y

Еще раз обратите внимание, что в ветке master после ответвления от С снимков не появилось, именно поэтому перемотка и возможна.

С помощью аргумента —no-ff можно запретить перемотку, а именно: сделать при слиянии новый отдельный снимок, хоть в нем и нет технической необходимости. Сделаем это:

git merge --no-ff hotfix

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

master: A - B - C   -  (M)
                 \     /
hotfix:           X - Y

Будет создан дополнительный снимок M и указатель переместится на него (хотя мог бы просто переместиться на Y, если б выполняли слияние без параметра —no-ff).

Когда именно стоит использовать параметр —no-ff сказать сложно — отчасти это вопрос вкуса. Некоторые любят делать отдельный снимок при любом важном слиянии.

Далее рассмотрим git pull.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *