Git 是分散式版本控制系統。Git 記錄一組檔案在特定時刻的完整狀態、提交之間的關係,以及每次修改的作者與說明。每位開發者的本機倉庫都可以保存完整歷史,因此即使暫時無法連接伺服器,仍能查看差異、建立提交和切換分支。

初學 Git 最容易混淆的不是命令,而是修改目前位於工作區、暫存區、本機歷史還是遠端倉庫。先建立資料流模型,再學習命令,便能根據操作目的選擇工具。

工作區 ──git add──> 暫存區 ──git commit──> 本機提交歷史 ──git push──> 遠端倉庫

1. 安裝與初始設定

1.1 安裝 Git

Ubuntu 或 Debian 可以執行 sudo apt update && sudo apt install git,Fedora、Rocky Linux 等系統可以執行 sudo dnf install git,macOS 搭配 Homebrew 時可以執行 brew install git。安裝完成後,用 git --version 檢查可執行檔與版本。

每個提交都會記錄作者姓名與電子郵件。下列全域設定套用到目前使用者的所有倉庫:

git config --global user.name "Your Name"
git config --global user.email "[email protected]"

使用 git config --global user.namegit config --global user.email 可以讀取設定。特定倉庫需要另一組身分時,在倉庫內省略 --global;倉庫設定會覆蓋同名的全域設定。

1.2 理解設定層級

Git 設定有三個常見層級:

層級常見位置設定選項作用範圍
系統/etc/gitconfig--system整台電腦
使用者~/.gitconfig--global目前使用者
倉庫.git/config不加層級選項目前倉庫

越接近倉庫的設定優先順序越高。git config --list --show-origin 會同時顯示設定值與來源檔案。常用初始設定如下:

git config --global init.defaultBranch main
git config --global core.editor "vim"
git config --global core.autocrlf input

Git 會在撰寫合併訊息、互動式 rebase 指令或標籤說明時開啟指定編輯器。需要熟悉編輯器基本操作時,可以參考站內的 Vim 使用指南;偏好圖形介面的讀者可以參考 Visual Studio Code 安裝與擴充套件

2. 建立倉庫並完成第一次提交

2.1 初始化或複製倉庫

從本機建立專案時,可以執行以下命令:

mkdir git-practice
cd git-practice
git init

git init 會建立 .git 目錄,用來保存物件資料庫、分支參照與倉庫設定。現有專案也能執行 git init,Git 不會刪除原有檔案。建立第一個檔案後執行 git status,Git 會把尚未納入追蹤的檔案列為 untracked。

遠端已經存在專案時,通常使用 git clone [email protected]:team/project.git。Clone 會下載提交歷史、建立工作區、加入名為 origin 的遠端,並簽出預設分支。git init 適合從本機開始新專案,git clone 適合加入現有專案。

2.2 分清四個資料區域

Git 日常操作涉及四個區域:

  1. 工作區(working tree):編輯器與檔案系統目前顯示的內容;
  2. 暫存區(index 或 staging area):下一次提交準備收錄的內容;
  3. 本機倉庫(local repository):已建立的提交、分支與標籤;
  4. 遠端倉庫(remote repository):團隊共享的提交與分支。

git add 的主要作用是把檔案目前的內容加入下一次提交。已追蹤檔案每次修改後也需要重新 add,才能更新暫存區中的版本。

2.3 檢查、暫存與提交

git status --short 適合快速查看檔案狀態。git diff 顯示工作區相對暫存區的差異,git diff --staged 顯示暫存區相對上一個提交的差異。提交前依次查看兩種 diff,可以確認哪些修改仍未暫存,以及下一次提交實際包含哪些內容。

建立檔案、選擇修改並提交的完整流程如下:

printf '# Git practice\n' > README.md
git status --short
git diff
git add README.md
git diff --staged
git commit -m "Add project README"

git add src/ 暫存指定目錄,git add . 暫存目前路徑內的新增、修改與刪除,git add --patch 則能逐段選擇修改。逐段暫存適合拆分大型修改,讓每個提交只表達一個連貫變更。提交訊息應說明修改帶來的結果,例如 Fix empty search results on mobileupdate files 更容易審查。

.gitignore 用來排除建置產物、暫存檔與本機設定:

node_modules/
dist/
.env
*.log

