跳轉到內容

Git 合併commit 精簡提交 歷史記錄管理

Git Commit Management

保持清晰、簡潔的 Git 提交歷史對於團隊協作和項目管理至關重要。本文將詳細介紹各種合併和整理 commit 的方法,幫助你打造專業的提交歷史。

為什麼要合併 Commit?

好處

  • 清晰的歷史記錄:每個 commit 代表一個完整的功能或修復
  • 便於回滾:可以快速定位和撤銷特定功能
  • Code Review 友好:審查者更容易理解變更
  • 專業的倉庫形象:展示良好的開發習慣

何時應該合併 Commit?

  • ✅ 多個小的 WIP(Work In Progress)提交
  • ✅ 修復之前提交的錯誤(typo、遺漏文件等)
  • ✅ 重構相關的代碼
  • ❌ 已經推送到共享分支的提交
  • ❌ 其他開發者可能依賴的提交

核心方法對比

命令特點適用場景風險等級
Merge合併兩個分支,保留所有歷史記錄集成功能分支到主分支
Rebase將一個分支的更改應用到另一個分支的頂部,形成線性歷史整理本地分支歷史
Reset --soft撤銷最後一次提交,但保留更改在工作目錄和暫存區重新組織最近的提交
Fixup創建修復某次提交的臨時提交,可配合 rebase -i 自動合併修正之前的提交
Squash將多個提交壓縮為一個清理 WIP 提交

Merge 的使用方法

Merge 是最安全的合併方式,它保留了完整的提交歷史。

基本合併流程

創建臨時分支並切換

sh
git checkout -b temp-branch

在臨時分支上做更改並提交

sh
echo "Some changes" > file.txt
git add file.txt
git commit -m "Changes on temp-branch"

切換回 main 併合並臨時分支

sh
git checkout main
git merge temp-branch

推送更改到遠程倉庫

sh
git push origin main

刪除臨時分支

sh
git branch -d temp-branch

Merge 的類型

1. Fast-forward Merge

當目標分支沒有新提交時,Git 會簡單地移動指針:

bash
# 當前狀態:
# main:     A --- B
# feature:       B --- C --- D

git checkout main
git merge feature

# 結果(Fast-forward):
# main/feature: A --- B --- C --- D

2. Three-way Merge

當兩個分支都有新提交時,會創建一個新的合併提交:

bash
# 當前狀態:
# main:     A --- B --- E
# feature:       B --- C --- D

git checkout main
git merge feature

# 結果(Three-way merge):
# main:     A --- B --- E --- M
#                  \         /
# feature:       C --- D

3. Squash Merge

將分支的所有提交壓縮為一個:

bash
git checkout main
git merge --squash feature
git commit -m "feat: Add complete feature X"

Merge 策略選項

bash
# 使用 recursive 策略(默認)
git merge -s recursive feature

# 使用 octopus 策略(合併多個分支)
git merge -s octopus branch1 branch2 branch3

# 使用 ours 策略(保留當前分支,忽略其他)
git merge -s ours feature

# 使用 subtree 策略(子項目合併)
git merge -s subtree --squash feature

處理合併衝突

bash
# 合併時出現衝突
git merge feature
# CONFLICT (content): Merge conflict in file.txt

# 1. 查看衝突文件
git status

# 2. 手動編輯解決衝突
# 打開文件,找到衝突標記:
# <<<<<<< HEAD
# 當前分支的內容
# =======
# 要合併分支的內容
# >>>>>>> feature

# 3. 標記衝突已解決
git add file.txt

# 4. 完成合並
git commit

# 或者中止合併
git merge --abort

Rebase 的使用方法

Rebase 可以創建線性的提交歷史,使日誌更清晰。

基本 Rebase 流程

創建臨時分支並切換

sh
git checkout -b temp-branch

在臨時分支上做更改並提交

sh
echo "Some changes" > file.txt
git add file.txt
git commit -m "Changes on temp-branch"

切換回 main 並進行 rebase

sh
git checkout main
git rebase temp-branch

推送更改到遠程倉庫

sh
git push origin main

刪除臨時分支

sh
git branch -d temp-branch

交互式 Rebase(強大工具)

bash
# 重新排列、編輯、合併最近的 5 個提交
git rebase -i HEAD~5

# 或者從特定提交開始
git rebase -i abc1234

編輯器中會顯示:

