たとえば、gitとdiff3で重複する複数行グループを置き換える方法

たとえば、gitとdiff3で重複する複数行グループを置き換える方法

従来のコードをリベースしていますが、スクリプト(通常はコードフォーマッタ)が原因で発生する多くの競合が見つかりました。変更は簡単で予測可能であるため、スクリプトを簡単に再実行してコードに変更を適用できます。これは通常、gitのdiffマーカーで囲まれた3つの同じ「スニペット」を残します。

3つの一致するフラグメントと違いマーカーを見つけて単一のフラグメントに変換するスクリプトをどのように作成しますか?

例:

git rebase次を生成します。

<<<<<<< HEAD
    ACA
    BCB
||||||| parent of 0cfd85b8e3... Beautify.
    AAA
    BBB
=======
    AAC
    BBC
>>>>>>> 0cfd85b8e3... Beautify.

フォーマッタを再実行すると、次の結果が表示されます。

<<<<<<< HEAD
    ACC
    BCC
||||||| parent of 0cfd85b8e3... Beautify.
    ACC
    BCC
=======
    ACC
    BCC
>>>>>>> 0cfd85b8e3... Beautify.

私はそれを次のように変換したいと思います:

    ACC
    BCC

私はgrep(新しい行で苦労しました)とpcregrep(逆参照/キャプチャグループで苦労しました)を含む非常に多くのことを試しました。どんなアイデアがありますか?

答え1

この問題に対してステートフル awk プログラムを使用すると、次のように明確にわかります。

awk '/^<<<<<<</ { state = 1; next }
     /^=======/ { state = 2; next }
     /^>>>>>>>/ { state = 0; next }
     state == 0 || state == 2' data
foo
prolog
    AAC
    BBC
epilog
bar

これには以下がdata含まれます。

foo
prolog
<<<<<<< HEAD
    ACA
    BCB
||||||| parent of 0cfd85b8e3... Beautify.
    AAA
    BBB
=======
    AAC
    BBC
>>>>>>> 0cfd85b8e3... Beautify.
epilog
bar

state == 0まだ初期化されていない場合はtrueなので、最初はステータス0になります。state私たちはそれを見ると状態1に切り替え<<<<<<、それを見ると状態2に切り替え、そして終了=======マーカーを見ると状態0に戻ります>>>>>>>。とても簡単です。

初期状態0(衝突トークンの外側)または状態2(衝突トークンの最後の部分)にある場合にのみ行を印刷します。

すべての場合において、コマンドはnext失敗動作が発生しないことを保証します。最後のケースだけが何も印刷するので、最初の3つのケースは何も印刷せず、競合マークを識別し、状態を変更する以外は何もしないことを知っています。したがって、どの状態であっても、衝突フラグは印刷されない。状態 2 から状態 0 に戻ると、誤って=======or>>>>>>>行は印刷されません。

最初のジョブでは印刷されていない状態1に切り替わるので、必ずしも必要ではありませんが、next含めることは一貫性を保つのに最適です。また効率:衝突マーカーは相互に排他的です。=======すでに検出されている場合はテストする必要があります<<<<<<<

答え2

TxRLispのawkマクロは従来のAwkよりも強力な範囲表現を持っているので、状態変数なしでこの問題を解決できます。

  • 範囲式は他の式と組み合わせることができます。例えば、スコープ式は、他のスコープの開始または終了であってもよい。

  • awk範囲式は、週末の条件だけでなく、マクロのどこでも使用できます。

  • 範囲式が入ります。9つの味開始と終了からレコードを除外したり、より多くのレコードを含むように範囲を拡張したりするために使用されます。

dataAwkの答えと同じファイルを使用してください。

$ txr awk.tl data
foo
prolog
    AAC
    BBC
epilog
bar

コードは次の場所にありますawk.tl

(awk
  (:let (beg (f^ #/<<<<<<</))
        (mid (f^ #/=======/))
        (end (f^ #/>>>>>>>/)))
  ((-rng- mid end))            ; print lines between ===...>>>, exclusive
  ((not (rng beg end))))       ; print lines outside <<<...>>>

分解:

まず(:let ...)フォームの範囲内でいくつかの語彙変数をバインドする句ですawk。単一引数の匿名関数である変数、begおよびmidを紹介します。end例えば(f^ /abc/)abc文字列を取得し、文字列の先頭に固定された正規表現に一致する関数を生成します。 diff3 衝突マーカーの開始、中間、終了を一致させる関数を定義しています。

背面には:let2つのセクションがあります(condition action)。節もないので、デフォルトのactionジョブは印刷です。これはAwkの一形態なので、同じルールが適用されます。

範囲はタグからまでの(-rng- mid end)レコードと一致します。このバリアントは、範囲の内部レコードに対してのみ true を生成し、最初と最後のレコードに対しては false を生成します。これがまさに私たちが望むものです。私たちは、この行を含めずに、間のすべての行を印刷したいと思います。 awkには範囲からエンドポイントを除外する機能はありません。midend-rng-======>>>>>>>

2番目の規則の条件はです(not (rng beg end))。この条件は、begtoend範囲内にないレコード、つまり diff3 競合コメント内にない場合に該当します。私たちはそれらをすべて印刷したいと思います。 awkにはそのような機能もありません。範囲式は他の演算子(たとえば、!否定)と組み合わせることはできません。

TXR Lispのスコープ機能のため、awkこのソリューションには状態変数は必要なく、次のレコード処理を続行するために暗黙的なループから明示的に分岐する必要がある規則はありません。

関連情報