Отказ от обратного слияния на Mercurial - PullRequest
77 голосов
/ 05 ноября 2008

Как вы можете обратить вспять эффект слияния на поляризованные ветви, не умирая от агонии?

Эта проблема мучила меня в течение месяцев , и я наконец сдался.

У вас есть 1 репозиторий с 2 ​​ именованными филиалами. А и Б.

Изменения, которые происходят с A, неизбежно произойдут на B.

Изменения, которые происходят непосредственно на 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, которая только что появилась (и получила отказ), которая выглядит как изменение на ветке А вместо "

О переписывании истории:

Проблема со всеми этими ретроактивными решениями заключается в следующем:

  1. У нас 9000 коммитов.
  2. Свежее клонирование, таким образом, занимает полчаса
  3. Если существует хотя бы один плохой клон репозитория где-то , существует вероятность того, что он снова соприкоснется с исходным хранилищем и снова ударит по нему.
  4. Все уже клонировали этот репозиторий, и теперь прошло несколько дней с текущими коммитами.
  5. Один из таких клонов - это живой сайт, так что "стереть его и начать с нуля" = "big nono"

(Признаюсь, многие из вышеперечисленных немного глупы, но они находятся вне моего контроля).

Единственные жизнеспособные решения - это те, которые предполагают, что люди могут и будут делать все неправильно, и что есть способ «исправить» эту ошибку.

Ответы [ 5 ]

50 голосов
/ 19 января 2010

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

Таким образом, у нас есть хранилище с отдельными ветками для поддерживаемой версии одного продукта. Как и в ситуации, изложенной в вопросе, все изменения, внесенные в ветку более ранней версии (т. Е. Исправления для этой версии), в конечном итоге должны быть объединены с ветками более поздних версий.

То есть, если что-то проверено в BRANCH_V8, оно должно быть объединено с BRANCH_V9.

Теперь один из разработчиков совершает следующую ошибку: он объединяет все изменения из BRANCH_V9 в BRANCH_V8 (т. Е. Объединение в неправильном направлении). Кроме того, после этого неудачного слияния он выполняет дополнительные коммиты, прежде чем заметит свою ошибку.

Итак, ситуация такая, как показано в графическом журнале ниже.

o  BRANCH_V8 - 13 - important commit right after the bad merge
|
o    BRANCH_V8 - 12 - wrong merge from BRANCH_V9
|\
| o  BRANCH_V8 - 11 - adding comment on BRANCH_V8 (ie. last known good state)
| |
o |  BRANCH_V9 - 10 - last commit on BRANCH_V9
| |

Мы можем исправить эту ошибку следующим образом:

  1. обновить локальный каталог до последнего хорошего состояния BRANCH_V8: hg update 11
  2. Создайте нового потомка этого последнего хорошего состояния:
    1. изменить какой-то файл $EDITOR some/file.txt (это необходимо, потому что Mercurial не разрешает пустые коммиты)
    2. зафиксировать эти изменения hg commit -m "generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9"
      Ситуация сейчас выглядит следующим образом:
      o  BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9
      |
      | o  BRANCH_V8 - 13 - important commit right after the bad merge
      | |
      | o  BRANCH_V8 - 12 - wrong merge from BRANCH_V9
      |/|
      o |  BRANCH_V8 - 11 - adding comment on BRANCH_V8
      | |
      | o  BRANCH_V9 - 10 - last commit on BRANCH_V9
      
  3. Слить вновь сгенерированную голову с ревизией, в которой произошло неудачное слияние, и выбросить все изменения перед фиксацией. Не просто объединяйте две головы, потому что тогда вы потеряете важный коммит, который также произошел после объединения!

    1. объединить: hg merge 12 (игнорировать любые конфликты)
    2. выбросить все изменения: hg revert -a --no-backup -r 14
    3. зафиксировать изменения: hg commit -m "throwing away wrong merge from BRANCH_V9" Ситуация теперь выглядит так:
      o    BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9
      |\
      | o  BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9
      | |
      +---o  BRANCH_V8 - 13 - important commit right after the bad merge
      | |
      o |  BRANCH_V8 - 12 - wrong merge from BRANCH_V9
      |\|
      | o  BRANCH_V8 - 11 - adding comment on BRANCH_V8
      | |
      o |  BRANCH_V9 - 10 - last commit on BRANCH_V9
      | |
      

    Т.е.. В BRANCH_V8 есть две главы: одна содержит исправление неудачного слияния, а другая содержит оставшийся важный коммит BRANCH_V8, произошедший сразу после объединения.

  4. Объединить две головы на BRANCH_V8:
    1. объединить: hg merge
    2. коммит: hg commit -m "merged two heads used to revert from bad merge"

