14

私は、バージョン管理システム (VCS) とは異なる 3 つのタイプの競合を区別したいと考えています。

  • 原文
  • 構文上の
  • セマンティック

テキストの競合は、マージまたは更新プロセスによって検出されるものです。これは、システムによってフラグが立てられます。結果のコミットは、競合が解決されるまで 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) の変更は次のとおりです。

Leftの「最適化」(ループ変数が取る値の変更):

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)

は37 でassertあるため失敗します。odds

だから私の質問は次のとおりです。これより簡単な例はありますか?コンパイルされた実行可能ファイルに新しいクラッシュが発生する簡単な例はありますか?

二次的な質問として、実際のコードでこれに遭遇したケースはありますか? 繰り返しになりますが、簡単な例は特に歓迎されます。

4

3 に答える 3

8

関連する単純な例を考え出すことは明らかではありません。このコメントは、その理由を最もよくまとめています。

変更が近くにある場合、些細な解決策が正しい可能性が高くなります (誤った解決策は、コードの同じ部分に影響を与える可能性が高く、その結果、重要な競合が発生する可能性が高くなります)。そうでない場合、問題は比較的早く、おそらく明白な形で現れます。

[これは基本的にあなたの例が示すものです]

しかし、コードの大きく離れた領域の変更間のマージによって導入されたセマンティック コンフリクトを検出するには、ほとんどのプログラマーよりも多くのプログラムを頭の中に保持する必要があります。または、カーネルのサイズのプロジェクトでは、どのプログラマーよりも多くのプログラムを保持する必要があります。
したがって、それらの 3 方向の差分を手動で確認したとしても、それは比較的役に立たない作業になります。その努力は、信頼の向上とはかけ離れたものになります。

実際、私はマージは厄介者であると主張し
ます。コードの異なるが相互に依存する部分の間のこの種のセマンティックな衝突は、それらが個別に進化できるようになった瞬間に避けられません。
この並行開発プロセスがどのように編成されているか – DVCS; CVCS; tarball とパッチ; 誰もがネットワーク共有上の同じファイルを編集します – その事実にはまったく影響しません.
マージはセマンティックの衝突を引き起こしませんが、プログラミングはセマンティックの衝突を引き起こします。

言い換えれば、マージ後に実際のコードで遭遇したセマンティック コンフリクトの実際のケースは単純ではなく、かなり複雑でした。


そうは言っても、 Martin Fowler の記事 Feature Branch で示されている最も単純な例は、メソッドの名前変更です。

私がより心配している問題は、セマンティック コンフリクトです。
これの簡単な例は、プラム教授がグリーン牧師のコードが呼び出すメソッドの名前を変更した場合です。リファクタリング ツールを使用すると、メソッドの名前を安全に変更できますが、コード ベースでのみ可能です。
したがって、G1-6 に foo を呼び出す新しいコードが含まれている場合、Plum 教授は自分のコード ベースを持っていないため、それを知ることができません。あなたは大きな合併でしか知りません。

関数の名前変更は、セマンティック コンフリクトの比較的明白なケースです。
実際には、それらははるかに微妙な場合があります。

テストはそれらを発見するための鍵ですが、マージするコードが多ければ多いほど、競合が発生する可能性が高くなり、それらを修正するのが難しくなります.
大規模なマージを怖がらせるのは、競合、特にセマンティック競合のリスクです。


Ole Lyngeが彼の回答(賛成) で言及しているように、 Martin Fowlerは今日 (この編集の時間)、次の図を含む「セマンティック コンフリクト」に関する投稿を書きました。

セマンティック コンフリクトの図

繰り返しますが、これは関数の名前変更に基づいていますが、内部関数のリファクタリングに基づくより微妙なケースが言及されています。

最も単純な例は、関数の名前を変更することです。メソッドが呼び出された場合
、メソッドの操作が簡単になると思います。clcBlcalculateBill

したがって、ここでの最初のポイントは、ツールがどれほど強力であっても、テキストの競合からしか保護されないということです。

ただし、それらに対処するのに非常に役立ついくつかの戦略があります

  • これらの最初のものはSelfTestingCodeです。テストはコードを効果的に調べて、コードのセマンティクスのビューがコードが実際に行うことと一致しているかどうかを確認します
  • 役立つ他のテクニックは、より頻繁にマージすることです

多くの場合、機能の分岐を容易にする方法に基づいて DVCS を正当化しようとする人がいます。しかし、それはセマンティックの競合の問題を見逃しています。
機能が数日以内に迅速に構築された場合、セマンティック コンフリクトが少なくなります (1 日未満の場合は、実質的に CI と同じです)。ただし、このような短いフィーチャー ブランチはあまり見られません。

ショットライブ ブランチとフィーチャー ブランチの中間点を見つける必要があると思います。
また、同じフィーチャー ブランチに開発者のグループがある場合、マージが重要になることがよくあります

于 2010-03-25T11:53:12.530 に答える
3

Martin Fowlerによるこの投稿の例を確認してください:http://martinfowler.com/bliki/SemanticConflict.html

于 2011-08-04T16:51:26.557 に答える