iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 27
2
# Outline
一、前言
二、概念
  2-1. Garbage Collect
  2-2. Pack

隨著鐵人賽即將完賽,對於整系列的結構也逐漸清晰。雖然很想繼續針對 GitLab CI 的部分去探討,但仍決定先回頭講聊聊與 Git 原理比較相關的主題,將系列缺漏的部分補上。今天就讓我們聊聊 Git 是如何將鬆散的儲存壓縮成較有效率的儲存結構吧。
—— Day 27

一、前言

正如前面〈Git Commit〉與〈Git Object〉所述,Git 會將每一個版本像照相一樣拍下來,每一個檔案都會儲存成 Git Blob Object,無論這個檔案是否與前一個版本相差無幾,對 Git 來說都是不同的物件,所以會原封不動的儲存下來,而不是像其他版本控制工具一樣是儲存兩個檔案之間的差異。儘管這樣的方式帶給我們許多好處,例如切換版本速度快、構成 Git 分散式版本控制的基礎等等,但是長久下來 Git 的資料庫不免逐漸笨重起來。

這個顯而易見的議題,Git 當然不會忽視,本章就來聊聊 Git 是如何解決這個議題的。

二、概念

2-1. Garbage Collect

事實上,Git 會不定期進行「垃圾收集」(garbage collet,gc) 的動作,觸發條件是 7,000 個左右的 loose object(即沒有被壓縮成 packfile 的 Git Object)或是 50 個 packfile。而這個動作主要會做幾件事情:

  1. 將所有有被參考的 loose objects 封裝成 packfiles
  2. 將較小的 packfiles 合併成一個大的 packfiles
  3. 將所有的 Git Reference 檔案合併成 packed-refs 檔案
  4. 將不被參考的 Git Objects 刪除,像是:
    • 沒有被任何 Tree Object 參考的 Blob Object
    • 沒有被任何 Commit Object 和 Tree Object 參考的 Tree Object
    • 沒有被任何 Git Reference 參考的 Commit Object

透過這些事情,就能將 Git Repository 所需要的空間縮小了。

# Command Synopsis: git-gc 
# Reference: https://git-scm.com/docs/git-gc
git gc [--options]

OPTIONS
  --aggressive
  --auto
  --quiet
  --prune=<date> | --no-prune
  --force
  --keep-largest-pack

2-2. Pack Loose Objects

那麼 Git 又是如何將這些 loose objects 封裝成 packfiles 的呢?Git 會去尋找檔案名稱與大小相近的檔案,並且只保存檔案不同版本之間的差異內容,如此就能兼具那些儲存差異的版本控制節省空間的優點。而為了提升效能,在封裝成 packfile 的同時,也會建立該 packfile 的 index 檔案,如此 Git 在尋找 Git Object 時,就能直接透過 index 快速找到該 Git Object 在 packfile 的位置以進行還原。

每一個 packfile 都會對應到一個 index,因此通常會有一組這樣命名的檔案:

  • pack-<SHA-1>.pack
  • pack-<SHA-1>.idx

詳細可以透過 git verify-pack 指令去查看指定的 packfile 儲存了哪些 Git Object 去深入暸解

# Command Synopsis: git-verify-pack 
# Reference: https://git-scm.com/docs/git-verify-pack
git verify-pack [--options] <pack>.idx

OPTIONS
  --verbose  
  --stat-only

OUTPUT FORMAT
	SHA-1 type size size-in-packfile offset-in-packfile depth base-SHA-1

2-3 Pack Refereces

除了 loose objects 和 packfiles 外,前面也提到所有的 Git Reference 檔案會合併成 packed-refs 檔案,該檔案的格式大概如下:

# pack-refs with: peeled
<SHA-1> <Reference Path>
<SHA-1> <Reference Path>
....

所以當某個 Reference 不存在於 .git/refs 目錄底下時,Git 便會去 .git/packed-refs 檔案中尋找。但若是我們更新某個 Reference,例如進行了 commit,所以當前 branch 對應的 commit SHA-1 值被更新了,此時 Git 是不會主動更新 packed-refs 中的值,而是在建立一個 Git Reference 檔案,直到 Git References 再度被後並成 packed-refs 檔案。

若是想直接手動進行合併,也可以直接輸入 git pack-refs 指令進行合併。

# Command Synopsis: git-pack-refs
# Reference: https://git-scm.com/docs/git-pack-refs
git pack-refs [--options]

OPTIONS
  --all
  --no-prune

上一篇
Git Stash
下一篇
Git Repository
系列文
Git 其然,Git 其所以然31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言