Como produzir um diff sem linhas idênticas solitárias

3

Digamos que eu tenha reescrito uma função C do zero. Muitas vezes acontece que algumas linhas são idênticas antes e depois, em particular linhas em branco e chaves de fechamento. Quando eu crio um diff unificado (usando git diff ou simples GNU diff -u ), essas linhas idênticas dividem o pedaço, tornando o patch mais difícil de ler para o revisor. A ambição de Diff de produzir a mínima diferença às vezes sacrifica a legibilidade, o que não é o que eu quero. Existe uma maneira de fazer o diff sacrificar a minimalidade para manter os longos pedaços juntos?

Exemplo: Considere isso, produzido por diff:


--- A   2018-10-01 09:37:37.606642955 +0200
+++ B   2018-10-01 09:37:40.405675295 +0200
@@ -1,6 +1,9 @@
 int fib(int n) {
-  if (n <= 1) {
-    return n;
+  int i, t1 = 0, t2 = 1;
+  for (i = 0; i < n; ++i) {
+    int next = t1 + t2;
+    t1 = t2;
+    t2 = next;
   }
-  return fib(n-1) + fib(n-2);
+  return t1;
 }
Na minha opinião, o seguinte patch equivalente teria sido mais fácil de ler, ao custo de apenas uma linha extra:

--- A   2018-10-01 09:37:37.606642955 +0200
+++ B   2018-10-01 09:37:40.405675295 +0200
@@ -1,6 +1,9 @@
 int fib(int n) {
-  if (n <= 1) {
-    return n;
-  }
-  return fib(n-1) + fib(n-2);
+  int i, t1 = 0, t2 = 1;
+  for (i = 0; i < n; ++i) {
+    int next = t1 + t2;
+    t1 = t2;
+    t2 = next;
+  }
+  return t1;
 }

Assim, uma heurística possível seria "se houver uma linha idêntica, e as linhas tiverem sido adicionadas e removidas antes e depois da linha, então considere a linha idêntica a ser alterada". Eu posso, claro, criar meu próprio script que aplica essa regra em um patch existente, mas existe uma ferramenta existente para resolver o meu problema?

    
por Erik Carstensen 01.10.2018 / 10:18

2 respostas

3

Você poderia implementar sua abordagem canalizando a saída diff -u para:

perl -0777 -pe '1 while s{^-.*\n\K((?:\+.*\n)+) ((.*\n)(?:-.*\n)+)}
                         {-$2$1+$3}mg'

Agora não estou convencido de que não mostrar linhas comuns solitárias sempre ajuda na legibilidade. Compare por exemplo:

--- A   2018-10-01 09:37:37.606642955 +0200
+++ B   2018-10-01 09:37:40.405675295 +0200
@@ -1,8 +1,11 @@
 int fib(int n) {
-  if (n <= 1) {
-    return n;
+  int i, t1 = 0, t2 = 1;
+  for (i = 0; i < n; ++i) {
+    int next = t1 + t2;
+    t1 = t2;
+    t2 = next;
   }
-  /* assinging foo */
+  /* assigning foo */
   foo = (bar ? bar : complicated_stuff(*a.asd.qwe|(FLAG1|FLAG2)));
-  return fib(n-1) + fib(n-2);
+  return t1;

com a saída do código perl :

--- A   2018-10-01 09:37:37.606642955 +0200
+++ B   2018-10-01 09:37:40.405675295 +0200
@@ -1,8 +1,11 @@
 int fib(int n) {
-  if (n <= 1) {
-    return n;
-  }
-  /* assinging foo */
-  foo = (bar ? bar : complicated_stuff(*a.asd.qwe|(FLAG1|FLAG2)));
-  return fib(n-1) + fib(n-2);
+  int i, t1 = 0, t2 = 1;
+  for (i = 0; i < n; ++i) {
+    int next = t1 + t2;
+    t1 = t2;
+    t2 = next;
+  }
+  /* assigning foo */
+  foo = (bar ? bar : complicated_stuff(*a.asd.qwe|(FLAG1|FLAG2)));
+  return t1;

Acho mais útil que diff mostre que essa linha complicada está inalterada. Na versão expandida, você seria tentado a procurar a diferença entre os dois.

Você pode substituir o ((.*\n) por ((.{0,20}\n) a considerar somente linhas comuns curtas e solitárias (ou (((?:\h*\H){0,10}\h*\n) para considerar apenas o número de não espaços em branco).

    
por 01.10.2018 / 11:35
0

O script a seguir melhora minha situação, detectando sequências "quase completamente diferentes" em um diff e considerando-as completamente diferentes. Procurando por melhores soluções.

#!/usr/bin/python
import sys
(mbefore, pbefore, mid, mafter) = ([], [], [], [])
def flush():
    for x in (mbefore, pbefore, mid, mafter):
        sys.stdout.write(''.join(x))
        del x[:]

for line in sys.stdin:
    if line[0] == '-':
        if mid:
            mafter.append(line)
        else:
            flush()
            mbefore.append(line)
    elif line[0] == '+':
        if mafter:
            mbefore.append('-' + mid[0][1:])
            pbefore.append('+' + mid[0][1:])
            mbefore += mafter
            pbefore.append(line)
            del mafter[:]
            del mid[:]
        elif not mid and mbefore:
            pbefore.append(line)
        else:
            flush()
            sys.stdout.write(line)
    elif line[0] == ' ':
        if pbefore and not mid:
            mid.append(line)
        else:
            flush()
            sys.stdout.write(line)
    else:
        flush()
        sys.stdout.write(line)
flush()
    
por 01.10.2018 / 10:41

Tags