從 TFVC 轉換到 GIT 後(延伸閱讀:該用 TFVC 還是 Git?),我的版控習慣與態度有兩大改變。

以前使用 TFVC 時,程式要丟進 TFS 伺服器才能做版控,我常在本機寫些實驗性質或私房工具小專案,不好把公用伺服器當自己家,便沒版控無法享受改壞可以重來的便利;GIT 支援離線使用,現在就算測完即刪的實驗專案,只要覺得程式改下去有後悔的可能性,我二話不說馬上 git init && git add . && git commit -m "init" 加入版控大膽修改,不爽就倒帶重來,無比暢快。程式測完隨手一刪,對別人沒半點影響,這麼方便又環保,沒理由不用呀!

第二項改變則是對於存入版控留存進度的時機,我不再有所顧忌。過去 TFVC 要 Check In 總要小心翼翼,一旦 Check In 會在伺服器留下永恆印記,即使後來更正重新 Check In,寫錯寫壞的程式永遠躺在那裡,一方面讓修改歷程變亂,另一方面成是開發者生涯的污點(呃,有那麼嚴重嗎?)。然而,Check In/Commit 就像開發歷程的檢查點(或是打 RPG 遊戲 的進度存檔),切得愈細密,愈能精準退到某個動作之前,減少無法回頭的遺憾。Git 的 Commit 會先存在本機,Push 到進伺服器前還有機會整理,因此我們可以依據開發需要,想 Commit 就 Commit,不必影響日後的歷程追查。像是設計師平常可用草稿紙隨意勾勒,反正交稿前會整理成整潔精美的最終版,如此創作才不會被綁手綁腳。同樣的,多了 Push 前可以修改的程序,開發者平日才敢大膽使用 Commit 發揮版控的最大效益。

這個 Push 前整理 Commit 的程序稱為 Squash,就是之前 Git 指令小抄裡當年我還不會而讓前輩搖頭嘆氣的重要技能。

這篇文章就來簡單介紹 Squash 吧。

用具體案例比較容易了解。假設我在修改專案加入功能及修正 Bug,一開始先新增 1.txt 並 Commit,寫一寫發現不夠再追加 2.txt 並 Commit,再寫一陣子發現自己豬頭漏了長官交待的要件,於是又加了 3.txt 並 Commit;接著修正另一個 Bug,陸續又 Commit 了 a.txt,b.txt。這次修改過程,共累積了五次 Commit 合計五個檔案。

專案改完我要 Push 到 TFS/Github,如此簡單的小修改分成五個 Commit,其他人看了會搖頭吧! 幸好,在 Push 前,我們可以用 Squash 合併它們。如果你使用的是 VS2019,在 Sync 操作介面可使用右鍵選單勾選連續幾個 Commit,選擇「Squash Commits...」將它們合併:

但 VS2019 GUI 提供的 Squash 功能只限從最後的 Commit (最上面一筆)向下合併連續的 Commit,如下圖,如果選取第二筆跟第三筆,選單無 Squash 可選:

在這個案例,從 VS2019 即可輕鬆將五個 Commit 合併成一筆再 Push。如果你使用的是 VS2017 以前的版本,或是想將五筆 Commit 合併成兩筆而非一筆,就需用 git 指令或其他 GUI Git 工具。(補充:Commit 並非愈少愈好,應依相關性及修改批次合併,以方便日後追查參考為分割依據)

git 指令是用 git rebase -i 完成 Squash,此處會以 Cmder 示範:(順便大推 Cmder,身為 Windows 開發人員少了它會輸在起跑點呀~)

執行 git rebase -i 會開啟文字編輯器(我的 Git 環境是指定 Notepad++ 為預設文字編輯器),git-rebase-todo 檔列出等待 Push 的 Commit 清單,每一筆包含操作動作、Commit GUID、Commit 說明三部分,預設動作都是 pick,如要與前一筆合併,將 pick 改成 squash 即可。我打算合併 1.txt、2.txt、3.txt 三個 Commit 成一筆,合併 a.txt、b.txt 兩筆成另一筆,將五個 Commit 整合成兩個。故修改結果如下:

pick 2eaf2ba add 1.txt
squash f7567ed add 2.txt
squash fd3e01e add 3.txt
pick 3c0297b add a.txt
squash 22b56a8 add b.txt

保留 1.txt,2.txt 及 3.txt 向上 squash 合併。保留 a.txt,b.txt 向上 squash。改好存檔關閉 Notepad++,會再彈出兩次 Notepad++,要求分別輸入合併後兩個 Commit 的文字說明:

設好文字說明存檔關閉 Notepad++,Git 將開始重算產生新的 Commit,如此,原本五筆 Commit 已整併成兩筆新的 Commit:(觀察可發現其 GUID 與原本的 1.txt 及 a.txt Commit 不同,是重算產生的新 Commit)

釐清觀念後,用 Git 指令操作 Squash 其實也還算簡單明瞭,飛快打字進行操作還能帶來當駭客的快感。但如果你仍習慣滑滑鼠,龍哥的文章 【狀況題】把多個 Commit 合併成一個 Commit有使用 SourceTree 軟體進行 Sqaush 的示範。在這裡則拿我常用的 Widnows Git GUI 軟體 - TortoiseGit 小海龜,進行示範。

開啟檔案總管在專案目錄右鍵選單開啟 TortoiseGit / Rebase...

清單原本會是空的,Upstream 請選擇 remotes/origin/master (或你的上游伺服器)並勾選 Force Rebase,五個 Commit 才會出現:

若要將五個 Commit 合併成兩個,最快的做法是選取要保留的兩個 Commit (1.txt 跟 a.txt),再選取 Squash unselected:

或者一一點選要 Squash 的 Commit,將它的動作改成 Squash (with commit below),效果相同:

按下「Start Rebase」,下方的視窗會切到 Commit Message 要求輸入合併後的 Commit 文字說明:

按下 Commit 後,Squash 作業就完成囉~

以上就是在 Git Push 前使用 Squash 整理 Commit 的小技巧,遲了三年,我終於上手了。噗~

[2021-08-27 更新] 感謝讀者小海分享,TortoiseGit 還有簡便的 Squash 操作方法。

選取相鄰要 Squash 的 Commit,按右鍵選單選「Combine to one commit」:

編譯訊息再按 Commit 鈕,大功告成。

Tips of squash commits in Visaul Studio to make Git hisotry concise before pushing.


Comments

# by 小海

小烏龜的 show log,可以選取多個 commit 項目,然後滑鼠右鍵 combind to one commit 也是合併,這跟 Squash 是同樣的?

# by Jeffrey

to 小海,感謝分享,已補充於本文。

Post a comment