Porównaj pliki kodu źródłowego, ignorując różnice w formatowaniu (np. Białe znaki, podziały wierszy, ...)

9

Szukam aplikacji, która może porównać dwa źródła C ++ i znaleźć różnice znaczące dla kodu (aby porównać wersje, które mogły zostać sformatowane inaczej). Co najmniej coś, co może ignorować zmiany białych znaków, tabulatorów i znaków nowej linii, które nie wpływają na funkcjonalność źródła (zauważ, że to, czy nowa linia jest uważana za białe znaki, zależy od języka , a C i C ++ robią to ). I idealnie coś, co może dokładnie zidentyfikować wszystkie różnice znaczące dla kodu. Jestem pod Ubuntu.

Zgodnie z diff --help | grep ignoreoczekiwaniami, spodziewałem diff -bBwZsię wykonać rozsądnie robotę (spodziewałem się fałszywych negatywów, które zostaną rozwiązane później). Niemniej jednak nie.

jeśli mam następujące pliki z fragmentami

test_diff1.txt

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

i test_diff2.txt

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

następnie

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

zamiast pustych wyników.

Użycie formatera kodu jako „filtra” na obu wejściach może odfiltrować te różnice, ale wówczas wynikowy wynik musiałby zostać powiązany z oryginalnymi danymi wejściowymi w celu końcowego raportowania różnic, aby zachować rzeczywisty tekst i numery wierszy. Cel jest więc możliwy do osiągnięcia bez kompilatora poprawnie ... Nie wiem jednak, czy coś jest dostępne.

Czy można osiągnąć cel diff? W przeciwnym razie, czy istnieje alternatywa (najlepiej dla wiersza poleceń)?

sancho.s ReinstateMonicaCellio
źródło

Odpowiedzi:

6

Możesz użyć dwdiff. Od man dwdiff:

dwdiff - program rozdzielający słowa z ogranicznikami

Program jest bardzo sprytny - patrz 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

Przetestuj za pomocą:

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

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

Następnie uruchom porównanie:

$ 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

Uwaga 100% commonpowyżej.

N0rbert
źródło
1

Wątpię, że to coś, co diff może zrobić. Jeśli w linii występują zmiany przestrzeni, to będzie działać (lub inne podobne programy, takie jak kompare). Co gorsza, możesz wyszukiwać i zamieniać i zwijać znaki tabulacji itp. Ale to, o co prosisz o zmiany białych znaków poza linią ...

Potrzebny byłby program, który rozumie język C ++. Zauważ, że wszystkie języki są różne, a Python w szczególności używa białych znaków do definiowania bloków kodu. W związku z tym wątpię, aby jakikolwiek program podobny do diff działałby z „dowolnym” (lub konkretnym) językiem programowania.

Możesz rozważyć jakiś parser, aby przejrzeć dwa pliki źródłowe, a następnie porównać wyniki tego parsera.

To jest poza moim doświadczeniem, ale sugeruję zajrzeć do Lexa i Yacca . To są strony Wikipedii; możesz rzucić okiem na stronę, która zawiera zwięzłe wyjaśnienie i przykład.

Promień
źródło
Nie sądzę, że potrzebuję czegoś, co w szczególności rozumie C ++ (przynajmniej do ignorowania różnic spowodowanych przez nowe linie), nie muszę kompilować źródeł. Musi się odpowiednio różnicować, niezależnie od języka. W rzeczywistości istnieje inna odpowiedź, która sugeruje dwdiff. Nadal muszę to przetestować, ale podany przykład wygląda przekonująco.
sancho.s ReinstateMonicaCellio
Lex / Yacc nie kompiluje kodu źródłowego per se. Dzieliłoby to na tokeny. Na przykład, jeśli masz „int foo = 0” vs „int bar = 0”, wyraźnie foo i bar to dwa różne słowa; ale w kontekście programu są w rzeczywistości identyczne. Jeśli chcesz wyłapać podobieństwa takie jak ten, możesz potrzebować jakiegoś parsera. Jeśli nie, to rzeczywiście sugestia dwdiff wydaje się bardzo dobra. Powodzenia!
Ray
0

W podobnej sytuacji, gdy musiałem porównać dwie gitgałęzie w sposób agnostyczny formatujący kod, zrobiłem to:

  1. utworzono oddziały tymczasowe:

    $ git co feature-a
    $ git co -b 1
    $ git co feature-b
    $ git co -b 2
    
  2. sformatował obie gałęzie za pomocą 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. dokonał faktycznego porównania:

    $ git diff -w -b 1 2
    

    ( -w -bpozwala na wszelki wypadek zignorować różnicę miejsca).

Może wolisz uncrustifypowyżej clang-format( uncrustify„s mod_full_brace_ifmoże być używany w celu wymuszenia wstawiania / usuwanie nawiasów klamrowych wokół pojedynczej linii if” s ciała).

Ponadto, jeśli GNU parallelnie jest zainstalowane, użyj xargs- robi to samo, ale trochę dłużej.

Andrey Starodubtsev
źródło