全部產品
Search
文件中心

Alibaba Cloud DevOps:如何使用Git LFS?

更新時間:Mar 20, 2025

本文主要介紹了為什麼要選擇使用Git LFS,進而說明了在Codeup中如何使用Git LFS,以及在使用Git LFS時常見問題的處理方式和Git LFS的工作原理。

為什麼選擇使用Git LFS?

Git LFS為瞭解決大檔案託管的效率問題,提供了五大特性:

  • 更大:支援GB層級的大檔案版本控制。

  • 更小:讓Git倉庫空間佔用減小。

  • 更快:倉庫的複製和拉取更快。

  • 透明:Git使用上對使用者完全透明。

  • 相容:許可權控制上完全相容(相容Codeup許可權控制)。

下載和安裝Git LFS

  • 下載:

  • 安裝:如果您選擇使用二進位包下載後安裝,直接執行解壓後的./install.sh指令碼即可,這個指令碼會做兩件事情:

    • 在$PATH中安裝Git LFS的二進位可執行檔。

    • 執行git lfs install命令,讓當前環境支援全域的LFS配置。

      # 讓倉庫支援LFS
      $ git lfs install
      Updated pre-push hook.
      Git LFS initialized.

讓本地新倉庫支援Git LFS

以下將以一個通用的情境進行實際的示範和說明(環境為Linux/macOS )。

步驟一:建立一個新的Git空倉庫

  1. 在Codeup上建立一個空白的新倉庫,名為“git-lfs”。

  2. 將該倉庫複製到本地,並進入該目錄:

    # 請注意替換您實際的程式碼程式庫地址
    $git clone https://codeup.aliyun.com/您的組織分組/git-lfs.git
    Cloning into 'git-lfs'...
    warning: You appear to have cloned an empty repository.
    $cd git-lfs
    $tree .git/hooks/
    .git/hooks/
    ├── applypatch-msg.sample
    ├── commit-msg.sample
    ├── execute-commands.sample
    ├── post-receive.sample
    ├── post-update.sample
    ├── pre-applypatch.sample
    ├── pre-commit.sample
    ├── prepare-commit-msg.sample
    ├── pre-push.sample
    ├── pre-rebase.sample
    ├── pre-receive.sample
    └── update.sample
    
    0 directories, 12 files
    # 此時Git LFS相關的Hook還未替換

步驟二:配置Git LFS

  1. 為了將以樣本.bigfile尾碼結尾的檔案使用Git LFS進行儲存,需要執行track命令建立追蹤:

    $git lfs track "*.bigfile"
    Tracking "*.bigfile"
    重要

    使用 lfs track 命令時,"*.bigfile"的雙引號非常重要,否則將影響pattern的檔案匹配功能。

    同理,如需跟蹤其他尾碼的檔案,如.jpg,可以寫為git lfs track "*.jpg"

  2. 執行git lfs track(不帶任何參數),可以查看當前已跟蹤的Git LFS File 類型:

    $git lfs track
    Listing tracked patterns
        *.bigfile (.gitattributes)
    Listing excluded patterns
  3. track 命令實際上是修改了倉庫中的.gitattributes檔案,將該檔案add添加到暫存區。

    $git add .gitattributes 
  4. 可以通過以下命令查看檔案相關變動:

    $git diff --cached
     diff --git a/.gitattributes b/.gitattributes
     new file mode 100644
     index 0000000..c441ad2
     --- /dev/null
     +++ b/.gitattributes
     @@ -0,0 +1 @@
     +*.bigfile filter=lfs diff=lfs merge=lfs -text

步驟三:讓Git LFS配置生效

為了讓"*.bigfile"的配置生效,需要將.gitattributes檔案進行提交:

$git commit -m "Add \"*.bigfile\" LFS config "
[master (root-commit) d052478] Add "*.bigfile" LFS config
 1 file changed, 1 insertion(+)
 create mode 100644 .gitattributes
$git log --oneline
d052478 (HEAD -> master) Add "*.bigfile" LFS config

步驟四:建立一個.bigfile檔案進行測試

接下來,在工作空間建立一個名為dyrone.bigfile的檔案,大小為1GB

# mac環境可以使用mkfile命令替換dd命令 
$dd if=/dev/zero of=dyrone.bigfile bs=1G count=1
1+0 records in
1+0 records out
1073741824 bytes (1.1 GB) copied, 2.41392 s, 445 MB/s
$du -sh dyrone.bigfile 
1.1G    dyrone.bigfile

dyrone.bigfile添加到暫存區:

$git add dyrone.bigfile

