2026-06-11 · 版本控制 · 分散式 · 快照思維 · 三大區域
沒有版本控制時,我們常用「複製整個資料夾」來保存進度,於是出現這種畫面:
版本控制系統(VCS)就是要有系統地解決這些問題,核心能力有四項:
| 需求 | VCS 提供的能力 |
|---|---|
| 記錄歷史 | 每次有意義的變更存成一個「版本」,附帶時間、作者、說明 |
| 回溯 | 隨時回到任何一個歷史版本,或比較版本之間的差異 |
| 分支實驗 | 開一條獨立線路嘗試新點子,不影響主線,失敗就丟掉 |
| 多人協作 | 多人各自修改,再有規則地合併,並追蹤每一行是誰改的 |
版本控制跟「雲端備份」(iCloud、Dropbox、Google Drive 自動同步)有什麼不一樣?聽起來都是把檔案存起來、還能救回舊版本。
雲端硬碟解決的是「遺失」風險——同步、備份,有些服務甚至保留一段時間內的舊版本。但對照上面那張表,它幾乎沒碰到後三項:
‧ 沒有「有意義的版本」:它是按時間或按次存檔,不是「你決定這是值得記錄的一版」;想找「上禮拜四改完那個功能的版本」只能用時間用猜的,不會有一句話告訴你那次改了什麼、為什麼改。
‧ 沒有分支:只有一條時間軸往前疊,不能同時維護「穩定版」與「實驗版」兩條並行歷史;想試新點子失敗要退回去,只能土法煉鋼複製一份資料夾——正是圖 1-1 那種混亂的來源。
‧ 沒有真正的合併機制:兩人同時編輯同個檔案,雲端硬碟通常是保留一份「衝突副本」(像 檔案 (張三的版本).docx)讓你自己人工比對貼回去;Git 能在行的層級判斷兩人改的是不是同一段,不衝突就自動合併(第 5、6 章)。
一句話:雲端備份救的是「檔案不見了」,版本控制解決的是「有意義地記錄、回溯、分支、協作」。(像 Google Docs 的「版本記錄」有時間軸,勉強算半套,但仍然沒有分支與合併的能力。)
VCS 分兩大類。早期的 SVN、CVS 是集中式:只有一台中央伺服器保存完整歷史,每個人手上只是「工作副本」。Git 則是分散式:每一次 clone 都會把整個版本庫(含全部歷史)複製到本機。
commit(提交到本機)和 push(送到遠端)是兩個分開的動作——提交不需要網路,只有要分享給別人時才需要連線。
既然 clone 把整個版本庫(含全部歷史)都複製下來了,為什麼 clone 之後只看得到主分支?
「整座版本庫都複製下來」和「看得到幾個分支」是兩件事。clone 確實把所有物件(全部歷史)與所有分支指標都抓到本機,只是分兩種存放方式:
‧ 遠端追蹤分支(refs/remotes/origin/*):origin/main、origin/dev… 全部都在。
‧ 本地分支(refs/heads/*):clone 只自動建立一個(遠端的預設分支,通常是 main)。
所以「只看到 main」是因為 git branch 只列本地分支。其他分支沒有不見,它們以 origin/xxx 的形式都在硬碟裡:
git branch # 只看到 * main ← 本地分支
git branch -r # origin/main, origin/dev … ← 全都在
git branch -a # 兩者都列出
要切到別的分支,資料早在本機,只要建一個本地分支去指它即可(離線就能做):
git switch dev # 新版 Git 自動對應 origin/dev
git checkout -b dev origin/dev # 舊寫法
為什麼這樣設計:工作目錄一次只能呈現一個 commit 的內容,所以 clone 後只「簽出」一個分支;其餘分支保留為 origin/* 追蹤分支,避免汙染本地命名空間,需要時再認領。
一句話:clone 把整座倉庫搬回家了,只是預設只幫你打開其中一個房間(main);其他房間的門都在,推開即可。
多數舊式 VCS 用差異(delta)思維:記錄每個檔案相對前一版「改了哪幾行」,要還原某版本就得把一連串差異疊加回去。
Git 用快照(snapshot)思維:每次提交,Git 記錄的是當下所有檔案的完整狀態。沒有變動的檔案,不會重複存,而是用一個指標指向前一版的內容(細節在第 2 章)。
git diff 看到的「差異」,其實是 Git 即時比對兩張快照算出來的結果,而非它儲存的形式。
上面提到「沒有變動的檔案,不會重複存」,那 Git 是怎麼判斷檔案有沒有差異的?
不是逐行比對文字內容(那是 git diff 顯示層在做的事),而是看整個檔案內容算出來的雜湊值有沒有變——這正是快照思維的核心:Git 判斷的是「這是不是同一份內容」,不是「哪裡不一樣」。具體分兩層:
‧ 身分怎麼定:內容餵進 SHA-1 算出雜湊值當「地址」,內容相同雜湊必相同,天生只會存一份,不需要額外比對(第 2 章 2.1 內容定址與 SHA)。
‧ 怎麼避免每次都整檔重算雜湊:.git/index 幫每個追蹤中的檔案記著上次的 stat 資訊(檔案大小、修改時間);status / add 先比對 stat 有沒有變,沒變就直接判定檔案沒動,省去重讀重算;stat 變了,才真的重新雜湊內容,拿去跟已存的 blob SHA 比對(第 4 章 4.2)。
一句話:Git 比對的是「內容的指紋」,不是「內容的每一行」。
使用 Git 時,你的檔案隨時處在三個區域之一。幾乎所有日常指令,本質都是把檔案在這三個區域之間搬動。先記住這張圖,後面章節都會回到它。
| 重點 | 一句話記住 |
|---|---|
| VCS 的價值 | 有系統地記錄歷史、回溯、分支實驗、多人協作 |
| 分散式 | 每個 clone 都是完整版本庫,可離線提交 |
| 快照思維 | Git 存的是每次提交的完整快照,不是差異 |
| 三大區域 | 工作目錄 →(add)→ 暫存區 →(commit)→ 版本庫 |
.git 裡究竟是用什麼物件、如何儲存的。