如何替换重复的多行组,例如从 git 和 diff3

如何替换重复的多行组,例如从 git 和 diff3

我正在重新调整遗留代码的基础,并发现许多由脚本(通常是代码格式化程序)引起的冲突。这些更改很简单且可预测,因此我可以轻松地重新运行脚本以将更改应用到代码中——这通常会给我留下三个相同的“片段”,周围环绕着 git 的 diff 标记。

如何编写脚本来查找三个匹配的片段及其差异标记,并将它们转换为单个片段?

例子:

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

最初我们处于状态 0,因为如果尚未初始化state == 0则为 true 。state我们在看到时切换到状态 1 <<<<<<,在看到时切换到状态 2 =======,然后在看到结束标记时返回到状态 0 >>>>>>>。很简单。

仅当处于初始状态 0(在任何冲突标记之外)或状态 2(冲突标记的最后部分)时,我们才打印行。

所有情况下的命令next都确保我们不会出现失败行为。只有最后一个案例打印任何内容,因此我们知道前三个案例除了识别冲突标记和更改状态之外什么也不做,而不打印任何内容。因此,无论我们处于什么状态,都不会打印冲突标记;我们知道,在状态 2 和返回到状态 0 时,我们不会意外打印=======or>>>>>>>行。

在第一个操作中,并不是next绝对必要的,因为它会转换为非打印状态 1,但包含它对于保持一致性很有好处。另外,效率:冲突标记是相互排斥的;=======如果您已检测到,为什么要测试<<<<<<<.

答案2

TXRLisp 的awk宏可以在没有状态变量的情况下解决这个问题,因为它比传统的 Awk 具有更强大的范围表达式:

  • 范围表达式可以与其他表达式组合。例如,范围表达式可以是另一个范围的开始或结束;

  • 范围表达式可以在宏中的任何地方使用awk,而不仅仅是作为主子句条件;和

  • 范围表达式进来九种不同口味用于排除开头和结尾的记录,或扩展范围以包含更多记录。

使用data与 Awk 答案相同的文件:

$ 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。我们引入变量begmidend,其值是单参数匿名函数。例如(f^ /abc/)产生一个函数,它接受一个字符串,并匹配正则表达式abc,锚定到该字符串的开头。我们正在定义用于匹配 diff3 冲突标记的开始、中间和结束的函数。

在 后面:let,我们有两个(condition action)子句。两个子句都没有action,因此默认操作是打印。这是 Awk 的一种形式,因此适用相同的规则。

该范围匹配从标记到 的(-rng- mid end)记录。该变体仅对于该范围的内部记录产生 true;它对于第一个和最后一个记录产生 false。这正是我们想要的:我们想要打印和之间的所有行,但不包括这些行。 awk 没有这样的功能来从范围中排除端点。midend-rng-======>>>>>>>

第二条规则的条件是(not (rng beg end))。对于不在begtoend范围内的记录,此条件为 true;即,不在 diff3 冲突注释内。我们想打印所有这些。 awk 也没有这样的功能:范围表达式不能与任何其他运算符(例如!求反)组合。

由于 TXR Lisp 中的范围功能awk,此解决方案中不需要状态变量,并且没有规则必须显式分支出隐式循环以继续处理下一条记录。

相关内容