Essayer de corriger les fins de ligne avec git filter-branch, mais sans succès

270

J'ai été mordu par le problème de fin de ligne Windows / Linux avec git. Il semble, via GitHub, MSysGit et d'autres sources, que la meilleure solution est d'avoir votre dépôt local défini pour utiliser des fins de ligne de style linux, mais défini core.autocrlfsur true. Malheureusement, je ne l'ai pas fait assez tôt, donc à chaque fois que je tire des changements, les fins de ligne sont bouchées.

Je pensais avoir trouvé une réponse ici, mais je ne peux pas le faire fonctionner pour moi. Ma connaissance de la ligne de commande Linux est au mieux limitée, donc je ne suis même pas sûr de ce que fait la ligne "xargs fromdos" dans son script. Je reçois toujours des messages à propos de l'absence d'un tel fichier ou répertoire, et lorsque je parviens à le pointer vers un répertoire existant, il me dit que je n'ai pas d'autorisations.

J'ai essayé cela avec MSysGit sous Windows et via le terminal Mac OS X.

Brian Donahue
la source
Je ne peux pas voter sur ce fil même assez. +1 ++ car il fournit la meilleure réponse en la matière.
sjas
D'accord avec Charles. Cependant, dans mon cas (avec Mac OS X 10.8)> git config core.autocrlf false a fonctionné, pas> git config core.autocrlf input
user1045085

Réponses:

187

La documentation git pour gitattributes documente désormais une autre approche pour «réparer» ou normaliser toutes les fins de ligne dans votre projet. Voici l'essentiel:

$ echo "* text=auto" >.gitattributes
$ git add --renormalize .
$ git status        # Show files that will be normalized
$ git commit -m "Introduce end-of-line normalization"

Si des fichiers qui ne doivent pas être normalisés apparaissent dans l'état git, désactivez leur attribut text avant d'exécuter git add -u.

manual.pdf -text

Inversement, la normalisation des fichiers texte que git ne détecte pas peut être activée manuellement.

weirdchars.txt text

Cela s'appuie sur un nouveau --renormalizedrapeau ajouté dans git v2.16.0, publié en janvier 2018. Pour les anciennes versions de git, il y a quelques étapes supplémentaires:

$ echo "* text=auto" >>.gitattributes
$ rm .git/index     # Remove the index to force git to
$ git reset         # re-scan the working directory
$ git status        # Show files that will be normalized
$ git add -u
$ git add .gitattributes
$ git commit -m "Introduce end-of-line normalization"
Russ Egan
la source
1
Pourriez-vous me dire quel est le but de la git reset, s'il vous plaît?
crdx
1
force git à reconstruire l'index, pendant lequel il analyse chaque fichier pour deviner si son binaire. Le rm supprime l'ancien index, reset construit le nouvel index.
Russ Egan du
16
Merci, cela a fonctionné pour moi. Une commande utile après l'exécution git statusest d'exécuter git diff --ignore-space-at-eoljuste pour être sûr que les seules modifications que vous effectuez sont les fins de ligne.
zelanix
1
Remarque: La seule "vraie" différence entre cette solution et l '"ancienne" solution réside dans la présence de .gitattributes (avec le contenu approprié). Sans cela, git resetne détectera aucune modification et est donc inutile.
Rob
3
Les instructions sur la gitattributes page ont été mis à jour pour tirer parti du --renormalizedrapeau ajouté dans v2.16.0 git qui a été publié en Janvier 2018. Le --renormalizedrapeau consolide le processus de fin de ligne re-traitement pour chaque fichier en tracked une seule commande: git add --renormalize ..
Mike Hill
389

Le moyen le plus simple de résoudre ce problème consiste à effectuer un commit qui corrige toutes les fins de ligne. En supposant que vous n'avez aucun fichier modifié, vous pouvez procéder comme suit.

# From the root of your repository remove everything from the index
git rm --cached -r .

# Change the autocrlf setting of the repository (you may want 
#  to use true on windows):
git config core.autocrlf input

# 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 "Fixed crlf issue"

