라인 엔딩은 git에서 엉망이되었습니다 - 거대한 라인 엔딩 수정 후 다른 지점의 변경 사항을 추적하는 방법은 무엇입니까?
문제
우리는 정기적 인 업데이트를받는 타사 PHP 엔진으로 작업하고 있습니다. 릴리스는 git의 별도의 지점에 보관되며 포크는 마스터 브랜치입니다.
이렇게하면 엔진의 새로운 릴리스에서 포크에 패치를 적용 할 수 있습니다.
내 문제는 우리 지점에 많은 노력을 기울인 후 엔진의 초기 수입이 CRLF 라인 엔딩으로 완료되었다는 것을 깨달았습니다.
나는 모든 파일을 LF로 변환했지만 100k 라인이 제거되고 100k 라인이 추가되면서 큰 커밋을 만들었습니다. 이는 분명히 우리가하려는 작업을 중단합니다. 해당 제 3 자 엔진의 공장 릴리스에서 패치를 쉽게 병합했습니다.
내가 무엇을 알고 있습니까? 이것을 어떻게 고칠 수 있습니까? 나는 이미 우리 포크에 수백 개의 커밋이 있습니다.
좋은 점은 어떻게 든 최초의 수입 후 그리고 우리 자신의 포크를 분기하기 전에 라인 엔딩을 수정하고 나중에 역사상 거대한 라인 엔딩 커밋을 제거하는 것입니다.
그러나 나는 git에서 이것을하는 방법을 모른다.
감사!
해결책
나는 마침내 그것을 해결했다.
정답은:
git filter-branch --tree-filter '~/Scripts/fix-line-endings.sh' -- --all
Fix-line-endings.sh는 다음을 포함합니다.
#!/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
모든 줄의 모든 커밋에서 모든 라인 결말이 고정 된 후, 나는 대화 형 레바 제를 사용하고 선 엔딩을 고정하는 모든 커밋을 제거했습니다.
이제 내 repo는 깨끗하고 신선하며 밀릴 준비가되었습니다 :)
방문자에 대한 참고 사항 : REPO가 푸시 / 클로닝 된 경우이 작업을 수행하지 마십시오.
다른 팁
앞으로이 문제를 피하십시오 core.autocrlf
문서화 된 설정 git config --help
:
core.autocrlf
사실이라면 git을 변환합니다
CRLF
텍스트 파일의 줄 끝에서LF
파일 시스템에서 읽을 때 파일 시스템에 쓸 때 반대로 변환합니다. 변수를 설정할 수 있습니다input
,이 경우 전환은 파일 시스템에서 읽는 동안 만 발생하지만 파일은 다음과 같이 작성됩니다.LF
선의 끝에. 파일은 "텍스트"로 간주됩니다 (즉 대상이된다autocrlf
메커니즘) 파일을 기반으로합니다crlf
속성 또는 ifcrlf
파일의 내용에 따라 지정되지 않습니다. 보다 gitattributes.
당신은 봤어? git rebase
?
다음과 같이 저장소의 기록을 다시 제기해야합니다.
- 라인 터미네이터 수정을 커밋하십시오
- Rebase를 시작하십시오
- 타사 수입 커밋을 먼저 맡기십시오
- 라인 터미네이터 수정을 적용하십시오
- 다른 패치를 적용하십시오
당신이 이해해야 할 것은 부서지다 모든 다운 스트림 리포지토리 - 부모 저장소에서 복제 된 저장소. 이상적으로 당신은 그것들과 처음부터 시작합니다.
업데이트: 샘플 사용 :
target=`git rev-list --max-count=3 HEAD | tail -n1`
get rebase -i $target
마지막 3 커밋에 대한 Rebase 세션을 시작합니다.
우리는 앞으로이 문제를 피하고 있습니다.
1) 모든 사람은 우선 순위색을 벗기는 편집기를 사용하고 모든 파일을 LF로 저장합니다.
2) 1) 실패한 경우 (누군가가 실수로 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 --
이 스크립트는 GNU GREP를 사용하고 Mac OS X에서 작동하지만 다른 플랫폼에서 사용하기 전에 테스트해야합니다 (Cygwin 및 BSD GREP에 문제가있었습니다).
3) 공백 오류가 발견되면 잘못된 파일에서 다음 스크립트를 사용합니다.
#!/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;
}
?>
하나의 솔루션 (반드시 가장 좋은 솔루션은 아닙니다)은 사용하는 것입니다. git-filter-branch 항상 올바른 라인 엔딩을 사용하도록 기록을 다시 작성합니다. 이것은 적어도 더 많은 수의 커밋에 대해 대화식 레바 제가 더 나은 솔루션이어야합니다. 또한 Git-Filter-Branch를 사용하여 병합을 처리하는 것이 더 쉬울 수 있습니다.
그것은 물론 역사가 있다고 가정합니다 출판되지 않았습니다 (저장소는 복제되지 않았다).