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