# If you're doing this on a Unix/Mac OSX clone then optionally remove
# the working tree and re-check everything out with the correct line endings.
git ls-files -z | xargs -0 rm
git checkout .
CB Bailey
la source
7
PS J'ai recommandé votre correctif aux gars de github.com et ils ont mis à jour leur guide d'aide pour utiliser votre solution (auparavant, il venait de recommander un nouveau clone et une réinitialisation matérielle, qui ne semblait pas obtenir tous les fichiers.) Help.github. com / traiter-avec-fin de ligne
Brian Donahue
31
Merci ... c'est une excellente solution. Je l'ai trouvé sur GitHub.
PHLAK
4
Vous pouvez également consulter config.safecrlf pour vous assurer que vous ne modifiez pas crlfs dans des fichiers non textuels (tels que binaires). Découvrez-le dans le docs kernel.org/pub/software/scm/git/docs/git-config.html .
vrish88
4
@ vrish88: Si vous êtes dans cette situation, cependant, vous risquez de souffrir de terminaisons mixtes et core.safecrlf peut en fait vous empêcher de faire ce que vous devez faire. Il est probablement plus facile de ne pas utiliser safecrlf. git ne se trompe pas souvent de détection de fichiers binaires et si c'est le cas, vous pouvez le marquer manuellement comme binaire avec un .gitattribute et récupérer la version correcte à partir du commit précédent.
CB Bailey
26
La nouvelle solution recommandée dans la réponse de Russ Egan ci-dessous est plus simple et n'implique pas de choses effrayantes comme la suppression de tout votre code source , donc je recommanderais vraiment aux gens de l'utiliser, même si cette ancienne solution a 10 fois plus de votes!
Porculus
11

Ma procédure pour gérer les fins de ligne est la suivante (combat testé sur de nombreux repos):

Lors de la création d'un nouveau référentiel:

  • mettre .gitattributesdans le tout premier commit avec d'autres fichiers typiques comme .gitignoreetREADME.md

Lorsqu'il s'agit d'un référentiel existant:

  • Créez / modifiez en .gitattributesconséquence
  • git commit -a -m "Modified gitattributes"
  • git rm --cached -r . && git reset --hard && git commit -a -m 'Normalize CRLF' -n"
    • -n( --no-verifyconsiste à ignorer les hooks de pré-validation)
    • Je dois le faire assez souvent pour le définir comme un alias alias fixCRLF="..."
  • répéter la commande précédente
    • oui, c'est vaudou, mais en général je dois exécuter la commande deux fois, la première fois il normalise certains fichiers, la deuxième fois encore plus de fichiers. En général, il est probablement préférable de répéter jusqu'à ce qu'aucun nouveau commit ne soit créé :)
  • faire plusieurs allers-retours entre l'ancienne (juste avant la normalisation) et la nouvelle branche. Après avoir changé de branche, git trouvera parfois encore plus de fichiers qui doivent être renormalisés!

Dans .gitattributesJe déclare tous les fichiers texte explicitement comme ayant LF EOL car généralement les outils Windows sont compatibles avec LF tandis que les outils non-Windows ne sont pas compatibles avec CRLF (même de nombreux outils de ligne de commande nodejs supposent LF et peuvent donc changer l'EOL dans vos fichiers).

Contenu de .gitattributes

Mon .gitattributesressemble généralement à:

*.html eol=lf
*.js   eol=lf
*.json eol=lf
*.less eol=lf
*.md   eol=lf
*.svg  eol=lf
*.xml  eol=lf

Pour savoir quelles extensions distinctes sont suivies par git dans le référentiel actuel, regardez ici

Problèmes après normalisation

Une fois cela fait, il y a une mise en garde plus commune cependant.

Dites que votre masterest déjà à jour et normalisé, puis vous passez à la caisse outdated-branch. Très souvent juste après avoir vérifié cette branche, git marque de nombreux fichiers comme modifiés.

La solution est de faire un faux commit ( git add -A . && git commit -m 'fake commit') puis git rebase master. Après le rebase, le faux commit devrait disparaître.

jakub.g
la source
1
Je pensais que je devenais fou, jusqu'à ce que je lise votre article, car j'ai dû exécuter la séquence de commandes spécifiée plusieurs fois également. Vaudou! ;)
Sean Fausett
Avec la version git 2.7.0.windows.1, j'ai utilisé ce qui suit: git rm --cached -r . && git reset --hard && git add . && git commit -m "Normalize EOL" -n
Sean Fausett
4
git status --short|grep "^ *M"|awk '{print $2}'|xargs fromdos