.gitignore 不會停止追蹤已經提交的檔案。若 .env 已被追蹤,使用 git rm --cached .env 從暫存區移除,再提交變更。密碼、API token 與私鑰不應進入提交歷史;稍後刪除檔案不會清除舊提交中的祕密。

3. 理解提交歷史與分支

3.1 Commit、分支與 HEAD

每個 commit 記錄專案快照、作者、時間、提交訊息,以及父提交。多個提交透過父子關係形成有向圖。HEAD 通常指向目前分支,分支名稱則指向該分支的最新提交。建立提交 D 後,mainHEAD 都會向前移動:

A──B──C──D  main, HEAD

分支不是專案的完整副本,而是一個可移動的提交參照。建立分支的成本很低,因此短期功能分支是 Git 協作的常見單位。

Commit ID 是 Git 根據物件內容計算出的雜湊值。修改提交內容、父提交或提交資訊會產生新的 ID,因此 rebase 和 amend 會重寫提交。命令通常只需使用不會混淆的 ID 前綴。HEAD~1 表示第一父鏈的上一個提交,HEAD~3 表示沿第一父鏈向前三個提交。

3.2 建立、切換與合併分支

新功能應在獨立分支開發。git switch -c feature/search 會建立並切換分支,git switch main 會回到主分支,git branch -vv 可以查看本機分支及其上游狀態。舊版 Git 常使用 git checkout 同時切換分支與還原檔案;現代 Git 提供 git switchgit restore,讓兩類操作更容易區分。

功能完成後,先切換到接收變更的分支,再合併功能分支:

git switch main
git merge feature/search

如果 main 沒有新提交,Git 只需向前移動分支參照,稱為 fast-forward merge。如果兩個分支都有新提交,Git 通常會建立具有兩個父提交的 merge commit,保留兩條開發線匯合的關係。

3.3 使用 Rebase 整理個人分支

Rebase 會把一組提交重新套用到新的基底。執行 git switch feature/search 後再執行 git rebase main,原本建立在舊基底上的功能提交會重建到最新 main 之後。

Rebase 可以產生線性歷史,但會改變 commit ID。尚未共享的個人分支適合整理;多人共同使用的分支若被任意重寫,其他開發者的歷史會產生分歧。功能合併後,git branch -d feature/search 會安全刪除已合併的本機分支;大寫 -D 會略過合併檢查,使用前應確認提交仍可從其他參照找到。

4. 遠端協作與衝突處理

4.1 Fetch、Pull 與 Push

git remote -v 顯示遠端名稱與 URL。origin 是 clone 時建立的慣用名稱,不是 Git 保留字。origin/main 是遠端追蹤分支,代表最近一次 fetch 後,本機所知道的遠端 main 狀態。

git fetch origin 下載遠端的新提交與分支參照,但不修改工作區或本機分支。Fetch 後可以用 git log --oneline main..origin/main 查看遠端新增提交,並用 git diff main...origin/main 比較兩條開發線。

git pull 通常等於 fetch 後再 merge。git pull --ff-only 只允許快進,遇到分歧時停止,讓開發者明確選擇 merge 或 rebase。偏好 rebase 的團隊可以使用 git pull --rebase。團隊應統一 pull 策略,避免每位成員產生不同的歷史形狀。

第一次發布功能分支時執行 git push --set-upstream origin feature/search,之後可以直接執行 git push。如果遠端分支包含本機沒有的提交,普通 push 會被拒絕;應先 fetch 並整合遠端提交。重寫個人遠端分支後若確實需要強制更新,git push --force-with-lease 會檢查遠端是否仍處於預期狀態,比不檢查狀態的 --force 更能避免覆蓋他人的新提交。

4.2 解決 Merge 與 Rebase 衝突

兩個分支修改同一段文字且結果不同時,Git 無法判斷需要保留哪一版,便會加入衝突標記:

<<<<<<< HEAD
目前分支的內容
=======
另一個分支的內容
>>>>>>> feature/search

Merge 發生衝突後,先用 git status 查看衝突清單。編輯每個衝突檔案,產生符合兩邊意圖的最終內容,刪除衝突標記並完成測試,再執行 git add path/to/resolved-filegit commitgit merge --abort 可以取消整次合併。

Rebase 發生衝突時,修正並暫存檔案後執行 git rebase --continuegit rebase --abort 可以取消整次 rebase。衝突解決的完成條件是程式符合預期並通過測試,不只是衝突標記已經消失。

