Compare arquivos de código-fonte, ignorando as diferenças de formatação (como espaço em branco, quebra de linha,…)

9

Estou procurando um aplicativo que possa comparar duas fontes C ++ e encontrar as diferenças significativas de código (para comparar versões que podem ter sido reformatadas de maneira diferente). No mínimo, algo que tenha a capacidade de ignorar alterações em espaços em branco, espaços de tabulação e novas linhas que não afetem a funcionalidade da fonte (observe que se uma nova linha é considerada espaço em branco depende do idioma , e C e C ++ o fazem ) E, idealmente, algo que possa identificar exatamente todas as diferenças significativas de código. Estou no Ubuntu.

De acordo com diff --help | grep ignore, eu esperava diff -bBwZfazer o trabalho razoavelmente (esperava obter alguns negativos negativos, para ser tratado mais tarde). No entanto, não.

se eu tiver os seguintes arquivos com trechos

test_diff1.txt

    else if (prop == "P1") { return 0; }

e test_diff2.txt

    else if (prop == "P1") {
        return 0;
    }

então

$ diff -bBwZ test_diff1.txt test_diff2.txt
1c1,3
<     else if (prop == "P1") { return 0; }
---
>     else if (prop == "P1") {
>         return 0;
>     }

em vez de resultados vazios.

O uso de um formatador de código como um "filtro" em ambas as entradas pode filtrar essas diferenças, mas a saída resultante teria que ser vinculada às entradas originais para o relatório final das diferenças para manter o texto e os números de linha reais. Portanto, o objetivo é atingível sem a necessidade de um compilador corretamente ... Não sei se há algo disponível.

O objetivo pode ser alcançado diff? Caso contrário, existe uma alternativa (preferencialmente, para linha de comando)?

sancho.s ReinstateMonicaCellio
fonte

Respostas:

6

Você pode usar dwdiff. De man dwdiff:

dwdiff - um programa diff de palavras delimitado

O programa é muito inteligente - veja dwdiff --help:

$ dwdiff --help
Usage: dwdiff [OPTIONS] <OLD FILE> <NEW FILE>
-h, --help                             Print this help message
-v, --version                          Print version and copyright information
-d <delim>, --delimiters=<delim>       Specify delimiters
-P, --punctuation                      Use punctuation characters as delimiters
-W <ws>, --white-space=<ws>            Specify whitespace characters
-u, --diff-input                       Read the input as the output from diff
-S[<marker>], --paragraph-separator[=<marker>]  Show inserted or deleted blocks
                               of empty lines, optionally overriding the marker
-1, --no-deleted                       Do not print deleted words
-2, --no-inserted                      Do not print inserted words
-3, --no-common                        Do not print common words
-L[<width>], --line-numbers[<width>]   Prepend line numbers
-C<num>, --context=<num>               Show <num> lines of context
-s, --statistics                       Print statistics when done
--wdiff-output                         Produce wdiff compatible output
-i, --ignore-case                      Ignore differences in case
-I, --ignore-formatting                Ignore formatting differences
-m <num>, --match-context=<num>        Use <num> words of context for matching
--aggregate-changes                    Allow close changes to aggregate
-A <alg>, --algorithm=<alg>            Choose algorithm: best, normal, fast
-c[<spec>], --color[=<spec>]           Color mode
-l, --less-mode                        As -p but also overstrike whitespace
-p, --printer                          Use overstriking and bold text
-w <string>, --start-delete=<string>   String to mark begin of deleted text
-x <string>, --stop-delete=<string>    String to mark end of deleted text
-y <string>, --start-insert=<string>   String to mark begin of inserted text
-z <string>, --stop-insert=<string>    String to mark end of inserted text
-R, --repeat-markers                   Repeat markers at newlines
--profile=<name>                       Use profile <name>
--no-profile                           Disable profile reading

Teste com:

cat << EOF > test_diff1.txt
    else if (prop == "P1") { return 0; }
EOF

cat << EOF > test_diff2.txt
    else if (prop == "P1") {
        return 0;
    }
EOF

Em seguida, inicie a comparação:

$ dwdiff test_diff1.txt test_diff2.txt --statistics
    else if (prop == "P1") {
        return 0;
    }
old: 9 words  9 100% common  0 0% deleted  0 0% changed
new: 9 words  9 100% common  0 0% inserted  0 0% changed

Observe 100% commonacima.

N0rbert
fonte
1

Duvido que isso seja algo que o diff possa fazer. Se houver mudanças de espaço dentro de uma linha, ele funcionará (ou outros programas similares, como o kompare). Na pior das hipóteses, você pode fazer uma pesquisa, substituir e recolher caracteres de tabulação, etc. Mas o que você está pedindo para alterar o espaço em branco além de uma linha

Você precisaria de um programa que entenda a linguagem C ++. Observe que todas as linguagens são diferentes e o Python, em particular, usa espaço em branco para definir blocos de código. Como tal, duvido que qualquer programa semelhante a diff funcione com "qualquer" (ou uma linguagem de programação específica).

Você pode considerar algum tipo de analisador para percorrer os dois arquivos de origem e comparar as saídas desse analisador.

Isso está além do meu histórico, mas sugiro que você examine Lex e Yacc . Estas são páginas da Wikipedia; você pode dar uma olhada nesta página, que fornece uma explicação concisa e um exemplo.

Raio
fonte
Eu não acho que preciso de algo que entenda C ++ em particular (pelo menos para ignorar diferenças devido a novas linhas), não preciso compilar as fontes. Ele só precisa diferir adequadamente, independentemente do idioma. Na verdade, há outra resposta que sugere dwdiff. Ainda temos que testá-lo, mas o exemplo fornecido parece convincente.
Sancho.s ReinstateMonicaCellio
Lex / Yacc não compila o código fonte, por si só. Isso o separaria em tokens. Por exemplo, se você tivesse "int foo = 0" vs "int bar = 0", claramente foo e bar são duas palavras diferentes; mas no contexto de um programa, eles são realmente idênticos. Se você quiser capturar semelhanças como essa, poderá precisar de algum tipo de analisador. Se não, então, de fato, a sugestão dwdiff parece muito boa. Boa sorte!
Raio
0

Em situação semelhante, quando precisei comparar duas gitramificações de forma independente de formatação de código, fiz o seguinte:

  1. ramificações temporárias criadas:

    $ git co feature-a
    $ git co -b 1
    $ git co feature-b
    $ git co -b 2
    
  2. formatou os dois ramos usando clang-format:

    $ git co 1
    $ find . -name '*.cpp' -print0 | parallel -0 -n 1 clang-format -i -style=google
    $ git ci -a -m1 --no-verify
    $ git co 2
    $ find . -name '*.cpp' -print0 | parallel -0 -n 1 clang-format -i -style=google
    $ git ci -a -m2 --no-verify
    
  3. fez comparação real:

    $ git diff -w -b 1 2
    

    ( -w -bpermite ignorar a diferença de espaço, apenas por precaução).

Pode preferir uncrustifysobre clang-format( uncrustify's mod_full_brace_ifpodem ser utilizados para aplicar a inserção / remoção de chavetas ao redor de uma única linha ifde corpo).

Além disso, se o GNU parallelnão estiver instalado, use xargs- ele faz o mesmo, mas um pouco mais.

Andrey Starodubtsev
fonte