Explication:

  • git status --short

    Cela affiche chaque ligne dont git est et n'est pas au courant. Les fichiers qui ne sont pas sous contrôle git sont marqués au début de la ligne par un «?». Les fichiers modifiés sont marqués d'un M.

  • grep "^ *M"

    Cela ne filtre que les fichiers qui ont été modifiés.

  • awk '{print $2}'

    Cela montre uniquement le nom de fichier sans aucun marqueur.

  • xargs fromdos

    Cela prend les noms de fichiers de la commande précédente et les exécute via l'utilitaire «fromdos» pour convertir les fins de ligne.

Lloyd Moore
la source
C'est génial. Je vous remercie. Pour ceux qui recherchent une solution utilisant Homebrew, utilisez dos2unixplutôt que fromdos.
Almir Sarajčić
4

Voici comment j'ai corrigé toutes les fins de ligne dans tout l'historique à l'aide de git filter-branch. Le ^Mcaractère doit être saisi avec CTRL-V+ CTRL-M. J'ai utilisé dos2unixpour convertir les fichiers car cela ignore automatiquement les fichiers binaires.

$ git filter-branch --tree-filter 'grep -IUrl "^M" | xargs -I {} dos2unix "{}"'
pfrenssen
la source
3

Le "| xargs fromdos" lit à partir de l'entrée standard (le fichier findtrouve) et l'utilise comme arguments pour la commande fromdos, qui convertit les fins de ligne. (Fromdos est-il standard dans ces environnements? J'ai l'habitude de dos2unix). Notez que vous pouvez éviter d'utiliser xargs (particulièrement utile si vous avez suffisamment de fichiers que la liste d'arguments est trop longue pour xargs):

find <path, tests...> -exec fromdos '{}' \;

ou

find <path, tests...> | while read file; do fromdos $file; done

Je ne suis pas totalement sûr de vos messages d'erreur. J'ai testé cette méthode avec succès. Quel programme produit chacun? Pour quels fichiers / répertoires n'avez-vous pas d'autorisations? Cependant, voici un essai pour deviner ce que cela pourrait être:

Un moyen simple d'obtenir une erreur de fichier introuvable pour le script est d'utiliser un chemin relatif - utilisez un chemin absolu. De même, vous pourriez obtenir une erreur d'autorisation si vous n'avez pas rendu votre script exécutable (chmod + x).

Ajoutez des commentaires et je vais essayer de vous aider!

Cascabel
la source
J'ai vu un autre exemple avec dos2unix et je pensais que c'était en quelque sorte copier des fichiers dans un dossier nommé ça, mais maintenant je comprends. Wow, semble évident maintenant. Merci de votre aide!
Brian Donahue
1

ok ... sous cygwin, nous n'avons pas de fromdos facilement disponible, et cet awk substeb vous explose si vous avez des espaces dans les chemins d'accès aux fichiers modifiés (que nous avions), donc j'ai dû le faire un peu différemment:

git status --short | grep "^ *M" | sed 's/^ *M//' | xargs -n 1 dos2unix

bravo à @lloyd pour l'essentiel de cette solution

Anton K
la source
-2

Suivez ces étapes si aucune des autres réponses ne vous convient:

  1. Si vous êtes sous Windows, faites-le git config --global core.autocrlf true; si vous êtes sous Unix, faitesgit config core.autocrlf input
  2. Courir git rm --cached -r .
  3. Supprimer le fichier .gitattributes
  4. Courir git add -A
  5. Courir git reset --hard

Ensuite, votre section locale devrait être propre maintenant.

zs2020
la source
4
Vraiment? La suppression du .gitattributesfichier est la solution au problème de fin de ligne?
Aleksandr M
Oui, veuillez répondre au commentaire de @AleksandrM
Mr_and_Mrs_D