Pytanie Zakończenia linii zawiodły w Git - jak śledzić zmiany z innej gałęzi po wielkiej naprawie końca linii?


Pracujemy z silnikiem PHP innej firmy, który otrzymuje regularne aktualizacje. Wersje są przechowywane w oddzielnej gałęzi w git, a nasz fork jest głównym oddziałem.

W ten sposób będziemy mogli nakładać łatki na nasze widelce z nowych wydań silnika.

Mój problem polega na tym, że po wielu zatwierdzeniach w naszym oddziale zdałem sobie sprawę, że początkowy import silnika został zakończony końcami linii CRLF.

Przekształciłem każdy plik na LF, ale to zrobiło ogromne zatwierdzenie, usunięto 100k linii i dodałem 100k linii, co oczywiście psuje to, co zamierzaliśmy zrobić: łatwo scalamy łatki z fabrycznych wersji tego silnika innej firmy.

Co mogę wiedzieć? Jak mogę to naprawić? Mam już setki zobowiązań na naszym widelcu.

Dobrym rozwiązaniem jest jakoś zakończenie linii zatwierdzenia po początkowym imporcie i przed rozgałęzieniem własnego widelca, a następnie usunięcie tego ogromnego zakończenia wiersza końcowego w historii.

Jednak nie mam pojęcia, jak to zrobić w Git.

Dzięki!


23
2018-06-18 10:37


pochodzenie




Odpowiedzi:


W końcu udało mi się go rozwiązać.

Odpowiedź to:

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 naprawieniu wszystkich zakończeń linii we wszystkich drzewach we wszystkich zatwierdzeniach, wykonałem interaktywny rebase i usunięto wszystkie zatwierdzenia, które naprawiały zakończenia linii.

Teraz moje repozytorium jest czyste i świeże, gotowe do pchnięcia :)

Uwaga dla odwiedzających: nie rób tego, jeśli twoje repo zostało pchnięte / sklonowane, ponieważ źle to zepsuje!


34
2018-06-29 22:08



To jest niesamowite! Jednak twój skrypt ma problemy ze spacjami w nazwach plików. Zamiast tego zrobiłem coś takiego: znajdź. -type f -print0 -a (-name '. [hc] "-o -name".p [yl] ') | xargs -0 fromdos - Enno
Zgadzam się, że to jest niesamowite. Jednak jedno pytanie: Podejście tutaj wyraźnie określa, które rozszerzenia plików mają usuwać CR. Chciałbym wiedzieć, czy można zamiast tego usunąć CRs ze wszystkich i tylko te poprawki, które heurystyki wykrywające tekst git rozważałyby tekst. (Heurystyki Gita sprawdzają rzeczywistą zawartość pliku, a nie nazwy plików). Na pierwszy rzut oka wydaje się, że gra będzie jeszcze lepsza z core.autocrlf = true. - Chris
Nie do końca to, o co prosiłem, ale stackoverflow.com/a/3092511 daje podejście, które wykorzystuje komendę unix "file", aby spróbować odróżnić pliki tekstowe od nietekstowych. - Chris
Odpowiedzi na Jak ustalić, czy Git obsługuje plik jako plik binarny lub tekst? Daj kilka kreatywnych sposobów na zrobienie tego, o co prosiłem. - Chris
Brakuje -o operand między .tekst i .html filtry. - nextgentech


Idąc dalej, unikaj tego problemu z core.autocrlf ustawienie, udokumentowane w git config --help:

core.autocrlf

Jeśli true, powoduje konwersję gita CRLF na końcu wierszy w plikach tekstowych do LF podczas czytania z systemu plików i konwertowania do tyłu podczas zapisu do systemu plików. Zmienna może być ustawiona na input, w takim przypadku konwersja odbywa się tylko podczas odczytu z systemu plików, ale pliki są zapisywane z LF na końcu linii. Plik jest uznawany za "tekst" (to znaczy być poddanym autocrlf mechanizm) na podstawie pliku crlf atrybut, lub jeśli crlf jest nieokreślony, na podstawie zawartości pliku. Widzieć gitattributes.


4
2018-01-08 18:25



dzięki za wskazówkę! używaliśmy tej funkcji, ale zachowywaliśmy się wtedy naprawdę błędnie (być może błędy zostały naprawione do tej pory) zobacz moją następną odpowiedź, aby zobaczyć, jak uniknęliśmy tego. - keo


Popatrzyłeś git rebase?

Konieczne będzie ponowne utworzenie historii repozytorium w następujący sposób:

  • zatwierdzić poprawki końca linii
  • uruchom bazę danych
  • w pierwszej kolejności pozostaw zatwierdzenie importu innej firmy
  • zastosuj poprawki końca linii
  • zastosuj swoje inne poprawki

Musisz jednak zrozumieć, że tak się stanie przerwa wszystkie repozytoria niższego rzędu - te, które są klonowane z repozytorium dla rodziców. Najlepiej, jeśli zaczniesz od zera.


Aktualizacja: użycie próbki:

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

Rozpoczyna sesję rebase dla ostatnich 3 commitów.


3
2018-06-18 10:41



msgstr "nie zgadzam się z tym, ponieważ HEAD jest przy ostatnim zatwierdzeniu, a konwersja LF jest powrotem do historii. - keo
na szczęście nikt jeszcze nie sklonował tego repo. - keo
jest jedna rzecz, która nie jest jeszcze jasna - jeśli zrobię rebase, kilka łatek będzie zawierało CRLF - tak jak zostały popełnione, gdy pliki były w crlf - jak sobie z tym poradzić? - keo


unikamy tego problemu w przyszłości dzięki:

1) każdy używa edytora, który usuwa końcowe białe spacje i zapisujemy wszystkie pliki za pomocą LF.

2) jeśli 1) zawiedzie (może - ktoś przypadkowo zapisuje go w CRLF z jakiegokolwiek powodu) mamy skrypt przed zatwierdzeniem, który sprawdza znaki 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 --

Skrypt ten 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 gregiem Cygwin i BSD)

3) W przypadku znalezienia jakichkolwiek białych znaków, 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
2018-01-09 11:25





Jednym rozwiązaniem (niekoniecznie najlepszym) byłoby użycie git-filter-branch przepisać historię, aby zawsze używać prawidłowych zakończeń linii. Powinno to być lepszym rozwiązaniem niż interaktywna baza, przynajmniej dla większej liczby zatwierdzeń; również łatwiej radzić sobie z połączeniami za pomocą git-filter-branch.

To oczywiście zakłada, że ​​historia była nie publikowany (repozytorium nie zostało sklonowane).


2
2018-06-18 12:01