[ @git @smartgit @merge ]
Merge — слияние веток (branch).
Здесь и далее подразумеваем, что есть две ветки:
- master
- wip (Work In Progress)
Важные термины
HEAD
'master'
ветка (branch)
Когда происходит новый коммит, git сперва читает файл HEAD и «понимает», на какой ветке и коммите находится проект as is. Обычно он указывает на ветку 'master'.
После этого git записывает в новый коммит информацию о его родителе — на основе информации из файла HEAD.
После того, как новый коммит создан, ссылка 'master' переключается на него.
Когда добавляется новая ветка (git checkout), ссылка HEAD меняется и начинает указывать на новый коммит, который «определяет» новую ветку и направление разработки проекта. С каждым новым коммитом содержимое ссылки HEAD обновляется.
А дальше можно делать разные штуки, вроде насильно приказать HEAD указывать на какой-то из коммитов в проекте (fast-forward)…
Общий алгоритм merge
- checkout 'master' — git checkout master
- выбрать ветку, ИЗ которой надо завезти изменения в 'master' и приказать слияние — git merge wip
Алгоритмы merge
Их несколько.
fast-forward
Слияние веток «перемоткой». Любопытно, что этот способ «скрывается» за обычной командой merge…
- git checkout master
- git merge wip
Это простейший алгоритм, но есть существенные нюансы. Мы подразумеваем, что вся информация из ветки 'wip' копируется в векту 'master', а на деле происходит другое — git лишь передвигает указатель HEAD с коммита из ветки 'master' на смердженный коммит из ветки 'wip'.
В результате ничего никуда не добавляется, а наоборот, ветка разработки (коммиты) продолжается по изгибу ветки от 'master' по пути, который когда-то назывался 'wip' и считался отдельным направлением. Может появиться (ошибочное) впечатление о том, что «Ветка выпрямляется» и разработка идёт в 'master'. На самом деле ветка wip объявляется веткой 'master'.
Это хороший алгоритм, когда ты работаешь в проекте один, и просто иногда отвлекаешься на дополнительные/отдельные темы, а потом то и дело объявляешь «а верну я разработку в основную линию». Некому возразить, всё под личным контролем. И не придётся бороться с конфликтами, бо их просто нет. Даже нечего коммить, по-сути, просто происходит условное переназначение текущего коммита основным в разработке.
merge-commit
Он же 'merge'.
Это алгоритм истинного слияния веток в проекте. Обычно используется в групповой разработке. Необязательное предусловие: working tree have 'clean' status.
Наглядное представление его работы — https://www.youtube.com/watch?v=PXK1hIifpWU
'merge' хорош, когда точно известно, что конфликтов в файлах из разных веток нет.
Если конфликты есть, начинает работать следующий алгоритм:
merge to working tree
Это отдельная ветка алгоритма 'merge'.
Если мы верим, что при слиянии коммитов не будет конфликтов — делаем 'merge' и всё ок. Если конфликты в файлах таки есть — их надо сперва разрулить, поэтому git прекращает алгоритм 'merge' и начинает действовать по алгоритму 'merge to working tree'.
Технически при этой операции создаётся новый коммит (он же ’merge-commit’), и при его подготовке git делает анализ существующих файлов в поисках вероятных конфликтов. Если они есть — этим надо заняться, последовательно разруливая каждый «затык».
В итоге происходит обновление файлов на hdd — где-то автоматически, где-то вручную. Изменения заливаются в репозиторий — 'merge'.
squash-merge
To squash переводится как «сдавить».
Это отдельная ветка алгоритма 'merge', можно назвать её «подготовительная операция перед слиянием».
Предположим, что работа по ветке 'wip' продвинулась очень интенсивно, в ней 1001 коммит. Её отличия от ветки 'master' существенны и местами ужасающи. Мерджить всё пошагово можно и даже нужно, но это может занять несколько дней/недель на согласования, споры и поиск решений.
В том числе и для избегания таких ситуаций надо делать атомарные коммиты, мелкими изменениями. Исправление ошибки — коммит. Переименовали какую-то функцию — сделали изменения во всех файлах, в которых она упоминается — коммит. Реализовали какую-то небольшую возможность — коммит. Реализовали большую возможность — сделать тэг, по которому можно будет идентифицировать все «тематические» коммиты.
Можно «сдавить» все изменения по ветке 'wip' в один коммит.
git merge --squash wip
Это в принципе упростит внесение изменений в 'master', но после этой операции исчезнет вся история изменений по интегрированным изменениям из ветки 'wip' (история изменений, а не сама ветка 'wip').