[ Пред. ] [ Содержание ] [ След. ]

merge

[ @git @smartgit @merge ]

 


Merge — слияние веток (branch).


Здесь и далее подразумеваем, что есть две ветки:


Важные термины


HEAD

это отдельный файл в скрытом каталоге '.git', в котором записано «где мы находимся сейчас».


'master'

это и главная ветка проекта, и ссылка на основной коммит в проекте. С него всё начинается, от него ответвляется и к нему, как правило, возвращается.


ветка (branch)

это ссылка на какой-то конкретный коммит.


Когда происходит новый коммит, git сперва читает файл HEAD и «понимает», на какой ветке и коммите находится проект as is. Обычно он указывает на ветку 'master'.


После этого git записывает в новый коммит информацию о его родителе — на основе информации из файла HEAD.


После того, как новый коммит создан, ссылка 'master' переключается на него.


Когда добавляется новая ветка (git checkout), ссылка HEAD меняется и начинает указывать на новый коммит, который «определяет» новую ветку и направление разработки проекта. С каждым новым коммитом содержимое ссылки HEAD обновляется.


А дальше можно делать разные штуки, вроде насильно приказать HEAD указывать на какой-то из коммитов в проекте (fast-forward)…


Подробности https://git-scm.com/book/ru/v2/%D0%92%D0%B5%D1%82%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B2-Git-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B-%D0%B2%D0%B5%D1%82%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B8-%D1%81%D0%BB%D0%B8%D1%8F%D0%BD%D0%B8%D1%8F


Общий алгоритм merge


  1. checkout 'master' — git checkout master
  2. выбрать ветку, ИЗ которой надо завезти изменения в 'master' и приказать слияние — git merge wip

Алгоритмы merge


Их несколько.


fast-forward


Слияние веток «перемоткой». Любопытно, что этот способ «скрывается» за обычной командой merge…


  1. git checkout master
  2. 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').