it-swarm.com.ru

Как прочитать вывод из git diff?

Страница man для git-diff довольно длинная и объясняет многие случаи, которые не кажутся необходимыми для новичка. Например:

git diff Origin/master
243
poseid

Давайте взглянем на пример расширенного diff из истории git (в commit 1088261f в репозитории git.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] == '-') {

Давайте проанализируем этот патч построчно.

  • Первая строка

    diff --git a/builtin-http-fetch.c b/http-fetch.c
    - это заголовок "git diff" в виде diff --git a/file1 b/file2. Имена файлов a/ и b/ одинаковы, если не использовать переименование/копирование (как в нашем случае). --git означает, что diff находится в формате "git" diff.
  • Далее идет одна или несколько расширенных строк заголовка. Первые три

    индекс сходства 95% 
     переименовывать из встроенного-http-fetch.c 
     переименовывать в http-fetch.c
    сообщает нам, что файл был переименован из builtin-http-fetch.c в http-fetch.c и что эти два файла идентичны на 95% (который использовался для обнаружения этого переименования).

    Последняя строка в расширенном заголовке diff, то есть
    индекс f3e63d7..e8f44ba 100644
    расскажет нам о режиме данного файла (100644 означает, что это обычный файл, а не, например, symlink, и что он не имеет исполняемого бита разрешения), и о сокращенном хеше прообраза (версия файла перед данным изменить) и postimage (версия файла после изменения). Эта строка используется git am --3way, чтобы попытаться выполнить трехстороннее слияние, если исправление не может быть применено само по себе.

  • Далее идет двухстрочный унифицированный заголовок diff

    --- a/builtin-http-fetch.c 
     +++ b/http-fetch.c
    По сравнению с результатом diff -U он не имеет ни времени изменения файла, ни времени изменения файла после имен файлов источника (preimage) и назначения (postimage). Если файл был создан, источником является /dev/null; если файл был удален, целью является /dev/null.
    Если для переменной конфигурации diff.mnemonicPrefix задано значение true, вместо префиксов a/ и b/ в этом двухстрочном заголовке вместо него можно использовать c/, i/, w/ и o/ в качестве префиксов соответственно; смотрите git-config (1)
  • Далее следуют один или несколько кусков различий; каждый блок показывает одну область, где файлы отличаются. Унифицированный формат блоков начинается со строки вроде

    @@ -1,8 +1,9 @@
    или же
    @@ -18,6 +19,8 @@ int cmd_http_fetch (int argc, const char ** argv, ...
    Это в формате @@ from-file-range to-file-range @@ [header]. Диапазон from-file находится в форме -<start line>,<number of lines>, а диапазон to-file - +<start line>,<number of lines>. И начальная строка, и количество строк относятся к положению и длине фрагмента в прообразе и постимейке соответственно. Если число строк не указано, это означает, что оно равно 0.

    Необязательный заголовок показывает функцию C, в которой происходит каждое изменение, если это файл C (например, параметр -p в GNU diff) или эквивалент, если таковой имеется, для других типов файлов.

  • Далее идет описание, где файлы отличаются. Строки, общие для обоих файлов, начинаются с пробела. Строки, которые на самом деле различаются между двумя файлами, имеют один из следующих символов индикатора в левом столбце печати:

    • '+' - здесь была добавлена ​​строка в первый файл.
    • '-' - Здесь была удалена строка из первого файла.


    Так, например, первый кусок

     #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;
    

    означает, что cmd_http_fetch был заменен на main, и была добавлена ​​строка const char *prefix;.

    Другими словами, перед изменением соответствующий фрагмент затем файла buildin-http-fetch.c выглядел так:

    #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;
    

    После изменения этот фрагмент теперь файла http-fetch.c выглядит следующим образом:

    #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;
    
  • Там может быть

    \ Нет новой строки в конце файла
    строка присутствует (это не в примере diff).

Как сказал Донал Феллоуз лучше всего попрактиковаться в чтении различий на реальных примерах, где вы знаете, что изменили.

Ссылки:

447
Jakub Narębski

@@ -1,2 +3,4 @@ часть diff

Эта часть заняла у меня некоторое время, чтобы понять, поэтому я создал минимальный пример.

Формат в основном совпадает с унифицированным diff diff -u.

Например:

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

Здесь мы удалили строки 2, 3, 14 и 15. Вывод:

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

@@ -1,6 +1,4 @@ означает:

  • -1,6: этот фрагмент соответствует строкам с 1 по 6 первого файла:

    1
    2
    3
    4
    5
    6
    

    - означает "старый", так как мы обычно вызываем его как diff -u old new.

  • +1,4 говорит, что этот фрагмент соответствует строкам с 1 по 4 второго файла.

    + означает "новый".

    У нас только 4 строки вместо 6, потому что 2 строки были удалены! Новый кусок просто:

    1
    4
    5
    6
    

@@ -11,6 +9,4 @@ для второго блока аналогичен:

  • в старом файле у нас есть 6 строк, начиная со строки 11 старого файла:

    11
    12
    13
    14
    15
    16
    
  • в новом файле у нас есть 4 строки, начиная с 9 строки нового файла:

    11
    12
    13
    16
    

    Обратите внимание, что строка 11 является 9-й строкой нового файла, поскольку мы уже удалили 2 строки в предыдущем блоке: 2 и 3.

Заголовок блока

В зависимости от вашей версии и конфигурации git вы также можете получить строку кода рядом со строкой @@, например, func1() { в:

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

Это также может быть получено с флагом -p простого diff.

Пример: старый файл:

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

Если мы удалим строку 6, diff покажет:

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

Обратите внимание, что это неправильная строка для func1: она пропустила строки 1 и 2.

Эта удивительная особенность часто говорит точно, к какой функции или классу принадлежит каждый кусок, что очень полезно для интерпретации diff.

Как именно работает алгоритм выбора заголовка, обсуждается в: Откуда взята выдержка из заголовка git diff?

Вот простой пример.

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

Вот объяснение (см. Подробности здесь ).

  • --git - это не команда, это означает, что это git-версия diff (не unix)
  • a/ b/ являются каталогами, они не настоящие. это просто удобство, когда мы имеем дело с одним и тем же файлом (в моем случае a/находится в индексе, а b/в рабочем каталоге)
  • 10ff2df..84d4fa2 являются идентификаторами BLOB-объектов этих двух файлов
  • 100644 - это "биты режима", указывающие, что это обычный файл (не исполняемый и не символическая ссылка)
  • --- a/file +++ b/file минус показывает строки в версии a /, но отсутствует в версии b /; и знаки плюс показывают строки, отсутствующие в /, но присутствующие в b/(в моем случае --- означает удаленные строки, а +++ означает добавленные строки в b/и этот файл в рабочем каталоге)
  • @@ -1,5 +1,5 @@ чтобы понять это, лучше работать с большим файлом; если у вас есть два изменения в разных местах, вы получите две записи, такие как @@ -1,5 +1,5 @@; Предположим, у вас есть файл line1 ... line100 и удаленный line10 и добавьте новый line100 - вы получите
@@ -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
21
irudyak

Формат вывода по умолчанию (который изначально исходит из программы, известной как diff, если вы хотите получить дополнительную информацию), известен как "унифицированный diff". Он содержит по существу 4 различных типа линий:

  • контекстные строки, которые начинаются с одного пробела,
  • линии вставки, которые показывают вставленную строку, начинающуюся с +,
  • строки удаления, начинающиеся с -, и
  • строки метаданных, которые описывают вещи более высокого уровня, например, о каком файле идет речь, какие параметры использовались для генерации различий, менял ли файл свои разрешения и т. д.

Я советую вам практиковать чтение различий между двумя версиями файла, где вы точно знаете, что вы изменили. Таким образом, вы узнаете, что происходит, когда увидите это.

14
Donal Fellows

На моем Mac:

info diff затем выберите: Output formats -> Context -> Unified format -> Detailed Unified:

Или online man diff в GNU по тому же пути к тому же разделу:

Файл: diff.info, узел: подробный унифицированный, следующий: пример унифицированный, вверх: унифицированный формат

Подробное описание унифицированного формата ......................................

Унифицированный формат вывода начинается с двухстрочного заголовка, который выглядит следующим образом:

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

Отметка времени выглядит как `2002-02-21 23: 30: 39.942229878 -0800 ', чтобы указать дату, время с долями секунды и часовой пояс.

Вы можете изменить содержимое заголовка с помощью опции --lala = LABEL; см. * Примечание к альтернативным именам ::.

Далее следуют один или несколько кусков различий; каждый блок показывает одну область, где файлы отличаются. Единицы унифицированного формата выглядят так:

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

Строки, общие для обоих файлов, начинаются с пробела. Строки, которые на самом деле различаются между двумя файлами, имеют один из следующих символов индикатора в левом столбце печати:

`+ 'Здесь добавлена ​​строка в первый файл.

`- 'Здесь была удалена строка из первого файла.

6
stefanB

В управлении версиями различия между двумя версиями представлены в том, что называется "diff" (или, как синоним, "патч"). Давайте подробно рассмотрим такую ​​разницу - и научимся ее читать.

Посмотрите на вывод diff. На основе этого вывода мы будем понимать вывод git diff.

enter image description here

Сравненные файлы a/b

Наш diff сравнивает два элемента друг с другом: элемент A и элемент B. В большинстве случаев A и B будут одним и тем же файлом, но в разных версиях. Хотя не очень часто, diff может также сравнить два совершенно не связанных между собой файла, чтобы показать, как они отличаются. Чтобы прояснить, что на самом деле сравнивается, вывод diff всегда начинается с объявления, какие файлы представлены буквами "A" и "B".

Метаданные файла

Метаданные файла, показанные здесь, являются очень технической информацией, которая вам, вероятно, никогда не понадобится на практике. Первые два числа представляют хэши (или, проще говоря: "идентификаторы") наших двух файлов: Git сохраняет каждую версию не только проекта, но и каждого файла как объект. Такой хеш идентифицирует объект файла в определенной ревизии. Последнее число является внутренним идентификатором режима файла (100644 - это просто "обычный файл", в то время как 100755 указывает на исполняемый файл, а 120000 представляет символическую ссылку).

Маркеры для a/b

Далее в выходных данных фактические изменения будут помечены как поступающие от A или B. Чтобы отделить их друг от друга, каждому A и B назначается символ: для версии A это знак минус ("-") и для версии B используется знак плюс ("+").

Chunk

Разница не показывает полный файл от начала до конца: вы не хотели бы видеть все в файле с 10 000 строк, когда изменились только 2 строки. Вместо этого он показывает только те части, которые были фактически изменены. Такая часть называется "кусок" (или "кусок"). В дополнение к фактически измененным строкам в чанке также есть немного "контекста": некоторые (неизменные) строки до и после модификации, чтобы вы могли лучше понять, в каком контексте произошло это изменение.

Заголовок блока

Каждому из этих блоков предшествует заголовок, заключенный между двумя маркерами @@. Git использует заголовок, чтобы сказать вам, какие строки были затронуты. В нашем случае следующие строки представлены в первом фрагменте:

  • Из файла A (представленного "-") извлекаются 6 строк, начиная со строки №. 34

  • Из файла B (обозначенного "+") отображаются 8 строк, также начиная со строки №. 34

Текст после закрывающей пары "@@" направлен на пояснение контекста, опять же: Git пытается отобразить имя метода или другую контекстную информацию о том, откуда этот фрагмент был взят в файле. Однако это сильно зависит от языка программирования и работает не во всех сценариях.

Изменения

К каждой измененной строке добавляется символ "+" или "-". Как объяснено, эти символы помогают понять, как именно выглядят версии A и B: строка, начинающаяся со знака "-", идет от A, а строка со знаком "+" - от B. В большинстве случаев Git выбирает A и B таким образом, что вы можете думать о A/- как о "старом" контенте, а B/+ как о "новом" контенте.

Давайте посмотрим на наш пример:

  • Изменение № 1 содержит две строки с добавлением "+". Поскольку для этих строк не было аналога в A (строк с "-" нет), это означает, что эти строки были добавлены.

  • Изменение № 2 как раз наоборот: в A у нас есть две строки, отмеченные знаком "-". Однако B не имеет эквивалента (без "+" строк), что означает, что они были удалены.

  • В изменении №3, наконец, некоторые строки были фактически изменены: две строки "-" были изменены, чтобы выглядеть как две строки "+" ниже.

Источник

5
Breaking Benjamin

Из вашего вопроса неясно, какую часть различий вы находите непонятной: фактическую разность или дополнительную информацию заголовка, которую печатает git. На всякий случай вот краткий обзор шапки.

Первая строка - это что-то вроде diff --git a/path/to/file b/path/to/file - очевидно, она просто говорит вам, для какого файла предназначен этот раздел diff. Если вы установите логическую переменную конфигурации diff.mnemonic prefix, a и b будут изменены на более описательные буквы, такие как c и w (дерево коммитов и работы).

Далее, есть "строки режима" - строки, дающие вам описание любых изменений, которые не связаны с изменением содержимого файла. Это включает в себя новые/удаленные файлы, переименованные/скопированные файлы и изменения разрешений.

Наконец, есть строка вроде index 789bd4..0afb621 100644. Вы, вероятно, никогда об этом не позаботитесь, но эти шестизначные шестнадцатеричные числа - это сокращенные SHA1-хэши старых и новых больших двоичных объектов для этого файла (большой двоичный объект - это git-объект, хранящий необработанные данные, например содержимое файла). И, конечно, 100644 - это режим файла - последние три цифры, очевидно, являются разрешениями; первые три дают дополнительную информацию о метаданных файла ( ТАК сообщение об этом ).

После этого вы переходите к стандартному унифицированному выводу различий (как в классическом diff -U). Он разделен на фрагменты - блок - это раздел файла, содержащий изменения и их контекст. Каждому блоку предшествует пара строк --- и +++, обозначающих рассматриваемый файл, тогда фактическая разница составляет (по умолчанию) три строки контекста по обе стороны от строк - и +, показывающих удаленные/добавленные строки.

3
Cascabel