ver tipos modificados log etiquetas cambios archivos archivo git history rewrite branch newline

tipos - Los finales de línea se desordenaron en Git: ¿cómo hacer un seguimiento de los cambios de otra rama después de una gran solución de final de línea?



tipos de etiquetas en git (5)

Estamos trabajando con un motor PHP de terceros que recibe actualizaciones periódicas. Los lanzamientos se guardan en una rama separada en git, y nuestra bifurcación es la rama maestra.

De esta manera podremos aplicar parches a nuestro tenedor a partir de las nuevas versiones del motor.

Mi problema es que, después de muchos compromisos con nuestra sucursal, me di cuenta de que la importación inicial del motor se realizó con los finales de línea CRLF.

Convertí todos los archivos a LF, pero esto hizo un gran compromiso, con 100k líneas eliminadas y 100k líneas agregadas, lo que obviamente rompe lo que pretendíamos hacer: combinar fácilmente los parches de las versiones de fábrica de ese motor de terceros.

¿Qué sabría yo? ¿Cómo puedo arreglar esto? Ya tengo cientos de confirmaciones en nuestro tenedor.

Lo que sería bueno es que de alguna manera los finales de línea corrijan la confirmación después de la importación inicial y antes de bifurcar nuestra propia bifurcación, y eliminando esa enorme línea final de confirmación más adelante en la historia.

Sin embargo no tengo idea de cómo hacer esto en Git.

¡Gracias!


¿ git rebase a git rebase ?

Deberá volver a basar el historial de su repositorio de la siguiente manera:

  • cometer los arreglos de terminador de línea
  • iniciar el rebase
  • Deje el compromiso de importación de terceros primero
  • aplicar los arreglos de terminador de línea
  • aplica tus otros parches

Sin embargo, lo que sí debe comprender es que esto romperá todos los repositorios posteriores, aquellos que se clonan de su repositorio principal. Lo ideal es que empieces desde cero con esos.

Actualización : muestra de uso:

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

Comenzará una sesión de rebase para los últimos 3 confirmaciones.


En el futuro, evite este problema con la configuración core.autocrlf , documentada en git config --help :

core.autocrlf

Si es verdadero, git convierte CRLF al final de las líneas en archivos de texto a LF al leer desde el sistema de archivos, y convierte en reversa al escribir en el sistema de archivos. La variable se puede configurar para que input , en cuyo caso la conversión solo ocurre mientras se lee desde el sistema de archivos, pero los archivos se escriben con LF al final de las líneas. Un archivo se considera "texto" ( es decir, debe estar sujeto al mecanismo autocrlf ) según el atributo crlf del archivo, o si crlf no está especificado, según el contenido del archivo. Ver gitattributes .


Estamos evitando este problema en el futuro con:

1) todos usan un editor que elimina espacios en blanco, y guardamos todos los archivos con LF.

2) si 1) falla (puede (alguien lo guarda accidentalmente en CRLF por cualquier motivo), tenemos un script de confirmación previa que verifica los 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 y funciona en Mac OS X, sin embargo, debe probarse antes de usarlo en otras plataformas (tuvimos problemas con Cygwin y BSD grep)

3) En caso de que encontremos algún error de espacio en blanco, usamos el siguiente script en archivos 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; } ?>


Finalmente logré resolverlo.

La respuesta es:

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

fix-line-endings.sh contiene:

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

Después de que todas las terminaciones de línea se fijaron en todos los árboles en todas las confirmaciones, hice una reorganización interactiva y eliminé todas las confirmaciones que corrigieron las terminaciones de línea.

Ahora mi repo está limpio y fresco, listo para ser empujado :)

Nota para los visitantes: ¡no hagas esto si tu repo ha sido empujado / clonado porque desordenará las cosas gravemente!


Una solución (no necesariamente la mejor) sería usar git-filter-branch para reescribir el historial y usar siempre los finales de línea correctos. Esta debería ser una mejor solución que el rebase interactivo, al menos para un mayor número de confirmaciones; También podría ser más fácil lidiar con fusiones usando git-filter-branch.

Eso es, por supuesto, asumiendo que la historia no fue publicada (el repositorio no fue clonado).