pick abc1234 First commit
pick def5678 Second commit
pick ghi9012 Third commit
pick jkl3456 Fourth commit
pick mno7890 Fifth commit

# Rebase pqr1234..mno7890 onto pqr1234
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]

常用操作示例

1. 合併多個提交

# 修改前
pick abc1234 feat: Add user model
pick def5678 fix: Typo in user model
pick ghi9012 refactor: Clean up user model

# 修改後(合併為一個)
pick abc1234 feat: Add user model
squash def5678 fix: Typo in user model
squash ghi9012 refactor: Clean up user model

2. 重新排序提交

# 修改前
pick abc1234 feat: Add login
pick def5678 feat: Add signup
pick ghi9012 docs: Update README

# 修改後(調整順序)
pick abc1234 feat: Add login
pick ghi9012 docs: Update README
pick def5678 feat: Add signup

3. 修改提交信息

# 修改前
pick abc1234 wip
pick def5678 wip
pick ghi9012 done

# 修改後
reword abc1234 feat: Implement authentication
reword def5678 feat: Add password validation
reword ghi9012 feat: Complete login flow

4. 刪除不需要的提交

# 修改前
pick abc1234 feat: Add feature
pick def5678 debug: Add console.log
pick ghi9012 test: Add tests

# 修改後(刪除調試提交)
pick abc1234 feat: Add feature
drop def5678 debug: Add console.log
pick ghi9012 test: Add tests

⚠️ Rebase 的黃金規則

永遠不要 rebase 已經推送到共享分支的提交!

bash
# ❌ 危險:不要這樣做
git checkout main
git pull origin main
git rebase feature  # 如果 main 已經推送到遠程

# ✅ 安全:只在本地分支使用
git checkout feature
git rebase main

Reset --soft 的使用方法

reset --soft 是重新組織最近提交的有用工具。

基本用法

在 main 分支上做更改並提交

sh
echo "Some changes" > file.txt
git add file.txt
git commit -m "Changes on main"

撤銷最後一次提交

sh
git reset --soft HEAD^

重新提交或進行其他操作,然後推送到遠程倉庫

sh
echo "Updated changes" > file.txt
git add file.txt
git commit -m "Updated changes on main"

git push origin main

Reset 的三種模式

bash
# 1. --soft: 只移動 HEAD,保留工作區和暫存區
git reset --soft HEAD~1
# 使用場景:合併多次提交

# 2. --mixed (默認): 移動 HEAD,清空暫存區,保留工作區
git reset HEAD~1
git reset --mixed HEAD~1
# 使用場景:重新添加文件

# 3. --hard: 移動 HEAD,清空暫存區和工作區(危險!)
git reset --hard HEAD~1
# 使用場景:完全放棄最近的提交

實際應用場景

場景 1:合併最近的 3 個提交

bash
# 當前歷史
# commit3: fix: typo
# commit2: fix: missing semicolon
# commit1: feat: Add user profile

# 合併為一個提交
git reset --soft HEAD~3
git commit -m "feat: Add user profile with validation"

場景 2:拆分一個大的提交

bash
# 撤銷最後一次提交,但保留更改
git reset --soft HEAD~1

# 分批提交
git add src/models/user.js
git commit -m "feat: Add user model"

git add src/controllers/user.js
git commit -m "feat: Add user controller"

git add src/tests/user.test.js
git commit -m "test: Add user tests"

場景 3:修改提交但不改變內容

bash
# 撤銷提交
git reset --soft HEAD~1

# 修改提交信息
git commit -m "feat: Better description here"

Fixup 的使用方法

fixup 是修正之前提交的優雅方式。

基本流程

查看提交歷史並找到要修復的提交的哈希

sh
git log --oneline
# 輸出:
# abc1234 feat: Add login form
# def5678 docs: Update README
# ghi9012 feat: Initial commit

進行修改並創建 fixup 提交

sh
# 修改代碼
git add .

# 用 --fixup 創建修復指定提交的臨時提交
git commit --fixup abc1234

# 這會創建一個特殊的提交消息:
# fixup! feat: Add login form

使用自動 rebase 將 fixup 合併進目標提交

sh
git rebase -i --autosquash abc1234^

注意:將 <commit-hash> 替換為目標提交的哈希值。git rebase 會自動把 fixup 提交合併到該目標提交中。

強制推送更新後的歷史