由於dyrone.bigfile尾碼命中了.gitattributes中設定的"*.bigfile"的檔案格式,所以將做為 LFS 檔案處理。

說明
  1. 什麼是Git LFS Pointer

Git LFS並不會將完整的檔案屬性和內容進行計算寫入INDEX(暫存區的實際檔案),而是會產生一個Git LFS Pointer檔案,該檔案指向了真正的LFS儲存物件名稱。可以通過執行git diff --cached命令查看倉庫INDEX和HEAD之間的差異,也就是dyrone.bigfile對應的Pointer檔案的內容:

$git diff --cached
diff --git a/dyrone.bigfile b/dyrone.bigfile
new file mode 100644
index 0000000..9d7c19f
--- /dev/null
+++ b/dyrone.bigfile
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:49bc20df15e412a64472421e13fe86ff1c5165e18b2afccf160d4dc19fe68a14
+size 1073741824

Pointer檔案內容所表達的含義:

  • version https://git-lfs.github.com/spec/v1 :代表git-lfs協議的版本。

    • version...v1:代表當前git-lfs服務端遵從的協議版本。

    • git-lfs.github.com:此網域名稱地址為git-lfs開源專案的官方地址。

  • oid sha256:49bc20df15e412a64472421e13fe86ff1c5165e18b2afccf160d4dc19fe68a14:

    • oid:代表Git LFS object id。

    • sha256:64位16進位,其代表真實檔案的名稱,名稱通過sha256產生,唯一值。

  • size: 代表檔案實際大小,單位:byte。

  1. 本地的Git LFS檔案是如何儲存的?

Pointer檔案被添加到暫存區的同時,真正的1GB的大檔案dyrone.bigfile被儲存在倉庫的LFS緩衝目錄下,名稱被修改為Pointer檔案中所指向的oid字串名稱:

$tree .git/lfs
.git/lfs
├── objects
│    └── 49
│         └── bc
│             └── 49bc20df15e412a64472421e13fe86ff1c5165e18b2afccf160d4dc19fe68a14
└── tmp
4 directories, 1 file

在本地倉庫中,Git LFS是按照這種方式來進行大檔案實際儲存,原本的dyrone.bigfile被表示成了兩個檔案,Pointer檔案的blob是對應Commit真正引用的對象,而實際LFS檔案被緩衝在.git/lfs目錄。而此時工作區中的檔案沒有發生任何變化。

步驟五:推送檔案到遠端

接下來,將dyrone.bigfile的變更提交並推送到遠端:

$git commit -m "Add a really big file"
[master 8032589] Add a really big file
 1 file changed, 3 insertions(+)
 create mode 100644 dyrone.bigfile

其中,"1 file changed, 3 insertions(+)" 表示Pointer檔案已經提交,可以執行git show HEAD查看提交詳情:

$git show HEAD
commit 8032589f47a748171e84da94ce6440fe139e99f9 (HEAD -> master)
Author: dyroneteng <tenglong***@alibaba-inc.com>
Date:   Tue Sep 15 17:25:58 2020 +0800

    Add a really big file

diff --git a/dyrone.bigfile b/dyrone.bigfile
new file mode 100644
index 0000000..9d7c19f
--- /dev/null
+++ b/dyrone.bigfile
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:49bc20df15e412a64472421e13fe86ff1c5165e18b2afccf160d4dc19fe68a14
+size 1073741824

接著,將提交推送到遠端Codeup已準備好的空倉庫:

$git push
Uploading LFS objects:   0% (0/1), 3.9 MB | 0 B/s                                                                                                                                                               
Uploading LFS objects:   0% (0/1), 79 MB | 30 MB/s                                                                                                                                                              
Uploading LFS objects:   0% (0/1), 207 MB | 50 MB/s                                                                                                                                                             
Uploading LFS objects:   0% (0/1), 326 MB | 51 MB/s                                                                                                                                                             
Uploading LFS objects:   0% (0/1), 534 MB | 56 MB/s                                                                                                                                                             
Uploading LFS objects: 100% (1/1), 1.1 GB | 58 MB/s, done.                                                                                                                                                      
Counting objects: 3, done.
Delta compression using up to 32 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 410 bytes | 410.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://codeup.aliyun.com/xxxxx/git-lfs.git
   d052478..8032589  master -> master

如果存在LFS檔案需要上傳,在推送過程中將會顯示LFS上傳進度。

至此,這個倉庫中.bigfile的檔案已經成功使用LFS進行管理,而其他檔案使用Git進行管理。

說明