Ситуация в конце BRANCH_V8 теперь исправлена ​​и выглядит следующим образом:

o    BRANCH_V8 - 16 - merged two heads used to revert from bad merge
|\
| o    BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9
| |\
| | o  BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9
| | |
o | |  BRANCH_V8 - 13 - important commit right after the bad merge
|/ /
o |  BRANCH_V8 - 12 - wrong merge from BRANCH_V9
|\|
| o  BRANCH_V8 - 11 - adding comment on BRANCH_V8
| |
o |  BRANCH_V9 - 10 - last commit on BRANCH_V9
| |

Теперь ситуация на BRANCH_V8 верна. Единственная оставшаяся проблема заключается в том, что следующее объединение от BRANCH_V8 до BRANCH_V9 будет неправильным, поскольку оно также будет объединено в 'fix' для плохого слияния, которое мы не хотим на BRANCH_V9. Хитрость заключается в том, чтобы объединить BRANCH_V8 и BRANCH_V9 в отдельных изменениях:

  • Первое слияние, от BRANCH_V8 до BRANCH_V9, правильные изменения на BRANCH_V8 до плохого слияния.
  • Второе слияние в ошибке слияния и ее исправление, и, без необходимости проверять что-либо, отбрасывать все изменения
  • В-третьих, объединить оставшиеся изменения с BRANCH_V8.

Подробно:

  1. Переключите ваш рабочий каталог на BRANCH_V9: hg update BRANCH_V9
  2. Слияние в последнем хорошем состоянии BRANCH_V8 (т. Е. Коммит, который вы сгенерировали, чтобы исправить неудачное слияние). Это слияние является слиянием, как любое обычное слияние, т.е. конфликты должны разрешаться как обычно, и ничего не нужно выбрасывать.
    1. объединить: hg merge 14
    2. коммит: hg commit -m "Merging in last good state of BRANCH_V8" Ситуация сейчас:
      @    BRANCH_V9 - 17 - Merging in last good state of BRANCH_V8
      |\
      | | o    BRANCH_V8 - 16 - merged two heads used to revert from bad merge
      | | |\
      | +---o  BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9
      | | | |
      | o | |  BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9
      | | | |
      | | o |  BRANCH_V8 - 13 - important commit right after the bad merge
      | | |/
      +---o  BRANCH_V8 - 12 - wrong merge from BRANCH_V9
      | |/
      | o  BRANCH_V8 - 11 - adding comment on BRANCH_V8
      | |
      o |  BRANCH_V9 - 10 - last commit on BRANCH_V9
      | |
      
  3. Объединить плохое объединение на BRANCH_V8 + его исправление и выбросить все изменения:
    1. объединить: hg merge 15
    2. отменить все изменения: hg revert -a --no-backup -r 17
    3. совершить слияние: hg commit -m "Merging in bad merge from BRANCH_V8 and its fix and throwing it all away" Текущая ситуация :
      @    BRANCH_V9 - 18 - Merging in bad merge from BRANCH_V8 and its fix and throwing it all away
      |\
      | o    BRANCH_V9 - 17 - Merging in last good state of BRANCH_V8
      | |\
      +-----o  BRANCH_V8 - 16 - merged two heads used to revert from bad merge
      | | | |
      o---+ |  BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9
      | | | |
      | | o |  BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9
      | | | |
      +-----o  BRANCH_V8 - 13 - important commit right after the bad merge
      | | |
      o---+  BRANCH_V8 - 12 - wrong merge from BRANCH_V9
      |/ /
      | o  BRANCH_V8 - 11 - adding comment on BRANCH_V8
      | |
      o |  BRANCH_V9 - 10 - last commit on BRANCH_V9
      | |
      
  4. Слияние оставшихся изменений от BRANCH_V8:
    1. объединить: hg merge BRANCH_V8
    2. коммит: hg commit -m "merging changes from BRANCH_V8"

