Лучший, более простой пример "семантического конфликта"?

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

  • textual
  • синтаксические
  • семантический

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

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

наконец, a семантический конфликт не попадает по 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 'optimisation' (изменение значений переменной цикла):

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)

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

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)

The assert не так odds - это 37.

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

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

2 ответов


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

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

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

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

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


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

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

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

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


As Оле Люнге упоминает в ответ (upvoted), Мартин Фаулер написал сегодня (время edit) сообщение о "семантическом конфликте", включая следующую иллюстрацию:

semantic conflict illustration

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

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

Итак, первый момент здесь что каким бы мощным ни был ваш инструмент, он защитит вас только от текстовых конфликтов.

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

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

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

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


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