提取两个补丁文件的公共子集

时间:2012-02-04 06:07:55

标签: version-control tree diff patch

我有三棵源树,A,B和C. A是原始树。 B和C是A的修改,由2个不同的开发人员创建。

我采用了A和B的差异,A和C。

但是两个差异文件中有许多常见的变化。含义A不是B和C的最新分歧点。相反,A被修改为D(我没有),B和C随后被修改为D.

我的问题是:我可以用两个差异文件(除了手工劳动)来提取它们的最大公共子集?以便我将该子集作为补丁应用于A以获得D

编辑1:插图:

A ---> D ---> B
        \---> C
编辑2:我看过patchutils工具,但没找到能满足我需求的工具。我也查看了this question但是那里提到的方法没有给出正确的输出。

2 个答案:

答案 0 :(得分:0)

我不知道你会如何手动完成这项工作,但你可以看一看三向合并概念:http://en.wikipedia.org/wiki/Merge_(revision_control)#Three-way_merge

一些出色的版本控制系统使用3向合并作为其合并算法(例如Mercurial)。您还可以在此处查找独立的3向合并工具:https://stackoverflow.com/questions/460198/best-free-3-way-merge-tool-for-windows

答案 1 :(得分:0)

好吧,伙计们,我提出了以下解决方案(不使用git,mercurial等)。 (免责声明:可能有拼写错误,可能需要更改才能在您身边工作)

基础方法/算法如下:

  1. 将两个差异文件拆分为较小的组件

  2. 将一个差异文件的组件与另一个差异文件的组件进行比较,并选择两者中相同的组件

  3. 加入所选组件以创建格式正确的新差异文件
  4. 我的每个diff文件都有文件级差异,每个文件级差异都有一个或多个帅哥。如果组件是指文件级组件,则可以使用patchutils工具“splitdiff”和“combinediff”完成提取,如下所示:

    $ # 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是不够的。我找到了一个工具here,它将文件拆分为单独的文件(我必须在该脚本中稍作修改才能使其在我的机器上运行...特别是我必须注释掉“require'file.rb' “线)。

    对于第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部分:我使用combinediff连接这些diff文件以创建一个最终的差异文件

    对于第3步第1部分,我创建了以下shell脚本(我们称之为combinehunks.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. 在继续这项工作之前,我必须从---+++标题行中删除时间戳(时间戳通常不同,并且会不必要地保持差异组件不相同)

    2. 我还在程序之前从diff文件中删除了Only in ...行。

    3. 对于大块级别的工作,我必须在比较之前更改@@行。基本上我删除了第2部分的行,即将@@ -nnn,nn +mmm,mm @@更改为@@ -nnn,nn @@。注意AB_components.mod与上面的AB_components的使用。这仅用于比较。进入最后差异的亨克必须有正确的@@线,否则combineiff将报告错误

    4. 'diff file'和'patch file'我的意思是一样的。在整个这项工作中,我使用统一的差异格式,即diff -u

    5. 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