finais de linha desarrumada no Git - como controlar as alterações de outro ramo depois de uma enorme linha terminando correção?

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

Pergunta

Estamos trabalhando com um 3º motor de PHP partido que recebe atualizações regulares. Os lançamentos são mantidos em um ramo separado no git, e nossa fork é o branch master.

Desta forma, vai ser capaz de aplicar patches ao nosso garfo das novas versões do motor.

O meu problema é que, depois de muitos commit nosso ramo, eu percebi que a importação inicial do motor foi feito com fins de linha CRLF.

Eu converti todos os arquivos para LF, mas isso fez uma enorme cometer, com 100k linhas removido e 100k linhas adicionadas, o que obviamente breaks que nós destinados a fazer:. Facilmente fundir em manchas de lançamentos de fábrica desse terceiro motor partido

O que whould eu sei? Como posso consertar isso? Já tenho centenas de commits em nosso garfo.

O que seria bom é fazer de alguma forma uma linha terminações corrigir cometem após a importação inicial e antes de ramificar o nosso próprio garfo, e removendo essa enorme linha que termina cometer mais tarde na história.

No entanto, eu não tenho nenhuma idéia de como fazer isso em Git.

Obrigado!

Foi útil?

Solução

Eu finalmente consegui resolvê-lo.

A resposta é:

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

fix-line-endings.sh contém:

#!/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

Depois de todas as terminações de linha foram fixadas em todas as árvores em todas as submissões, fiz uma alteração de base interactiva e removeu todas as submissões que foram fixação terminações de linha.

Agora meu repo é limpo e fresco, pronto para ser empurrado:)

Nota aos visitantes: não faça isso se o seu repo foi empurrado / clonado porque vai estragar as coisas mal

Outras dicas

No futuro, evitar este problema com a configuração core.autocrlf, documentada em git config --help :

core.autocrlf

Se for verdade, faz CRLF convertido git no final de linhas em arquivos de texto para LF ao ler a partir do sistema de arquivos, e convertido em sentido inverso ao escrever para o sistema de arquivos. A variável pode ser definida como input, caso em que a conversão só acontece durante a leitura do sistema de arquivos, mas os arquivos são escritos com LF no final das linhas. Um arquivo é considerado "texto" ( i. ser submetido ao mecanismo autocrlf) com base no atributo crlf do arquivo, ou se crlf não é especificado, com base no conteúdo do arquivo. Consulte gitattributes .

Será que você olha para git rebase?

Você vai precisar de re-base de história do seu repositório, como segue:

  • comprometer as correções de terminador de linha
  • iniciar o rebase
  • deixar a importação de terceiros cometer primeira
  • aplicar as correções de terminador de linha
  • aplicar seus outros patches

O que você precisa entender é que embora essa vontade pausa todos os repositórios a jusante - aqueles que são clonados a partir de seu repositório pai. Idealmente, você vai começar a partir do zero com aqueles.


Atualização : uso de amostra:

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

Vai começar uma sessão rebase para os últimos 3 commits.

estamos evitar este problema no futuro com:

1) todo mundo usa um editor que tira arrastando espaços em branco, e salvar todos os arquivos com LF.

2) se 1) não (pode - alguém acidentalmente salva em CRLF por qualquer motivo), temos um roteiro pré-commit que verifica a existência de caracteres 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 $'\r$'
    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 --

Este script usa GNU grep, e funciona em Mac OS X, no entanto, ele deve ser testado antes do uso em outras plataformas (teve problemas com Cygwin e BSD grep)

3) Em caso de nós encontrar algum erro de espaço em branco, usamos o seguinte script em arquivos errôneos:

#!/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;
    }
?>

Uma solução (não necessariamente o melhor) seria usar git-filter- ramo de reescrever a história sempre usar fins de linha corretos. Isto deve ser melhor solução que alteração de base interactiva, pelo menos para a maior número de confirmações; Também pode ser mais fácil de lidar com fusões usando git-filter-branch.

Isso é, naturalmente, assumindo que a história era não publicado (repositório não foi clonado).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top