借助AI學習開源代碼git0.7之九diff-files
diff-files.c 是一個用于比較工作目錄中的文件和 Git 索引(暫存區)中文件的工具。
實質上,它是 git diff命令在不指定特定提交時功能的核心實現。
主要功能分析:
1. 核心功能
diff-files 的主要任務是逐一檢查索引(cache)中的每個文件,并將其與工作目錄中的對應文件進行比較,以找出差異。它能檢測出以下幾種狀態:
- 已修改 (Modified): 索引中的文件版本與工作目錄中的文件內容或模式(權限)不一致。
- 已刪除 (Deleted): 文件存在于索引中,但在工作目錄中被刪除了。
- 未合并 (Unmerged): 在合并沖突后,文件處于未合并狀態。
- 新增 (Added): (這個工具本身不直接顯示新增文件,因為新增的文件如果未被 git add,它就不會出現在索引中。git status 會處理這種情況。)
2. 代碼結構
代碼的邏輯主要在 main 函數中,可以分為以下幾個步驟:
2.1. 參數解析:
- 循環解析以 - 開頭的選項。
- -p: 生成補丁(patch)格式的輸出,而不僅僅是文件列表。
- -q: 安靜模式,不顯示已刪除文件的信息。
- -z: 使用 \0 作為行終止符,而不是換行符,便于腳本處理。
- -r, -s: 這兩個選項在代碼里是空操作,可能是為了兼容性或歷史原因保留的。
- 在選項之后的所有參數都被視為路徑規范(pathspec),用于過濾要比較的文件。
2.2. 讀取索引:
- 調用 read_cache() 函數將 .git/index 文件的內容加載到內存 (active_cache) 中。如果失敗,程序會退出。
2.3. 遍歷索引條目:
- 代碼通過一個 for 循環遍歷 active_cache 中的每一個文件條目 (struct cache_entry)。
2.4. 路徑匹配:
- 如果用戶提供了路徑參數,matches_pathspec() 函數會檢查當前文件是否匹配用戶給定的路徑。如果不匹配,則跳過該文件。
2.5. 處理未合并文件:
- 通過 ce_stage(ce) 檢查文件是否處于合并沖突狀態。如果是,則調用 show_unmerge() 打印未合并信息,并跳過該文件所有相關的條目。
2.6. 與工作目錄文件比較:
- 使用 stat() 系統調用獲取工作目錄中對應文件的元數據(如模式、大小、修改時間等)。
- 如果 stat() 返回 ENOENT (文件不存在),說明文件被刪除了。此時調用 show_file(‘-’, ce) 來顯示刪除信息。
- 調用 cache_match_stat(ce, &st) 將索引中的元數據和從 stat() 獲取的元數據進行比較。
- 如果 cache_match_stat 返回 0,表示文件沒有變化,直接跳到下一個文件。
- 如果文件有變化,則調用 show_modified() 來顯示修改信息。
3. 關鍵函數
- show_unmerge(path): 顯示未合并的文件。
- show_file(pfx, ce): 顯示被刪除(pfx 為 -)或新增(pfx 為 +)的文件信息。
- show_modified(…): 顯示被修改的文件信息。
- matches_pathspec(…): 檢查文件名是否符合用戶指定的路徑模式。
4. 輸出格式
這個程序有兩種輸出模式,由 -p 選項控制:
- 默認模式 (無
-p
): 輸出一個摘要列表,每行一個文件,格式類似于:- U path/to/unmerged_file (未合并)
- -000000 blob path/to/deleted_file (已刪除)
- *100644->100644 blob <old_sha1>-><new_sha1> path/to/modified_file (已修改)
- 補丁模式 (有
-p
): 調用 diff.h 中定義的 diff_unmerge, diff_addremove, diff_change 等函數,生成標準的 diff/patch 格式輸出,詳細展示文件的具體改動內容。
總結
diff-files.c 是 Git 中一個基礎且重要的工具,它連接了 Git 的核心數據結構(索引)和用戶的實際工作目錄,
為 git diff 和 git status等命令提供了底層支持。它的實現直接、高效,專注于核心比較邏輯。