web-dev-qa-db-ja.com

patchおよびdiffを使用して2つのファイルをマージし、競合を自動的に解決する方法

差分とパッチについて読みましたが、必要なものを適用する方法がわかりません。私はそれがかなり単純だと思いますので、私の問題を示すためにこれらの2つのファイルを取ります:

a.xml

<resources>
   <color name="same_in_b">#AAABBB</color>
   <color name="not_in_b">#AAAAAA</color>
   <color name="in_b_but_different_val">#AAAAAA</color>
   <color name="not_in_b_too">#AAAAAA</color>
</resources>

b.xml

<resources>
   <color name="same_in_b">#AAABBB</color>
   <color name="in_b_but_different_val">#BBBBBB</color>
   <color name="not_in_a">#AAAAAA</color>
</resources>

次のような出力が必要です(順序は関係ありません)。

<resources>
   <color name="same_in_b">#AAABBB</color>
   <color name="not_in_b">#AAAAAA</color>
   <color name="in_b_but_different_val">#BBBBBB</color>
   <color name="not_in_b_too">#AAAAAA</color>
   <color name="not_in_a">#AAAAAA</color>
</resources>

マージには、この単純なルールに沿ったすべての行が含まれている必要があります。

  1. ファイルの1つだけにある行
  2. 行に同じ名前タグが付いているが値が異なる場合、2番目の行から値を取得します

私はこのタスクをbashスクリプト内に適用したいので、別のプログラムがより適している場合は、diffとpatchを実行する必要はありません。

20
Rafael T

これにはpatchは必要ありません。変更を抽出して、ファイルの変更されていない部分なしで送信するためのものです。

ファイルの2つのバージョンをマージするためのツールはmergeですが、_@vonbrand_が書いたように、2つのバージョンが分岐した「ベース」ファイルが必要です。それなしでマージを行うには、次のようにdiffを使用します。

_diff -DVERSION1 file1.xml file2.xml > merged.xml
_

次のように、変更の各セットをCスタイルの_#ifdef_/_#ifndef_ "プリプロセッサ"コマンドで囲みます。

_#ifdef VERSION1
<stuff added to file1.xml>
#endif
...
#ifndef VERSION1
<stuff added to file2.xml>
#endif
_

2つのファイル間でラインまたはリージョンが異なる場合、次のような「競合」が発生します。

_#ifndef VERSION1
<version 1>
#else /* VERSION1 */
<version 2>
#endif /* VERSION1 */
_

したがって、出力をファイルに保存し、それをエディターで開きます。 _#else_が表示される場所を検索し、手動で解決します。次に、ファイルを保存して_grep -v_を介して実行し、残りの#if(n)defおよび_#endif_行を削除します。

_grep -v '^#if' merged.xml | grep -v '^#endif' > clean.xml
_

今後は、ファイルの元のバージョンを保存してください。 mergeは、追加情報の助けを借りて、より良い結果を与えることができます。 (ただし注意してください:_-p_を使用しない限り、mergeはファイルの1つを編集します。マニュアルをお読みください)。

24
alexis

merge(1)は、おそらくあなたが望むものに近いでしょうが、そのためには2つのファイルの共通の祖先が必要です。

(汚い!)それを行う方法は:

  1. 最初と最後の行を取り除き、grep(1)を使用してそれらを除外します
  2. 結果をまとめる
  3. sort -uソートされたリストを残し、重複を排除します
  4. 最初/最後の行を置き換える

うーん...線に沿って何か:

echo '<resources>'; grep -v resources file1 file2 | sort -u; echo '</resources>'

行う可能性があります。

6
vonbrand

sdiff(1)-ファイルの違いを並べてマージ

使用 --outputオプション。これにより、2つのファイルがインタラクティブにマージされます。単純な commands を使用して、変更を選択するか、変更を編集します。

EDITOR環境変数が設定されていることを確認する必要があります。 "eb"のようなコマンドのデフォルトのエディターは通常 ed、行エディター です。

EDITOR=nano sdiff -o merged.txt file1.txt file2.txt
5

ここで動作する簡単なソリューション最大10ファイルをマージ

#!/bin/bash

strip(){
    i=0
    for f; do
        sed -r '
            /<\/?resources>/ d
            s/>/>'$((i++))'/
        ' "$f"
    done
}

strip "$@" | sort -u -k1,1 -t'>' | sed '
    1 s|^|<resources>\n|
    s/>[0-9]/>/
    $ a </resources>
'

最初に来る引数が優先されることに注意してくださいしたがって、次のように呼び出す必要があります。

script b.xml a.xml

b.xmlではなくa.xmlから保持される共通の値を取得します。

script b.xml a.xmlアウト:

<resources>
   <color name="in_b_but_different_val">#BBBBBB</color>
   <color name="not_in_a">#AAAAAA</color>
   <color name="not_in_b">#AAAAAA</color>
   <color name="not_in_b_too">#AAAAAA</color>
   <color name="same_in_b">#AAABBB</color>
</resources>
1
neurino

別の恐ろしいハック-単純化できるかもしれませんが、:P

#!/bin/bash

i=0

while read line
do
    if [ "${line:0:13}" == '<color name="' ]
    then
        a_keys[$i]="${line:13}"
        a_keys[$i]="${a_keys[$i]%%\"*}"
        a_values[$i]="$line"
        i=$((i+1))
    fi
done < a.xml

i=0

while read line
do
    if [ "${line:0:13}" == '<color name="' ]
    then
        b_keys[$i]="${line:13}"
        b_keys[$i]="${b_keys[$i]%%\"*}"
        b_values[$i]="$line"
        i=$((i+1))
    fi
done < b.xml

echo "<resources>"

i=0

for akey in "${a_keys[@]}"
do
    print=1

    for bkey in "${b_keys[@]}"
    do
        if [ "$akey" == "$bkey" ]
        then
            print=0
            break
        fi
    done

    if [ $print == 1 ]
    then
        echo "  ${a_values[$i]}"
    fi

    i=$(($i+1))
done

for value in "${b_values[@]}"
do
    echo "  $value"
done

echo "</resources>"
1
frostschutz

OK、2回目の試行、Perlで(not製品の品質、チェックなし!):

#!/usr/bin/Perl

open(A, "a.xml");

while(<A>) {
  next if(m;^\<resource\>$;);
  next if(m;^\<\/resource\>$;);
  ($name, $value) = m;^\s*\<color\s+name\s*\=\s*\"([^"]+)\"\>([^<]+)\<\/color\>$;;
  $nv{$name} = $value if $name;
}

close(A);

open(B, "b.xml");

while(<B>) {
  next if(m;^\<resource\>$;);
  next if(m;^\<\/resource\>$;);
  ($name, $value) = m;^\s*\<color\s+name\s*\=\*\"([^"]+)\"\>([^<]+)\<\/color\>$;;
  $nv{$name} = $value if $name;
}

close(B);

print "<resource>\n";
foreach (keys(%nv)) {
    print "   <color name=\"$_\">$nv{$_}</color>\n";
}
print "</resource>\n";
0
vonbrand

もう1つは、cutとgrepを使用して...(a.xml b.xmlを引数として使用)

#!/bin/bash

zap='"('"`grep '<color' "$2" | cut -d '"' -f 2 | tr '\n' '|'`"'")'
echo "<resources>"
grep '<color' "$1" | grep -E -v "$zap"
grep '<color' "$2"
echo "</resources>"
0
frostschutz