Làm thế nào để đọc đầu ra từ git diff?


270

Trang dành cho nam git-diffkhá dài và giải thích nhiều trường hợp dường như không cần thiết cho người mới bắt đầu. Ví dụ:

git diff origin/master

1
bằng cách sử dụng một trình soạn thảo văn bản khác, các ký hiệu phạm vi @ ... @ cho số dòng trở nên rõ ràng.
đặt ra vào

Trình soạn thảo văn bản nào?
Jus12

Câu trả lời:


488

Hãy xem ví dụ nâng cao khác với lịch sử git (trong cam kết 1088261f trong kho 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] == '-') {

Hãy phân tích dòng vá này theo từng dòng.

  • Dòng đầu tiên

    diff --git a / buildin-http-fetch.cb / http-fetch.c
    là một tiêu đề "git diff" trong mẫu diff --git a/file1 b/file2. Tên a/b/tên giống nhau trừ khi có liên quan đến đổi tên / sao chép (như trong trường hợp của chúng tôi). Điều --gitnày có nghĩa là diff ở định dạng diff "git".

  • Tiếp theo là một hoặc nhiều dòng tiêu đề mở rộng. Ba đầu tiên

    chỉ số tương tự 95%
    đổi tên từ buildin-http-fetch.c
    đổi tên thành http-fetch.c
    cho chúng tôi biết rằng tệp đã được đổi tên từ builtin-http-fetch.cthành http-fetch.cvà hai tệp đó giống nhau 95% (được sử dụng để phát hiện đổi tên này).

    Dòng cuối cùng trong tiêu đề diff mở rộng, đó là
    chỉ số f3e63d7..e8f44ba 100644
    cho chúng tôi biết về chế độ của tệp đã cho ( 100644có nghĩa là đó là tệp thông thường chứ không phải là symlink và nó không có bit cấp phép thực thi) và về hàm băm của preimage (phiên bản của tệp trước khi thay đổi đã cho) và postimage ( phiên bản của tập tin sau khi thay đổi). Dòng này được sử dụng git am --3wayđể cố gắng thực hiện hợp nhất 3 chiều nếu bản vá không thể được áp dụng.

  • Tiếp theo là tiêu đề diff hai dòng thống nhất

    --- a / buildin-http-fetch.c
    +++ b / http-fetch.c
    So với diff -Ukết quả, nó không có tên tệp từ thời gian sửa đổi tệp cũng như thời gian sửa đổi tệp sau thời gian tên nguồn (tiền tố) và tên đích (bưu chính). Nếu tập tin được tạo, nguồn là /dev/null; nếu tập tin bị xóa, mục tiêu là /dev/null.
    Nếu bạn đặt diff.mnemonicPrefixbiến cấu hình là true, ở vị trí của a/b/tiền tố trong tiêu đề hai dòng này bạn có thể có thay c/, i/, w/o/như tiền tố, tương ứng với những gì bạn so sánh; xem git-config (1)

  • Tiếp đến là một hoặc nhiều người khác biệt; mỗi hunk hiển thị một khu vực nơi các tệp khác nhau. Hunk định dạng hợp nhất bắt đầu với dòng như

    @@ -1,8 +1,9 @@
    hoặc là
    @@ -18,6 +19,8 @@ int cmd_http_fetch (int argc, const char ** argv, ...
    Nó ở định dạng @@ from-file-range to-file-range @@ [header]. Phạm vi từ tệp có dạng và phạm vi -<start line>,<number of lines>đến tệp là +<start line>,<number of lines>. Cả hai dòng bắt đầu và số dòng đều tương ứng với vị trí và độ dài của hunk trong preimage và postimage. Nếu số dòng không hiển thị thì có nghĩa là 0.

    Tiêu đề tùy chọn hiển thị chức năng C trong đó mỗi thay đổi xảy ra, nếu đó là tệp C (như -ptùy chọn trong GNU diff) hoặc tương đương, nếu có, đối với các loại tệp khác.

  • Tiếp đến là mô tả về nơi các tập tin khác nhau. Các dòng chung cho cả hai tệp bắt đầu bằng một ký tự khoảng trắng. Các dòng thực sự khác nhau giữa hai tệp có một trong các ký tự chỉ báo sau trong cột in bên trái:

    • '+' - Một dòng đã được thêm vào đây vào tệp đầu tiên.
    • '-' - Một dòng đã bị xóa ở đây từ tệp đầu tiên.


    Vì vậy, ví dụ, chunk đầu tiên

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

    có nghĩa là cmd_http_fetchđã được thay thế bởi main, và const char *prefix;dòng đó đã được thêm vào.

    Nói cách khác, trước khi thay đổi, đoạn thích hợp của tệp 'dựng sẵn-http-fetch.c' trông như thế này:

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

    Sau khi thay đổi, đoạn này của tệp 'http-fetch.c' trông giống như thế này:

    #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;
    
  • Có thể có

    \ Không có dòng mới ở cuối tệp
    dòng hiện tại (nó không phải là trong ví dụ khác).

Như Donal Fellows nói rằng tốt nhất là thực hành đọc khác biệt trên các ví dụ thực tế, nơi bạn biết những gì bạn đã thay đổi.

Người giới thiệu:


1
@Geremia: Git sử dụng các heuristic dựa trên sự tương tự để đổi tên phát hiện ... và cũng để di chuyển mã và phát hiện sao chép trong git blame -C -Cđó, đó là cách nó hoạt động; đó là quyết định thiết kế Git. Định dạng git diff chỉ hiển thị chỉ số tương tự (hoặc không giống nhau) cho người dùng.
Jakub Narębski

1
@Geremia: Nói chính xác hơn, [header]là phần trước gần nhất giống như khi bắt đầu chức năng đi trước một hunk. Trong hầu hết các trường hợp, dòng này bao gồm tên của hàm trong đó khối của diff là. Đây là cấu hình với diffgitattribution được đặt thành trình điều khiển diff và trình điều khiển diff bao gồm cả xfuncnamebiến cấu hình.
Jakub Narębski

1
@AnthonyGeoghegan: các dòng có thể bị xóa (sau đó số dòng trong postimage là 0) hoặc được thêm vào (sau đó số dòng trong preimage là 0).
Jakub Narębski

1
@KasunSiyambalapitiya: Định dạng khác biệt hợp nhất mà Git sử dụng (trái ngược với định dạng khác biệt ngữ cảnh ^ [1]) không phân biệt giữa dòng được sửa đổi, và dòng bị xóa và thêm. [1]: gnu.org/software/diffutils/manual/html_node/Context-Format.html
Jakub Narębski

1
@ JakubNarębski: Số dòng mặc định là 1, không phải là 0. Nó đơn giản như vậy. Trong thực tế, nó chỉ xuất hiện dưới dạng "-1" và / hoặc "+1" cho các tệp một dòng vì không có ngữ cảnh để hiển thị.
Guido Flohr

68

@@ -1,2 +3,4 @@ một phần của khác

Phần này khiến tôi mất một lúc để hiểu, vì vậy tôi đã tạo ra một ví dụ tối thiểu.

Các định dạng về cơ bản là giống nhau diff -u.

Ví dụ:

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

Ở đây chúng tôi đã loại bỏ các dòng 2, 3, 14 và 15. Kết quả:

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

@@ -1,6 +1,4 @@ có nghĩa:

  • -1,6có nghĩa là phần này của tệp đầu tiên bắt đầu ở dòng 1 và hiển thị tổng cộng 6 dòng. Do đó, nó hiển thị các dòng 1 đến 6.

    1
    2
    3
    4
    5
    6
    

    -có nghĩa là "cũ", như chúng ta thường gọi nó là diff -u old new.

  • +1,4có nghĩa là đoạn này của tệp thứ hai bắt đầu ở dòng 1 và hiển thị tổng cộng 4 dòng. Do đó, nó hiển thị các dòng 1 đến 4.

    + có nghĩa là "mới".

    Chúng tôi chỉ có 4 dòng thay vì 6 vì đã xóa 2 dòng! Hunk mới chỉ là:

    1
    4
    5
    6
    

@@ -11,6 +9,4 @@ cho hunk thứ hai là tương tự:

  • trên tệp cũ, chúng tôi có 6 dòng, bắt đầu từ dòng 11 của tệp cũ:

    11
    12
    13
    14
    15
    16
    
  • trên tệp mới, chúng tôi có 4 dòng, bắt đầu từ dòng 9 của tệp mới:

    11
    12
    13
    16
    

    Lưu ý rằng dòng 11này là dòng thứ 9 của tệp mới vì chúng tôi đã xóa 2 dòng trên hunk trước: 2 và 3.

Tiêu đề Hunk

Tùy thuộc vào phiên bản và cấu hình git của bạn, bạn cũng có thể nhận được một dòng mã bên cạnh @@dòng, ví dụ func1() {: trong:

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

Điều này cũng có thể thu được với -pcờ của đồng bằng diff.

Ví dụ: tập tin cũ:

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

Nếu chúng tôi loại bỏ dòng 6, khác biệt hiển thị:

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

Lưu ý rằng đây không phải là dòng chính xác cho func1: nó bỏ qua các dòng 12.

Tính năng tuyệt vời này thường cho biết chính xác chức năng hoặc lớp mà mỗi hunk thuộc về, rất hữu ích để giải thích khác biệt.

Làm thế nào thuật toán để chọn tiêu đề hoạt động chính xác được thảo luận tại: Đoạn trích trong tiêu đề hunk diff đến từ đâu?


11
Điều này là cho bất cứ ai vẫn chưa hiểu rõ. Trong @@ -1,6 +1,4 @@xin đừng đọc -1như minus onehoặc +1plus onethay vì đọc như line 1 to 6ở cũ (đầu tiên) tập tin. Lưu ý ở đây - implies "old"không trừ. BTW, cảm ơn đã làm rõ ... haash.
dkjain

từ đây @@ -1,8 +1,9 @@ có thể diễn giải những gì thực sự đã xảy ra. ví dụ 1) một dòng đã được thêm 2) một dòng đang được sửa đổi và một dòng được thêm vào và cứ thế. Hoặc là từ một cách khác, vì nên có cách để có được chúng khi git diff Correclty xác định những dòng nào đã được sửa đổi trong mã. Xin hãy giúp tôi vì tôi thực sự cần phải giải quyết vấn đề này
Kasun Siyambalapitiya

Xin lưu ý rằng nó không chính xác và rất sai lệch, tuyên bố này trong câu trả lời ở trên: " +1,4nói rằng phần này tương ứng với dòng 1 đến 4 của tệp thứ hai ". Điều này là do +1,4có thể đề cập đến các dòng bối cảnh không dự phòng. Thay vào đó, " +1,4" thực sự có nghĩa là gì " 4các dòng (tức là các dòng ngữ cảnh) trong 'phiên bản' của tệp đó ". Điều quan trọng là phải hiểu ý nghĩa của +, -<whitespace>vào đầu những dòng này, vì nó áp dụng cho việc giải thích hunks. Một ví dụ trực quan hơn: youtube.com/watch?v=1tqMjJeyKpw
Damilola Olowookere

23

Đây là ví dụ đơn giản.

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

Đây là một lời giải thích (xem chi tiết ở đây ).

  • --git không phải là một lệnh, điều này có nghĩa là đây là phiên bản git của diff (không phải unix)
  • a/ b/là những thư mục, chúng không có thật. thật tiện lợi khi chúng ta xử lý cùng một tệp (trong trường hợp của tôi a / nằm trong chỉ mục và b / nằm trong thư mục làm việc)
  • 10ff2df..84d4fa2 là ID blob của 2 tệp này
  • 100644 là các bit chế độ của người dùng, có nghĩa là đây là một tệp thông thường (không thể thực thi và không phải là một liên kết tượng trưng)
  • --- a/file +++ b/filedấu trừ cho thấy các dòng trong phiên bản a / nhưng thiếu từ phiên bản b /; và dấu cộng hiển thị các dòng bị thiếu trong a / nhưng hiện diện trong b / (trong trường hợp của tôi --- có nghĩa là các dòng bị xóa và +++ có nghĩa là các dòng được thêm vào trong b / và đây là tệp trong thư mục làm việc)
  • @@ -1,5 +1,5 @@để hiểu điều này tốt hơn là làm việc với một tệp lớn; nếu bạn có hai thay đổi ở những nơi khác nhau, bạn sẽ nhận được hai mục như @@ -1,5 +1,5 @@; giả sử bạn có tệp line1 ... line100 và xóa line10 và thêm dòng mới 100 - bạn sẽ nhận được:
@@ -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

Cảm ơn. "100644 là các bit chế độ, chỉ ra rằng đây là một tệp thông thường (không thể thực thi và không phải là một liên kết tượng trưng)". "Bit chế độ" là một khái niệm trong Linux, hay chỉ trong Git?
Tim

@Tim Không cụ thể để git. 3 chữ số bên phải ( 644) sẽ được đọc theo số bát phân (giá trị: 1, 2, 4 tương ứng là quyền eXecute, Write và Read) và tương ứng theo thứ tự đó cho Chủ sở hữu (Người dùng), sau đó Nhóm, sau đó là các quyền khác. Vì vậy, trong ngắn hạn 644có nghĩa là nếu được viết tượng trưng u=rw,og=r, mọi người đều có thể đọc được nhưng chỉ có thể ghi được bởi chủ sở hữu. Các chữ số khác ở bên trái mã hóa thông tin khác, như nếu đó là một liên kết tượng trưng, ​​v.v ... Các giá trị có thể được nhìn thấy github.com/git/git/blob/ , một trong những vị trí đầu tiên ở vị trí này là "tệp thông thường".
Patrick Mevzek

15

Định dạng đầu ra mặc định (ban đầu xuất phát từ một chương trình được biết là diffnếu bạn muốn tìm kiếm thêm thông tin) được gọi là một tập hợp khác biệt của Hồi giáo. Nó chứa 4 loại đường khác nhau:

  • dòng bối cảnh, bắt đầu với một không gian duy nhất,
  • các dòng chèn hiển thị một dòng đã được chèn, bắt đầu bằng một +,
  • các dòng xóa, bắt đầu bằng một -, và
  • các dòng siêu dữ liệu mô tả những thứ ở cấp độ cao hơn như tệp này đang nói về, tùy chọn nào được sử dụng để tạo khác biệt, liệu tệp có thay đổi quyền của nó hay không, v.v.

Tôi khuyên bạn nên thực hành đọc khác nhau giữa hai phiên bản của tệp nơi bạn biết chính xác những gì bạn đã thay đổi. Như thế bạn sẽ nhận ra những gì đang diễn ra khi bạn nhìn thấy nó.


5
+1: Gợi ý về thực hành là một điều rất tốt - có thể nhanh hơn nhiều so với việc cố gắng đọc tài liệu một cách ám ảnh.
Cascabel

6

Trên máy mac của tôi:

info diffsau đó chọn: Output formats-> Context-> Unified format-> Detailed Unified:

Hoặc người đàn ông trực tuyến khác trên gnu theo cùng một đường dẫn đến cùng một phần:

Tệp: diff.info, Nút: Hợp nhất chi tiết, Tiếp theo: Ví dụ Hợp nhất, Lên: Định dạng hợp nhất

Mô tả chi tiết về định dạng hợp nhất ......................................

Định dạng đầu ra hợp nhất bắt đầu bằng tiêu đề hai dòng, trông như thế này:

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

Dấu thời gian trông giống như `2002 / 02-21 23: 30: 39.942229878 -0800 'để chỉ ngày, giờ với giây phân số và múi giờ.

Bạn có thể thay đổi nội dung của tiêu đề bằng tùy chọn `--label = LABEL '; xem * Lưu ý Tên thay thế ::.

Tiếp đến là một hoặc nhiều người khác biệt; mỗi hunk hiển thị một khu vực nơi các tệp khác nhau. Hunk định dạng thống nhất trông như thế này:

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

Các dòng chung cho cả hai tệp bắt đầu bằng một ký tự khoảng trắng. Các dòng thực sự khác nhau giữa hai tệp có một trong các ký tự chỉ báo sau trong cột in bên trái:

`+ 'Một dòng đã được thêm vào đây vào tệp đầu tiên.

`- 'Một dòng đã bị xóa ở đây từ tệp đầu tiên.


1
Lưu ý rằng git không in phần 'XXX-FILE-MODUALATION-TIME', vì nó không có ý nghĩa đối với hệ thống kiểm soát phiên bản. Để so sánh các tệp trên dấu thời gian của hệ thống tệp có thể hoạt động như kiểm soát phiên bản "người nghèo".
Jakub Narębski

3

Không rõ từ câu hỏi của bạn phần nào của các khác biệt bạn thấy khó hiểu: thực sự khác biệt, hoặc thông tin tiêu đề bổ sung git in. Chỉ trong trường hợp, đây là một tổng quan nhanh về tiêu đề.

Dòng đầu tiên giống như diff --git a/path/to/file b/path/to/file- rõ ràng nó chỉ cho bạn biết phần này của diff là dành cho. Nếu bạn đặt biến cấu hình boolean diff.mnemonic prefix, absẽ được thay đổi thành các chữ cái mô tả hơn như cw(cây cam kết và công việc).

Tiếp theo, có "các dòng chế độ" - các dòng cung cấp cho bạn một mô tả về bất kỳ thay đổi nào không liên quan đến việc thay đổi nội dung của tệp. Điều này bao gồm các tệp mới / bị xóa, đổi tên / sao chép tệp và thay đổi quyền.

Cuối cùng, có một dòng như thế index 789bd4..0afb621 100644. Có thể bạn sẽ không bao giờ quan tâm đến nó, nhưng các số hex gồm 6 chữ số đó là các băm SHA1 viết tắt của các đốm màu cũ và mới cho tệp này (một blob là một đối tượng git lưu trữ dữ liệu thô như nội dung của tệp). Và tất nhiên, 100644chế độ của tệp - ba chữ số cuối cùng rõ ràng là quyền; ba phần đầu cung cấp thêm thông tin siêu dữ liệu ( bài SO mô tả điều đó ).

Sau đó, bạn tiếp tục với đầu ra khác biệt thống nhất tiêu chuẩn (giống như cổ điển diff -U). Nó được chia thành nhiều phần - một hunk là một phần của tệp chứa các thay đổi và bối cảnh của chúng. Mỗi hunk được đi trước bởi một cặp ---+++các dòng biểu thị tệp đang được đề cập, sau đó khác biệt thực tế là (theo mặc định) ba dòng ngữ cảnh ở hai bên -+các dòng hiển thị các dòng bị xóa / thêm.


++ cho indexdòng. Xác nhận vớigit hash-object ./file
Ciro Santilli 冠状 病 六四 事件
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.