簡體中文 ▾ 主題 ▾ 最新版本 ▾ gitdiffcore 最後更新於 2.44.0

名稱

gitdiffcore - 調整差異輸出

概要

git diff *

描述

git diff-indexgit diff-filesgit diff-tree 等差異命令可以在顯示差異輸出之前,以非傳統方式處理它們發現的差異。這種處理統稱為“diffcore 轉換”。本簡要說明描述了它們是什麼以及如何使用它們來生成比傳統差異輸出更容易理解的差異輸出。

操作鏈

git diff-* 系列命令的工作原理是首先比較兩組檔案

  • git diff-index 比較“樹”物件的內容與工作目錄(當不使用 --cached 標誌時),或比較“樹”物件與索引檔案(當使用 --cached 標誌時);

  • git diff-files 比較索引檔案與工作目錄的內容;

  • git diff-tree 比較兩個“樹”物件的內容;

在所有這些情況下,命令本身首先透過命令列上給定的任何路徑規範(pathspecs)可選地限制兩組檔案,然後比較兩組結果檔案中對應的路徑。

路徑規範用於限制差異操作的範圍。它們會移除指定路徑名集之外的檔案對。例如,如果輸入的檔案對集包含

:100644 100644 bcd1234... 0123456... M junkfile

但如果命令呼叫是 git diff-files myfile,那麼 junkfile 條目將從列表中刪除,因為只考慮“myfile”。

比較結果從這些命令傳遞到內部稱為“diffcore”的機制,其格式類似於不使用 -p 選項時的輸出。例如:

in-place edit  :100644 100644 bcd1234... 0123456... M file0
create         :000000 100644 0000000... 1234567... A file4
delete         :100644 000000 1234567... 0000000... D file5
unmerged       :000000 000000 0000000... 0000000... U file6

diffcore 機制接收此類比較結果列表(其中每個結果稱為“檔案對”,儘管在這一點上每個都只涉及單個檔案),並將其轉換為另一個列表。目前有 5 種此類轉換:

  • diffcore-break

  • diffcore-rename

  • diffcore-merge-broken

  • diffcore-pickaxe

  • diffcore-order

  • diffcore-rotate

這些轉換按順序應用。git diff-* 命令找到的檔案對集用作 diffcore-break 的輸入,diffcore-break 的輸出用作下一個轉換的輸入。最終結果然後傳遞給輸出例程,並生成 diff-raw 格式(參見 git diff-* 命令手冊的“輸出格式”部分)或 diff-patch 格式。

diffcore-break:用於拆分完全重寫

鏈中的第二個轉換是 diffcore-break,由 git diff-* 命令的 -B 選項控制。它用於檢測表示“完全重寫”的檔案對,並將其拆分為表示刪除和建立的兩個檔案對。例如,如果輸入包含以下檔案對:

:100644 100644 bcd1234... 0123456... M file0

如果它檢測到檔案“file0”被完全重寫,它會將其更改為:

:100644 000000 bcd1234... 0000000... D file0
:000000 100644 0000000... 0123456... A file0

為了拆分檔案對,diffcore-break 檢查檔案修改前後內容(即,在上述示例中,其 SHA-1 內容 ID 為“bcd1234…”和“0123456…”的內容)之間的更改程度。原始內容的刪除量和新材料的插入量相加,如果超過“斷點分數”,則檔案對被拆分為兩部分。斷點分數預設為原始和結果中較小者大小的 50%(即,如果編輯縮小了檔案,則使用結果的大小;如果編輯延長了檔案,則使用原始的大小),並且可以透過在“-B”選項後給出一個數字(例如,“-B75”表示使用 75%)進行自定義。

diffcore-rename:用於檢測重新命名和複製

此轉換用於檢測重新命名和複製,由 git diff-* 命令的 -M 選項(檢測重新命名)和 -C 選項(同時檢測複製)控制。如果輸入包含這些檔案對:

:100644 000000 0123456... 0000000... D fileX
:000000 100644 0000000... 0123456... A file0

並且已刪除檔案 fileX 的內容與已建立檔案 file0 的內容足夠相似,則重新命名檢測會合並這些檔案對並建立:

:100644 100644 0123456... 0123456... R100 fileX file0

當使用“-C”選項時,修改過的檔案和已刪除檔案(如果使用“--find-copies-harder”選項,也包括未修改檔案)的原始內容被視為重新命名/複製操作中原始檔的候選。如果輸入是這些檔案對,它們涉及修改過的檔案 fileY 和新建立的檔案 file0,

:100644 100644 0123456... 1234567... M fileY
:000000 100644 0000000... bcd3456... A file0

檔案 fileY 的原始內容和檔案 file0 的結果內容進行比較,如果它們足夠相似,則更改為:

:100644 100644 0123456... 1234567... M fileY
:100644 100644 0123456... bcd3456... C100 fileY file0

在重新命名和複製檢測中,都使用 diffcore-break 中使用的相同“更改程度”演算法來確定兩個檔案是否“足夠相似”,並且可以透過在“-M”或“-C”選項後給出一個數字(例如,“-M8”表示使用 8/10 = 80%)來定製不同於預設 50% 的相似度分數。

