Ветки и хирургия

Параметры слияния

Обычное слияние — это одна кнопка, и в большинстве случаев на этом история заканчивается. Эта страница — про остальные случаи: лок-файл, который конфликтует при каждом слиянии; файл, который кто-то переформатировал по отступам; вендоренный проект, у которого пути не совпадают. Ключи для всех трёх ситуаций есть в git уже много лет — просто они закопаны в man-странице, которую никто не открывает посреди конфликта.

Правый клик по ветке → Слияние с параметрами… — и в строках веток и удалённых веток в боковой панели, и на цветных бейджах ссылок в графе, у которых общий блок меню, — либо ⌘K → Слияние с параметрами.

Параметры слияния, с точной командой git, выписанной снизу

Команда печатается по мере того, как вы её собираете. Она нужна, чтобы сверить её с документацией — и чтобы в следующий раз запустить из терминала, без этого диалога.

Когда ханк конфликтует

Выбор Флаг Что означает
Остановиться и спросить меня — По умолчанию. Разрешаете вы
Оставить сторону этой ветки -X ours Спорные ханки разрешаются в пользу того, что уже выгружено
Взять входящую сторону -X theirs Спорные ханки разрешаются в пользу приходящей ветки

-X ours — это не -s ours. Здешний ключ решает судьбу только тех ханков, которые действительно конфликтуют; все прочие изменения из другой ветки сливаются как обычно. Стратегия под названием ours — которую Gitcito не предлагает — берёт ваше дерево целиком и выбрасывает другую сторону, порождая коммит слияния, который заявляет, что содержит работу, которой в нём нет. Это различие — самое непонятое место во всей теме слияний в git.

Он не может решить всё. Конфликт «изменено/удалено» — одна сторона отредактировала файл, другая его удалила — это не конфликт содержимого, и -X оставляет его вам. И правильно: не существует такой версии «предпочитать наше», которая ответила бы, должен ли удалённый файл вернуться.

Пробелы

Выбор Флаг
Игнорировать изменения в существующих пробелах -X ignore-space-change
Игнорировать пробелы полностью -X ignore-space-at-eol, -X ignore-all-space

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

Результат сохраняет пробелы другой стороны на затронутых ею строках, так что прогнать после этого форматтер — неплохая мысль.

Что записать

Выбор Флаг С чем вы останетесь
Fast-forward, когда возможно — Коммит слияния только там, где история разошлась
Всегда делать коммит слияния --no-ff Коммит слияния даже при fast-forward, так что ветка навсегда останется видимой в графе
Только fast-forward, иначе отказ --ff-only Ничем, если понадобилось бы настоящее слияние. Полезно как проверка
Squash --squash Изменениями в индексе, слияние не записано, коммит пишете вы
Слить, но не коммитить --no-commit Слиянием в индексе и в процессе — можно сначала осмотреть или поправить

Squash и --no-commit — не одно и то же. Squash вообще забывает, что слияние было: git не записывает второго родителя, и в следующий раз ветка будет выглядеть несмёрженной. --no-commit — это слияние в процессе, которое просто ждёт вас: MERGE_HEAD установлен, и коммит завершает его обычным образом.

--ff-only не падает молча. Если понадобился бы коммит слияния, git отказывается и ничего не сдвигается — именно это и делает его хорошей проверкой здравого смысла перед скриптованным слиянием.

Стратегия

Стратегия Для чего
По умолчанию (ort) Для всего. Современное трёхстороннее слияние git
subtree Две стороны живут по разным путям — проект, вендоренный в подкаталог этого
resolve Старое трёхстороннее слияние. Иногда справляется там, где ort сдаётся на перекрёстной истории

-s subtree — тот случай, который стоит запомнить. Слияние обновлений из проекта, лежащего в vendor/parser/, иначе прочиталось бы как «все файлы удалены, все файлы добавлены»; стратегия subtree сначала вычисляет сдвиг путей. Весь процесс целиком — в сабтри.

Почему тут конфликт

Внутри резолвера конфликтов есть кнопка Почему тут конфликт. Она запускает git log --merge для файла перед вами и перечисляет по каждой стороне коммиты, которые его трогали с момента расхождения веток.

Коммиты с каждой стороны, которые трогали конфликтный файл

Маркеры конфликта говорят, что столкнулось. Это говорит, кто это изменил, когда и зачем — а именно этот вопрос обычно и решает исход, и именно он заставляет пойти и спросить человека, прежде чем выбирать сторону.

Если тут пусто — значит, ни одна сторона не коммитила изменение именно этого файла: столкновение пришло из переименования или переноса каталога уровнем выше.

Ограничения, о которых стоит знать

  • Параметры действуют на одно слияние. Они не запоминаются и не влияют ни на обычный пункт Слить в текущую, ни на меню перетаскивания.
  • Отмена по-прежнему работает: слияние с параметрами записывает ту же запись отмены, которая сбрасывает на ORIG_HEAD.
  • Осьминожьи слияния (больше двух веток разом) здесь не предлагаются.
  • Пункты «Слить X в Y» в меню коммита остаются обычными слияниями. Используйте сам бейдж ссылки, когда нужны параметры.
  • -X решает молча. Ничто не помечает, какие ханки разрешились автоматически, так что на важном слиянии прочитайте дифф после, а не доверяйте отсутствию конфликтов.

См. также: Слияние и ребейз · Конфликты · Сабтри · Радар конфликтов

Улучшить этот перевод на GitHub