Каковы три файла в трехстороннем слиянии для интерактивного перебазирования с использованием git и meld?

Допустим, я делаю интерактивный ребаз с git rebase -i, Если возникает какой-либо конфликт, мне может быть предложен конфликт слияния, и мне будет предложено выполнить слияние в 3 направлениях. С помощью meldМне представлены три окна: LOCAL (оставил), ??? (в середине) и REMOTE (право). Здесь ??? Я имею в виду, просто meld не предоставляет специального имени для добавления в файл.

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

Что каждый из этих файлов в трехстороннем слиянии представляет во время интерактивной перебазировки? И когда я редактирую эти файлы, какова моя цель?

Обновление: на основе комментариев и экспериментов, которые я вижу:

  • Оставил (LOCAL): Ваша локальная версия файла на данный момент в последовательности воспроизведения коммита.
  • Правильно (REMOTE): Состояние файла сразу после первоначального применения текущего коммита.
  • Посередине: родительский элемент права в исходной последовательности фиксации.

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

Обратите внимание, что эта конфигурация, по-видимому, специфична для слияния, по крайней мере, до некоторой степени. Трехстороннее поведение Git для слияния может отличаться для других редакторов.

2 ответа

Решение

Средняя версия является базой слияния, так же как и с git merge,

(Имя "other" может быть более подходящим, чем "remote", поскольку не требуется, чтобы другая сторона слияния была удаленной, и поскольку Mercurial последовательно использует для него имя "other", а не то, что Git должен соответствовать Mercurial, но некоторая согласованность может быть хорошей. Обратите внимание, что Git также использует имена "наши" и "их", поэтому мы никогда не получим 100% -ную согласованность с Git.:-))

Но подождите, как там база слияния?

Всегда есть база слияния.

Обычно нам даже не нужно его искать, так как каждый патч применяется чисто, когда рассматривается как патч (без попытки трехстороннего слияния). Но иногда патч не будет применяться корректно, и мы вынуждены отступить от тройного слияния.

(Кстати, вы можете отключить этот запасной вариант. --3way, --no-3way, а также am.threeWay в документации git-am, хотя ссылка на эту страницу уже устарела, поскольку эти элементы управления недавно изменились.)

$ git rebase -i
pick aaaaaaa first commit
pick bbbbbbb second commit
pick ccccccc third commit

Давайте также нарисуем график фиксации, чтобы мы могли видеть, от чего мы делаем ребазинг:

              A - B - C   <-- branch
            /
... - o - *
            \
              G - H       <-- origin/branch

Мы будем собирать вишни A, B, а также C (A знак равно aaaaaaa и т. д.), чтобы мы получили этот результат, в конце концов:

              A - B - C   [abandoned]
            /
... - o - *           A' - B' - C'   <-- branch
            \       /
              G - H       <-- origin/branch

Давайте внимательно посмотрим на первый вишнёвый A,

Это сравнивает A против своего родителя, который является совершить * и пытается применить полученный diff для фиксации H,

совершить H однако, несколько отошел от коммита *, На самом деле, мы можем найти базу слияния между A а также H и это... совершить *, На самом деле это довольно приличная база слияния, хотя лучше, если Git сможет просто применить исправление как есть, без необходимости возвращаться к трехстороннему коду слияния.

Итак, совершите * является базой слияния при сборке вишни A на H, Когда слияние завершено, мы получаем новый коммит A', (Его новый идентификатор SHA-1 может быть aaaaaa1 например. Возможно нет; давай просто назовем это A'.)

Теперь мы будем черри B, Это различия B по сравнению с его родителем, который A и пытается применить diff к A',

совершить A' однако, несколько отошел от коммита B, На самом деле, мы можем найти базу слияния между B а также A' и это... совершить * снова. К сожалению, это жалкая база слияния. К счастью, Git возвращается к нему, только если патч не может быть применен как есть, и обычно это возможно. Но если это не может, Git будет отличаться * против B а также * против A' и попытаться объединить эти два различия. Обратите внимание, что * против B включает в себя все изменения, которые мы сделали в A , но * против A' также включает в себя все те же A изменения, поэтому, если нам повезет, Git замечает уже внесенные изменения и не дублирует их. редактировать чит коды (Этот код недавно изменился в версии 2.6, хотя общая стратегия остается прежней.)

Рассмотрим фактический результат git diff когда используется, чтобы показать только изменение от коммита A совершать B, Это включает в себя index линия:

diff --git a/foo b/foo
index f0b98f8..0ea3286 100644

Значение слева - это (сокращенно) хеш для версии файла foo в коммите A, Значение справа - это хеш версии файла в коммите B,

Git просто подделывает базу слияния с левой стороны хэша. Другими словами, версия файла в коммите A становится поддельной базой слияния. (Git проходит --build-fake-ancestor в git apply, Это требует, чтобы определенные объекты файловых объектов были в хранилище, но они есть, поскольку они находятся в коммите. A, Для исправлений, отправляемых по электронной почте, Git использует этот же код, но BLOB-объект может присутствовать или отсутствовать.)

Обратите внимание, что Git на самом деле делает это, когда вишня выбирает коммит A также, но на этот раз базовый файл слияния является версией коммита *, которая действительно является базой слияния.

Наконец, мы вишня C, Это различия B против C как мы понесли A против B последний раз. Если мы сможем применить патч как есть, хорошо; если нет, мы вернемся к использованию commit * как база слияния снова. Это снова довольно жалкая база слияния. так же, как и раньше, делая вид, что версия в B была общая база.

Кстати, это также объясняет, почему вы склонны снова и снова видеть одни и те же конфликты слияний для этих ребаз: мы каждый раз используем одну и ту же базу слияния. (Включение git rerere может помочь.)

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

Другие вопросы по тегам