4.3 從功能分支送出審查

一套常見的協作流程如下:

git switch main
git pull --ff-only
git switch -c feature/search-filter
# 編輯並測試
git add --patch
git diff --staged
git commit -m "Add topic filter to article search"
git push --set-upstream origin feature/search-filter

推送分支後,在 GitLab 建立 merge request。審查產生的新修改可以繼續提交到同一分支,merge request 會自動更新。需要把測試接到提交與 merge request 時,可以繼續閱讀 GitLab CI/CD 指南;希望自行管理程式碼平台時,可以參考 使用 Docker 部署 GitLab

5. 安全復原與暫存工作

5.1 根據修改所在區域選擇工具

不同復原命令處理不同區域:

目的命令歷史是否改寫
丟棄工作區修改git restore path/to/file
取消暫存並保留工作區修改git restore --staged path/to/file
修正最新的本機提交git commit --amend
撤銷已共享提交git revert <commit-id>否,建立新提交
移動目前分支git reset

Restore 可能丟棄尚未保存的工作區內容,執行前應先查看 git diff。Amend 會建立新的 commit ID,適合修正尚未共享的最新提交。Revert 建立反向提交並保留原提交,適合公開分支與多人協作。

Reset 會移動目前分支,並依模式決定是否改動暫存區與工作區:

命令暫存區工作區
git reset --soft HEAD~1保留保留
git reset --mixed HEAD~1重設保留
git reset --hard HEAD~1重設重設

--hard 會丟棄工作區與暫存區的內容。Reset 適合整理尚未共享的本機歷史,撤銷公開分支上的提交通常應使用 revert。

5.2 使用 Reflog 找回提交

git reflog 記錄本機 HEAD 和分支參照的移動。Reset 或 rebase 後若需要找回舊提交,可以從 reflog 找到 commit ID,再執行 git branch recovery <commit-id> 建立分支保留。Reflog 是具有保留期限的本機記錄,不會同步到遠端,因此不能取代遠端備份。

5.3 暫存尚未完成的修改

需要臨時切換分支,但修改尚未適合提交時,可以執行以下流程:

git stash push -m "WIP search form"
git switch main
# 處理臨時工作
git switch feature/search
git stash pop

git stash apply 會恢復修改並保留 stash 記錄,git stash pop 會在成功恢復後刪除記錄。預設 stash 不包含 untracked 檔案;需要一併收起時使用 git stash push --include-untracked -m "WIP full tree"。Stash 適合短期切換,需要長期保存或分享的工作應建立清楚命名的分支與提交。

6. 標籤、金鑰與版本發布

標籤通常用來標記正式版本。Annotated tag 會保存建立者、日期與說明:

git tag -a v1.0.0 -m "Release 1.0.0"
git show v1.0.0
git push origin v1.0.0

分支會隨新提交移動,版本標籤通常固定指向特定提交。正式版本若需要修正,建立新版本標籤比移動既有標籤更清楚。發布前應確認目標 commit 已通過測試,版本號與變更記錄一致,建置產物可由相同原始碼重建。

使用 SSH 連接遠端倉庫時,可以執行 ssh-keygen -t ed25519 -C "[email protected]" 建立金鑰。把 .pub 公鑰內容加入 Git 託管平台,私鑰則留在本機並設定適當權限。私鑰不能貼入網站、聊天訊息或 Git 倉庫。金鑰管理與伺服器端 SSH 加固可以參考 Linux 雲伺服器初始化與安全加固

7. 診斷順序與後續練習

遇到 Git 問題時,先收集狀態,再執行會修改歷史或工作區的命令:

git status
git branch -vv
git remote -v
git log --oneline --decorate --graph --all -20
git diff
git diff --staged

狀態輸出可以回答目前所在分支、分支追蹤的遠端、尚未提交的修改,以及提交圖如何分叉。判斷修改位於工作區、暫存區、本機歷史或遠端後,再選擇 add、commit、fetch、merge、rebase、push、restore、revert 或 reset,可以避免大部分誤操作。

初學者可以在練習倉庫依次完成三個任務:先建立兩個小提交,再從第一個提交建立功能分支並製造一次可控衝突,最後分別使用 revert 與 reset 觀察提交圖。每一步都搭配 git statusgit diff 和圖形化 git log,Git 的區域模型與分支模型會比單純背誦命令更容易掌握。