Les fins de lignes égarées dans Git - comment suivre les modifications d'une autre branche après un énorme problème de fin de ligne?

StackOverflow https://stackoverflow.com/questions/1011985

Question

Nous travaillons avec un moteur PHP tiers qui reçoit des mises à jour régulières. Les versions sont conservées sur une branche distincte dans git, et notre fork est la branche principale.

Ainsi, nous pourrons appliquer des correctifs à notre fourche à partir des nouvelles versions du moteur.

Mon problème est que, après de nombreux engagements dans notre branche, j'ai réalisé que l'importation initiale du moteur avait été effectuée avec des fins de ligne CRLF.

J'ai converti chaque fichier au format LF, mais cela a créé un énorme commit, avec 100 000 lignes supprimées et 100 000 lignes ajoutées, ce qui rompt évidemment ce que nous voulions faire: fusionner facilement les correctifs des versions d'usine de ce moteur tiers.

Que saurais-je? Comment puis-je réparer cela? J'ai déjà des centaines de commits sur notre fork.

Ce qui serait bien, c’est d’effectuer une correction de fin de ligne après l’importation initiale et avant de créer une branche, et de supprimer cette énorme ligne qui se termine plus tard dans l’historique.

Cependant, je ne sais pas comment faire cela dans Git.

Merci!

Était-ce utile?

La solution

J'ai finalement réussi à le résoudre.

La réponse est:

git filter-branch --tree-filter '~/Scripts/fix-line-endings.sh' -- --all

fix-line-endings.sh contient:

#!/bin/sh
find . -type f -a \( -name '*.tpl' -o -name '*.php' -o -name '*.js' -o -name '*.css' -o -name '*.sh' -o -name '*.txt' -iname '*.html' \) | xargs fromdos

Une fois que toutes les fins de ligne ont été corrigées dans toutes les arborescences de tous les commits, j'ai créé une rebase interactive et supprimé toutes les commits qui corrigeaient les fins de ligne.

Maintenant, mon dépôt est propre et frais, prêt à être poussé:)

Note aux visiteurs: ne le faites pas si votre référentiel a été poussé / cloné car cela gâcherait tout!!

Autres conseils

A l'avenir, évitez ce problème avec le paramètre core.autocrlf , documenté dans git config --help :

  

core.autocrlf

     

Si la valeur est true, git convertit CRLF à la fin des lignes dans les fichiers texte en LF lors de la lecture du système de fichiers, et la conversion est inversée lors de l'écriture sur le système de fichiers. La variable peut être définie sur input , auquel cas la conversion n'a lieu que lors de la lecture du système de fichiers, mais les fichiers sont écrits avec LF à la fin des lignes. Un fichier est considéré comme un "texte". ( c'est-à-dire être soumis au mécanisme autocrlf ) en fonction de l'attribut crlf du fichier, ou si crlf n'est pas spécifié, basé sur le contenu du fichier. Voir gitattributes .

Avez-vous examiné git rebase ?

Vous devrez refaire l'historique de votre référentiel, comme suit:

  • valider les correctifs de terminaison de ligne
  • démarrer la rebase
  • laissez d'abord la validation d'importation tierce
  • appliquer les correctifs de terminaison de ligne
  • appliquez vos autres correctifs

Ce que vous devez comprendre, c’est que cela va casser tous les référentiels en aval - ceux qui sont clonés à partir de votre référant parent. Idéalement, vous partirez de zéro avec ceux-là.

Mise à jour : exemple d'utilisation:

target=`git rev-list --max-count=3 HEAD | tail -n1`
get rebase -i $target

va commencer une session de rebase pour les 3 derniers commits.

nous évitons ce problème à l'avenir avec:

1) Tout le monde utilise un éditeur qui supprime les espaces, et nous sauvegardons tous les fichiers avec LF.

2) si 1) échoue (cela peut arriver - quelqu'un l'enregistre accidentellement dans CRLF pour quelque raison que ce soit), nous avons un script de pré-validation qui recherche les caractères CRLF:

#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by git-commit with no arguments.  The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit" and set executable bit

# original by Junio C Hamano

# modified by Barnabas Debreceni to disallow CR characters in commits


if git rev-parse --verify HEAD 2>/dev/null
then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

crlf=0

IFS="
"
for FILE in `git diff-index --cached $against`
do
    fhash=`echo $FILE | cut -d' ' -f4`
    fname=`echo $FILE | cut -f2`

    if git show $fhash | grep -EUIlq 

Ce script utilise GNU grep et fonctionne sous Mac OS X. Toutefois, il doit être testé avant de pouvoir être utilisé sur d'autres plates-formes (nous avons eu des problèmes avec Cygwin et BSD grep)

3) Au cas où nous trouverions des erreurs d'espacement, nous utilisons le script suivant sur les fichiers erronés:

