Як читати вихід з git diff?


270

Сторінка чоловіка git-diffдосить довга, і пояснюється багато випадків, які не здаються необхідним початківцю. Наприклад:

git diff origin/master

1
використовуючи інший текстовий редактор, позначення діапазону @ ... @ для номерів рядків стали очевидними.
poseid

Який текстовий редактор?
Jus12

Відповіді:


488

Розглянемо приклад розширеного відмінності від історії git (у фільмі 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] == '-') {

Дозволяє аналізувати цей патч за рядком.

  • Перший рядок

    razl - git a / buildin-http-fetch.cb / http-fetch.c
    є заголовком "git diff" у формі diff --git a/file1 b/file2. Файли a/та b/назви файлів однакові, якщо не йдеться про перейменування / копію (як у нашому випадку). Це --gitозначає, що diff є у форматі "git".

  • Далі - один або кілька розширених рядків заголовка. Перші три

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

    Останній рядок у розширеному шафі diff, який є
    індекс f3e63d7..e8f44ba 100644
    розкажіть нам про режим заданого файлу ( 100644означає, що це звичайний файл, а не, наприклад, посилання, а також що він не має виконавчого біта дозволу), а також про скорочений хеш-преймідж (версія файлу перед заданою зміною) та пост-зображення ( версія файлу після зміни). Цей рядок використовується для того, git am --3wayщоб спробувати здійснити тристороннє злиття, якщо виправлення неможливо застосувати самостійно.

  • Далі - дворядковий уніфікований заголовок diff

    --- a / buildin-http-fetch.c
    +++ b / http-fetch.c
    У порівнянні з diff -Uрезультатом, він не має файлів-файлів-модифікацій-часу, ані часу-модифікацій-файлів після джерела (попереднього зображення) та пункту призначення (пост-зображення). Якщо файл створено, джерелом є /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;рядок було додано.

    Іншими словами, перед зміною відповідний фрагмент файлу 'izgrain-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;
    
  • Може бути

    \ Немає нового рядка в кінці файлу
    рядок присутній (він не є в прикладі розл.).

Як сказали стипендіати Доналу , найкраще практикувати читання відрізняється на прикладах із реального життя, де ви знаєте, що змінилося.

Список літератури:


1
@Geremia: Git використовує евристику на основі подібності для виявлення перейменувань ... а також для виявлення переміщення коду та копіювання в git blame -C -C, ось так це працює; це рішення дизайну Git. Формат git diff просто показує індекс подібності (або несхожості) на користувача.
Якуб Нарубський

1
@Geremia: Якщо точніше, [header]це найближчий передує, як і з початком функції, що передує лупі. У більшості випадків цей рядок містить назву функції, в якій знаходиться відрізок. Це може бути налаштовано за допомогою diffgitattribute, встановленої для diff драйвера, і diff драйвера, включаючи xfuncnameзмінну конфігурації.
Якуб Нарбський

1
@AnthonyGeoghegan: рядки можуть бути видалені (тоді кількість рядків у зображенні дорівнює 0) або додані (тоді кількість рядків у попередньому зображенні дорівнює 0).
Якуб Нарбський

1
@KasunSiyambalapitiya: Уніфікований формат diff, який використовує Git (на відміну від контекстного диференціального формату ^ [1]), не розрізняє модифіковану лінію та видалений та доданий рядок. [1]: gnu.org/software/diffutils/manual/html_node/Context-Format.html
Якуб Нарбський

1
@ JakubNarębski: Кількість рядків за замовчуванням до 1, а не до 0. Це так просто. На практиці він відображається лише як "-1" та / або "+1" для однорядних файлів, оскільки контексту для відображення немає.
Гвідо Флор

68

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

Ця частина знадобила мені час, щоб зрозуміти, тому я створив мінімальний приклад.

Формат в основному такий же, як 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 - 6.

    1
    2
    3
    4
    5
    6
    

    -означає "старий", як ми його зазвичай називаємо diff -u old new.

  • +1,4означає, що цей фрагмент другого файлу починається з рядка 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 hunk?


11
Це для тих, хто ще не зовсім зрозумів. У @@ -1,6 +1,4 @@pls не читайте -1як minus oneабо +1як plus oneнатомість читайте це як line 1 to 6у старому (першому) файлі. Зверніть увагу тут - implies "old"не на мінус. До речі, дякую за роз’яснення… хааш.
dkjain

з цього @@ -1,8 +1,9 @@ чи можна інтерпретувати те, що насправді сталося. наприклад 1) додано один рядок 2) один рядок змінюється і додається один рядок тощо. Або це з іншого способу, оскільки має бути спосіб їх отримання, оскільки git diff коректність визначає, які рядки були змінені в коді. Будь ласка, допоможіть мені, як мені справді потрібно розібратися в цьому
Kasun Siyambalapitiya

