Почему отказы Mercurial в одной ветви влияют на другие ветви? - PullRequest
15 голосов
/ 29 февраля 2012

Это сложная ситуация, чтобы объяснить, так что терпите меня. У меня есть хранилище Mercurial с 2 основными ветками, по умолчанию и dev .

Работа обычно выполняется в именованной ветви из dev (функциональная ветвь). В любой момент может быть много ветвей функций. Когда работа в этой ветке завершена, она объединяется обратно в dev .

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

Как только ветвь релиза готова к выпуску, она объединяется в default (поэтому default всегда представляет состояние кода в работе). Работа в ветке dev и функциях продолжается как обычно.

Проблема возникает, когда приходит время сделать еще один выпуск, включая функцию, которая была отменена в предыдущем выпуске. Новая ветвь релиза создается как обычно (выключено dev ). Эта новая ветвь релиза теперь содержит функцию, которая была исключена из предыдущей ветки релиза (поскольку откат был выполнен в ветке релиза, а набор изменений слияния остается в ветке dev ).

На этот раз, когда ветвь выпуска готова к выпуску и объединена в по умолчанию , любые изменения, которые были отменены в результате возврата слияния в предыдущей ветви выпуска, не объединяются в по умолчанию . Почему это так? Поскольку новая ветвь релиза содержит все наборы изменений ветви функций (ничего не отменено), почему ветвь default также не получает все эти наборы изменений?

Если все вышеперечисленное трудно выполнить, вот скриншот из TortoiseHg, который показывает основную проблему. «branch1» и «branch2» являются ветвями функций, «release» и «release2» являются ветвями релиза:

enter image description here

Ответы [ 2 ]

26 голосов
/ 29 февраля 2012

Я считаю, что проблема в том, что слияния работают не так, как вы думаете.Вы пишете

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

Когда вы объединяете две ветви, неправильно думать, что они применяют все изменения из одной ветви в другую.Таким образом, ветвь default не "получает" никаких изменений от release2.Я знаю, что мы обычно так думаем о слияниях, но это неточно.

Что действительно происходит при слиянии двух наборов изменений, так это:

  1. Mercurial находит общего предкадля двух наборов изменений.

  2. Для каждого файла, который отличается между двумя наборами изменений, Mercurial запускает трехстороннее объединение алгоритм с использованием файла предкафайл в первом наборе изменений и файл во втором наборе изменений.

В вашем случае вы объединяете редакцию 11 и 12. Наименее общий предок - редакция 8. Это означает, чтоMercurial запустит трехстороннее слияние версий файлов оттуда:

  • Редакция 8: без возврата

  • Редакция 11: отклонена ветвь элемента

  • Редакция 12: нет возврата

Inтрехстороннее слияние, изменения всегда козыри без изменений.Mercurial видит, что файлы были изменены между 8 и 11, и не видит изменений между 8 и 12. Таким образом, он использует измененную версию из ревизии 11 в слиянии.Это относится к любому трехстороннему алгоритму слияния.Полная таблица слияния выглядит следующим образом, где old, new, ... - содержимое соответствующих блоков в трех файлах:

ancestor  local  other -> merge
old       old    old      old (nobody changed the hunk)
old       old    new      new (they changed the hunk)
old       new    old      new (you changed the hunk)
old       new    new      new (hunk was cherry picked onto both branches)
old       foo    bar      <!> (conflict, both changed hunk but differently)

Боюсь, что changege merge changesetне следует отказываться от вообще из-за этого удивительного поведения слияния.Mercurial 2.0 и более поздние версии прервут и будут жаловаться, если вы попытаетесь отказаться от слияния.

В общем, можно сказать, что трехсторонний алгоритм слияния предполагает, что все изменения хороши .Поэтому, если вы объедините branch1 в dev, а затем отмените объединение с отступом, тогда алгоритм объединения будет думать, что состояние «лучше», чем раньше.Это означает, что вы не можете просто повторно объединить branch1 в dev на более позднем этапе, чтобы получить обратно отклоненные изменения.

Что вы можете сделать, это использовать «фиктивное объединение» при объединениив default.Вы просто объединяете и всегда сохраняете изменения из ветки релиза, в которую вы сливаетесь, default:

$ hg update default
$ hg merge release2 --tool internal:other -y
$ hg revert --all --rev release2
$ hg commit -m "Release 2 is the new default"

Это позволит обойти проблему и заставить default быть похожим на release2.Это предполагает, что в default не вносятся абсолютно никакие изменения без объединения в ветку выпуска.

Если вы должны иметь возможность выпускать выпуски с пропущенными функциями, то «правильный» способ - вообще не объединять эти функции.Слияние - это серьезное обязательство: вы говорите Mercurial, что набор изменений слияния теперь имеет все хорошее от обоих его предков.До тех пор, пока Mercurial не позволит вам выбрать собственную базовую ревизию при слиянии , алгоритм трехстороннего слияния не позволит вам передумать относительно возврата.

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

release: ... o --- o --- m1 --- m2
                        /      /
feature-A:   ... o --- o      /
                             /
feature-B:  ... o --- o --- o 

Теперь вы решили, что функция A была плохой, и вы отказались от слияния:

release: ... o --- o --- m1 --- m2 --- b1
                        /      /
feature-A:   ... o --- o      /
                             /
feature-B:  ... o --- o --- o 

Затем вы добавили еще одну функцию в ветку релиза:

release: ... o --- o --- m1 --- m2 --- b1 --- m3
                        /      /             /
feature-A:   ... o --- o      /             /
                             /             /
feature-B:  ... o --- o --- o             /
                                         /
feature-C:  ... o --- o --- o --- o --- o 

Если вы теперь хотите снова ввести функцию A, вы можете отменить b1:

release: ... o --- o --- m1 --- m2 --- b1 --- m3 --- b2
                        /      /             /
feature-A:   ... o --- o      /             /
                             /             /
feature-B:  ... o --- o --- o             /
                                         /
feature-C:  ... o --- o --- o --- o --- o 

Мы можем добавить дельты на график, чтобы лучше показать, чтогде и когда меняется:

                     +A     +B     -A     +C     --A
release: ... o --- o --- m1 --- m2 --- b1 --- m3 --- b2

После этого второго возврата вы можете снова объединиться с feature-A в случае добавления туда новых изменений.График, который вы объединяете, выглядит так:

release: ... o --- o --- m1 --- m2 --- b1 --- m3 --- b2
                        /      /             /
feature-A:   ... o -- a1 - a2 /             /
                             /             /
feature-B:  ... o --- o --- o             /
                                         /
feature-C:  ... o --- o --- o --- o --- o 

и вы сливаете a2 и b2.Общий предок будет a1.Это означает, что единственными изменениями, которые необходимо учитывать при трехстороннем объединении, являются изменения между a1 и a2 и a1 и b2.Здесь b2 уже содержит основную часть изменений "в" a2, поэтому слияние будет небольшим.

0 голосов
/ 29 февраля 2012

Ответ Мартина, как обычно, на деньги, но я просто хотел добавить свои 2p.

Еще один способ думать об этом - то, что backout ничего не удаляет, он добавляет обратное изменение.

Итак, когда вы сливаетесь, вы не делаете:

Branch after changes <-> Branch before changes => Result with changes

вы делаете:

Branch after changes <-> Branch after changes with removal of changes => Result with changes removed.

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

...