(аналогично этому вопросу , но с некоторым контекстом и демонстрацией того, почему rerere
не является ответом.)
Для данной истории:
/...o origin/master
o...o...o...o...o...o...o master
\...o........../ topic
У меня есть ветка темы, которую я слил в master и сделал еще один коммит.Между тем, кто-то из апстрима сделал еще один коммит на origin / master, поэтому я больше не могу выдвинуть своего мастера как есть.
Я хочу переместить мой master на origin / master без изменения SHA коммитов по теме и безпотеря разрешения конфликта уже выполнена на мастере.(Это самый распространенный случай, когда я хочу сохранить коммиты слияния, поэтому я удивлен, что это, по-видимому, так сложно.)
С включенным rerere
, git rebase -p
почти работает - для любых конфликтов в исходном слиянии, он запоминает, что я сделал, чтобы исправить их, и повторно применяет это (хотя он оставляет файл помеченным как конфликтующий, поэтому я должен помнить, чтобы пометить каждый как уже разрешенный, без перезапуска разрешения конфликта нафайл, который слегка раздражает из внешнего интерфейса TortoiseGit).Но если были какие-либо другие изменения в файлах, которые также были исправлены в коммите слияния (например, строки, которые были просто добавлены в слияние без конфликтов, но все еще нуждались в исправлении из-за изменений в другом месте), они теряются.
Вот в чем дело, хотя.В моем (возможно, ошибочном) понимании коммитов слияния, они состоят из двух (или более) родителей и уникального набора изменений (используется для хранения разрешений конфликтов, плюс любые другие изменения, сделанные до фиксации слияния или внесенные позднее в коммит слияния).Похоже, что rebase -p
воссоздает коммит слияния, но полностью отбрасывает этот дополнительный набор изменений.
Почему он не повторно применяет набор изменений из исходного коммита слияния?Это сделало бы rerere излишним и не потеряло бы эти дополнительные изменения.Он мог оставить затронутые файлы помеченными как конфликтные, если требовал подтверждения от человека, но во многих случаях этого автоматического разрешения было бы вполне достаточно.
Другими словами, пометить некоторые коммиты выше:
/...N origin/master
o...o...o...o...B...M...A master
\...T........../ topic
T - the commit on topic
B - the merge-base of origin/master and master
N - the new commit on origin/master
M - the merge between B and T
A - the extra post-merge commit
M имеет родителей B и T и уникальный набор изменений Mc.При создании M 'git выполняет новое слияние между родителями N и T и отбрасывает Mc.Почему git не может просто повторно применить Mc вместо того, чтобы выбросить его?
В конце я хочу, чтобы история выглядела так:
o...o...o...o...B...N...M'...A' master
\...T............../
Где M 'и A' меняют SHA1 сребаз, но M 'включает в себя ревизию Mc, а T не менял SHA1 или родителя.И теперь я могу быстро переместиться из оригинала / мастера к A '.
Я также заметил, что есть новая опция --rebase-merges
, которая сначала звучит хорошо, а потом приводит к правильному графику -но точно так же, как --preserve-merges
по-прежнему останавливается с конфликтами на M 'и теряет любые уникальные изменения в Mc, не сохраняемые иначе как rerere.
Альтернативная формулировка вопроса, которая может быть более полезной:
С учетом начального состояния, указанного выше, и только что начавшегося интерактивного перебазирования, которое теперь находится в состояниях HEAD1 или HEAD2:
/...........(T)
/ \
/ /...M' HEAD2
/ /... HEAD1
/ /...N origin/master
o...o...o...o...B...M...A master
\...T........../ topic
(HEAD1 извлек N, но больше ничего не сделал; HEAD2 создалновое слияние с N в качестве родителя 1 и T в качестве родителя 2, но еще не зафиксировано из-за неразрешенных конфликтов)
Существует ли некоторая последовательность команд rebase и / или git, которая будет:
- Рассчитать разность Mc между M и B (выбирая B, потому что другой родительский T не меняется)
- Примените это к конфликтующему дереву M '(которое должно завершитьсяy разрешать все конфликты, если только N не вводит новые) ИЛИ Просто примените это поверх N (без предварительного слияния) - они должны быть эквивалентны;вторая может быть проще
- Пауза для человека, чтобы разрешить любые оставшиеся конфликты, введенные N, если таковые имеются.
- Передать M 'как слияние между N и T
- Продолжитькак обычно (в этом случае перестановка A на A 'поверх M')
И почему git не делает это по умолчанию?