Watch at Ease看見世界,讀懂生活。

科技 · 實用指南

Git LFS clone 後只剩指標檔案?重新 clone 前先核對屬性與物件

clone 後的工作樹裡出現指標文字,只是症狀,不是根因。先保留本機改動、識別精確 LFS 物件,再把屬性和篩選器設定與下載、身分驗證、本機物件及 checkout 故障分開判斷,然後才考慮重新 clone 或重設。

Git LFS 小型指標透過屬性與物件身分核對後解析為受保護大檔案的原創概念圖
原創概念圖:改動工作樹前,先核對指標、屬性與精確物件身分。 Watch at Ease

Git LFS 在 Git 中保存小型指標,把大物件放在另一套儲存中。工作樹裡出現指標,可能是簽出檔案時尚未安裝 Git LFS、主動跳過了 smudge、屬性規則沒有匹配該路徑、物件沒有擷取成功,或 checkout 為了保護已修改檔案而沒有替換內容。它本身無法證明儲存庫損壞。

最安全的診斷順序,是沿著物件經過指標、屬性、篩選器、本機快取、遠端擷取和工作樹 checkout 的路徑逐層核對。每一層產生的證據和修正方式都不同。盲目重新 clone 既可能重複同一設定錯誤,也可能浪費頻寬並丟掉有用的本機現場。

本指南以唯讀核對為主:不改寫歷史、不執行 hard reset 或 clean、不繞過憑證,也不刪除現有 clone。最終交付是一份簡短證據記錄和最小安全下一動作。

1. 保留工作樹,並確認看到的確實是指標

先執行 `git status --short` 並保存輸出。測試前,把任何經過本機編輯、看起來像指標的檔案和未追蹤工作複製到儲存庫外。診斷期間不要執行 `git reset --hard`、`git clean`、歷史遷移,也不要刪除 clone。

唯讀開啟異常的小文字檔案。正常的 Git LFS v1 指標包含規格行、`oid sha256:` 行和聲明的 `size`。記錄相對路徑、OID 與大小。如果檔案只是看起來像指標,卻無法按格式解析,應歸入單獨的內容損壞分支。

若當前版本支援,可執行 `git lfs pointer --check --file="PATH"`,也可以與文件中的指標結構核對。不要把儲存庫憑證、簽名下載網址或私有遠端細節放進公開證據。

2. 核對這條路徑是否真的受 LFS 屬性管理

在受影響的提交內容中執行 `git check-attr -a -- "PATH"`。對已由 LFS 追蹤的路徑,檢查 Git 實際讀取到的 filter、diff、merge 與 text 屬性。僅憑工作樹檔案名,無法證明當前屬性規則已匹配。

檢查 Git 中對應版本的 `.gitattributes`,不要只看尚未提交的本機副本。屬性模式依賴路徑;上游 FAQ 還指出,跨平台差異可能暴露錯誤模式,或讓在追蹤規則生效前提交的歷史檔案表現不同。

如果該提交內容中的路徑沒有 LFS filter 標記,就先停止。只安裝 Git LFS 或擷取物件,並不能單獨修復屬性不匹配。必須另行制定變更方案,確認應修改哪些檔案和提交,再考慮 renormalization 或 migration。

3. 記錄本機 Git LFS 篩選與選擇狀態

記錄 `git lfs version` 和已脫敏的 `git lfs env`。確認當前使用者和儲存庫已安裝 Git LFS,並記錄建立工作樹時是否使用了 `GIT_LFS_SKIP_SMUDGE=1`、`git lfs install --skip-smudge` 或等效篩選設定。

不要先假設所有 LFS 物件都應下載,應先檢查 include 與 exclude 規則。pull 手冊說明了路徑篩選,而 partial clone 或 sparse checkout 也可能刻意限制進入工作樹的路徑。

同時記錄 sparse-checkout 狀態和所有 partial-clone filter。已有 Git LFS issue 展示了 partial checkout 與 include 篩選相關的真實“只剩指標”現象,但只有在屬性、篩選器、擷取和 checkout 等常規邊界都核對後,才應考慮該特定回歸分支。

4. 把擷取物件與簽出物件分開判斷

`git lfs pull` 組合了 fetch 與 checkout;單獨的 `git lfs checkout` 不會下載缺失內容,只會使用本機 LFS 儲存中已有的物件替換符合條件的指標。這解釋了為什麼反覆 checkout 可能毫無變化。

在大範圍下載前,先針對受影響引用與路徑做限定擷取。例如核對儲存庫配置後執行 `git lfs fetch REMOTE REF --include="PATH"`,並記錄精確的遠端、引用與第一條脫敏錯誤。

