git-diff à ignorer ^ M

474

Dans un projet où certains fichiers contiennent ^ M comme séparateurs de nouvelle ligne. Différer ces fichiers est apparemment impossible, car git-diff le voit car le fichier entier n'est qu'une seule ligne.

En quoi diffère-t-on de la version précédente?

Existe-t-il une option comme "traiter ^ M comme un saut de ligne en cas de différence"?

prompt> git-diff "HEAD^" -- MyFile.as 
diff --git a/myproject/MyFile.as b/myproject/MyFile.as
index be78321..a393ba3 100644
--- a/myproject/MyFile.cpp
+++ b/myproject/MyFile.cpp
@@ -1 +1 @@
-<U+FEFF>import flash.events.MouseEvent;^Mimport mx.controls.*;^Mimport mx.utils.Delegate
\ No newline at end of file
+<U+FEFF>import flash.events.MouseEvent;^Mimport mx.controls.*;^Mimport mx.utils.Delegate
\ No newline at end of file
prompt>

MISE À JOUR:

maintenant j'ai écrit un script Ruby qui vérifie les 10 dernières révisions et convertit CR en LF.

require 'fileutils'

if ARGV.size != 3
  puts "a git-path must be provided"
  puts "a filename must be provided"
  puts "a result-dir must be provided"
  puts "example:"
  puts "ruby gitcrdiff.rb project/dir1/dir2/dir3/ SomeFile.cpp tmp_somefile"
  exit(1)
end

gitpath = ARGV[0]
filename = ARGV[1]
resultdir = ARGV[2]

unless FileTest.exist?(".git")
  puts "this command must be run in the same dir as where .git resides"
  exit(1)
end

if FileTest.exist?(resultdir)
  puts "the result dir must not exist"
  exit(1)
end
FileUtils.mkdir(resultdir)

10.times do |i|
  revision = "^" * i
  cmd = "git show HEAD#{revision}:#{gitpath}#{filename} | tr '\\r' '\\n' > #{resultdir}/#{filename}_rev#{i}"
  puts cmd 
  system cmd
end
neoneye
la source
7
vous avez peut-être voulu git diff -b- je l'ai montré dans stackoverflow.com/a/46265081/58794
Jason Pyeron
6
Avec Git 2.16 (Q1 2018), vous en aurez git diff --ignore-cr-at-eol. Voir ma réponse ci-dessous .
VonC
7
@JasonPyeron et pour les futurs Googleurs: j'ai dû chercher qui git diff -best identique à git diff --ignore-space-change.
Gogowitsch

Réponses:

392

GitHub suggère que vous devez vous assurer d'utiliser uniquement \ n comme caractère de nouvelle ligne dans les dépôts git. Il existe une option de conversion automatique:

$ git config --global core.autocrlf true

Bien sûr, cela est censé convertir crlf en lf, tandis que vous voulez convertir cr en lf. J'espère que cela fonctionne toujours…

Et puis convertissez vos fichiers:

# Remove everything from the index
$ git rm --cached -r .

# Re-add all the deleted files to the index
# You should get lots of messages like: "warning: CRLF will be replaced by LF in <file>."
$ git diff --cached --name-only -z | xargs -0 git add

# Commit
$ git commit -m "Fix CRLF"

core.autocrlf est décrit sur la page de manuel .

nes1983
la source
1
Non, bien sûr que non, une fois que le paramètre est là, il se convertit en silence lors de la validation. Si tout fonctionne comme je le pense, c'est…
nes1983
1
Le problème est que j'ai déjà certains fichiers dans le référentiel qui ont des terminaisons CRLF et d'autres qui n'en ont pas. Je soupçonne qu'Adobe Flash ajoute CRLF même si j'utilise la version Mac. J'ai besoin de comparer avec les anciennes révisions de ces fichiers. La conversion des fins de ligne à partir de maintenant ne résout pas le problème avec les anciennes révisions: - /
neoneye
65
Vous ne travaillez pas avec les fichiers CRLF ici, du moins pas dans l'exemple que vous avez publié. C'est un fichier mac à l'ancienne (utilise simplement \ r pour EOL). C'est pourquoi le diff est affiché sur une seule ligne. Un fichier utilisant dos EOL montrerait chaque ligne distinctement avec un ^ M de fin, que vous pourriez dire à gérer via git config core.whitespace cr-at-eol.
jamessan
12
Je suis en train, mais je continue à recevoir au warning: LF will be replaced by CRLFlieu de warning: CRLF will be replaced by LF, et je suis sous Linux. Une idée pourquoi? Je veux que tout se termine avec LF, pas CRLF!
trusktr
5
@trusktr, il m'est arrivé la même chose. Sous Linux, avec CRLF accidentel, utilisez git config --global core.autocrlf input, suivez les étapes de cette réponse (rm, add, commit), et vous obtiendrez warning: CRLF will be replaced by LF. The file will have its original line endings in your working directory.. Supprimez les fichiers (car ils contiennent le CRLF incorrect d'origine) et extrayez-les à nouveau du dernier commit "Fix CRLF".
jmmut
370

