라인 엔딩은 git에서 엉망이되었습니다 - 거대한 라인 엔딩 수정 후 다른 지점의 변경 사항을 추적하는 방법은 무엇입니까?

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

문제

우리는 정기적 인 업데이트를받는 타사 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 속성 또는 if crlf 파일의 내용에 따라 지정되지 않습니다. 보다 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를 사용하여 병합을 처리하는 것이 더 쉬울 수 있습니다.

그것은 물론 역사가 있다고 가정합니다 출판되지 않았습니다 (저장소는 복제되지 않았다).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top