Как прочитать вывод из git diff? - PullRequest
246 голосов
/ 27 марта 2010

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

git diff origin/master

Ответы [ 7 ]

450 голосов
/ 27 марта 2010

Давайте рассмотрим пример расширенного 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.
  • Далее следуют одна или несколько расширенных строк заголовка.Первые три

    similarity index 95%
    rename from builtin-http-fetch.c
    rename to http-fetch.c
    говорят нам, что файл был переименован с builtin-http-fetch.c на http-fetch.c и что эти два файла идентичны на 95% (что использовалось для определения этого переименования).

    Последняя строка врасширенный заголовок diff, который
    index 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].Диапазон «от файла» имеет вид -<start line>,<number of lines>, а диапазон «к файлу» - +<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;.

    Другими словами, перед изменением соответствующий фрагмент файла 'builtin-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;
    
  • Возможно, строка

    \ No newline at end of file
    присутствует (это не в примере diff).

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

Ссылки:

57 голосов

@@ -1,2 +3,4 @@ часть различий

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

Формат в основном такой же, как и diff -u унифицированный diff.

Например:

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, разница покажет:

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

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

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

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

21 голосов
/ 19 сентября 2014

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

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-объектов этих 2 файлов
  • 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
14 голосов
/ 27 марта 2010

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

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

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

6 голосов
/ 19 апреля 2018

В управлении версиями различия между двумя версиями представлены в том, что называется «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 используется знак плюс ("+").

Кусок

Дифференциал не показывает полный файл от начала до конца: вы не хотели бы видеть все в файле с 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, наконец, некоторые строки были фактически изменены: две строки "-" были изменены, чтобы выглядеть как две строки "+" ниже.

Источник

6 голосов
/ 27 марта 2010

На моем Mac:

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

Или онлайн человекdiff в GNU по тому же пути к тому же разделу:

Файл: diff.info, Узел: подробный унифицированный, Далее: Пример унифицированный, Вверх: унифицированный формат

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

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

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

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

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

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

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

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

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

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

3 голосов
/ 27 марта 2010

Из вашего вопроса неясно, какую часть различий вы находите запутанной: фактическую разность или дополнительную информацию заголовка, которую печатает 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). Он разделен на фрагменты - блок - это раздел файла, содержащий изменения и их контекст. Каждому блоку предшествует пара строк --- и +++, обозначающих рассматриваемый файл, тогда фактическая разница составляет (по умолчанию) три строки контекста по обе стороны от строк - и +, показывающих удаленные / добавленные строки.

...