Końcówki linii popsute w Git - jak śledzić zmiany z innej gałęzi po ogromnej poprawce zakończenia linii?
Pracujemy z zewnętrznym silnikiem PHP, który otrzymuje regularne aktualizacje. Wydania są przechowywane w oddzielnej gałęzi w git, a naszym widelcem jest gałąź master.
W ten sposób będziemy mogli zastosować łatki do naszego widelca z nowych wersji silnika.
Mój problem polega na tym, że po wielu commitach do naszej gałęzi, zdałem sobie sprawę, że wstępny import silnika został wykonany z zakończeniami linii CRLF.
Przekonwertowałem każdy plik do LF, ale to stworzyło ogromny commit, ze 100k linii usunięto i dodano 100k linii, co oczywiście łamie to, co zamierzaliśmy zrobić: łatwo scalać łatki z fabrycznych wydań tego silnika 3rd party.
Co ja mam wiedzieć? Jak mogę to naprawić? Mam już setki zobowiązań na widelcu.
Co byłoby dobre to jakoś zrobić końcówki linii naprawić commit po początkowym imporcie i przed rozgałęzieniem naszego własnego forka, i usunąć ten ogromny commit kończący linię później w historii.
Jednak nie mam pojęcia jak to zrobić w Git.
Dzięki!
5 answers
W końcu udało mi się to rozwiązać.
Odpowiedź brzmi:
git filter-branch --tree-filter '~/Scripts/fix-line-endings.sh' -- --all
Fix-line-endings.sh zawiera:
#!/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
Po tym, jak wszystkie zakończenia linii zostały naprawione we wszystkich drzewach we wszystkich commitach, zrobiłem interaktywną rebase i usunąłem wszystkie commity, które naprawiały zakończenia linii.
Teraz mój repo jest czysty i świeży, gotowy do pchnięcia:)
Uwaga dla odwiedzających: nie rób tego, jeśli twój repo został wypchnięty / sklonowany, ponieważ będzie to źle wszystko!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-09 20:57:46
Idąc dalej, unikaj tego problemu z ustawieniem core.autocrlf
, udokumentowanym w git config --help
:
rdzeń.autocrlf
If true, sprawia, że git konwertuje
CRLF
na końcu linii w plikach tekstowych naLF
podczas odczytu z systemu plików i konwertuje odwrotnie podczas zapisu do systemu plików. Zmienną można ustawić nainput
, w którym to przypadku konwersja odbywa się tylko podczas odczytu z systemu plików, ale pliki są zapisywane za pomocąLF
na końcu linii. A plik jest traktowany jako "tekst" (tzn. jest poddawany mechanizmowiautocrlf
) na podstawie atrybutucrlf
pliku, lub jeślicrlf
jest nieokreślony, na podstawie zawartości pliku. Zobacz gitattributes .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-08 18:25:43
Spojrzałeś na git rebase
?
Będziesz musiał ponownie oprzeć historię swojego repozytorium, w następujący sposób:
- Zatwierdź poprawki terminatora linii
- Uruchom rebase
- najpierw zostaw commit importu strony trzeciej
- zastosuj poprawki terminatora linii
- nanieść inne plastry
Musisz jednak zrozumieć, że spowoduje to złamanie wszystkich kolejnych repozytoriów - tych, które są sklonowane z twojego macierzystego repo. Idealnie zaczniesz od zera.
Update : przykładowe użycie:
target=`git rev-list --max-count=3 HEAD | tail -n1`
get rebase -i $target
Rozpocznie sesję rebase dla ostatnich 3 commitów.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 10:53:33
Unikamy tego problemu w przyszłości za pomocą:
1) każdy używa edytora, który usuwa końcowe spacje, a wszystkie pliki zapisujemy za pomocą LF.
2) Jeśli 1) nie powiedzie się (może - ktoś przypadkowo zapisze go w CRLF z jakiegokolwiek powodu) mamy skrypt Pre-commit, który sprawdza czy nie ma znaków 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 --
Ten skrypt używa GNU grep i działa na Mac OS X, jednak powinien być przetestowany przed użyciem na innych platformach (mieliśmy problemy z Cygwin i BSD grep)
3) w jeśli znajdziemy jakieś błędy spacji, używamy następującego skryptu na błędnych plikach:
#!/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;
}
?>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-09 11:25:40
Jednym z rozwiązań (niekoniecznie najlepszym) byłoby użycie git-filter-branch do przepisania historii, aby zawsze używać poprawnych zakończeń linii. Powinno to być lepsze rozwiązanie niż interaktywny rebase, przynajmniej dla większej liczby commitów; może być również łatwiejsze do radzenia sobie z połączeniami przy użyciu git-filter-branch.
Oczywiście zakładając, że historia nie została opublikowana (repozytorium nie zostało sklonowane).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-10 10:06:37