Git
英文 ▾ 主題 ▾ 最新版本 ▾ gitdiffcore 最後更新於 2.44.0

名稱

gitdiffcore - 調整 diff 輸出

概要

git diff *

描述

diff 命令 git diff-indexgit diff-filesgit diff-tree 可以被告知以非傳統的方式操作它們找到的差異,然後顯示 diff 輸出。這種操作統稱為「diffcore 轉換」。這份簡短的筆記描述了它們是什麼,以及如何使用它們產生比傳統方式更容易理解的 diff 輸出。

操作鏈

git diff-* 系列的工作方式是首先比較兩組檔案

  • git diff-index 比較「樹狀」物件的內容和工作目錄(當未使用 --cached 標誌時)或「樹狀」物件和索引檔案(當使用 --cached 標誌時);

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

  • git diff-tree 比較兩個「樹狀」物件的內容;

在所有這些情況下,命令本身首先可選擇性地透過在命令列上給定的任何路徑規格來限制兩組檔案,並比較兩個結果檔案集中相應的路徑。

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

: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" 選項後給定數字來自訂,以使用與預設 50% 不同的相似度分數(例如,"-M8" 告訴它使用 8/10 = 80%)。

請注意,當重新命名偵測開啟但複製和分解偵測都關閉時,重新命名偵測會新增一個初步步驟,該步驟會先檢查檔案是否在保留其檔案名稱的情況下跨目錄移動。如果有檔案被新增至目錄,其內容與從不同目錄中刪除的同名檔案足夠相似,則會將它們標記為重新命名,並將它們排除在稍後的二次步驟之外(該步驟會成對比較所有未比對的檔案,以找到由最高內容相似度決定的「最佳」比對)。因此,舉例來說,如果刪除的 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 將這種檔案對視為重新命名/複製偵測的候選者,但是如果以這種方式分解的檔案對未與其他檔案對比對以建立重新命名/複製,則此轉換會將它們合併回原始的「修改」。

「變更範圍」參數可以從預設的 80% 進行調整(也就是說,除非原始資料刪除超過 80%,否則被拆分的配對會合併回單一修改)。您可以透過在 -B 選項後給予第二個數字來調整,例如:

  • -B50/60 (給予 diffcore-break 50% 的「拆分分數」,對於 diffcore-merge-broken 則使用 60%)。

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

請注意,先前的實作會將被拆分的配對視為獨立的建立和刪除修補程式。這是不必要的技巧,而最新的實作總是將所有被拆分的配對合併回修改。但是,為了方便審閱這種完全重寫的情況,產生的修補程式輸出格式會有所不同,它會顯示舊版本的所有內容,並加上 - 前綴,然後顯示新版本的所有內容,並加上 + 前綴。

diffcore-pickaxe:用於偵測指定字串的加入/刪除

此轉換會將檔案配對的集合限制為那些在 pre-image 和 post-image 之間以特定方式變更指定字串的檔案配對。 -S<文字區塊> 和 -G<正規表示式> 選項用於指定尋找這些字串的不同方式。

「-S<文字區塊>」會偵測 pre-image 和 post-image 中指定文字區塊的出現次數不同的檔案配對。依照定義,它不會偵測檔案內的移動。此外,當變更集整體移動一個檔案而不影響感興趣的字串時,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 的典型排序檔案可能會像這樣

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