Зауважте, що це неправильне та дуже оманливе твердження у відповіді вище: " +1,4сказано, що цей фрагмент відповідає рядкам 1 - 4 другого файлу ". Це тому, що +1,4може посилатися на непередбачувані рядки контексту. Швидше, що +1,4насправді означає, що "" є 4рядки (тобто рядки контексту) у цій "версії" файлу ". Важливо , щоб зрозуміти сенс +, -і <whitespace>на початку цих рядків, як це відноситься до інтерпретації скнари. Більш наочний приклад: youtube.com/watch?v=1tqMjJeyKpw
Damilola Olowookere

23

Ось простий приклад.

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 та доданий новий рядок100 - ви отримаєте:
@@ -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

Дякую. "100644 - це біти режиму, що вказує, що це звичайний файл (не виконуваний файл і не символічне посилання)". Це "біти режиму" в Linux або просто в Git?
Тим

@Tim Не специфічно для git. Праві 3 цифри ( 644) повинні читатись у вісімці (значення: 1, 2, 4 відповідно eXecute, Write та Read дозвіл) і відповідає в цьому порядку власнику (користувачеві), потім групуванню, а потім іншим дозволам. Отже, коротше, 644це означає, що якщо написано символічно u=rw,og=r, це читається для всіх, але може бути написано лише власником. Інші цифри, що знаходяться зліва, кодують іншу інформацію, наприклад, якщо це символьне посилання тощо. Значення можна побачити github.com/git/git/blob/… , перший 1 у цій позиції - "звичайний файл".
Патрік Мевзек

15

Формат виводу за замовчуванням (який спочатку походить від програми, відомої так, diffніби ви хочете шукати більше інформації) відомий як "уніфікований розл." Він містить по суті 4 різних типів рядків:

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

Я раджу, що ви на практиці читання відрізняються між двома версіями файлу, де ви точно знаєте, що ви змінили. Так, ви зрозумієте, що відбувається, коли побачите це.


5
+1: Про практику дуже гарна пропозиція - можливо, набагато швидше, ніж намагатися нав’язливо читати документацію.
Каскабель

6

На моєму комп'ютері:

info diffпотім виберіть: Output formats-> Context-> Unified format-> Detailed Unified:

Або людина в Інтернеті відрізняється від 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...

Рядки, спільні для обох файлів, починаються з пробілу. Рядки, які насправді відрізняються між двома файлами, містять один із наступних символьних символів у лівій колонці друку:

`+ 'Тут було додано рядок до першого файлу.

`- 'З першого файлу тут видалено рядок.


1
Зауважте, що git не друкує частину "XXX-FILE-MODIFICATION-TIME", оскільки це не має сенсу для системи контролю версій. Для порівняння файлів у файлових системах часові позначки можуть функціонувати як "бідний чоловік" управління версіями.
Якуб Нарбський

3

З вашого запитання незрозуміло, яку частину дифузів ви вважаєте заплутаною: власне diff чи надруковані додаткові відомості заголовка. На всякий випадок, ось короткий огляд заголовка.

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

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

Нарешті, є така лінія, як index 789bd4..0afb621 100644. Вам, мабуть, ніколи не буде про це байдуже, але ці 6-значні шістнадцяткові цифри - це скорочені хеші SHA1 старого та нового крапок цього файлу (blob - це git-об’єкт, який зберігає необроблені дані, як вміст файлу). І звичайно, 100644це режим файлу - три останні цифри, очевидно, є дозволами; перші три дають додаткову інформацію про метадані файлів ( повідомлення, що описує це ).

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


++ для indexрядка. Підтвердженоgit hash-object ./file
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.