請注意,當重新命名檢測開啟但複製和斷點檢測都關閉時,重新命名檢測會新增一個初步步驟,首先檢查檔案是否在目錄之間移動但檔名保持不變。如果一個目錄中新增的檔案內容與從不同目錄中刪除的同名檔案內容足夠相似,它會將它們標記為重新命名,並將其排除在後續的二次步驟之外(該步驟會成對比較所有不匹配的檔案以找到“最佳”匹配,由最高內容相似度決定)。因此,例如,如果一個已刪除的 docs/ext.txt 和一個已新增的 docs/config/ext.txt 足夠相似,它們將被標記為重新命名,並阻止一個可能與已刪除的 docs/ext.txt 更相似的已新增 docs/ext.md 在後續步驟中被視為重新命名目標。因此,初步的“匹配相同檔名”步驟使用稍高的閾值來將檔案對標記為重新命名,並停止考慮其他更好的匹配候選。在此初步遍歷中,每個檔案最多進行一次比較;因此,如果在精確重新命名檢測後,整個目錄層次結構中仍有多個 ext.txt 檔案,則對於這些檔案,可能會跳過此初步步驟。

注意。當“-C”選項與 --find-copies-harder 選項一起使用時,git diff-* 命令會將未修改的檔案對以及已修改的檔案對都提供給 diffcore 機制。這使得複製檢測器可以考慮將未修改的檔案作為複製源候選,但代價是會使其變慢。如果沒有 --find-copies-hardergit diff-* 命令只能在被複制的檔案恰好在同一變更集中被修改時檢測到複製。

diffcore-merge-broken:用於將完全重寫重新合併

此轉換用於將由 diffcore-break 拆分但未被 diffcore-rename 轉換為重新命名/複製的檔案對重新合併為單個修改。當使用 diffcore-break 時,此轉換總是執行。

為了將拆分的檔案對重新合併,它使用了與 diffcore-break 和 diffcore-rename 不同的“更改程度”計算方法。它只計算原始內容的刪除量,不計算插入量。如果你從一個 100 行的文件中只刪除了 10 行,即使你添加了 910 行新內容以形成一個新的 1000 行文件,你也沒有進行完全重寫。diffcore-break 拆分這種情況是為了幫助 diffcore-rename 將此類檔案對視為重新命名/複製檢測的候選,但如果以這種方式拆分的檔案對未與其他檔案對匹配以建立重新命名/複製,則此轉換會將它們重新合併回原始的“修改”。

“更改程度”引數可以透過給 -B 選項提供第二個數字來調整,而不是預設的 80%(即,除非原始材料的刪除量超過 80%,否則拆分對將重新合併為單個修改),例如:

  • -B50/60(給 diffcore-break 50% 的“斷點分數”,對 diffcore-merge-broken 使用 60%)。

  • -B/60(同上,因為 diffcore-break 預設為 50%)。

請注意,早期實現會將拆分的檔案對作為單獨的建立和刪除補丁。這是一種不必要的技巧,最新實現總是將所有拆分的檔案對重新合併回修改中,但對於此類完全重寫,生成的補丁輸出格式會有所不同,以便於審查,它會顯示以 - 為字首的舊版本的所有內容,後跟以 + 為字首的新版本的所有內容。

diffcore-pickaxe:用於檢測指定字串的新增/刪除

此轉換將檔案對集限制為那些以特定方式更改前像(preimage)和後像(postimage)之間指定字串的檔案對。-S<文字塊> 和 -G<正則表示式> 選項用於指定查詢這些字串的不同方式。

“-S<文字塊>”檢測那些前像和後像中指定文字塊出現次數不同的檔案對。根據定義,它不會檢測檔案內移動。此外,當一個變更集整體移動一個檔案而不影響目標字串時,diffcore-rename 會像往常一樣啟動,而 -S 會忽略該檔案對(因為在該重新命名檢測到的檔案對中,該字串的出現次數沒有改變)。當與 --pickaxe-regex 一起使用時,<文字塊> 將被視為擴充套件 POSIX 正則表示式進行匹配,而不是字面字串。

“-G<正則表示式>”(助記:grep)檢測那些文字差異中包含與給定正則表示式匹配的新增或刪除行的檔案對。這意味著它將檢測檔案內(或重新命名檢測認為是同一檔案)的移動,這會產生噪音。此實現會執行兩次 diff 並進行 grep,這可能會非常耗時。為了加快速度,沒有 textconv 過濾器的二進位制檔案將被忽略。

當使用 -S-G 而不帶 --pickaxe-all 時,輸出中只保留符合各自條件的檔案對。當使用 --pickaxe-all 時,如果一個變更集中即使只有一個檔案對符合各自條件,則整個變更集都會被保留。此行為旨在使在整個變更集上下文中審查更改變得更容易。

diffcore-order:用於根據檔名排序輸出

此功能用於根據使用者(或專案)的偏好對檔案對進行重新排序,由 git diff-* 命令的 -O 選項控制。

這需要一個文字檔案,其中每一行都是一個 shell glob 模式。檔案中較早行匹配 glob 模式的檔案對會在較晚行匹配的檔案對之前輸出,而那些不匹配任何 glob 模式的檔案對則最後輸出。

例如,核心 Git 的典型 orderfile 可能看起來像這樣:

README
Makefile
Documentation
*.h
*.c
t

diffcore-rotate:用於更改輸出從哪個路徑開始

此轉換接受一個路徑名,並旋轉檔案對集,使給定路徑名的檔案對排在最前面,可選地丟棄其之前的路徑。這用於實現 --skip-to--rotate-to 選項。當指定的路徑名不在檔案對集中時會報錯,但與“git log”系列命令一起使用時報錯並不實用,因為期望“git log”命令顯示的每個提交都修改給定路徑是不合理的。因此,當與“git log”一起使用時,輸出將從與給定路徑名排序相同或緊隨其後的第一個檔案對開始。

將此轉換與 diffcore-order 結合使用會產生意外結果,因為當 diffcore-order 生效時,此轉換的輸入可能未排序。

GIT

Git[1] 套件的一部分

scroll-to-top