finais de linha desarrumada no Git - como controlar as alterações de outro ramo depois de uma enorme linha terminando correção?
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!
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 paraLF
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 comoinput
, caso em que a conversão só acontece durante a leitura do sistema de arquivos, mas os arquivos são escritos comLF
no final das linhas. Um arquivo é considerado "texto" ( i. ser submetido ao mecanismoautocrlf
) com base no atributocrlf
do arquivo, ou secrlf
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).