Лучший, более простой пример «семантического конфликта»? - PullRequest
11 голосов
/ 25 марта 2010

Мне нравится отличать три разных типа конфликтов от системы контроля версий (VCS):

  • текстуальное
  • синтаксический
  • семантический

A текстовый конфликт - это конфликт, обнаруженный процессом слияния или обновления. Это помечено системой. Фиксация результата не разрешена VCS, пока конфликт не будет разрешен.

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

Наконец, семантический конфликт не помечается VCS, результат компилируется, но при выполнении кода могут возникнуть проблемы. В легких случаях получаются неверные результаты. В тяжелых случаях может произойти сбой. Даже они должны быть обнаружены перед фиксацией очень осторожным программистом, либо путем проверки кода или модульного тестирования.

В моем примере семантического конфликта используются SVN (Subversion) и C ++, но эти варианты на самом деле не имеют отношения к сути вопроса.

Базовый код:

int i = 0;
int odds = 0;
while (i < 10)
{
    if ((i & 1) != 0)
    {
        odds *= 10;
        odds += i;
    }
    // next
    ++ i;
}
assert (odds == 13579)

Изменения влево (L) и вправо (R) следующие.

Слева 's' оптимизация '(изменение значений, которые принимает переменная цикла):

int i = 1; // L
int odds = 0;
while (i < 10)
{
    if ((i & 1) != 0)
    {
        odds *= 10;
        odds += i;
    }
    // next
    i += 2; // L
}
assert (odds == 13579)

Справа '' оптимизация '(изменение использования переменной цикла):

int i = 0;
int odds = 0;
while (i < 5) // R
{
    odds *= 10;
    odds += 2 * i + 1; // R
    // next
    ++ i;
}
assert (odds == 13579)

Это результат слияния или обновления, и он не обнаруживается SVN (что является правильным поведением для VCS), поэтому это не текстовый конфликт. Обратите внимание, что он компилируется, поэтому это не синтаксический конфликт.

int i = 1; // L
int odds = 0;
while (i < 5) // R
{
    odds *= 10;
    odds += 2 * i + 1; // R
    // next
    i += 2; // L
}
assert (odds == 13579)

Сбой assert, потому что odds равен 37.

Итак, мой вопрос заключается в следующем. Есть ли более простой пример, чем этот? Есть ли простой пример, когда скомпилированный исполняемый файл имеет новый сбой?

В качестве вторичного вопроса, встречались ли случаи этого в реальном коде? Опять же, простые примеры особенно приветствуются.

Ответы [ 2 ]

8 голосов
/ 25 марта 2010

Не очевидно предложить простые релевантные примеры, и этот комментарий лучше всего подытоживает:

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

[Это в основном то, что иллюстрирует ваш пример]

Но обнаружение семантических конфликтов, вызванных слиянием между изменениями в сильно разделенных областях кода, скорее всего, потребует удержания большего количествапрограмма в вашей голове, чем большинство программистов - или в проектах размером с ядро, чем любой программист.
Так что даже если вы просматривали эти 3-сторонние различия вручную, это было бы сравнительно бесполезным упражнением: усилиебыло бы очень непропорционально увеличению уверенности.

На самом деле, я бы сказал, что слияние - это красная сельдь:
этот вид семантического столкновения между несопоставимыми, но взаимозависимыми частями кода неизбеженмомент, когда они могут развиваться отдельно.
Как организован этот параллельный процесс разработки - DVCS;ЦВК;тарболы и патчи;все редактируют одни и те же файлы на общем сетевом ресурсе - это не имеет никакого значения.
Слияние не вызывает семантических столкновений, программирование вызывает семантические столкновения.

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


Это, как говорится, простейший пример, проиллюстрированный МартиномФаулер в своей статье Feature Branch - это метод переименования:

Проблема, о которой я больше всего беспокоюсь, - это семантический конфликт.
Простой пример этого заключается в том, что если профессор Plum изменит имяметода, который вызывает код Преподобного Грина.Инструменты рефакторинга позволяют вам безопасно переименовывать метод, но только на основе кода.
Так что, если G1-6 содержит новый код, вызывающий foo, профессор Плам не может сказать в своей базе кода, поскольку у него его нет.Вы узнаете только о большом слиянии.

Переименование функции является относительно очевидным случаем семантического конфликта.
На практике они могут быть гораздо более тонкими.

Тестыявляются ключом к их обнаружению, но чем больше кода для слияния, тем выше вероятность возникновения конфликтов и тем сложнее их устранить .
Это риск возникновения конфликтов, особенно семантических конфликтов, что делает большие слияния страшными.


Как Оле Линж упоминает в его ответ (голосование выше), Мартин Фаулер написал сегодня (время этого редактирования) пост о «семантическом конфликте», включая следующую иллюстрацию:

semantic conflict illustration

Опять же, это основано на переименовании функции, хотя и в более тонком случаена основе внутреннего рефакторинга функции упоминаются:

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

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

Однако есть пара стратегий, которые могут существенно помочьмы имеем с ними дело

  • Первый из них - SelfTestingCode .Тесты эффективно проверяют наш код, чтобы увидеть, соответствуют ли его представления о семантике кода тому, что код делает на самом деле
  • Другой способ, который помогает, - это объединение чаще

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

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

3 голосов
/ 04 августа 2011

Посмотрите пример в этом посте Мартина Фаулера: http://martinfowler.com/bliki/SemanticConflict.html

...