一. 前言:不是所有修改,都值得整條 branch 一起搬走 #
你一定遇過這種場景:功能分支上做了一整串改動,但其中只有一個小修正非常急,你想先把它補到正式環境;或者同事在另一條 branch 修掉一個 bug,你只想拿那一顆 commit,不想把其他還沒 review 的東西一起帶過來。這時候,直接 merge 往往太重,rebase 又是在整理歷史,目的完全不同。
git cherry-pick 的價值,就在於它幫你做一件很精準的事:只把你指定的 commit 套用到目前分支。不是整條 branch 搬家,也不是重寫整段歷史,而是把某顆或某幾顆 commit 的變更內容,重新套到你現在所在的位置。
如果你前面看過拍拍君寫的 Git Branch 策略完全攻略、Git rebase 完全攻略 和 Git stash 實戰,那今天這篇剛好把 Git 工作流再補上一塊:branch 告訴你工作線怎麼切,rebase 告訴你歷史怎麼整理,stash 幫你暫存現場,而 cherry-pick 則是負責在多條線之間做「局部搬運」。
這篇文章會從最基本的概念開始,一路講到日常實戰:單顆 commit 搬運、多顆 commit 批次挑選、hotfix 回補、衝突處理、-x、-e、--no-commit 的用法,以及 cherry-pick merge commit 時要注意什麼。先講結論:這是一把手術刀,不是鏟土機。用得好,它是救火神器;用不好,兩週後你就會看不懂 repo 為什麼同一段修正出現三次。
二. 安裝與基本準備:指令是內建的,觀念才是重點 #
git cherry-pick 是 Git 內建功能,所以不需要另外安裝套件。先確認版本:
git --version
如果你還沒裝 Git,可以用常見方式安裝:
# macOS
brew install git
# Ubuntu / Debian
sudo apt update
sudo apt install git
# Windows
winget install --id Git.Git -e
身份設定如果還沒做,也先補上:
git config --global user.name "拍拍君"
git config --global user.email "pypy@example.com"
真正重要的,其實不是「會不會打 cherry-pick」,而是你有沒有先看懂要搬哪顆 commit。開始之前,至少先熟這兩個指令:
git log --oneline --graph --decorate --all
git show <commit>
第一個用來看歷史圖,第二個用來確認某顆 commit 到底改了什麼。很多 cherry-pick 事故,不是因為這個指令本身很危險,而是因為人根本沒看清楚自己在撿哪一顆。
先假設目前分支長這樣:
A---B---C main
\
D---E---F feature/payment
其中 D 是付款流程骨架,E 是修正金額四捨五入 bug,F 是第三方 API 串接,還沒測完。這時正式環境很急,你只想把 E 搬回 main。這,就是 cherry-pick 最典型的使用場景。
還有一句話先記起來:你搬的是「變更內容」,不是把原本那顆 commit 原封不動搬過去。Git 會把那顆 commit 的 diff 重新套用到你目前所在的分支,然後產生一顆新的 commit,所以新 hash 幾乎一定不同。內容很像,但不是同一個物件;這也就是為什麼之後看 log、merge 或 blame 時,會跟你直覺中的「複製貼上 commit」不太一樣。
三. 先搞懂核心:cherry-pick 到底做了什麼 #
3.1 最基本的單一 commit 搬運 #
假設你現在在 main:
git switch main
然後你確認要搬的 commit 是 e4f5a6b:
git cherry-pick e4f5a6b
如果一切順利,Git 會把那顆 commit 的變更套用到 main,並自動建立新的 commit。歷史會從這樣:
A---B---C main
\
D---E---F feature/payment
變成這樣:
A---B---C---E' main
\
D---E---F feature/payment
E' 不是原本的 E,而是「把 E 的修改內容重新套到 main 後」產生的新 commit。這個觀念後面非常重要,因為它直接關係到為什麼可能出現重複修正,以及為什麼 cherry-pick 不適合被濫用成長期同步工具。
3.2 它跟 merge 的差別在哪裡? #
如果這裡你改用:
git merge feature/payment
那你通常會把 D、E、F 全部一起帶過來。可你現在明明只想要 bug fix,所以 merge 太重。cherry-pick 的重點,就是只取你要的那一部分。它很像從 branch 上抽出一張 patch,但又保留 Git 對內容變更的理解。
3.3 它跟 rebase 又差在哪裡? #
拍拍君用一句話幫你分:
merge:把兩段歷史接起來rebase:把一串 commit 換基底重放cherry-pick:挑個別 commit 套到目前分支
如果你想整理自己 branch 的歷史,通常想到的是 rebase;如果你想把別處某幾顆修正帶來現在這條線,通常想到的才是 cherry-pick。兩者都會「重放變更」,但使用意圖完全不同。
3.4 最容易誤會的點:它不是 branch 同步工具 #
很多人一開始會把 cherry-pick 當成「手動同步 branch」的方式:main 修一顆就 pick 到 release,release 修一顆再 pick 回 main,feature 修一顆又 pick 到 hotfix。短期很爽,長期就很容易把 repo 變成 patch 地獄。同一修正可能出現多次、各分支又補上各自的小改動,最後連自己都看不懂哪些才是真正來源。
所以拍拍君會建議:cherry-pick 很適合局部補丁與精準回補,不適合拿來當整個團隊的主要同步機制。
四. 最常見的實戰:挑單顆、挑多顆、挑完再改 #
4.1 場景一:把 hotfix 從開發分支搬回 main #
假設你在 feature/payment 上修了一個很急的 bug。先看 log:
git log --oneline
你看到:
f8a1c92 connect provider api
7b3d412 fix rounding bug in total amount
2c9e7ab scaffold payment flow
如果你只想搬中間那顆修正:
git switch main
git cherry-pick 7b3d412
這是最標準用法,特別適合某顆 bug fix 很獨立、其他功能還沒準備好上線、正式環境只想先補一刀的情況。
4.2 場景二:一次挑多顆連續 commit #
有時你想搬的不只一顆,而且它們剛好是連續的。這時可以直接指定範圍:
git cherry-pick A^..C
這代表把 A 到 C 這段都挑過來。例如:
git cherry-pick 3fd12ab^..91ac44e
這種寫法很適合某個小功能剛好是連續 2 到 3 顆 commit,而你又想保留原本的提交粒度。當然,範圍選錯也很容易把你本來不想要的 commit 一起帶進來,所以在動手前,最好先跑:
git log --oneline --reverse <start>^..<end>
確認清楚再 pick。
4.3 場景三:一次挑多顆不連續 commit #
如果要搬的是不連續幾顆,可以直接列出來:
git cherry-pick 8a1b2c3 d4e5f6a 7b8c9d0
Git 會依照你列出的順序逐顆套用。這對回補 release branch 很常見:一顆是登入 bug fix,一顆是 API timeout 修正,一顆是 log 補強,它們散落在不同地方,但都想補進 release/1.4。
4.4 場景四:先套變更,但先不要立即 commit #
有時你想把某顆 commit 的內容帶過來,但想順手修改一下,或跟其他變更合成一顆。這時可以用:
git cherry-pick --no-commit <commit>
或簡寫:
git cherry-pick -n <commit>
它會把變更套到工作目錄與 index,但不自動產生 commit。接著你可以檢查狀態:
git status
git diff --staged
確認沒問題後,再自己 commit:
git commit -m "hotfix: fix payment rounding on main"
這很適合想改一下 commit message、補少量平台差異,或把多顆小修正合成更乾淨提交的情境。
4.5 場景五:保留來源資訊,用 -x 最安心
#
如果你是在團隊協作環境,尤其是 release / stable branch 回補,拍拍君很推薦加上 -x:
git cherry-pick -x 7b3d412
這會在新的 commit message 後面自動加上:
(cherry picked from commit 7b3d412...)
它的好處很直接:日後查 log 時知道來源、reviewer 比較容易追到原始討論、backport 記錄更清楚。如果你的團隊常常要在 main、release、hotfix 之間回補修正,-x 幾乎可以當預設。
4.6 場景六:套用時順手編輯 commit 訊息 #
如果你想在 cherry-pick 當下改 commit message:
git cherry-pick -e <commit>
Git 會打開編輯器,讓你調整提交訊息。這很適合原分支上的 commit message 太內部、太簡寫,或者你搬到另一條 branch 時想改成更符合版本線的描述。例如原本在 feature branch 上只叫 fix payment bug,搬到 production hotfix branch 時,你可能更想寫成 hotfix: correct payment rounding for production checkout。
五. 衝突處理與風險控制:真的卡住時,不要硬推 #
5.1 cherry-pick 也會衝突,而且很正常 #
很多人以為只挑一顆 commit,應該就不太會衝突。其實不一定。只要目前分支跟來源分支的上下文差很多,哪怕只搬一顆,也一樣可能撞到。例如同一個函式在兩邊都被改過、檔名已經改掉、原 commit 依賴另一顆尚未搬過來的前置修改,這些都很常見。
當衝突發生時,Git 通常會停下來,顯示類似訊息:
error: could not apply 7b3d412... fix rounding bug in total amount
這時候先別 panic。正常流程是:
git status
看哪些檔案衝突,手動修完後:
git add <resolved-files>
git cherry-pick --continue
5.2 不想繼續了,就 abort #
如果你發現這顆 commit 依賴太多前置改動、衝突大到不值得手修,或者你其實挑錯 commit 了,可以直接取消:
git cherry-pick --abort
它會把工作目錄恢復到 cherry-pick 開始前的狀態。這跟 rebase --abort 一樣,是非常重要的保命按鈕。不要在一團混亂時硬推,先退,再重看歷史,通常比你帶著半殘狀態繼續衝安全很多。
5.3 如果只是想先停著,還有 quit #
某些情況你可能不想完全回復,只是想退出 cherry-pick 流程。這時可以看看:
git cherry-pick --quit
--abort 會嘗試回到開始前的狀態,--quit 則是結束 sequencer 狀態,但保留目前工作樹內容。實務上,拍拍君最常用的還是 --continue 與 --abort,不過知道 --quit 的存在,遇到複雜狀況時會更有餘裕。
5.4 小心「看起來能 pick,實際上邏輯不完整」 #
這是最常見的隱性風險。有些 commit 在 diff 上看起來很獨立,但實際上依賴前面某個 refactor 或資料結構調整。例如:
A: rename PaymentResult fields
B: fix timeout handling
如果你只 pick B,表面上可能套得進去,但執行時就炸了,因為欄位名稱已經對不上。所以 cherry-pick 之前,除了看 git show,也要問自己:這顆 commit 有沒有隱含依賴?它是不是建立在某次 refactor 之上?我要搬過去的 branch,結構真的一致嗎?
5.5 避免重複修正:不是能 pick 就應該 pick #
如果某顆修正其實之後會透過 merge 正式進來,你就要考慮是否值得先 cherry-pick。因為未來 merge 時,雖然 Git 常常能辨認相似內容,但歷史層面上還是可能讓人困惑:blame 不好追、log 看起來像重複修了同一個 bug、reviewer 看到兩顆近似 commit 不知道差別在哪。
所以拍拍君的建議是:緊急回補很適合 cherry-pick;長期同步則優先考慮 merge、rebase 或更正規的 backport 流程。
5.6 cherry-pick merge commit 時要格外小心 #
大多數情況下,你不太會直接 pick merge commit。因為 merge commit 有多個 parent,Git 不知道你想以哪一邊當主要基底。如果真的要 pick merge commit,通常要指定 -m:
git cherry-pick -m 1 <merge-commit>
-m 1 的意思是選第一個 parent 當 mainline。但這種操作比一般 cherry-pick 複雜很多;如果你沒有很清楚這顆 merge commit 的來源結構,拍拍君真心建議先不要。通常更安全的做法是:找到真正需要的那幾顆普通 commit,逐顆挑過來。
六. 進階實戰:release 回補、backport、批次搬運 #
6.1 經典案例:把 main 的修正 backport 到 release branch #
假設你有這些分支:main 持續開發,release/1.4 穩定維護。某天你在 main 修了一個安全性 bug,但 release/1.4 也要補,流程通常是這樣:
git switch release/1.4
git log --oneline main
git cherry-pick -x <security-fix-commit>
修完如果有衝突,測試後再 push:
git push origin release/1.4
這種情境下,-x 幾乎是必開,因為過幾個月後,你一定會很感謝當時的自己有留下來源紀錄。
6.2 經典案例:hotfix 先上 production,再回補 main #
另一種很常見的流程是反過來:你在 production hotfix branch 上先救火,正式上線後,這顆修正也應該回補到 main,避免之後新版本把 bug 帶回來。例如:
git switch hotfix/payment-rounding
# 修 bug
git commit -m "hotfix: correct payment rounding"
上線後回補:
git switch main
git cherry-pick -x <hotfix-commit>
這個流程很合理,因為 hotfix branch 往往很短、目的很單純。比起整條 branch merge 回來,精準挑修正通常更乾淨。
6.3 批次搬運前,先用 log 做清單 #
如果你打算回補 3 到 5 顆 commit,拍拍君不建議憑記憶直接敲。先整理清單:
git log --oneline --no-merges main --grep='fix\|hotfix'
或搭配檔案路徑篩選:
git log --oneline main -- src/payment src/checkout
確認完之後,再依序 pick:
git cherry-pick -x a1b2c3d e4f5a6b 91ac44e
這會比你一邊翻 log 一邊亂貼 hash 穩得多。
6.4 如果你需要先 review 再 commit,用 -n 很好用
#
有些團隊流程是先把幾顆修正套進 release branch,確認測試通過後,再整理成一顆版本修正提交。這時可以這樣做:
git switch release/1.4
git cherry-pick -n a1b2c3d
git cherry-pick -n e4f5a6b
git cherry-pick -n 91ac44e
接著檢查 staged 內容:
git diff --staged
最後一次 commit:
git commit -m "release: backport checkout fixes for 1.4.3"
這種方式的優點是版本修正紀錄集中,缺點則是你失去原本一顆顆 commit 的粒度。要不要這樣做,就看你們團隊比較重視哪一種可追蹤性。
6.5 想確認某顆 commit 是否已經等價存在,可先比 diff #
有時你不確定某個修正是不是早就被手動改過,可以先看 patch:
git show <commit>
或比較分支差異:
git diff release/1.4..main -- src/payment
如果內容其實已經存在,就不要為了「形式上補流程」再 pick 一次。版本控制最怕的,不是少打一個指令,而是做了讓歷史更難懂的多餘操作。
七. 什麼時候不該用 cherry-pick #
拍拍君很喜歡這個指令,但也想幫它洗刷一個冤名:它不是所有分支問題的答案。以下幾種情況,通常不建議優先用 cherry-pick。
7.1 你其實要的是整段功能,不是局部修補 #
如果某個 feature branch 已經完整,也準備好進主線,那正常做法通常是 merge 或 squash merge。這時候硬把裡面的 commit 一顆顆 cherry-pick,只會讓流程變繞。
7.2 你想長期雙向同步兩條 branch #
如果兩條 branch 長期都會互相補修正,只靠 cherry-pick 很容易把歷史搞成手工 patch 地獄。這時應該回頭設計分支策略,而不是指望每次都靠人工挑 commit 維持秩序。
7.3 你根本還沒確認 commit 依賴 #
如果你看到一顆 commit 標題很誘人,就直接 pick,很容易發生 build 過不了、測試掛掉,或者程式能跑但邏輯微妙出錯。先看 git show,必要時連前後 commit 一起看;真的不確定,寧可晚五分鐘判斷,也不要早五秒製造新坑。
結語:把它當手術刀,不要當鏟土機 #
git cherry-pick 最迷人的地方,就是精準。你可以只拿一顆 bug fix、只回補一段 hotfix、只把某幾個經過驗證的 commit 帶到穩定分支;在救火、回補、backport 這些情境裡,它真的非常好用。
但也正因為它很精準,你更需要知道自己在切哪裡。拍拍君自己的使用心法很簡單:先看 log,再 pick;能加 -x 就加 -x;有衝突先停,別亂推;不要拿它當長期同步機制。如果你能把這幾句記住,git cherry-pick 幾乎就不太會從神器變成事故現場。
接下來如果你想把 Git 工作流整套補齊,很推薦把下面幾篇一起看:branch 怎麼切、rebase 什麼時候用、stash 什麼時候收現場。把這些工具放在正確位置,你的 repo 真的會乾淨很多。