sh
git push --force-with-lease

通過合理使用 fixuprebase --autosquash,可以讓 Git 歷史更簡潔、易讀。

Fixup vs Amend

bash
# Amend: 修改最後一次提交
git add forgotten-file.js
git commit --amend --no-edit

# Fixup: 修改之前的任意提交
git add bugfix.js
git commit --fixup abc1234
git rebase -i --autosquash abc1234^

自動化 Fixup 工作流

配置 Git 別名簡化操作:

bash
# 添加別名
git config --global alias.fixup '!f() { git commit --fixup $1; }; f'
git config --global alias.squash '!f() { git rebase -i --autosquash $1^; }; f'

# 使用
git fixup abc1234
git squash abc1234

Squash 合併

使用 merge --squash

bash
# 將 feature 分支的所有提交壓縮為一個
git checkout main
git merge --squash feature
git commit -m "feat: Complete feature implementation"

使用 rebase -i squash

bash
git rebase -i HEAD~5
# 在編輯器中將 pick 改為 squash 或 fixup

使用 reset --soft

bash
# 合併最近 5 個提交
git reset --soft HEAD~5
git commit -m "feat: Combined feature"

高級技巧

1. Cherry-pick 特定提交

bash
# 從其他分支選擇性地應用提交
git cherry-pick abc1234
git cherry-pick abc1234 def5678 ghi9012

# 繼續或中止
git cherry-pick --continue
git cherry-pick --abort

2. 使用 stash 輔助重組

bash
# 暫存當前工作
git stash

# 重組提交
git rebase -i HEAD~3

# 恢復暫存的工作
git stash pop

3. 創建提交模板

bash
# 創建模板文件 ~/.gitmessage
cat > ~/.gitmessage << EOF
# <type>(<scope>): <subject>

# <body>

# <footer>
EOF

# 配置使用模板
git config --global commit.template ~/.gitmessage

4. 自動清理 WIP 提交

bash
# 查找所有 WIP 提交
git log --oneline --grep="WIP"

# 批量處理
git rebase -i --autosquash HEAD~20

最佳實踐

1. 提交原子性

bash
# ✅ 好的提交:每個提交完成一個獨立的功能
git commit -m "feat: Add user authentication"
git commit -m "test: Add auth tests"
git commit -m "docs: Document auth API"

# ❌ 不好的提交:混合多個無關變更
git commit -m "Update stuff"

2. 使用規範的提交信息

遵循 Conventional Commits 規範:

bash
# 格式:<type>(<scope>): <subject>

git commit -m "feat(auth): Add OAuth2 support"
git commit -m "fix(api): Handle null pointer exception"
git commit -m "docs(readme): Update installation guide"
git commit -m "refactor(core): Simplify error handling"
git commit -m "test(unit): Add coverage for edge cases"

3. 定期整理歷史

bash
# 在推送到遠程之前整理
git rebase -i origin/main

# 合併相關的 fixup 提交
git rebase -i --autosquash HEAD~10

4. 保護重要分支

在 GitHub/GitLab 上設置分支保護:

  • 禁止強制推送
  • 要求 Code Review
  • 要求 CI 檢查通過

常見問題排查

問題 1:Rebase 後出現衝突

bash
# 解決衝突
git status
# 編輯衝突文件
git add resolved-file.txt
git rebase --continue

# 或者中止 rebase
git rebase --abort

問題 2:誤刪了重要提交

bash
# 使用 reflog 找回
git reflog
# 找到丟失的提交 hash
git cherry-pick lost-commit-hash

問題 3:合併後歷史混亂

bash
# 重置到合併前的狀態
git reset --hard HEAD@{1}

# 或者使用 revert 撤銷合併
git revert -m 1 merge-commit-hash

總結

掌握 Git commit 管理技巧可以顯著提升工作效率:

  1. Merge:安全集成,保留完整歷史
  2. Rebase:線性歷史,清晰簡潔
  3. Reset:靈活重組最近提交
  4. Fixup:優雅修正歷史提交
  5. Squash:合併相關變更

關鍵原則:

  • ✅ 在本地分支自由重組
  • ❌ 不要改寫已共享的歷史
  • ✅ 保持提交的原子性
  • ✅ 使用規範的提交信息

下一步學習:

記住:好的提交歷史是給未來的自己和其他開發者的禮物!🎁

最後更新於: