Try   HackMD

Git LFS(Large File Storage)

Sharon Mai20230524, Wed

↪️總目錄:Git and GitHub相關

情境:上傳GitHub時,當單個文件大小超過100MB出現無法上傳的錯誤訊息該如何解決呢?

下載

Git LFS

啟用

git lfs install

追蹤大型文件

使用Git LFS追蹤每個超過100MB的大型文件

  • 本次範例為Detectron2的 .pkl文件

    ​​​​git lfs track "*.pkl"
    

創建 .gitattributes文件

告知Git哪些文件為大型文件處理

  1. 在專案路徑中創建文件.gitattributes並與.gitnore同層級

  2. 文件.gitattributes輸入:

    ​​​​.pkl filter=lfs diff=lfs merge=lfs -text
    ​​​​<此處輸入大型文件相對位置> filter=lfs diff=lfs merge=lfs -text
    

    完成代碼如下:

    ​​​​.pkl filter=lfs diff=lfs merge=lfs -text
    ​​​​backend/prj_webserver/detectron2_weights/model_final_f10217.pkl filter=lfs diff=lfs merge=lfs -text
    

上傳

加入 .gitattributes文件

git add .gitattributes

留言

git commit -m "使用 Git LFS 跟踪大型文件"

推送

git push -u origin ai_test

若無錯誤訊息則成功!

錯誤訊息

remote: error: Trace: 493715c1b5d40443d4dce3a849f2764022676f9516bf631617b5bc4a4b2a3091
remote: error: See https://gh.io/lfs for more information.
remote: error: File yingmiai_webserver/detectron2_weights/model_final_f10217.pkl is 169.60 MB; this exceeds GitHub's file size limit of 100.00 MB
remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com.
To https://github.com/name/prj.git
! [remote rejected] ai_test -> ai_test (pre-receive hook declined)
error: failed to push some refs to 'https://github.com/name/prj.git'

原因

Commit後發現有單個文件有超過100MB的大型文件存在。

解決方式

思路

刪除本地端Push失敗的Commit歷史紀錄中的大型文件( model_final_f10217.pkl ),再設定好git FLS再繼續使用

注意:將會使用到--force命令,請務必與開發團隊討論。
詳情請查看注意事項

流程

  1. 將所有提交中刪除 model_final_f10217.pkl 文件

    ​​​​git filter-branch --force --index-filter "git rm --cached --ignore-unmatch prj_webserver/detectron2_weights/model_final_f10217.pkl" --prune-empty --tag-name-filter cat -- --all
    
    命令說明

    尚未深入瞭解先以ChatGPT所查到的紀錄為主。

    原理:遍歷整個存儲庫的歷史,並在每個提交中刪除指定的文件。同時移除那些變為空的提交,並重寫所有的標籤和分支引用。在這個過程中,使用了一些選項和命令來確保即使在某些情況下可能出現的問題(如文件不存在,或臨時分支已存在)也能正確執行。

    • git filter-branch:這是一個命令,它用於遍歷並修改 Git 存儲庫的所有 commit。

    • --force:此選項允許 git filter-branch 命令忽略臨時分支是否已存在的檢查,如果存在,則直接覆蓋它們,避免出現錯誤。

    • --index-filter:此選項允許您指定一個將在每個 commit 中運行的命令,這個命令用於修改 commit 的內容。在這個例子中,這個命令是 git rm cached ignore-unmatch yingmiai_webserver/detectron2_weights/model_final_f10217.pkl,它的作用是在每個 commit 中刪除指定的文件。

    • git rm --cached --ignore-unmatch:此命令用於從 Git 索引(或稱暫存區)中移除文件。ignore-unmatch 選項使命令在文件不存在時不會產生錯誤,而會繼續執行。

    • --prune-empty:此選項會刪除所有因為前述操作而變成空的 commit。

    • --tag-name-filter cat -- --all:此部分的命令用來重寫所有的標籤名稱和分支引用。cat 是一個 Unix 命令,它在這裡的作用是不改變標籤名(簡單地“返回”或“輸出”標籤名)。 all 則表示應用此操作到所有的分支和標籤。

    命令執行流程

    首先,git filter-branch 是一種強大的命令,允許我們在整個存儲庫歷史中對每個提交(commit)進行操作。在此使用 index-filter 選項指定了一個命令,該命令會在每個提交上執行。

    git rm --cached:這將從 Git 索引(也稱為暫存區)中移除文件。Git 索引是一個臨時區域,存儲了下一個提交的內容。因此,這個命令會使下一個提交不再包含被移除的文件。

    --ignore-unmatch:這是一個選項,如果指定的文件在某個提交中不存在,它會使 git rm 命令繼續執行,而不是停止並報錯。

    然後,--prune-empty 選項會移除那些變為空的提交,也就是那些由於文件刪除而不再包含任何更改的提交。

    最後,--tag-name-filter cat -- --all 將重寫所有的標籤名稱和分支引用。在這個例子中,cat 命令簡單地返回(或輸出)原來的標籤名稱,所以標籤名稱並不會改變。

  2. 清除 Git 緩存以釋放空間

    ​​​​git reflog expire --expire=now --all && git gc --prune=now --aggressive
    
  3. 推送

    ​​​​git push --force