En développant sur Windows, j'ai rencontré ce problème lors de l'utilisation git tfs. Je l'ai résolu de cette façon:

git config --global core.whitespace cr-at-eol

Cela indique essentiellement à Git qu'un CR de fin de ligne n'est pas une erreur. En conséquence, ces ennuyeux ^Mpersonnages ne semblent plus à la fin des lignes git diff, git showetc.

Il semble laisser les autres paramètres tels quels; par exemple, les espaces supplémentaires à la fin d'une ligne apparaissent toujours comme des erreurs (surlignées en rouge) dans le diff.

(D'autres réponses ont fait allusion à cela, mais ce qui précède est exactement comment définir le paramètre. Pour définir le paramètre pour un seul projet, omettez le --global.)

MODIFIER :

Après de nombreux essais de fin de ligne, j'ai eu la meilleure chance, lorsque je travaillais sur une équipe .NET, avec ces paramètres:

  • AUCUN paramètre core.eol
  • AUCUN paramètre core.whitespace
  • AUCUN paramètre core.autocrlf
  • Lorsque vous exécutez le programme d'installation de Git pour Windows, vous obtiendrez ces trois options:
    • Extraire le style Windows, valider les fins de ligne de style Unix <- choisissez celui-ci
    • Commander en l'état, valider les fins de ligne de style Unix
    • Commander tel quel, valider tel quel

Si vous devez utiliser le paramètre d'espaces, vous ne devriez probablement l'activer que par projet si vous avez besoin d'interagir avec TFS. Oubliez simplement --global:

git config core.whitespace cr-at-eol

Si vous devez supprimer certains paramètres de base. *, Le moyen le plus simple consiste à exécuter cette commande:

git config --global -e

Cela ouvre votre fichier global .gitconfig dans un éditeur de texte et vous pouvez facilement supprimer les lignes que vous souhaitez supprimer. (Ou vous pouvez mettre «#» devant eux pour les commenter.)

Ryan Lundy
la source
30
Pour ceux qui trouvent cela maintenant, il convient de noter que Commander style Windows, commit de fin de ligne de style Unix auto-ensembles core.autocrlfàtrue
Carpenter K.
14
Notez que la ligne git config --global core.whitespace cr-at-eoldésactiverait les autres paramètres par défaut. Il y a trois valeurs par défaut: blank-at-eol, blank-at-eof et space-before-tab. Donc, pour activer cr-at-eol tout en gardant les autres que vous devez utiliser git config --global core.whitespace blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol.
Zitrax
2
Pour mon projet (c'était le checkout sur Windows et je le vois sur Linux), je me cr-at-eolsuis débarrassé de ^Mla fin des lignes git diff, mais GIT a toujours montré ces lignes comme différentes, bien que la fin de la ligne soit la seule différence.
Jānis Elmeris
SourceInsight continue d'appuyer sur le caractère ^ M, et git montre toujours la différence aux fins de ligne. La commande de @ Zitrax est la bonne réponse à mon cas, git diff affiche une sortie agréable et propre.
Lê Quang Duy
3
Je pense que git a besoin d'un peu plus de complexité, de quelques paramètres plus conflictuels pour la fin de ligne. Je pense que git devrait être plus préoccupé par mes espaces blancs. Par exemple, lancez une erreur fatale non liée et laissez le référentiel dans un état corrompu lorsque vous rencontrez des fins de ligne Mac sur une machine Windows (mais pas Linux). Je veux dire pourquoi devrais-je utiliser un VCS qui dérangerait ses affaires et me laisser utiliser les terminaisons de ligne que je veux? Je vois qu'ils essaient, mais ils devraient ajouter une demi-douzaine de comportements de fin de ligne supplémentaires, pour résoudre un problème qui n'existe pas. Ils sont presque là! Continuez.
Rolf
125

Essayez git diff --ignore-space-at-eol, ou git diff --ignore-space-change, ou git diff --ignore-all-space.

Jakub Narębski
la source
22
Rien de tout cela n'affecte vraiment le caractère qui identifie la nouvelle ligne.
nes1983
4
J'ai également essayé avec "-w" mais pas de chance, le traite toujours comme une seule ligne. Prochain projet, je dois me rappeler de ne jamais mettre de CR dans le code source.
neoneye
3
Souvenez-vous juste de git config --global core.autocrlf true, ou boguez les gens de git jusqu'à ce qu'ils
deviennent
10
Cela a résolu mon problème sans avoir à modifier mes autocrlfparamètres. Merci!
nneonneo
11
ces drapeaux n'ont aucun effet pour moi ... montre toujours ^ M comme diffs
Magnus
103

Regarde aussi:

core.whitespace = cr-at-eol

ou équivalent,

[core]
    whitespace = cr-at-eol

whitespaceest précédé d'un caractère de tabulation .

Vladimir Panteleev
la source
4
Oui, cela a fait que l'outil git diff (également utilisé dans git show) a cessé de m'embêter sur les ^Ms sur les lignes modifiées! :)
Rijk
2
pour une raison quelconque, cela n'a pas fonctionné pour moi. Je l'ai essayé avec les signes = et no =. git diffaffiche toujours ^ M caractères.
Dennis
6
Deux façons de procéder: premièrement, ajoutez la ligne ci-dessus textuellement à votre .gitconfig soit dans .git / config, soit dans ~ / .gitconfig; deux, git config --global core.whitespace cr-at-eol(où --global est facultatif si vous le voulez juste sur le dépôt sur lequel vous êtes)
K. Carpenter
Cela a fonctionné pour moi sur Windows 7, même si je l'ai simplement mis sous [core]pour que je puisse remplacer le core.préfixe par un caractère TAB.
Rufflewind
Cette question était au- dessus comment cacher ^Mdans git diff, et non pas sur la façon de ne pas mettre en ^ M en premier lieu. Cela signifie que la réponse acceptée de modification core.autocrlfn'est pas la meilleure car elle modifie silencieusement les fichiers sans confirmation de l'utilisateur.
deddebme
45

Pourquoi les mettez-vous ^Mdans votre git diff?

Dans mon cas, je travaillais sur un projet développé sous Windows et j'ai utilisé OS X. Quand j'ai changé du code, j'ai vu ^Mà la fin des lignes que j'avais ajoutées git diff. Je pense que les ^Mapparaissaient parce que leurs fins de ligne étaient différentes de celles du reste du fichier. Étant donné que le reste du fichier a été développé sous Windows, il a utilisé des CRfins de ligne et sous OS X, il utilise des LFfins de ligne.

Apparemment, le développeur Windows n'a pas utilisé l'option " Extraire le style Windows, valider les fins de ligne de style Unix " lors de l'installation de Git.

Alors, que devons-nous faire à ce sujet?

Vous pouvez demander aux utilisateurs de Windows de réinstaller git et d'utiliser l' option " Extraire le style Windows, valider les fins de ligne de style Unix ". C'est ce que je préférerais, car je vois Windows comme une exception dans ses caractères de fin de ligne et Windows corrige son propre problème de cette façon.

Si vous optez pour cette option, vous devez cependant corriger les fichiers actuels (car ils utilisent toujours les CRfins de ligne). Je l'ai fait en suivant ces étapes:

  1. Supprimez tous les fichiers du référentiel, mais pas de votre système de fichiers.

    git rm --cached -r .
    
  2. Ajoutez un .gitattributesfichier qui force certains fichiers à utiliser une LFfin de ligne as. Mettez ceci dans le fichier:

    *.ext text eol=crlf
    

    Remplacez .extpar les extensions de fichier que vous souhaitez faire correspondre.

  3. Ajoutez à nouveau tous les fichiers.

    git add .
    

    Cela affichera des messages comme celui-ci:

    warning: CRLF will be replaced by LF in <filename>.
    The file will have its original line endings in your working directory.
    
  4. Vous pouvez supprimer le .gitattributesfichier sauf si vous avez des utilisateurs Windows tenaces qui ne veulent pas utiliser l' option " Extraire le style Windows, valider les fins de ligne de style Unix ".

  5. Engagez-vous et poussez tout.

  6. Supprimez et extrayez les fichiers applicables sur tous les systèmes où ils sont utilisés. Sur les systèmes Windows, assurez-vous qu'ils utilisent désormais l' option " Extraire le style Windows, valider les fins de ligne de style Unix ". Vous devez également le faire sur le système sur lequel vous avez exécuté ces tâches, car lorsque vous avez ajouté les fichiers, git a déclaré:

    The file will have its original line endings in your working directory.
    

    Vous pouvez faire quelque chose comme ceci pour supprimer les fichiers:

    git ls | grep ".ext$" | xargs rm -f
    

    Et puis ceci pour les récupérer avec les terminaisons de ligne correctes:

    git ls | grep ".ext$" | xargs git checkout
    

    Bien sûr, remplacer .extpar l'extension que vous souhaitez.

Maintenant, votre projet utilise uniquement des LFcaractères pour les fins de ligne, et les méchants CRcaractères ne reviendront jamais :).

L'autre option consiste à appliquer les fins de ligne de style Windows. Vous pouvez également utiliser le .gitattributesfichier pour cela.

Plus d'informations: https://help.github.com/articles/dealing-with-line-endings/#platform-all

gitaarik
la source
4
Pour corriger toutes les fins de ligne dans un fichier spécifique, si vous utilisez Sublime Text, vous pouvez aller dans View-> Line Endingset cliquer sur Unix.
Topher Hunt
Qu'est-ce que cela ^Msignifie exactement ? Est-ce une nouvelle ligne Windows ou Linux? Ou s'agit-il simplement d'une nouvelle ligne "différente" par rapport aux autres nouvelles lignes du fichier?
buhtz
Bon, je pense que c'est juste une nouvelle ligne "différente" (différente de la plupart des autres)
gitaarik
-1 car réinstaller git git config --global core.autocrlf trueest exagéré, et l'anti- CRcampagne / anti- campagne semble tangentielle à la question.
RJFalconer
41

Existe-t-il une option comme "traiter ^ M comme un saut de ligne en cas de différence"?

Il y en aura une avec Git 2.16 (T1 2018), car la difffamille de commandes " " a appris à ignorer les différences de retour chariot à la fin de la ligne.

Voir commit e9282f0 (26 octobre 2017) de Junio ​​C Hamano ( gitster) .
Aidé par: Johannes Schindelin ( dscho) .
(Fusionné par Junio ​​C Hamano - gitster- dans commit 10f65c2 , 27 nov.2017 )

diff: --ignore-cr-at-eol

Une nouvelle option --ignore-cr-at-eolindique à la machine diff de traiter un retour chariot à la fin d'une ligne (complète) comme s'il n'existait pas.

Tout comme les autres --ignore-*options " " pour ignorer les différents types de différences d'espaces, cela vous aidera à revoir les modifications réelles que vous avez apportées sans vous laisser distraire par une CRLF<->LFconversion parasite effectuée par votre programme d'édition.

VonC
la source
@kaartic Merci d'avoir édité la réponse et référencé le bon commit!
VonC
3
Bien qu'il soit généralement bon de définir git config --global core.autocrlf truecomme dans la réponse acceptée, cela répond plus directement à la question du PO: «Existe-t-il une option comme« traiter ^ M comme une nouvelle ligne en cas de divergence »?
drkvogel
1
Depuis Git 2.20, cela ne cache pas ^ M's
user1944491
@ user1944491 Je n'ai remarqué aucune régression, ce qui signifie qu'il ignore toujours eol quand il diffère avec cette option dans Git 2.26.
VonC
@VonC L'utilisation de cet argument dans la commande git diff n'a pas fonctionné. La définition de ma valeur core.whitespace n'a pas été activée, git version 2.20.1 (Apple Git-117)mais l'ajout de la réponse core.pager de Jason Pyeron l'a corrigé. YMMV évidemment.
user1944491
26

TL; DR

Changez le core.pageren "tr -d '\r' | less -REX", pas le code source

C'est pourquoi

Ces embêtants ^ M illustrés sont un artefact de la colorisation et du téléavertisseur. entrez la description de l'image ici Elle est causée par less -Rune option de pager git par défaut. (le pager par défaut de git est less -REX)

La première chose à noter est que git diff -bcela ne montrera pas les changements dans les espaces blancs (par exemple le \ r \ n vs \ n)

installer:

git clone https://github.com/CipherShed/CipherShed
cd CipherShed

Un test rapide pour créer un fichier Unix et modifier les fins de ligne ne montrera aucun changement avec git diff -b:

echo -e 'The quick brown fox\njumped over the lazy\ndogs.' > test.txt
git add test.txt
unix2dos.exe test.txt
git diff -b test.txt

Nous notons que forcer un tuyau à moins n'affiche pas le ^ M, mais active la couleur et less -Rfait:

git diff origin/v0.7.4.0 origin/v0.7.4.1 | less
git -c color.ui=always diff origin/v0.7.4.0 origin/v0.7.4.1 | less -R

Le correctif est indiqué en utilisant un tuyau pour supprimer le \ r (^ M) de la sortie:

git diff origin/v0.7.4.0 origin/v0.7.4.1
git -c core.pager="tr -d '\r' | less -REX"  diff origin/v0.7.4.0 origin/v0.7.4.1

Une alternative imprudente consiste à l'utiliser less -r, car elle passera par tous les codes de contrôle, pas seulement les codes de couleur.

Si vous souhaitez simplement modifier directement votre fichier de configuration git, voici l'entrée à mettre à jour / ajouter:

[core]
        pager = tr -d '\\r' | less -REX
Jason Pyeron
la source
J'ai eu ce problème dans un référentiel où certains fichiers avaient des \r\nfins de ligne et certains avaient des \nfins de ligne (je ne sais pas si c'est pertinent); diffs du premier a montré le ^Mdans les lignes modifiées (c'est-à-dire les +lignes). core.autocrlfa été défini sur true. La course à pied git config core.pager "tr -d '\r' | less -REX"s'est débarrassée des embêtants ^M. Merci!
labreuer
5
Merci pour cela. C'est la seule réponse si vous devez travailler avec des fins de ligne différentes dans vos référentiels - par exemple, vous utilisez la caisse en l'état, vous vous engagez telle quelle, à dessein.
Mike
git diff -best ce que je cherchais, mais j'apprécie l'explication approfondie.
Martin Burch
Voilà la réponse! Je vous remercie. le drapeau -b n'a pas fonctionné pour moi.
Chris
Oui! De toutes les réponses à cette question, la modification de la [core]section du fichier git "config" en ajoutant pager = tr -d '\\r' | less -REXétait la seule réponse qui a fonctionné pour moi. Je vous remercie!
Rashiki
13

J'ai lutté avec ce problème pendant longtemps. De loin, la solution la plus simple est de ne pas se soucier des caractères ^ M et d'utiliser simplement un outil de diff visuel qui peut les gérer.

Au lieu de taper:

git diff <commitHash> <filename>

essayer:

git difftool <commitHash> <filename>
Ian Wojtowicz
la source
1
Merci! De plus, je viens de lancer "git difftool" et il a essentiellement comparé tous les fichiers modifiés dans une boucle
Bhanuprakash D
2

Comme indiqué par VonC, cela a déjà été inclus dans git 2.16+. Malheureusement, le nom de l'option ( --ignore-cr-at-eol) diffère de celui utilisé par GNU diff auquel je suis habitué ( --strip-trailing-cr).

Lorsque j'ai été confronté à ce problème, ma solution était d'appeler GNU diff au lieu du diff intégré de git, car mon git est plus ancien que 2.16. Je l'ai fait en utilisant cette ligne de commande:

GIT_EXTERNAL_DIFF='diff -u --strip-trailing-cr "$2" "$5";true;#' git diff --ext-diff

Cela permet d'utiliser --strip-trailing-cret toutes les autres options de diff GNU.

Il y a aussi cette autre façon:

git difftool -y -x 'diff -u --strip-trailing-cr'

mais il n'utilise pas les paramètres de pager configurés, c'est pourquoi je préfère le premier.

Pedro Gimeno
la source
Alternative intéressante à ma réponse. A voté.
VonC