事情要從一次不規範的程式碼開發開始說起
背景故事
時間
2024年某個風平浪靜的週五晚上
地點
中國,北京,西二旗,某網際網路大廠會議室
人物
小杰,小A,小B,老K
對話
老K:昨天提交的程式碼被測試打回來了!為什麼小B沒開發完的內容也一起提交上去了?
小B:啊?我不清楚啊,我在開發分支B開發完一部分就提交到test分支進行聯調了啊
小A:額(''!),我把test分支合併到釋出分支提交給測試了,因為我跟小杰最開始在各自的分支開發,但是中間聯調的時候,為了圖方便直接在test分支上改,改來改去就直接在test分支上開發了。。。
老K:什麼?你怎麼能直接在test分支開發?
老K:正確的開發流程規範應該是:現在各自的開發分支上開發,然和合併到test分支上進行聯調,聯調沒有問題在提交發布release分支進行測試和部署,驗證沒問題在把各自的開發分支合併到基線分支master
老K:現在要把程式碼回滾,誰做的事情誰負責。小杰,你去把test分支上的程式碼抽出來放到單獨一個開發分支上
小杰:啊?
小杰接受這個任務,準備把test分支上他跟小A多次提交的內容跟轉移到一個純淨的開發分支,小杰決定使用git cherry pick
這個命令
git cherry-pick
介紹
git cherry-pick
是 Git 中的一個非常有用的命令,它允許你從一個分支中選擇特定的提交(commit)應用到當前的分支。這個命令在需要引入某些特定功能或修復而不想進行完整的分支合併時特別有用。
使用示例
假設你有以下 Git 分支結構:
* 5a3d5f2 (feature) Add new feature
* c7e33a5 Fix bug B
* 1a2b3c4 Fix bug A
* 9d8e7f6 (main) Initial commit
現在你在 main
分支上,想要將 feature
分支中修復 bug A 的提交 (1a2b3c4
)引入當前分支。你可以這樣做:
-
切換到目標分支(假設是
main
分支):git checkout main
-
使用
git cherry-pick
命令:git cherry-pick 1a2b3c4
執行上述命令後,提交
1a2b3c4
的更改會被應用到main
分支上。 -
使用範圍(range)來批次 cherry-pick. 假設你要 cherry-pick 從 commitA 到 commitB 之間的所有 commit(包含 commitA 但不包含 commitB),你可以使用以下命令:
git cherry-pick commitA^..commitB
-
使用多個單獨的 commit 來批次 cherry-pick . 假設你有一系列的 commit 雜湊 commit1, commit2, commit3,你可以使用以下命令:
git cherry-pick commit1 commit2 commit3
-
解決可能的衝突:在 cherry-pick 的過程中,如果遇到衝突,Git 會提示你。你需要手動解決這些衝突並繼續 cherry-pick
# 解決衝突後,新增解決後的檔案 git add <conflicted-file> # 繼續 cherry-pick git cherry-pick --continue
注意事項
- 衝突處理:如果在
cherry-pick
的過程中,存在檔案衝突,Git 會暫停操作,並提示衝突檔案。你需要手動解決這些衝突,然後使用git add <file>
新增解決後的檔案,最後執行git cherry-pick --continue
繼續操作。如果你想中止cherry-pick
,可以使用git cherry-pick --abort
。 - 保持提交歷史乾淨:頻繁使用
cherry-pick
可能會導致提交歷史變得複雜。在使用前,評估是否可以透過別的操作(如合併或重置)來實現相同的目標。 - 避免重複提交:如果你已經
cherry-pick
了一個提交,再次嘗試cherry-pick
同一個提交可能會引發問題。Git 會提示你已經包含了相同的更改。 - 順序和依賴關係:如果一個提交依賴於之前的其他提交,
cherry-pick
這些提交時需要注意順序,以避免破壞程式碼的完整性。
解決方案
第一次嘗試
# Step 1: 建立新的分支 xsj_0701
git checkout master
git pull origin master
git checkout -b xsj_0701
# Step 2: 檢視 stable_test 分支的 commit 歷史
git log stable_test
# Step 3: 批次 cherry-pick commit
git checkout xsj_0701
git cherry-pick commitA^..commitB # 使用範圍
# 或者
git cherry-pick commit1 commit2 commit3 # 使用多個單獨的 commit 雜湊
# Step 4: 解決可能的衝突
# 解決衝突後
git add <conflicted-file>
git cherry-pick --continue
# Step 5: 推送新的分支
git push origin xsj_0701
小杰使用批次範圍cherry-pick,這個範圍大約包含了10個commit,正當小杰吭哧吭哧的解決幾個衝突之後,cherry-pick突然停止,沒有衝突,檢視當前commit也只到add cache
這個提交這裡,如下圖所示
為什麼cherry-pick會停止呢?
小杰經過觀察發現,停止的位置是merge節點
當你嘗試 cherry-pick 一個 merge commit 時,Git 需要更多資訊來決定如何處理合併。預設情況下,Git 不會自動 cherry-pick merge commit,因為它無法確定你想要保留哪個分支的變更。
要解決這個問題,你可以使用以下方法:
方法 1: 跳過 merge commit
如果你不需要 cherry-pick 這個 merge commit,可以手動跳過它。你可以透過在失敗後繼續 cherry-pick 後續的 commit 來實現:
在發生停止後,手動跳過 merge commit 並繼續 cherry-pick 後續的 commit:
git cherry-pick --skip
# 然後繼續 cherry-pick 後續的 commit
git cherry-pick <remaining-commits>
方法 2: 使用 cherry-pick -m 選項
如果你確實需要 cherry-pick 這個 merge commit,可以使用 -m
選項。-m
選項需要一個引數來指定父提交的索引,通常使用 1
表示第一父提交。
- 繼續 cherry-pick merge commit 並指定父提交索引:
git cherry-pick -m 1 5b30dd90
- 繼續後續的 cherry-pick:
git cherry-pick <remaining-commits>
第二次嘗試
# Step 1: 建立新的分支 xsj_0701
git checkout master
git pull origin master
git checkout -b xsj_0701
# Step 2: 檢視 stable_test 分支的 commit 歷史
git log stable_test
# Step 3: 批次 cherry-pick commit
git checkout xsj_0701
git cherry-pick commitA^..commitB # 使用範圍
# 或者
git cherry-pick commit1 commit2 commit3 # 使用多個單獨的 commit 雜湊
# Step 4: 解決可能的衝突
# 解決衝突後
git add <conflicted-file>
git cherry-pick --continue
# Step 5: 跳過merge節點
git cherry-pick --skip
# Step 6: 推送新的分支
git push origin xsj_0701
經過一下午的奮戰,小杰終於把test分支上的開發內容都遷移到純淨開發分支,然後屁顛屁顛去跟老K彙報了