簡要解讀此過程的標準輸出:

  • 如果推送的檔案中,包含Git LFS檔案,那麼實際上push將被分為兩個部分:

    • 第一部分: Git LFS oid檔案上傳。

    • 第二部分: 當LFS oid檔案上傳結束後,繼續使用Git協議上傳Pointer檔案的相關對象。

  • 第一部分 "Uploading LFS objects: 100% (1/1), 1.1 GB | 58 MB/s, done." , 會顯示Git LFS oid檔案上傳過程中的進度資訊,如總上傳個數、當前上傳、oid檔案大小和上傳速度等。

  • 第二部分 "Writing objects: 100% (3/3), 410 bytes | 410.00 KiB/s, done." ,例子共三個對象:blob(1)、tree(1)、commit(1)。

  • 兩個部分,任一部分失敗,則整個推送將失敗。

除了過程的標註輸出增多的變化以外,也側面體現了Git LFS是如何進行推送的:在推送時,Git LFS檔案被單獨上傳到LFS 伺服器上,而Pointer檔案保持不變推送到Git 伺服器上,可以觀察倉庫目錄大小來驗證這一點。

可以發現,本地倉庫大小僅僅為188K(排除LFS緩衝目錄),這也基本是遠端Git倉庫的大小,也達到了Git LFS瘦身倉庫的目的。

$du -sh --exclude=lfs .git 
188K    .git

$du -sh .git
1.1G    .git

複製已使用Git LFS的倉庫

本地需預先已安裝Git LFS工具,否則複製後的LFS檔案將以指標而非期望的原始檔案呈現。當複製一個已經使用的Git LFS倉庫時,git-lfs會自動替換倉庫中相關hook,進而讓Git LFS生效。

# 範例倉庫,如嘗試,可替換為實際已經 LFS 的倉庫地址
$ git clone gi*@codeup.aliyun.com:您的組織分組/dyrone.git
Cloning into 'dyrone'...
remote: Counting objects: 41, done.
remote: Total 41 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (41/41), 2.98 MiB | 5.01 MiB/s, done.
Filtering content: 100% (3/3), 5.01 MiB | 3.83 MiB/s, done.
# Filtering content表示正在將下載下來的LFS檔案smudge為實際工作區的檔案
$ cd dyrone
$ tree .git/hooks
.git/hooks
├── applypatch-msg.sample
├── commit-msg.sample
├── execute-commands.sample
├── fsmonitor-watchman.sample
├── post-checkout
├── post-commit
├── post-merge
├── post-update.sample
├── pre-applypatch.sample
├── pre-commit.sample
├── pre-merge-commit.sample
├── pre-push
├── pre-push.sample
├── pre-rebase.sample
├── pre-receive.sample
├── prepare-commit-msg.sample
└── update.sample

0 directories, 17 files

可以看到,例如pre-push等Git LFS需要用到的相關Hook已經被替換,進而支援Git Flow,對使用者Git操作保持相容和透明。

如何將歷史檔案轉換為LFS管理

更多複雜情境遷移指南,參見LFS 遷移指南

如何撤銷LFS跟蹤並使用Git管理

您可以取消繼續跟蹤某類檔案,並將其從cache中清理:

git lfs untrack "*.bigfile"
git rm --cached "*.bigfile"

如果您想將這些檔案添加回常規Git跟蹤,可以執行以下操作:

git add "*.bigfile"
git commit -m "restore "*.bigfile" to git from lfs"

Git LFS工作原理

在前面的一些樣本之後,相信大家對Git LFS有了一定的理解,其工作原理如下:

Git LFS情境

Git 2

如圖所示,可以針對jpg格式的圖片利用Git LFS的儲存功能,在推送過程中將其上傳至大檔案儲存體服務。同時,與大檔案對應的指標檔案將與其他普通代碼檔案一併推送到遠端Git倉庫中。

Git LFS處理流程介紹

Git 3

Git情境

Git 1

無論是針對小型的代碼文字檔,還是較大型的圖片檔案,在相關變更從本地提交到遠程倉庫時,所有相關檔案資源都會完整地儲存在Git伺服器上。以圖片為例,如果圖片檔案數量逐漸增多且改動頻次持續上升,倉庫的體積將會迅速膨脹。

Git LFS的限制

  • Windows平台,單個檔案不支援超過4Gissues 2434

  • Windows使用者必須保證已經安裝Git Credential Manager,否則可能導致操作被無限掛起,issues 1763

  • 不同於Gitlab硬式編碼LFS下載token逾時時間(30分鐘),Codeup會根據將要下載的檔案清單動態計算token逾時時間,但是如果位於網路環境不好的環境,仍舊可能導致token逾時的情況。如果需要根據需求調整,可以聯絡Codeup系統管理員處理。