як використовувати патч і розбіг для об'єднання двох файлів і автоматичного вирішення конфліктів


19

Я читав про diff та патч, але не можу зрозуміти, як застосувати те, що мені потрібно. Я думаю, що це досить просто, тому щоб показати свою проблему, візьміть ці два файли:

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. будь-який рядок, який знаходиться лише в одному з файлів
  2. якщо рядок має той самий тег імені, але інше значення, візьміть значення з другого

Я хочу застосувати це завдання всередині bash-скрипту, тому воно не обов'язково повинно робити роботу з diff і patch, якщо інша програма краще підходить


diffможе сказати вам, які рядки є в одному файлі, але не в іншому, а лише про деталізацію цілих рядків. patchпідходить лише для внесення однакових змін у аналогічний файл (можливо, інша версія того ж файлу або зовсім інший файл, де, однак, номер рядків та навколишні рядки для кожної зміни однакові вашому вихідному файлу). Так ні, вони не особливо підходять для цього завдання. Ви можете поглянути, wdiffале рішення, ймовірно, вимагає спеціального сценарію. Оскільки ваші дані схожі на XML, можливо, ви захочете шукати якийсь інструмент XSL.
трійчатка

1
Чому всі відповіді зі спеціальними сценаріями? Об’єднання - це стандартна і складна проблема, і для цього є хороші інструменти. Не винаходити колесо.
alexis

Відповіді:


23

Вам цього не потрібно patch; це для вилучення змін та надсилання їх без незмінної частини файлу.

Інструмент для об'єднання двох версій файлу є merge, але, як @vonbrandписали, вам потрібен "базовий" файл, від якого розходилися ваші дві версії. Щоб зробити злиття без нього, використовуйте diffтак:

diff -DVERSION1 file1.xml file2.xml > merged.xml

Він додасть кожен набір змін у командах C-style #ifdef/ #ifndef"препроцесор", наприклад:

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

Якщо рядок або область відрізняються між двома файлами, ви отримаєте "конфлікт", який виглядає приблизно так:

#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може дати вам набагато кращі результати за допомогою додаткової інформації. (Будьте уважні: mergeредагуйте один із файлів на місці, якщо не використовуєте -p. Прочитайте посібник).


Я щось додав, якщо у мене виник конфліктsed -e "s/^#else.*$/\/\/ conflict/g"
lockwobr

1
Я не думаю, що це гарна ідея. Як я писав у своїй відповіді, ви повинні видаляти #elseрядки вручну, в редакторі під час вирішення конфлікту.
alexis

6

merge(1) ймовірно, ближче до того, що ви хочете, але для цього потрібен спільний пращур для ваших двох файлів.

(Брудний!) Спосіб це зробити:

  1. Позбудьтеся першого та останнього рядків, використовуйте grep(1)для їх виключення
  2. Збийте результати разом
  3. sort -u залишає відсортований список, усуває дублікати
  4. Замініть перший / останній рядок

Гумм ... щось по лінії:

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

може зробити.


працює в цьому конкретному прикладі, але НЕ в цілому: Якщо значення name in_b_but_different_valмає #00AABBсортування, це покладе верхнє значення та видалить друге значення замість першого
Рафаель T

для оптимального рішення в цьому випадку вам доведеться проаналізувати XML, з реальним аналізатором XML не хаками вище, і створити з цього новий об'єднаний вихід XML. diff / patch / sort etc. - просто всі хаки, пристосовані до "конкретних прикладів", для загального рішення вони просто неправильні інструменти
frostschutz

@ Альцгеймер, витягніть щось просте, щоб нам показати ...
vonbrand

Мабуть, diff3працює так само. Потрібен загальний файл пращурів. Чому не існує простого інструменту CLI, який просто об'єднує 2 файли разом на основі diffпоказаних даних.
CMCDragonkai

5

sdiff (1) - бічне поєднання файлових відмінностей

Використовуйте --outputопцію, це дозволить інтерактивно об'єднати будь-які два файли. За допомогою простих команд виберіть зміну або відредагуйте зміни.

Ви повинні переконатися, що встановлена EDITORзмінна середовища. Редактор за замовчуванням для команд типу "eb" зазвичай edє редактором рядків .

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

1
Я вважаю, що використовувати vimяк редактор як краще. Але це найкраще рішення, воно також поставляється з diffкомандою!
CMCDragonkai

1

Ось просте рішення, яке працює в об'єднанні до 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

Ще один жахливий злом - можна спростити, але: 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>"

0

Добре, друга спроба, тепер у Perl ( не якість виробництва, не перевірка!):

#!/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

Ще один, використовуючи cut and 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>"

echoє типовою дією, тому xargs echoзайвою. Чому б вам просто tr '\n' '|'все одно?
tripleee

Хороший момент - це просто швидкий зліт. Я відредагую.
frostschutz
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.