Comparer les fichiers de code source, en ignorant les différences de formatage (comme les espaces blancs, les sauts de ligne,…)

9

Je recherche une application qui puisse comparer deux sources C ++ et trouver les différences significatives du code (pour comparer des versions qui ont pu être reformatées différemment). Au minimum, quelque chose qui a la capacité d'ignorer les changements dans les espaces blancs, les espaces de tabulation et les sauts de ligne qui n'affectent pas la fonctionnalité de la source (notez que si un saut de ligne est considéré comme un espace blanc dépend du langage , et C et C ++ le font ). Et, idéalement, quelque chose qui peut identifier exactement toutes les différences significatives du code. Je suis sous Ubuntu.

Selon diff --help | grep ignore, je m'attendais diff -bBwZà faire raisonnablement le travail (je m'attendais à obtenir des faux négatifs, pour être traités plus tard). Néanmoins, ce n'est pas le cas.

si j'ai les fichiers suivants avec des extraits

test_diff1.txt

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

et test_diff2.txt

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

puis

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

au lieu de résultats vides.

L'utilisation d'un formateur de code comme "filtre" sur les deux entrées peut filtrer ces différences, mais la sortie résultante devrait alors être liée aux entrées d'origine pour le rapport final des différences afin de conserver le texte et les numéros de ligne réels. Donc l'objectif est réalisable sans avoir besoin d'un compilateur correctement ... Je ne sais pas si quelque chose est disponible, cependant.

Peut-on atteindre l'objectif diff? Sinon, existe-t-il une alternative (de préférence, pour la ligne de commande)?

sancho.s ReinstateMonicaCellio
la source

Réponses:

6

Vous pouvez utiliser dwdiff. De man dwdiff:

dwdiff - un programme de diff mot délimité

Le programme est très intelligent - voir 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

Testez-le avec:

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

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

Lancez ensuite la comparaison:

$ 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

Veuillez noter 100% commonci-dessus.

N0rbert
la source
1

Je doute que c'est quelque chose que diff puisse faire. S'il y a des changements d'espace dans une ligne, cela fonctionnera (ou d'autres programmes similaires comme kompare). Au pire, vous pouvez faire une recherche et remplacement et réduire les caractères de tabulation, etc. Mais ce que vous demandez pour les espaces blancs change au-delà d'une ligne ...

Vous auriez besoin d'un programme qui comprend le langage C ++. Notez que tous les langages sont différents et Python, en particulier, utilise des espaces pour définir des blocs de code. En tant que tel, je doute qu'un programme général semblable à un diff fonctionnerait avec "n'importe quel" langage de programmation (ou un langage spécifique).

Vous pourriez envisager une sorte d'analyseur pour parcourir les deux fichiers source, puis comparer les sorties de cet analyseur.

Cela dépasse mes antécédents, mais je vous suggère de vous pencher sur Lex et Yacc . Ce sont des pages Wikipédia; vous voudrez peut-être jeter un œil à cette page qui donne une explication concise et un exemple.

Rayon
la source
Je ne pense pas avoir besoin de quelque chose qui comprenne le C ++ en particulier (au moins pour ignorer les différences dues aux sauts de ligne), je n'ai pas besoin de compiler les sources. Il suffit de différencier de manière appropriée, quelle que soit la langue. Il y a en fait une autre réponse qui suggère dwdiff. Je dois encore le tester, mais l'exemple fourni semble convaincant.
sancho.s ReinstateMonicaCellio
Lex / Yacc ne compile pas le code source en soi. Cela le séparerait en jetons. Par exemple, si vous aviez "int foo = 0" vs "int bar = 0", clairement foo et bar sont deux mots différents; mais dans le cadre d'un programme, ils sont en fait identiques. Si vous voulez attraper des similitudes comme celle-ci, vous aurez peut-être besoin d'une sorte d'analyseur. Si vous ne le faites pas, alors en effet, la suggestion dwdiff semble très bonne. Bonne chance!
Ray
0

Dans une situation similaire, lorsque j'ai eu besoin de comparer deux gitbranches de manière agnostique au formatage de code, j'ai fait ceci:

  1. créé des succursales temporaires:

    $ git co feature-a
    $ git co -b 1
    $ git co feature-b
    $ git co -b 2
    
  2. formaté les deux branches en utilisant 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. a fait une comparaison réelle:

    $ git diff -w -b 1 2
    

    ( -w -bvous permet d'ignorer la différence d'espace, juste au cas où).

Vous préférerez peut-être uncrustifyplus de clang-format(celui uncrustify-ci mod_full_brace_ifpeut être utilisé pour forcer l'insertion / suppression des accolades autour du corps d'une seule ligne if).

De plus, si GNU paralleln'est pas installé, utilisez xargs- il fait de même, mais un peu plus longtemps.

Andrey Starodubtsev
la source