Jak odczytać wyjście z git diff?

Strona podręcznika dla git-diff jest dość długa i wyjaśnia wiele przypadków, które nie wydają się być konieczne dla początkujących. Na przykład:

git diff origin/master
Author: user3207158, 2010-03-27

7 answers

Przyjrzyjmy się przykładowi advanced diff z git history (w commit 1088261f w git.repozytorium git):

diff --git a/builtin-http-fetch.c b/http-fetch.c
similarity index 95%
rename from builtin-http-fetch.c
rename to http-fetch.c
index f3e63d7..e8f44ba 100644
--- a/builtin-http-fetch.c
+++ b/http-fetch.c
@@ -1,8 +1,9 @@
 #include "cache.h"
 #include "walker.h"

-int cmd_http_fetch(int argc, const char **argv, const char *prefix)
+int main(int argc, const char **argv)
 {
+       const char *prefix;
        struct walker *walker;
        int commits_on_stdin = 0;
        int commits;
@@ -18,6 +19,8 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
        int get_verbosely = 0;
        int get_recover = 0;

+       prefix = setup_git_directory();
+
        git_config(git_default_config, NULL);

        while (arg < argc && argv[arg][0] == '-') {

Przeanalizujmy tę łatkę linia po linii.

  • Pierwsza linia

    diff --git a/builtin-http-fetch.c b/http-fetch.c
    jest nagłówkiem" git diff " w formie diff --git a/file1 b/file2. Nazwy plików a/ i b/ są takie same, chyba że w grę wchodzi zmiana nazwy/kopia (jak w naszym przypadku). --git oznacza, że diff jest w formacie" git".
  • Następne to jeden lub więcej rozszerzonych nagłówków linie. Pierwsze trzy

    similarity index 95%
    rename from builtin-http-fetch.c
    rename to http-fetch.c
    powiedz nam, że plik został przemianowany z builtin-http-fetch.c na http-fetch.c i że te dwa pliki są w 95% identyczne (co zostało użyte do wykrycia tej zmiany nazwy).

    ostatnia linijka w rozszerzonym nagłówku diff, która jest
    index f3e63d7..e8f44ba 100644
    mówi nam o trybie danego pliku (100644 oznacza, że jest to zwykły plik, a nie np. dowiązanie symboliczne, i że nie ma on bitów uprawnień wykonywalnych), oraz o skróconym hashu preimage (Wersja pliku przed daną zmianą) i postimage (Wersja pliku przed daną zmianą). plik po zmianie). Ta linia jest używana przez git am --3way do próby połączenia trójdrożnego, jeśli patch nie może zostać zastosowany.

  • Następny jest dwuliniowy zunifikowany nagłówek diff

    --- a/builtin-http-fetch.c
    +++ b/http-fetch.c
    w porównaniu do wyniku diff -U nie posiada on nazw od-Pliku-modification-time ani do-pliku-modification-time po źródłowych (preimage) i docelowych (postimage) plikach. Jeśli plik został utworzony, źródłem jest /dev/null; jeśli plik został usunięty, docelowym jest /dev/null.
    Jeśli ustawisz zmienną konfiguracyjną diff.mnemonicPrefix na true, w miejsce przedrostków a/ i b/ w tym dwuliniowym nagłówku można zamiast c/, i/, w/ i o/ jako prefiksy, odpowiednio do tego, co porównujesz; zobacz git-config (1)
  • Następnie pojawia się jeden lub więcej fragmentów różnic; każdy fragment pokazuje jeden obszar, w którym Pliki się różnią. Unified format hunks zaczyna się od linii

    @@ -1,8 +1,9 @@
    lub
    @@ -18,6 +19,8 @@ int cmd_http_fetch(int argc, const char **argv, ...
    jest w formacie @@ from-file-range to-file-range @@ [header]. Zakres from-file - range ma postać -<start line>,<number of lines>, a to-file-range to +<start line>,<number of lines>. Zarówno start-line, jak i liczba linii odnosi się do pozycji i długości kawałka odpowiednio w preimage i postimage. Jeśli liczba linii nie jest pokazana, oznacza to, że jest równa 0.

    Opcjonalny nagłówek pokazuje funkcję C, w której następuje każda zmiana, jeśli jest to plik C (jak opcja -p W GNU diff), lub odpowiednik, jeśli istnieje, dla innych typów plików.

  • Następnie pojawia się opis różnic w plikach. Linie wspólne dla obu plików rozpoczynają się znakiem spacji. Linie, które faktycznie różnią się pomiędzy dwoma plikami znajduje się jeden z następujących znaków wskaźnikowych w lewej kolumnie drukowania:

    • '+ ' -- wiersz został dodany tutaj do pierwszego pliku.
    • ' - '-- linia zostaĹ ' a usuniÄ ™ ta z pierwszego pliku.


    Tak więc, na przykład, pierwszy kawałek

     #include "cache.h"
     #include "walker.h"
    
    -int cmd_http_fetch(int argc, const char **argv, const char *prefix)
    +int main(int argc, const char **argv)
     {
    +       const char *prefix;
            struct walker *walker;
            int commits_on_stdin = 0;
            int commits;
    

    Oznacza, że cmd_http_fetch został zastąpiony przez main, a wiersz const char *prefix; został dodany.

    Innymi słowy, przed zmianą, odpowiedni fragment then ' builtin-http-fetch.C ' File looked tak:

    #include "cache.h"
    #include "walker.h"
    
    int cmd_http_fetch(int argc, const char **argv, const char *prefix)
    {
           struct walker *walker;
           int commits_on_stdin = 0;
           int commits;
    

    Po zmianie fragmentu now ' http-fetch.plik c wygląda tak:

    #include "cache.h"
    #include "walker.h"
    
    int main(int argc, const char **argv)
    {
           const char *prefix;
           struct walker *walker;
           int commits_on_stdin = 0;
           int commits;
    
  • Może być

    \ No newline at end of file
    Linia obecna(nie jest w przykładzie diff).

Jak Donal Fellows powiedział najlepiej jest ćwiczyć czytanie różnic na rzeczywistych przykładach, gdzie wiesz, co zmieniłeś.

Referencje:

 420
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
2017-05-23 12:26:23

@@ -1,2 +3,4 @@ część diff

Ta część zajęła mi trochę czasu, aby zrozumieć, więc stworzyłem minimalny przykład.

Format jest zasadniczo taki sam jak diff -u unified diff.

Na przykład:

diff -u <(seq 16) <(seq 16 | grep -Ev '^(2|3|14|15)$')

Tutaj usunęliśmy linie 2, 3, 14 i 15. Wyjście:

@@ -1,6 +1,4 @@
 1
-2
-3
 4
 5
 6
@@ -11,6 +9,4 @@
 11
 12
 13
-14
-15
 16

@@ -1,6 +1,4 @@ oznacza:

  • -1,6: ten kawałek odpowiada linii 1 do 6 pierwszego pliku:

    1
    2
    3
    4
    5
    6
    

    - znaczy "stary", jak zwykle powołujemy się na to jako diff -u old new.

  • +1,4 mówi, że ten kawałek odpowiada linii 1 do 4 drugiego pliku.

    + oznacza "nowy".

    Mamy tylko 4 linie zamiast 6, ponieważ 2 linie zostały usunięte! Nowy przystojniak jest po prostu:

    1
    4
    5
    6
    

@@ -11,6 +9,4 @@ dla drugiego jest analogiczne:

  • W starym pliku mamy 6 linii, zaczynających się od linii 11 starego pliku:

    11
    12
    13
    14
    15
    16
    
  • W Nowym pliku mamy 4 linie, zaczynające się od linii 9 nowego pliku:

    11
    12
    13
    16
    

    Zauważ, że linia 11 jest dziewiątą linią nowego pliku, ponieważ usunęliśmy już 2 linie z poprzedniego kawałka: 2 i 3.

Hunk header

W zależności od wersji i konfiguracji git, możesz również uzyskać linię kodu obok linii @@, np. func1() { w:

@@ -4,7 +4,6 @@ func1() {

Można to również uzyskać za pomocą flagi -p zwykłej diff.

Przykład: stary plik:

func1() {
    1;
    2;
    3;
    4;
    5;
    6;
    7;
    8;
    9;
}

Jeśli usuniemy linia 6, diff pokazuje:

@@ -4,7 +4,6 @@ func1() {
     3;
     4;
     5;
-    6;
     7;
     8;
     9;

Zauważ, że nie jest to prawidłowa linia dla func1: pominęła linie 1 i 2.

Ta niesamowita funkcja często mówi dokładnie, do której funkcji lub klasy należy każdy kawałek, co jest bardzo przydatne do interpretacji różnic.

Jak dokładnie działa algorytm wyboru nagłówka jest omówione w: skąd pochodzi fragment w nagłówku Git diff hunk?

 49
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
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
2017-05-23 12:10:28

Oto prosty przykład.

diff --git a/file b/file 
index 10ff2df..84d4fa2 100644
--- a/file
+++ b/file
@@ -1,5 +1,5 @@
 line1
 line2
-this line will be deleted
 line4
 line5
+this line is added

Oto Wyjaśnienie (zobacz szczegóły tutaj ).

  • --git nie jest poleceniem, to znaczy, że jest to wersja git diff (nie unix)
  • a/ b/ są katalogami, nie są prawdziwe. jest to po prostu wygoda, gdy mamy do czynienia z tym samym plikiem (w moim przypadku a/ jest w indeksie, a b/ w katalogu roboczym)
  • 10ff2df..84d4fa2 są identyfikatorami blob tych 2 plików
  • 100644 jest "bitami trybu", co wskazuje, że jest to zwykły plik (nie wykonywalny i nie dowiązanie symboliczne)
  • --- a/file +++ b/file znaki minus pokazują linie w wersji a/, ale brakujące w wersji b/ ; A znaki plus pokazują linie brakujące w a/, ale obecne w b/ (w moim przypadku --- oznacza usunięte linie, a +++ oznacza dodane linie w b / i to plik w katalogu roboczym)
  • @@ -1,5 +1,5 @@ aby to zrozumieć, lepiej pracować z dużym plikiem; jeśli masz dwie zmiany w różnych miejscach, otrzymasz dwa wpisy typu @@ -1,5 +1,5 @@; Załóżmy, że masz plik line1 ... line100 i usunięte line10 i dodać nowy line100-otrzymasz:
@@ -7,7 +7,6 @@ line6
 line7
 line8
 line9
-this line10 to be deleted
 line11
 line12
 line13
@@ -98,3 +97,4 @@ line97
 line98
 line99
 line100
+this is new line100
 20
Author: irudyak,
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
2014-09-19 11:35:07

Domyślny format wyjściowy (który pochodzi z programu znanego jako diff, jeśli chcesz znaleźć więcej informacji) jest znany jako "unified diff". Zawiera zasadniczo 4 różne rodzaje linii:

  • linie kontekstowe, które rozpoczynają się pojedynczą spacją,
  • linie wstawiania, które pokazują linię, która została wstawiona, które zaczynają się od +,
  • linie delecji, które zaczynają się od - i
  • linie metadanych opisujące rzeczy wyższego poziomu, takie jak ten plik chodzi o to, jakie opcje zostały użyte do wygenerowania różnic, czy plik zmienił swoje uprawnienia, itd.

Radzę poćwiczyć czytanie różnic pomiędzy dwoma wersjami pliku, w którym dokładnie wiesz, co zmieniłeś. W ten sposób rozpoznacie, co się dzieje, kiedy to zobaczycie.

 12
Author: Donal Fellows,
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-03-27 14:33:23

Na moim Macu:

info diff następnie wybierz: Output formats -> Context -> Unified format -> Detailed Unified :

Lub Online man diff na gnu podążając tą samą ścieżką do tej samej sekcji:

Plik: diff.info, węzeł: szczegółowy Unified, Next: Example Unified, Up: Unified Format

Szczegółowy opis jednolitego formatu ......................................

Unified output format rozpoczyna z dwuliniowym nagłówkiem, który wygląda tak:

 --- FROM-FILE FROM-FILE-MODIFICATION-TIME
 +++ TO-FILE TO-FILE-MODIFICATION-TIME

Znacznik czasu wygląda jak `2002-02-21 23: 30: 39.942229878 -0800' aby wskazać data, godzina z ułamkiem sekund i strefy czasowej.

Możesz zmienić zawartość nagłówka z opcją '--label=LABEL'; zobacz * Note Alternate Names.::.

Dalej jeden lub więcej przystojniaków z różnice; Każdy kawałek pokazuje jeden obszar gdzie pliki różnią się. Unified format wygląda tak:

 @@ FROM-FILE-RANGE TO-FILE-RANGE @@
  LINE-FROM-EITHER-FILE
  LINE-FROM-EITHER-FILE...

Linie wspólne dla obu plików zacznij od znaku spacji. Na linie, które faktycznie różnią się między dwa pliki mają jeden z następujących znaki wskaźnika w lewym wydruku kolumna:

`+' Do pierwszego pliku została dodana linia.

`-' Linia została tutaj usunięta z pierwszego pliku.

 6
Author: stefanB,
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-03-27 14:23:36

Nie jest jasne z twojego pytania, która część diffów jest myląca: faktycznie diff lub dodatkowe informacje nagłówka Git drukuje. Na wszelki wypadek, oto szybki przegląd nagłówka.

Pierwsza linijka to coś w stylu diff --git a/path/to/file b/path/to/file - oczywiście po prostu mówi do jakiego pliku służy ta sekcja Diffa. Jeśli ustawisz zmienną Boolean config diff.mnemonic prefix, a i b zostaną zmienione na bardziej opisowe litery, takie jak c i w (commit i praca drzewo).

Następnie są "mode lines" - linie dające OPIS wszelkich zmian, które nie wymagają zmiany zawartości pliku. Obejmuje to nowe / usunięte pliki, przemianowane / skopiowane pliki i zmiany uprawnień.

W końcu jest taka linijka index 789bd4..0afb621 100644. Prawdopodobnie nigdy cię to nie obchodzi, ale te 6-cyfrowe liczby szesnastkowe są skrótem SHA1 starych i nowych obiektów blob dla tego pliku(obiekt blob jest obiektem git przechowującym surowe dane, takie jak zawartość pliku). I oczywiście, {[7] } jest trybem pliku - trzy ostatnie cyfry to oczywiście uprawnienia; pierwsze trzy dają dodatkowe informacje o metadanych pliku (więc Post opisujący to ).

Następnie przechodzimy do standardowego unified diff output(podobnie jak klasyczne diff -U). Jest podzielony na kawałki - kawałek jest sekcją pliku zawierającą zmiany i ich kontekst. Każdy plik jest poprzedzony parą linii --- i +++ oznaczających dany plik, wtedy rzeczywisty diff to (domyślnie) trzy linie kontekstu po obu stronach linii - i + pokazujące usunięte / dodane linie.

 3
Author: Cascabel,
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
2017-05-23 12:18:15

W kontroli wersji różnice między dwiema wersjami są przedstawione w tzw. " różnicy "(lub synonimicznie"poprawki"). Przyjrzyjmy się dokładnie takiemu rozróżnieniu - i nauczmy się go czytać.

Spójrz na wyjście Diffa. Na podstawie tego wyjścia zrozumiemy wyjście git diff.

Tutaj wpisz opis obrazka

Porównywane pliki a / b

Nasz diff porównuje ze sobą dwa elementy: element A i element B. w większości przypadków A i B będą tym samym plikiem, ale w różne wersje. Chociaż nie jest używany bardzo często, diff może również porównać dwa całkowicie niepowiązane ze sobą pliki, aby pokazać, jak się różnią. Aby wyjaśnić, co jest właściwie porównywane, wyjście diff zawsze zaczyna się od zadeklarowania, które pliki są reprezentowane przez "A "i"B".

Metadane Pliku

Pokazane tutaj metadane pliku to bardzo techniczna informacja, której prawdopodobnie nigdy nie będziesz potrzebował w praktyce. Pierwsze dwie liczby reprezentują hashe (lub mówiąc prościej: "IDs") z nasze dwa pliki: Git zapisuje każdą wersję nie tylko projektu, ale także KAŻDEGO pliku jako obiekt. Taki hash identyfikuje obiekt pliku przy określonej rewizji. Ostatnia liczba jest wewnętrznym identyfikatorem trybu pliku (100644 jest tylko "zwykłym plikiem", podczas gdy 100755 Określa plik wykonywalny, a 120000 reprezentuje dowiązanie symboliczne).

Markery dla a / b

Dalej na wyjściu, rzeczywiste zmiany będą oznaczone jako pochodzące z A lub B. aby je odróżnić, A i B każdy ma przypisany symbol: dla wersji A jest to znak minus ( " - " ), a dla wersji B znak plus ("+") jest używany.

Chunk

Różnica nie pokazuje całego pliku Od początku do końca: nie chcesz widzieć wszystkiego w pliku 10 000 linii, gdy tylko 2 linie się zmieniły. Zamiast tego pokazuje tylko te części, które zostały faktycznie zmodyfikowane. Taka część nazywa się" kawałkiem "(lub"kawałkiem"). Oprócz faktycznie zmienionych linii, fragment zawiera również trochę "context": kilka (niezmienionych) linii przed i po modyfikacji, abyś mógł lepiej zrozumieć, w jakim kontekście zaszła ta zmiana.

Chunk Header

Każdy z tych kawałków jest poprzedzony nagłówkiem. Zamknięty w dwóch znakach " @ " każdy, Git informuje, które linie zostały dotknięte. W naszym przypadku następujące linie są reprezentowane w pierwszym fragmencie:

  • Z pliku A (reprezentowanego przez" -") wydobywa się 6 linii zaczynających się od linii nr. 34

  • Z pliku B (reprezentowanego przez"+") wyświetlane jest 8 linii, również zaczynających się od linii nr. 34

Tekst po zamykającej parze "@ @ " ma na celu wyjaśnienie kontekstu, ponownie: Git próbuje wyświetlić nazwę metody lub inne informacje kontekstowe o tym, skąd ten fragment został pobrany w pliku. Jednak w dużym stopniu zależy to od języka programowania i nie działa we wszystkich scenariuszach.

Zmiany

Każda zmieniona linia jest poprzedzona z symbolem " + "lub" -". Jak wyjaśniono, symbole te pomagają zrozumieć, jak dokładnie wyglądają wersje A i B: linia poprzedzona znakiem " - "pochodzi z A, podczas gdy linia ze znakiem" + " pochodzi z B. W większości przypadków, Git wybiera A i B w taki sposób, że możesz myśleć o a/ - jako o "starej" zawartości, a B / + jako o "nowej" zawartości.

Spójrzmy na nasz przykład:

  • Zmiana # 1 zawiera dwie linie poprzedzone znakiem"+". Ponieważ dla tych linii nie istniał żaden odpowiednik w A (nie linie z" -"), Oznacza to, że linie te zostały dodane.

  • Zmiana # 2 jest dokładnie odwrotna: W A Mamy dwie linie oznaczone znakami" -". Jednak B nie ma odpowiednika (nie ma linii"+"), co oznacza, że zostały usunięte.

  • W zmianie # 3, w końcu niektóre linie zostały zmodyfikowane: dwie linie " - "zostały zmienione, aby wyglądały jak dwie linie" + " poniżej.

Źródło

 2
Author: Breaking Benjamin,
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
2018-04-19 13:12:21