適合學習理解的 Git 變基(Rebase)案例
為了幫助你更好地理解 Git 變基(Rebase)的操作和效果,下面通過一個簡單的案例來演示變基的過程和影響。
案例背景
假設我們有一個 Git 倉庫,包含兩個分支:
- 主分支(main):包含最新的穩定代碼。
- 特性分支(feature):開發新功能的分支,從
main
分支的某個舊提交創建。
初始提交歷史
A --- B --- C --- D (main)\E --- F --- G (feature)
- A、B、C、D:
main
分支的提交。 - E、F、G:
feature
分支的提交,基于main
分支的提交B
。
場景
在 feature
分支開發期間,main
分支有了新的提交 C
和 D
。現在,我們希望將 feature
分支的變更應用到 main
分支的最新提交 D
之后,使提交歷史更加線性。
變基操作步驟
1. 切換到 feature
分支
git checkout feature
2. 執行變基操作
git rebase main
3. Git 的操作過程
- 找到共同祖先:Git 找到
feature
分支和main
分支的共同祖先提交B
。 - 應用提交:將
feature
分支上的每個提交(E
、F
、G
)依次應用到main
分支的最新提交D
之后。 - 解決沖突(如果有):如果在應用提交時遇到沖突,需要手動解決沖突后繼續變基。
4. 變基完成后的提交歷史
A --- B --- C --- D (main)\E' --- F' --- G' (feature)
- E’、F’、G’:變基后的新提交,基于
main
分支的最新提交D
。 - 原提交 E、F、G:被新提交 E’、F’、G’ 替代,提交哈希值發生變化。
詳細步驟演示
步驟 1:創建初始提交歷史
# 初始化倉庫
git init# 創建 main 分支并提交
git checkout -b main
echo "Initial commit" > file.txt
git add file.txt
git commit -m "A"echo "Second commit" >> file.txt
git add file.txt
git commit -m "B"echo "Third commit" >> file.txt
git add file.txt
git commit -m "C"echo "Fourth commit" >> file.txt
git add file.txt
git commit -m "D"# 創建 feature 分支并提交
git checkout -b feature HEAD~3 # 基于提交 B 創建分支
echo "Feature commit 1" >> file.txt
git add file.txt
git commit -m "E"echo "Feature commit 2" >> file.txt
git add file.txt
git commit -m "F"echo "Feature commit 3" >> file.txt
git add file.txt
git commit -m "G"
步驟 2:執行變基操作
# 切換到 feature 分支
git checkout feature# 變基到 main 分支
git rebase main
步驟 3:查看變基后的提交歷史
# 查看提交歷史
git log --oneline --graph --all
輸出示例:
* 1234567 (HEAD -> feature) G' - Feature commit 3
* 2345678 F' - Feature commit 2
* 3456789 E' - Feature commit 1
* 4567890 (main) D - Fourth commit
* 5678901 C - Third commit
* 6789012 B - Second commit
* 7890123 A - Initial commit
案例解析
1. 變基前
feature
分支的提交E
、F
、G
基于main
分支的舊提交B
。- 提交歷史呈現分支結構,不夠線性。
2. 變基后
feature
分支的提交E'
、F'
、G'
基于main
分支的最新提交D
。- 提交歷史變得線性,易于閱讀和理解。
3. 提交哈希值變化
- 變基會重寫提交歷史,導致提交哈希值發生變化(如
E
變為E'
)。 - 這也是變基不適用于已推送的共享分支的原因。
類比理解
類比:
想象你正在寫一本小說,main
分支是主線劇情,feature
分支是支線劇情。
- 變基前:支線劇情基于主線劇情的某個舊章節。
- 變基后:你將支線劇情的修改應用到主線劇情的最新章節之后,使整個故事更加連貫。
注意事項
-
不要在已推送的共享分支上使用
rebase
:
如果feature
分支已經推送到遠程倉庫,并且其他開發者基于該分支進行了開發,使用rebase
會導致提交歷史不一致,引發問題。 -
備份分支:
在進行rebase
操作前,建議創建分支備份,以防出現問題。 -
理解提交歷史:
rebase
會改變提交歷史,需要理解其影響,避免誤操作。
總結
- 變基(Rebase):將當前分支的變更應用到目標分支的最新提交之后,使提交歷史更加線性。
- 案例演示:通過
feature
分支變基到main
分支,展示了變基的操作和效果。 - 適用場景:本地開發分支整合、清理提交歷史。
- 注意事項:如果分支已經推送到遠程倉庫,其他開發者可能已經基于該分支的提交進行了開發。變基后,提交歷史發生變化,導致其他開發者的本地倉庫與遠程倉庫不一致。 不要對已經推送的記錄進行變基!
變基之后仍然存在兩個分支,但 feature 分支的基準點(base)發生了變化。具體來說,feature 分支原來是基于 main 分支的提交 B,變基后變成了基于 main 分支的最新提交 D。
圖片中, 右邊 在dev分支上 變基
變基后合并操作的步驟
1. 前提條件
- 已完成變基:假設你已經對某個分支(如
feature
分支)執行了git rebase main
,使其基于main
分支的最新提交。 - 目標分支:你希望將變基后的
feature
分支合并到main
分支。
2. 切換到目標分支
git checkout main
- 目的:確保你在要合并到的目標分支上。
3. 更新目標分支
git pull origin main
- 目的:確保目標分支是最新的,避免合并沖突。
4. 合并變基后的分支
git merge feature
- 情況 1:如果變基后沒有沖突,Git 會自動完成合并,生成一個合并提交(如果使用
--no-ff
選項)。 - 情況 2:如果有沖突,Git 會提示你解決沖突。
5. 解決沖突(如果有)
- 查看沖突文件:
git status
- 手動編輯沖突文件:解決沖突后,標記沖突已解決:
git add <conflicted-file>
- 完成合并:
git commit
6. 推送更新到遠程倉庫
git push origin main
- 目的:將合并后的更改推送到遠程倉庫。
變基后合并的注意事項
-
確保變基已完成且無沖突:
- 在合并前,確保
feature
分支已經成功變基到main
分支的最新提交。 - 解決變基過程中可能出現的沖突。
- 在合并前,確保
-
避免在已推送的共享分支上變基:
- 如果
feature
分支已經推送到遠程倉庫,并且其他開發者基于該分支進行了開發,變基可能導致問題。 - 建議:在本地分支上變基,合并前與其他開發者溝通。
- 如果
-
使用
--no-ff
選項(可選):- 合并時可以使用
--no-ff
(no fast-forward)選項,保留分支歷史,便于查看合并記錄。
git merge --no-ff feature
- 合并時可以使用
-
備份重要分支:
- 在進行合并操作前,建議備份重要分支,以防出現問題。
變基后合并的優勢
- 線性提交歷史:變基后,提交歷史更加線性,易于閱讀和理解。
- 減少合并提交:如果變基成功,合并時可能不會產生額外的合并提交(取決于是否使用
--no-ff
)。 - 清晰的開發流程:通過變基和合并,可以保持代碼庫的整潔和一致性。
變基后合并的示例
場景描述
- 初始狀態:
main
分支:A --- B --- C --- D
feature
分支:A --- B --- E --- F --- G
(基于B
)
- 變基后:
feature
分支:A --- B --- C --- D --- E' --- F' --- G'
(基于D
)
合并操作
-
切換到
main
分支:git checkout main
-
更新
main
分支:git pull origin main
-
合并
feature
分支:git merge feature
-
解決沖突(如果有):
- 編輯沖突文件,標記解決,完成合并。
-
推送更新:
git push origin main
-
最終提交歷史:
A --- B --- C --- D --- E' --- F' --- G' (main, feature)