#!/usr/bin/env php
<?php

    // Remove various whitespace errors and convert to LF from CRLF line endings
    // written by Barnabas Debreceni
    // licensed under the terms of WFTPL (http://en.wikipedia.org/wiki/WTFPL)

    // handle no args
    if( $argc <2 ) die( "nothing to do" );


    // blacklist

    $bl = array( 'smarty' . DIRECTORY_SEPARATOR . 'templates_c' . DIRECTORY_SEPARATOR . '.*' );

    // whitelist

    $wl = array(    '\.tpl', '\.php', '\.inc', '\.js', '\.css', '\.sh', '\.html', '\.txt', '\.htc', '\.afm',
                    '\.cfm', '\.cfc', '\.asp', '\.aspx', '\.ascx' ,'\.lasso', '\.py', '\.afp', '\.xml',
                    '\.htm', '\.sql', '\.as', '\.mxml', '\.ini', '\.yaml', '\.yml'  );

    // remove $argv[0]
    array_shift( $argv );

    // make file list
    $files = getFileList( $argv );

    // sort files
    sort( $files );

    // filter them for blacklist and whitelist entries

    $filtered = preg_grep( '#(' . implode( '|', $wl ) . ')$#', $files );
    $filtered = preg_grep( '#(' . implode( '|', $bl ) . ')$#', $filtered, PREG_GREP_INVERT );

    // fix whitespace errors
    fix_whitespace_errors( $filtered );





    ///////////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////////////


    // whitespace error fixer
    function fix_whitespace_errors( $files ) {
        foreach( $files as $file ) {

            // read in file
            $rawlines = file_get_contents( $file );

            // remove \r
            $lines = preg_replace( "/(\r\n)|(\n\r)/m", "\n", $rawlines );
            $lines = preg_replace( "/\r/m", "\n", $lines );

            // remove spaces from before tabs
            $lines = preg_replace( "/\040+\t/m", "\t", $lines );

            // remove spaces from line endings
            $lines = preg_replace( "/[\040\t]+$/m", "", $lines );

            // remove tabs from line endings
            $lines = preg_replace( "/\t+$/m", "", $lines );

            // remove EOF newlines
            $lines = preg_replace( "/\n+$/", "", $lines );

            // write file if changed and set old permissions
            if( strlen( $lines ) != strlen( $rawlines )){

                $perms = fileperms( $file );

                // Uncomment to save original files

                //rename( $file, $file.".old" );
                file_put_contents( $file, $lines);
                chmod( $file, $perms );
                echo "${file}: FIXED\n";
            } else {
                echo "${file}: unchanged\n";
            }

        }
    }

    // get file list from argument array
    function getFileList( $argv ) {
        $files = array();
        foreach( $argv as $arg ) {
          // is a direcrtory
            if( is_dir( $arg ) )  {
                $files = array_merge( $files, getDirectoryTree( $arg ) );
            }
            // is a file
            if( is_file( $arg ) ) {
                $files[] = $arg;
            }
        }
        return $files;
    }

    // recursively scan directory
    function getDirectoryTree( $outerDir ){
        $outerDir = preg_replace( ':' . DIRECTORY_SEPARATOR . '$:', '', $outerDir );
        $dirs = array_diff( scandir( $outerDir ), array( ".", ".." ) );
        $dir_array = array();
        foreach( $dirs as $d ){
            if( is_dir( $outerDir . DIRECTORY_SEPARATOR . $d ) ) {
                $otherdir = getDirectoryTree( $outerDir . DIRECTORY_SEPARATOR . $d );
                $dir_array = array_merge( $dir_array, $otherdir );
            }
            else $dir_array[] = $outerDir . DIRECTORY_SEPARATOR . $d;
        }
        return $dir_array;
    }
?>
\r

Ce script utilise GNU grep et fonctionne sous Mac OS X. Toutefois, il doit être testé avant de pouvoir être utilisé sur d'autres plates-formes (nous avons eu des problèmes avec Cygwin et BSD grep)

3) Au cas où nous trouverions des erreurs d'espacement, nous utilisons le script suivant sur les fichiers erronés:

<*> then echo $fname contains CRLF characters crlf=1 fi done if [ $crlf -eq 1 ] then echo Some files have CRLF line endings. Please fix it to be LF and try committing again. exit 1 fi exec git diff-index --check --cached $against --

Ce script utilise GNU grep et fonctionne sous Mac OS X. Toutefois, il doit être testé avant de pouvoir être utilisé sur d'autres plates-formes (nous avons eu des problèmes avec Cygwin et BSD grep)

3) Au cas où nous trouverions des erreurs d'espacement, nous utilisons le script suivant sur les fichiers erronés:

<*>

Une solution (pas nécessairement la meilleure) serait d'utiliser git-filter- branche pour réécrire l’historique afin de toujours utiliser les fins de ligne correctes. Cela devrait être une meilleure solution que la rebase interactive, au moins pour un plus grand nombre de commits; De plus, il pourrait être plus facile de gérer les fusions en utilisant git-filter-branch.

Cela suppose bien sûr que l'historique n'était pas publié (le référentiel n'était pas cloné).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top