Отказ от обратного слияния на Mercurial
Как вы можете обратить вспять эффект слияния на поляризованные ветви, не умирая от агонии?
Эта проблема мучила меня несколько месяцев, и я наконец сдался.
У вас есть 1 репозиторий с 2 именованными ветвями. А и Б.
Изменения, которые происходят с А, неизбежно произойдут на Б.
Изменения, которые происходят непосредственно на B, НЕ ДОЛЖНЫ происходить на A.
В такой конфигурации слияние "B" с "A" создает ужасную проблему в хранилище, так как все изменения в B появляются в A, как если бы они были сделаны в A.
Единственный "нормальный" способ выхода из этой ситуации - это "откат" слияния, то есть:
hg up -r A
hg backout -r BadMergeRev --parent BadMergerevBeforeOnA
Который выглядит все прекрасно и модно, пока вы не решите объединиться позже в правильном направлении, и вы в конечном итоге получите все виды неприятных вещей, происходящих и код, который был стерт / закомментирован специально для ветви B, внезапно становится не стертым или не комментированным.
До сих пор не было работоспособного решения этой проблемы, кроме как "пусть он сделает свое дело, а потом решит все проблемы вручную", и, честно говоря, это немного глупо.
Вот изображение, проясняющее проблему:
[Исходное изображение потеряно]
Файлы C & E (или изменения C & E) должны появляться только в ветви b, а не в ветви a. Ревизия A9 здесь (ветка a, revno 9) является началом проблемы.
Версии A10 и A11 являются фазами "Объединение возврата" и "Объединение возврата".
И ревизия B12 является ртутной, ошибочно многократно сбрасывая изменения, которые должны были быть исключены.
Эта дилемма вызвала большое разочарование и синий дым, и я хотел бы положить этому конец.
Заметка
Может быть очевидным ответом является попытка запретить обратное слияние, либо с помощью хуков, либо с помощью политик. Я обнаружил, что возможность исправить это довольно высока, и вероятность того, что это произойдет, настолько высока, что даже с помощью контрмер вы все равно должны Предположим, что это неизбежно произойдет, и вы сможете решить ее, когда это произойдет.
Разработать
В модели я использовал отдельные файлы. Это заставляет проблему звучать просто. Они просто представляют произвольные изменения, которые могут быть отдельной строкой.
Кроме того, чтобы добавить оскорбление к травме, произошли существенные изменения в ветви A, что оставляет постоянную проблему: "сделать изменения в ветви A конфликтующими с изменениями в ветви B, которые только что появились (и получили отказ), что выглядит как изменение? на ветке А вместо "
Уловки переписывания истории:
Проблема со всеми этими ретроактивными решениями заключается в следующем:
- У нас 9000 коммитов.
- Свежее клонирование, таким образом, занимает полчаса
- Если где-то существует хотя бы один плохой клон репозитория, существует вероятность того, что он снова соприкоснется с исходным репозиторием и снова ударит его.
- Все уже клонировали этот репозиторий, и теперь прошло несколько дней с постоянными коммитами.
- Один из таких клонов - это живой сайт, поэтому "стереть его и начать с нуля" = "большой ноно"
(Признаюсь, многие из вышеперечисленных немного глупы, но они находятся вне моего контроля).
Единственные жизнеспособные решения - это те, которые предполагают, что люди могут и будут делать все неправильно, и что есть способ "исправить" эту ошибку.
5 ответов
Я думаю, что нашел решение, которое навсегда исправляет неудачное слияние, и которое не требует, чтобы вы вручную проверяли любые различия. Хитрость заключается в том, чтобы вернуться в историю и генерировать коммиты параллельно неудачному слиянию.
Таким образом, у нас есть репозиторий с отдельными ветками для поддерживаемой версии одного продукта. Как и в ситуации, изложенной в вопросе, все изменения, внесенные в ветку более ранней версии (т. Е. Исправления ошибок в этой версии), должны в конечном итоге быть объединены с ветвями более поздних версий.
В частности, если что-то зарегистрировано в BRANCH_V8, оно должно быть объединено с BRANCH_V9.
Теперь один из разработчиков совершает следующую ошибку: он объединяет все изменения из BRANCH_V9 в BRANCH_V8 (т.е. объединение в неправильном направлении). Кроме того, после этого неудачного слияния он выполняет некоторые дополнительные коммиты, прежде чем заметит свою ошибку.
Таким образом, ситуация такая, как показано в графическом журнале ниже.
o BRANCH_V8 - 13 - важный коммит сразу после неудачного слияния | o BRANCH_V8 - 12 - неправильное объединение с BRANCH_V9 | \ | o BRANCH_V8 - 11 - добавление комментария к BRANCH_V8 (т.е. последнее известное исправное состояние) | | о | BRANCH_V9 - 10 - последний коммит на BRANCH_V9 | |
Мы можем исправить эту ошибку следующим образом:
- обновите локальный каталог до последнего хорошего состояния BRANCH_V8:
hg update 11
- Создайте нового потомка этого последнего хорошего состояния:
- изменить какой-то файл
$EDITOR some/file.txt
(это необходимо, потому что Mercurial не разрешает пустые коммиты) - совершить эти изменения
hg commit -m "generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9"
Ситуация сейчас выглядит следующим образом:o BRANCH_V8 - 14 - создание коммита на BRANCH_V8 для исправления неправильного слияния из BRANCH_V9 | | o BRANCH_V8 - 13 - важный коммит сразу после неудачного слияния | | | o BRANCH_V8 - 12 - неправильное объединение с BRANCH_V9 | / | о | BRANCH_V8 - 11 - добавление комментария к BRANCH_V8 | | | o BRANCH_V9 - 10 - последний коммит на BRANCH_V9
- изменить какой-то файл
Объедините вновь созданную головку с ревизией, в которой произошло неудачное объединение, и выбросьте все изменения перед фиксацией. Не просто объединяйте две головы, потому что тогда вы потеряете важный коммит, который произошел и после объединения!
- объединить:
hg merge 12
(игнорировать любые конфликты) - выбросить все изменения:
hg revert -a --no-backup -r 14
- зафиксировать изменения:
hg commit -m "throwing away wrong merge from BRANCH_V9"
Ситуация теперь выглядит так:o BRANCH_V8 - 15 - выбрасывать неверное объединение из BRANCH_V9 |\ | o BRANCH_V8 - 14 - создание коммита на BRANCH_V8 для исправления неправильного слияния из BRANCH_V9 | | +---o BRANCH_V8 - 13 - важный коммит сразу после неудачного слияния | | о | BRANCH_V8 - 12 - неправильное слияние с BRANCH_V9 |\| | o BRANCH_V8 - 11 - добавление комментария к BRANCH_V8 | | о | BRANCH_V9 - 10 - последний коммит на BRANCH_V9 | |
То есть. BRANCH_V8 содержит две главы: одна содержит исправление неудачного слияния, а другая содержит оставшийся важный коммит BRANCH_V8, произошедший сразу после объединения.
- объединить:
- Объединить две головы на BRANCH_V8:
- объединить:
hg merge
- совершить:
hg commit -m "merged two heads used to revert from bad merge"
- объединить:
Ситуация в конце BRANCH_V8 теперь исправлена и выглядит следующим образом:
o BRANCH_V8 - 16 - объединение двух головок, используемых для возврата из неправильного объединения |\ | o BRANCH_V8 - 15 - выбрасывать неверное слияние из BRANCH_V9 | |\ | | o BRANCH_V8 - 14 - создание коммита на BRANCH_V8 для исправления неправильного слияния из BRANCH_V9 | | | о | | BRANCH_V8 - 13 - важный коммит сразу после неудачного слияния |/ / o | BRANCH_V8 - 12 - неправильное слияние с BRANCH_V9 | \ | | o BRANCH_V8 - 11 - добавление комментария к BRANCH_V8 | | о | BRANCH_V9 - 10 - последний коммит на BRANCH_V9 | |
Теперь ситуация на BRANCH_V8 верна. Единственная оставшаяся проблема заключается в том, что следующее слияние от BRANCH_V8 до BRANCH_V9 будет некорректным, так как оно также будет слито в 'fix' для плохого слияния, которое мы не хотим на BRANCH_V9. Хитрость заключается в том, чтобы объединить BRANCH_V8 с BRANCH_V9 в отдельных изменениях:
- Первое слияние, от BRANCH_V8 до BRANCH_V9, правильные изменения на BRANCH_V8 от до неудачного слияния.
- Второе слияние в ошибке слияния и ее исправлении, и, не проверяя ничего, отбрасывает все изменения
- В-третьих, объедините оставшиеся изменения с BRANCH_V8.
В деталях:
- Переключите ваш рабочий каталог на BRANCH_V9:
hg update BRANCH_V9
- Слияние в последнем хорошем состоянии BRANCH_V8 (т. Е. Сгенерированный вами коммит, чтобы исправить неудачное слияние). Это слияние является слиянием, как любое обычное слияние, т.е. конфликты должны разрешаться как обычно, и ничего не нужно выбрасывать.
- объединить:
hg merge 14
- совершить:
hg commit -m "Merging in last good state of BRANCH_V8"
Ситуация сейчас:@ BRANCH_V9 - 17 - Слияние в последнем хорошем состоянии BRANCH_V8 |\ | | o BRANCH_V8 - 16 - слились две головы, используемые для возврата от неудачного слияния | | |\ | +---o BRANCH_V8 - 15 - выбрасывать неверное объединение из BRANCH_V9 | | | | | о | | BRANCH_V8 - 14 - создание коммита на BRANCH_V8 для исправления неправильного слияния из BRANCH_V9 | | | | | | о | BRANCH_V8 - 13 - важный коммит сразу после неудачного слияния | | |/ +---o BRANCH_V8 - 12 - неправильное объединение с BRANCH_V9 | |/ | o BRANCH_V8 - 11 - добавление комментария к BRANCH_V8 | | о | BRANCH_V9 - 10 - последний коммит на BRANCH_V9 | |
- объединить:
- Объединить плохое объединение на BRANCH_V8 + его исправление и выбросить все изменения:
- объединить:
hg merge 15
- отменить все изменения:
hg revert -a --no-backup -r 17
- совершить слияние:
hg commit -m "Merging in bad merge from BRANCH_V8 and its fix and throwing it all away"
Текущая ситуация:@ BRANCH_V9 - 18 - Слияние с ошибкой из BRANCH_V8 и ее исправление и выбрасывание всего этого |\ | o BRANCH_V9 - 17 - Слияние в последнем хорошем состоянии BRANCH_V8 | |\ +-----o BRANCH_V8 - 16 - слились две головы, используемые для возврата от плохого слияния | | | | о ---+ | BRANCH_V8 - 15 - выбрасывать неверное слияние из BRANCH_V9 | | | | | | о | BRANCH_V8 - 14 - создание коммита на BRANCH_V8 для исправления неправильного слияния из BRANCH_V9 | | | | +-----o BRANCH_V8 - 13 - важный коммит сразу после неудачного слияния | | | o---+ BRANCH_V8 - 12 - неправильное слияние с BRANCH_V9 |/ / | o BRANCH_V8 - 11 - добавление комментария к BRANCH_V8 | | о | BRANCH_V9 - 10 - последний коммит на BRANCH_V9 | |
- объединить:
- Объединить оставшиеся изменения от BRANCH_V8:
- объединить:
hg merge BRANCH_V8
- совершить:
hg commit -m "merging changes from BRANCH_V8"
- объединить:
В итоге ситуация выглядит так:
@ BRANCH_V9 - 19 - объединение изменений из BRANCH_V8 |\ | o BRANCH_V9 - 18 - плохое слияние с BRANCH_V8 и его исправление и выбрасывание всего этого | |\ | | o BRANCH_V9 - 17 - Слияние в последнем хорошем состоянии BRANCH_V8 | | |\ о | | | BRANCH_V8 - 16 - слились две головы, используемые для возврата из плохого слияния |\| | | | o---+ BRANCH_V8 - 15 - выбрасывать неверное объединение из BRANCH_V9 | | | | | | | o BRANCH_V8 - 14 - создание коммита на BRANCH_V8 для исправления неправильного слияния из BRANCH_V9 | | | | о | | | BRANCH_V8 - 13 - важный коммит сразу после неудачного слияния |/ / / o---+ BRANCH_V8 - 12 - неправильное слияние с BRANCH_V9 |/ / | o BRANCH_V8 - 11 - добавление комментария к BRANCH_V8 | | о | BRANCH_V9 - 10 - последний коммит на BRANCH_V9 | |
После всех этих шагов, в которых вам не нужно проверять какие-либо различия вручную, BRANCH_V8 и BRANCH_V9 являются правильными, и будущие слияния от BRANCH_V8 к BRANCH_V9 также будут правильными.
В крайнем случае вы можете экспортировать репозиторий в кучу различий, отредактировать историю, а затем склеить обратно то, что вам нужно - в новый репозиторий, чтобы избежать риска повреждения. Возможно, не так уж плохо для вашего примера, но я не знаю, как выглядит настоящая история.
Я ссылался на эту страницу при выполнении более простой операции:
http://strongdynamic.blogspot.com/2007/08/expunging-problem-file-from-mercurial.html
Хорошо, начните с создания нового пустого хранилища в отдельном каталоге от сломанного хранилища (hg init). Теперь потяните и включите последнюю известную хорошую версию в новый репозиторий; убедитесь, что вы не вытягиваете плохое слияние и действительно вытягиваете все перед ним. В старом хранилище обновите до последней известной исправной версии A и сделайте следующее:
hg graft r1 r2 r3
где r1-3 - изменения, сделанные после неудачного слияния. Вы можете получить конфликты в этой точке; исправить их.
Это должно привести к новым изменениям по сравнению с последней известной версией A. Перетащите эти новые изменения в новый репозиторий. Просто, чтобы дважды проверить, что вы ничего не пропустили, сделайте входящие hg против старого хранилища. Если вы видите что-либо кроме неудачного слияния и r1-3, потяните его.
Выбросьте старый репозиторий. Вы сделали Слияния нет в новом хранилище вообще, и вам никогда не приходилось переписывать историю.
Итак, вы хотите объединить только несколько наборов изменений из B в A? Отказ от изменений, которые вы делали, - это действительно плохая идея, поскольку вы уже пострадали.
Вы должны либо использовать расширение трансплантата, либо иметь третью ветвь, где вы вносите общие изменения, чтобы объединить их как в А, так и в В.
После долгих обсуждений с некоторыми полезными людьми по #mercurial на freenode, mpm предоставил частичное решение, которое, кажется, работает для моего тестового примера (я создал поддельное хранилище, пытаясь воспроизвести сценарий)
Тем не менее, по моему фактическому хранилищу, по причинам, которые я не совсем понимаю, это все еще не идеально.
Вот схема предлагаемого в настоящее время способа решения этой проблемы:
[Исходное изображение потеряно]
Теперь это не так сложно исправить, но мне все равно приходится сравнивать различия (то есть: b46:b11 против b46:b8, a43:a10 против a43:a9) и вручную редактировать некоторые изменения.
Не закрывайте этот вопрос / не отвечайте до тех пор, пока не получите гарантированный способ работы с любым хранилищем.
Важный
Любой, кто пробует это, должен клонировать свой репозиторий и играть с ним, как в песочнице. Как вы должны делать с любым процессом слияния, потому что, если он пойдет не так, вы можете просто выбросить его и начать заново.