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!

Author: keo, 2009-06-18

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!

 34
Author: keo,
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 na LF podczas odczytu z systemu plików i konwertuje odwrotnie podczas zapisu do systemu plików. Zmienną można ustawić na input, 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 mechanizmowi autocrlf) na podstawie atrybutu crlf pliku, lub jeśli crlf jest nieokreślony, na podstawie zawartości pliku. Zobacz gitattributes .

 4
Author: Greg Bacon,
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.

 3
Author: Robert Munteanu,
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;
    }
?>
 2
Author: keo,
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).

 2
Author: Jakub Narębski,
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