среда, 23 мая 2012 г.

Ubuntu 12.04: git, merge, mergetool, meld

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

Отмечу, что я уже давно традиционно разрешаю конфликты при слиянии и рибэйзе с помощью связки git mergetool и meld.

Разрешение конфликтов в Git

Краткий экскурс в процесс. Когда git в процессе слияния обнаруживает конфликты в файлах он прямо в этих же файлах устанавливает маркеры конфликтов в виде секций с разделителями "<<<<<<<" и ">>>>>>>". Нужно вручную убрать эти разделители и выбрать нужный код, после чего добавить файл в индекс git и сделать коммит. Конфликт считается разрешенным

Чтобы автоматизировать и облегчить процесс при большом количестве файлов, а также визуализировать различие в изменениях существует утилита git mergetool. Для каждого из конфликтных файлов она создает еще четыре вспомогательных служебных файла. На примере файла test.txt такими файлами будут:

  • test.txt.BACKUP.4071.txt - копия текущего файла test.txt (с маркерами конфликтов внутри)
  • test.txt.BASE.4071.txt - файл в ревизии, которая являющется базовой для обоих конфликтных версий
  • test.txt.LOCAL.4071.txt - "наша" версия файла
  • test.txt.REMOTE.4071.txt - "чужая" версия файла

После создания вспомогательных файлов mergetool вызывает любую указанную в параметрах команду и передает созданные файлы в качестве параметров. Это может быть консольная утилита автоматического слияния, полноценная GUI-программа для ручного слияния или любая другая команда. После того как устранение конфликтов в файле завершено mergetool сам добавляет файл в индекс и удаляет все вспомогательные файлы.

Я, как уже отметил ранее, использую в качестве такой команды утилиту meld.

Кто виноват?

Так вот - есть проблема. После обновления до 12.04 обновилось множество пакетов, в том числе и git и meld. Если раньше mergetool вызывал meld в режиме 2-way-merge, то сейчас meld вызываетcя в режиме 3-way-merge.

Ubuntu 11.10, Git 1:1.7.5.4-1, Meld 1.5.2-1ubuntu2

До обновления meld вызывался в режиме 2-way-merge. Это очень простой режим в котором все конфликты помечаются самим движком git, а meld используется только как удобный визуальный редактор. В трех панелях meld показываются три файла: "наш", конфликтный файл с маркерами конфликтов, "чужой" файл. Как я уже отметил, режим очень прост и в своей сути мало отличается от прямого редактирования конфликтного файла в любом текстовом редакторе.

Ubuntu 12.04, Git 1:1.7.9.5-1, Meld 1.5.3-1ubuntu1

После обновления Ubuntu новая версия meld вызывается (внезапно!) в режиме 3-way-merge. В этом режиме mergetool передает в meld три файла: "наш" файл, базовую для для обоих конфликтных версий ревизию файла, "чужой" файл. Сам конфликтный файл, в котором git сделал соответствующие метки конфликтов в утилиту не передается. Таким образом все сделанные в git маркеры конфликтов игнорируются, а весь процесс определения и разрешения конфликтов отдается на откуп самому meld.

Вот такой вот сюрприз. Есть люди, которым такое новое поведение видимо нравится. И действительно - в простейших случаях конфликты разрешать очень удобно: видно начальную версию файла до изменений, а также видно два последующих изменения, которые и привели к конфликту. В предыдущем же варианте вызова были видны только два изменения, оформленные в виде конфликтной секции - а вот что было до этих изменений (базовую версию) meld не показывал.

К сожалению, на практике новый режим работы meld к использованию не пригоден. Скорее всего дело в том, что git априори обладает более полной информацией о всех сделанных изменениях и, используя ее, git помечает конфликты точно и аккуратно, тогда же как meld вынужден работать только с тремя слепками файлов, а вся история ему недоступна.

Что делать?

К счастью, можно настроить mergetool как угодно и после этого не зависеть от изменений в запрограммированном по-умолчанию поведении git и mergetool. Сначала программируем оба варианта поведения и даем им уникальные имена.

git config --global mergetool.meld2way.cmd 'meld "$LOCAL" "$MERGED" "$REMOTE"'
git config --global mergetool.meld3way.cmd 'meld --output "$MERGED" "$LOCAL" "$BASE" "$REMOTE"'

После этого можно вызывать команды "git mergetool -t meld2way" и "git mergetool -t meld3way". Также можно указать установку старого поведения в качестве варианта по умолчанию.

git config --global merge.tool meld2way

После этого git mergetool/meld ведут себя также покладисто как и раньше.

2 комментария: