извлечь общее подмножество двух файлов патчей - PullRequest
0 голосов
/ 04 февраля 2012

У меня есть три исходных дерева, A, B и C. A - исходное дерево. B и C - это модификации A, созданные двумя разными разработчиками.

Я взял различия A с B и A с C.

Но есть много изменений в двух общих файлах diff. Значение A не является последней точкой расхождения для B и C. Вместо этого A был изменен до точки, где он стал D (которого у меня нет), а B и C были затем изменены по D.

Мой вопрос: что я могу сделать с двумя файлами diff (кроме ручного труда), чтобы извлечь их максимальное общее подмножество? чтобы я применил это подмножество как патч к A, чтобы получить D

РЕДАКТИРОВАТЬ 1: Иллюстрация:

A ---> D ---> B
        \---> C

РЕДАКТИРОВАТЬ 2: Я посмотрел на инструменты patchutils, но не нашел тот, который делает то, что мне нужно. Я также рассмотрел этот вопрос , но упомянутый там метод не дает правильного вывода.

Ответы [ 2 ]

0 голосов
/ 06 февраля 2012

Хорошо, ребята, я придумал следующее решение (без использования git, mercurial и т. Д.).(ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: может иметь опечатки, может потребоваться изменения для работы на вашей стороне)

Основной метод / алгоритм заключается в следующем:

  1. Разделить оба файла diff на более мелкие компоненты

  2. Сравните компоненты одного файла сравнения с компонентами другого и выберите те, которые идентичны в обоих

  3. Объедините выбранные компоненты, чтобы создать новый файл сравненияфайл с правильным форматированием

Каждый из моих файлов различий имеет разности на уровне файлов, и каждый разностный уровень имеет один или несколько блоков.Если под компонентами я подразумеваю компоненты уровня файла, то извлечение может быть выполнено с помощью инструментов patchutils «splitdiff» и «комбинированный» следующим образом:

$ # Step 1
$ mkdir AB_components; cp AB.diff AB_components; cd AB_components
$ splitdiff -ad AB.diff
$ cd ..
$ mkdir AC_components; cp AC.diff AC_components; cd AC_components
$ splitdiff -ad AC.diff
$ cd ..
$
$ # Step 2
$ mkdir AD_components;
$ for f in `diff -rs AB_components AC_components | grep 'are identical$' | cut -d' ' -f2 | cut -d'/' -f2`; do cp AB_components/$f AD_components; done
$
$ # Step 3
$ cd AD_components; touch AD.diff
$ for f in `ls ._*`; do combinediff AD.diff $f > tmpfile; mv tmpfile AD.diff; done

Однако если под компонентами я подразумеваю отдельные фрагменты, тогда splitdiff недостаточно,Я нашел инструмент здесь , который разбивает файл на отдельные фрагменты (мне пришлось внести небольшие изменения в этот скрипт, чтобы он работал на моем компьютере ... в частности, мне пришлось закомментировать файл «require».рб '"линия).

Для шага 2 мне пришлось запустить двойной цикл for для поиска «идентичных» блоков:

$ for f in `ls AB_components.mod/*`; do for g in `ls AC_components.mod/*`; do diff -s $f $g | grep 'are identical$'; done; done > identical_hunks
$ for f in `cat identical_hunks | cut -d' ' -f2`; do cp AB_components/`basename $f` AD_components; done

Для объединения мне пришлось выполнить двухэтапный процесс:

  • Шаг 3, часть 1: я сначала объединил фрагменты, принадлежащие к одному и тому же файлу (файлам), чтобы создать разность для каждого файла
  • Шаг 3, часть 2: я использовал комбинированный файл, чтобы объединить эти файлы различий для созданияокончательный файл сравнения

Для шага 3, часть 1, я создал следующий сценарий оболочки (назовем его combhunks.sh):

#!/bin/bash
filename=$1
echo 'diff header line:'
firstpatchfile=`ls -1v $filename.*.patch | head -1`
head -2 $firstpatchfile
files=`ls -1v $filename.*.patch`
for f in $files; do tail -n +3 $f; done

и использовал его следующим образом:

$ mkdir AD_filelevel_components; cd AD_filelevel_components
$ for f in `ls ../AD_components/* | rev | cut -d'.' -f3- | rev | sort | uniq`; do ../combinehunks.sh $f > `basename $f`.patch; done

Шаг 3, часть 2, аналогичен шагу 3 в случае уровня файла, за исключением использования каталога AD_filelevel_components вместо AD_components.

Предупреждения / Примечания:

  1. Мне пришлось удалить временные метки из строк заголовка --- и +++, прежде чем продолжить эту работу (временные метки часто бывают разными и излишне не позволят компонентам diff быть идентичными)

  2. Я также удалил Only in ... строки из файла сравненияДля этой процедуры.

  3. Для работы на уровне ломаных мест перед сравнением мне пришлось поменять строки @@.В основном я удалил 2-ую часть строк, то есть изменил @@ -nnn,nn +mmm,mm @@ на @@ -nnn,nn @@.Обратите внимание на использование AB_components.mod по сравнению с AB_components выше. Это только для сравнения.Ловушки, которые идут в финальную разность, должны иметь правильные строки @@, в противном случае вместе сiffiff будет сообщать об ошибках

  4. Под «diff file» и «patch file» я имею в виду одно и то же.В этой работе я использовал исключительно унифицированный формат diff, т.е. diff -u

AB_components.mod был создан так:

$ cp -r AB_components{,.mod}
$ cd AB_components.mod
$ for f in `ls`; do sed -i -e 's/@@ \(.*\) \(.*\) @@$/@@ \1 @@/g' $f; done

РЕДАКТИРОВАТЬ 1: мне пришлось взятьследующий дополнительный шаг, чтобы исправить проблему с ошибочным кодом рубина (упомянутый в моем комментарии ниже):

$ cd ..; cp -r AB_components{,.mod2}; cd AB_components.mod2
$ for f in `ls`; do echo $f:`tail -1 $f`; done | grep ':diff ' | cut -d':' -f1 > ../bad_files
$ for f in `cat ../bad_files`; do head -n -1 ../AB_components/$f > $f; done
0 голосов
/ 04 февраля 2012

Я понятия не имею, как бы вы сделали это вручную, но вы могли бы взглянуть на концепцию трехстороннего слияния: http://en.wikipedia.org/wiki/Merge_(revision_control)#Three-way_merge

В какой-то замечательной системе управления ревизиями для слияния используется трехстороннее слияниеалгоритм (такой как Mercurial).Вы также можете найти автономные инструменты для трехстороннего слияния здесь: https://stackoverflow.com/questions/460198/best-free-3-way-merge-tool-for-windows

...