Git Squash:合併多個提交的實用技巧
在軟體開發的過程中,版本控制系統 Git 扮演著不可或缺的角色。它不僅幫助開發者追蹤程式碼的變動,還提供了強大的協作功能。而在眾多 Git 功能中,git squash 是一個非常實用且能顯著提升專案歷史記錄清晰度的技巧。本文將深入探討 git squash 是什麼、為什麼需要它、以及如何在實際開發中運用這項技術。
什麼是 Git Squash?
簡單來說,Git Squash 是一種將多個連續的 Git 提交(commits)合併成一個單一提交的操作。想像一下,你在開發一個新功能時,可能會有許多中間性的提交,例如:「初步實現功能 A」、「修復功能 A 中的一個 bug」、「改進功能 A 的錯誤處理」等等。這些提交對於開發過程本身可能很有用,但在最終合併到主分支時,它們可能會使專案的提交歷史變得冗長且難以閱讀。
git squash 的目的就是解決這個問題。它允許你在合併到主分支之前,將這些瑣碎或中間性的提交「壓扁」成一個有意義、描述清晰的單一提交。
為什麼需要 Git Squash?
Git Squash 的主要優點包括:
-
保持提交歷史的清晰與整潔:
一個乾淨、有條理的提交歷史能讓專案的維護者和未來的開發者更容易理解專案的演變過程。透過 Squash,你可以將一個功能的完整開發過程濃縮為一個單一的提交,這個提交清晰地描述了所引入的功能或修復。 -
更容易進行版本回溯與管理:
當提交數量減少,並且每個提交都代表一個完整的功能或有意義的變動時,你需要回溯到某個特定版本或者找出引入某個 Bug 的提交時,會更加容易。 -
隱藏不必要的細節:
開發過程中,你可能會有一些嘗試性的、錯誤的或者只是為了保存進度而做的提交。這些提交對於最終的專案歷史來說是雜訊。Squash 允許你篩選掉這些內部細節,只保留對專案有實際貢獻的提交。 -
簡化 Code Review 流程:
當一個 Pull Request (PR) 包含了數十個小提交時,Reviewer 需要花費更多時間去理解每個提交的意圖。如果將這些提交 Squash 成幾個邏輯上獨立的大提交,甚至一個提交,Reviewer 可以更專注於最終的變動內容,從而提高 Code Review 的效率。
何時使用 Git Squash?
雖然 Git Squash 好處多多,但並非所有情況都適用。以下是一些建議使用的場景:
- 合併功能分支到主分支前: 這是最常見的應用場景。在一個功能開發完成,準備將其合併到
main或master等主分支之前,將其所有相關提交 Squash 成一個或少數幾個邏輯清晰的提交。 - 清理個人工作分支: 如果你的工作分支上有很多臨時性的或不規範的提交,你可以在推送(push)到遠端倉庫或者發起 PR 之前,對其進行清理。
- 修正歷史錯誤: 如果你不小心提交了包含敏感資訊或錯誤的提交,並且這個提交還沒有被推送到公共分支,你可以使用 Squash 結合
git rebase -i來修正它。
如何執行 Git Squash?
Git Squash 通常是透過 git rebase -i(互動式變基)命令來完成的。以下是一個基本的操作步驟:
假設你目前在一個名為 feature/new-feature 的分支上,並且這個分支是從 main 分支切出來的。你希望將 feature/new-feature 分支上所有新的提交 Squash 成一個提交,然後再合併到 main。
-
切換到要操作的分支:
bash
git checkout feature/new-feature -
執行互動式變基:
你需要告訴 Git 你想從哪一個提交開始 Squash。通常是從你的功能分支與目標分支(例如main)分叉的地方開始。你可以使用git log或git merge-base main feature/new-feature來找到這個共同祖先提交的哈希值。或者,你可以指定想要 Squash 的提交數量。例如,如果你想 Squash 最近的 3 個提交,可以這樣做:
bash
git rebase -i HEAD~3
如果你想 Squash 自main分支以來的 所有 提交,可以這樣做:
bash
git rebase -i main
這個命令會打開一個文本編輯器,顯示類似以下的內容:
“`
pick 1a2b3c4 Commit message 1
pick 5d6e7f8 Commit message 2
pick 9h0i1j2 Commit message 3Rebase 7k8l9m0..9h0i1j2 onto 7k8l9m0 (3 commands)
Commands:
p, pick
= use commit r, reword
= use commit, but edit the commit message e, edit
= use commit, but stop for amending s, squash
= use commit, but meld into previous commit f, fixup
= like “squash”, but discard this commit’s log message x, exec
= run command (the rest of the line) for each commit b, break = stop here (continue rebase later with ‘git rebase –continue’)
d, drop
= remove commit l, label
t, reset
m, merge [-C
| -c ] . create a merge commit
These lines can be re-ordered; they are executed from top to bottom.
If you remove a line here WITHOUT changing its commit message, it will be skipped.
However, if you remove a line and change its commit message, it will be reworded.
If you make a commit and don’t push it, it will be lost.
“`
-
編輯 Rebase 指令:
在編輯器中,保留第一個提交的pick命令(這將是 Squash 後的基礎提交),然後將所有你想要合併的後續提交的pick改為squash或s。例如,如果你想將所有三個提交合併成一個:
pick 1a2b3c4 Commit message 1
s 5d6e7f8 Commit message 2
s 9h0i1j2 Commit message 3
保存並關閉編輯器。 -
編輯新的提交訊息:
Git 會再次打開一個編輯器,讓你為合併後的單一提交編寫新的提交訊息。它會預設將所有被 Squash 的提交訊息列出來,你可以刪除不必要的內容,寫一個清晰、簡潔、能概括所有變動的訊息。“`
This is a combination of 3 commits.
The first commit’s message is:
Commit message 1
The second commit’s message is:
Commit message 2
The third commit’s message is:
Commit message 3
Please enter the commit message for your changes. Lines starting
with ‘#’ will be ignored, and an empty message aborts the commit.
On branch feature/new-feature
Changes to be committed:
new file: file3.txt
modified: file1.txt
你可以將其修改為:
feat: Implement new feature with improved error handlingThis commit introduces the complete new feature,
including initial implementation, bug fixes,
and improved error handling.
“`
保存並關閉編輯器。 -
完成 Squash:
至此,你的多個提交已經成功 Squash 成一個提交了。現在你的feature/new-feature分支上只會有一個新的提交,歷史記錄也變得更加整潔。 -
(可選)強制推送到遠端:
如果你的feature/new-feature分支之前已經推送到遠端倉庫,並且你現在已經改變了其歷史記錄(因為git rebase會重寫歷史),你需要使用強制推送:
bash
git push --force-with-lease origin feature/new-feature
注意: 強制推送到已經被他人拉取或基於其工作的遠端分支是非常危險的,可能會導致他人工作丟失。因此,只在你確定沒有其他人基於這個遠端分支工作時才使用--force-with-lease,或者與團隊成員溝通好。
最佳實踐
- 及早 Squash: 在你的功能分支還沒有被推送到公共倉庫,或者還沒有被其他人基於其工作之前,盡早進行 Squash。
- 每個 Squash 代表一個邏輯單元: 儘量確保 Squash 後的每個提交都代表一個完整的、有意義的邏輯單元(例如一個新功能、一個 Bug 修復或一個重構)。
- 清晰的提交訊息: 即使 Squash 後,新的提交訊息也應該清晰、簡潔,概括所有被合併的變動。
- 避免 Squash 已經推送的公共分支: 永遠不要 Squash 已經推送到公共倉庫(例如
main或master)的提交,因為這會重寫歷史,給團隊協作帶來混亂。
結語
Git Squash 是一個強大的工具,能夠幫助開發者維護乾淨、有條理的專案提交歷史。透過熟練運用 git rebase -i,你可以將開發過程中的零散提交整理成精煉且有意義的提交,從而提升專案的可維護性和協作效率。在你的日常開發工作中,不妨嘗試將 Git Squash 納入你的版本控制工作流程,你會發現它能帶來巨大的改變。