В итоге ситуация выглядит так:

@    BRANCH_V9 - 19 - merging changes from BRANCH_V8
|\
| o    BRANCH_V9 - 18 - Merging in bad merge from BRANCH_V8 and its fix and throwing it all away
| |\
| | o    BRANCH_V9 - 17 - Merging in last good state of BRANCH_V8
| | |\
o | | |  BRANCH_V8 - 16 - merged two heads used to revert from bad merge
|\| | |
| o---+  BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9
| | | |
| | | o  BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9
| | | |
o | | |  BRANCH_V8 - 13 - important commit right after the bad merge
|/ / /
o---+  BRANCH_V8 - 12 - wrong merge from BRANCH_V9
|/ /
| o  BRANCH_V8 - 11 - adding comment on BRANCH_V8
| |
o |  BRANCH_V9 - 10 - last commit on BRANCH_V9
| |

После всех этих шагов, при которых вам не нужно проверять какие-либо различия вручную, значения BRANCH_V8 и BRANCH_V9 являются правильными, и будущие слияния от BRANCH_V8 к BRANCH_V9 также будут правильными.

2 голосов
/ 18 апреля 2012

ОК, начните с создания нового пустого хранилища в отдельном каталоге из поврежденного хранилища (hg init). Теперь потяните и включите последнюю известную хорошую версию в новый репозиторий; убедитесь, что вы не извлекаете неудачное слияние и делаете вытягиваете все перед этим. В старом репозитории обновите до последней известной хорошей версии A и сделайте следующее:

hg graft r1 r2 r3

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

Это должно привести к новым изменениям по сравнению с последней известной верной версией A . Перетащите эти новые изменения в новый репозиторий. Просто чтобы дважды проверить, что вы ничего не пропустили, сделайте входящие hg против старого хранилища. Если вы видите что-либо кроме неудачного слияния и r1-3, потяните его.

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

2 голосов
/ 06 ноября 2008

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

Я ссылался на эту страницу при выполнении более простой операции:

http://strongdynamic.blogspot.com/2007/08/expunging-problem-file-from-mercurial.html

1 голос
/ 06 ноября 2008

После долгих обсуждений с некоторыми полезными людьми по #mercurial на freenode, mpm предоставил частичное решение, которое, похоже, работает для моего тестового примера (я создал поддельное хранилище, пытаясь воспроизвести сценарий)

Тем не менее, в моем реальном хранилище, по причинам, которые я не совсем понимаю, он все еще не идеален.

Вот схема предлагаемого в настоящее время способа решения этой проблемы:

[Исходное изображение потеряно]

Теперь меньше проблемы, которую нужно решить, но мне все еще приходится сравнивать различия (т.е.: b46: b11 против b46: b8, a43: a10 против a43: a9) и вручную редактировать некоторые возвращается обратно.

Не закрывать этот вопрос / принимать ответ, пока я не получу гарантированный способ, который работает в любом хранилище.

Важно

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

1 голос
/ 06 ноября 2008

Итак, вы хотите объединить только несколько наборов изменений из B в A? Отмена изменений, которые вы делали, - это действительно плохая идея, поскольку вы уже пострадали.

Вы должны либо использовать расширение трансплантата, либо иметь третью ветвь, где вы вносите общие изменения, чтобы объединить их как в A, так и в B.

...