若 fetch 明確回傳身分驗證、授權、物件缺失或配額錯誤,就停在該邊界。普通 Git clone 成功,並無法證明獨立的 LFS 物件端點也接受了要求,或仍保存該 OID。

5. checkout 前先核對本機儲存中的精確 OID

擷取成功後,確認本機 LFS 儲存中存在與記錄的 SHA-256 OID 對應的物件,而且位元組長度等於指標聲明的 size。應使用 Git LFS 命令和儲存庫診斷,不要根據同名檔案猜測。

適合當前儲存庫時,執行 `git lfs fsck --objects` 並保留輸出。本機物件大小或雜湊摘要不符,屬於完整性問題;本機完全沒有該物件,則仍是擷取或遠端可用性問題。

不要因為檔案名相同,就把任意二進位檔案覆蓋到指標上。指標 OID 才是內容身分。正確 checkout 必須解析到精確物件,而且不改動無關檔案。

6. 讓 checkout 保護改動,再解釋結果

確認精確物件已在本機後,執行路徑限定的 `git lfs checkout -- "PATH"`。checkout 手冊說明它不會覆蓋已修改檔案。因此,如果指標形式的檔案與索引不同,沒有變化可能是主動保護,而非 Git LFS 安裝失敗。

對照 `git status --short`、`git diff -- "PATH"` 與索引中記錄的指標。還原乾淨佔位符前,先備份所有本機修改,並判斷差異究竟是使用者工作、換行損壞,還是異常 checkout 結果。

checkout 成功後,確認工作樹檔案已不再被解析為指標,大小與聲明值一致;在常規 Git LFS 佈局允許時,再核對其雜湊摘要與 OID。

證據 | 失敗邊界 | 最小安全動作 路徑沒有 LFS filter 屬性 | 屬性 | 另行修正並審核追蹤規則,不要盲目擷取 未安裝 LFS,或刻意跳過 smudge | 篩選器安裝 | 安裝或設定 LFS,再做限定 pull fetch 回傳身分驗證或物件缺失錯誤 | 遠端存取或物件可用性 | 修復存取或復原精確 OID,不必重新 clone 物件已在本機,但 checkout 留下已修改指標 | 工作樹保護 | 備份改動、比較索引,再做路徑限定 checkout 其他檢查通過後,只有 partial/sparse 路徑失敗 | 簽出選擇或回歸 | 升級為 issue 前,用精確版本和最小篩選條件重現

7. 驗證還原,並整理可交給支援人員的證據

驗收條件必須具體:指標能解析,OID 與聲明大小已記錄;fetch 後本機存在同一 OID;工作樹路徑包含解析後的內容;無關改動保持不變。最後再用預期應用程式成功開啟檔案,作為實際使用層面的補充檢查。

提交支援要求時,請包括已脫敏的 Git 與 Git LFS 版本、作業系統、指標 OID 前綴與大小、`check-attr` 結果、篩選器和 include/exclude 狀態、遠端名稱、fetch 結果、本機物件結果、sparse 或 partial checkout 狀態,以及路徑限定 checkout 結果。觀察與結論必須分開寫。

只有在屬性、設定、遠端存取、物件存在和修改保護都已排除後,最小重現仍失敗,才應回報可能的版本特定回歸。不要把所有只剩指標的 checkout 都歸為同一個已回報的 issue。

常見問題

Git clone 成功,為什麼 Git LFS 檔案仍未下載? Git 儲存庫與 LFS 物件服務是兩個邊界。即使未安裝 LFS、跳過 smudge、路徑被排除、憑證失敗或遠端物件缺失,Git 仍可能成功 clone 指標檔案。

`git lfs checkout` 會下載缺失物件嗎? 不會。checkout 只使用本機 LFS 儲存中已有的物件。缺失物件必須先由 fetch 或 pull 取得。

應該執行 `git reset --hard` 來替換指標嗎? 診斷階段不要這樣做。hard reset 可能丟掉無關工作,也無法證明所需 LFS 物件可用。先記錄狀態、屬性、OID 與擷取證據。

看到指標文字是否表示檔案損壞? 不一定。指標本來就是正常儲存庫資料。問題在於工作樹沒有填入與之匹配的大物件,而多種不同邊界都可能造成該狀態。

Git LFS 從遠端擷取物件到本機快取,再受保護地簽出到工作資料夾的原創概念圖
原創概念圖:fetch 負責取得物件;checkout 是保護已修改檔案的獨立步驟。 Watch at Ease