如果在某分支開發的過程中,想要引入另一分支特定的 commit、而沒有要把另一分支所有的 commit 都合併進來,可以用 git cherry-pick
指令。
所以 git cherry-pick
(好像在摘什麼櫻桃)指令是怎麼運作的?先讓我們來觀察。
首先在 main
分支上,先建立一個名為 main_first.txt
的檔案:
echo "Main first" > main_first.txt
接著把這個檔案加到預存區:
git add main_first.txt
再讓它變成一個 commit:
git commit -m "Initial commit"
在 main
分支上的 commit 建立完成。現在我們新做一個 feature
分支:
git branch feature
切換到 feature
分支:
git switch feature
在 feature
分支上,先刪除 main_first.txt
:
git rm main_first.txt
建立名為 feature_first.txt
檔案:
echo "Feature first" > feature_first.txt
把它加到預存區:
git add feature_first.txt
再建立 feature
分支的一個 commit:
git commit -m "Add first file on feature"
緊接著刪除剛剛的 feature_first.txt
:
git rm feature_first.txt
再建立 feature
分支的第二個檔案:
echo "Feature second" > feature_second.txt
把第二個檔案加到預存區:
git add feature_second.txt
建立 feature
分支的第二個 commit:
git commit -m "Add second file on feature"
目前 git log
顯示結果如下:
.git/
資料夾如下:
.git/
├── hooks/
├── info/
│
├── logs/
│ ├── refs/
│ │ └── heads/
│ │ ├── feature
│ │ └── main
│ └── HEAD
│
├── objects/
│ ├── 3c/ # commit物件
│ │ └── b9770af9af551b7022155f3c17527822fc373e
│ ├── 5f/ # blob物件
│ │ └── 235d202e2c52ec7dab7c1fddf207895840b5ea
│ ├── 7e/ # blob物件
│ │ └── 658af32c000a2f561e7f12d105b80fc6ae444a
│ ├── 8e/ # tree物件
│ │ └── 643c16a985fbaebda443d7f3529a7c097a30fb
│ ├── 40/ # blob物件
│ │ └── 3ae3cd922d7f08f70c57b8042f779557e5bc6b
│ ├── 57/ # tree物件
│ │ └── 73f102a281b8688d4adccdc459c61acd341a4e
│ ├── 63/ # tree物件
│ │ └── cce6b78854718597be6696d2414cad2dbc015f
│ ├── cf/ # commit物件
│ │ └── 87caa9b4ffcdd8e46b540c167cc4fe524835b3
│ ├── f3/ # commit物件
│ │ └── 98a13c4ed225d3c5dbf543579d0938b857fece
│ └── info/
│ └── pack/
│
├── refs/
│ ├── heads/
│ │ ├── feature # 指向3cb9770af9af551b7022155f3c17527822fc373e
│ │ └── main # 指向cf87caa9b4ffcdd8e46b540c167cc4fe524835b3
│ └── tags/
│
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
└── index
圖示如下,與 git log
看到的架構一致:
目前 main
分支上只有 cf87aa9...
一個 commit、feature
上則有 f398a13...
與 3cb9770...
兩個 commit。
現在我希望將 f398a13...
這一個 commit 從 feature
分支搬到 main
分支,因為要搬的只有一個 commit,因此不會用 git merge
或 git rebase
,不然會把 feature
分支上所有的 commit 都搬到 main 分支。
這時候可以先切到 main
分支:
git switch main
接著下 git cherry-pick
指令,把指定 commit 搬過來:
git cherry-pick f398a13
這時終端機顯示變化:
我們一一來理解當中的訊息內容:
[main 14faf14] Add first file on feature
表示在 main
分支上多了一個雜湊碼為 14faf14...
的 commit,其訊息為 "Add first file on feature"
,也就是原 feature
分支被搬過來的 f398a13...
內容。
Date: Thu Aug 21 20:00:13 2025 +0800
以 git cherry-pick
完成搬運 commit 的日期與時間。
2 files changed, 1 insertion(+), 1 deletion(-)
表示有兩個檔案發生變化,包含一行新內容、一行被刪除的內容。
create mode 100644 feature_first.txt
從 f398a13...
搬過來的內容有原本 main
分支沒有的 feature_first.txt
檔案,所以這邊新增此檔案。
delete mode 100644 main_first.txt
值得一提的是,因為被搬過來的 f398a13...
並沒有 main_first.txt
檔案,因此搬過來後,main
分支最新的 commit 14faf14...
並沒有 main_first.txt
檔案。
如果此時用 git log
檢視,會發現訊息為 "Add second file on feature"
出現在 feature
與 main
兩分支上,但在兩分支的雜湊碼並不相同。
此時 .git/
資料夾結構如下:
├── hooks/
├── info/
│
├── logs/
│ ├── refs/
│ │ └── heads/
│ │ ├── feature
│ │ └── main #多一條cf87caa9b4ffcdd8e46b540c167cc4fe524835b3 14faf149e78d2cae05929f3a942d4d3601dcfb66 Ralph <ralph@ralphmail.com> 1755781180 +0800 cherry-pick: Add first file on feature
│ └── HEAD #包含cf87caa9b4ffcdd8e46b540c167cc4fe524835b3 14faf149e78d2cae05929f3a942d4d3601dcfb66 Ralph <ralph@ralphmail.com> 1755781180 +0800 cherry-pick: Add first file on feature
│
├── objects/
│ ├── 3c/
│ │ └── b9770af9af551b7022155f3c17527822fc373e
│ ├── 5f/
│ │ └── 235d202e2c52ec7dab7c1fddf207895840b5ea
│ ├── 7e/
│ │ └── 658af32c000a2f561e7f12d105b80fc6ae444a
│ ├── 8e/
│ │ └── 643c16a985fbaebda443d7f3529a7c097a30fb
│ ├── 14/ # 新commit物件
│ │ └── faf149e78d2cae05929f3a942d4d3601dcfb66
│ ├── 40/
│ │ └── 3ae3cd922d7f08f70c57b8042f779557e5bc6b
│ ├── 57/
│ │ └── 73f102a281b8688d4adccdc459c61acd341a4e
│ ├── 63/
│ │ └── cce6b78854718597be6696d2414cad2dbc015f
│ ├── cf/
│ │ └── 87caa9b4ffcdd8e46b540c167cc4fe524835b3
│ ├── f3/
│ │ └── 98a13c4ed225d3c5dbf543579d0938b857fece
│ └── info/
│ └── pack/
│
├── refs/
│ ├── heads/
│ │ ├── feature # 仍指向3cb9770af9af551b7022155f3c17527822fc373e
│ │ └── main # 改指向14faf149e78d2cae05929f3a942d4d3601dcfb66
│ └── tags/
│
├── COMMIT_EDITMSG # 內文為最新commit(14faf14...)的訊息Add second file on feature
├── config
├── description
├── HEAD
└── index
圖示如下:
由 git cat-file -p
找到的關係圖可看出,git cherry-pick
會做出一個新 commit,上面的 commit 時間與被搬運 commit 建立時間不同,而且因為檔案內容相同,會直接指向被搬運 commit 指向的 tree 物件。
使用 git cherry-pick
搬運特定 commit 時,會在目的地分支做出一個新 commit,但新 commit 會指向被搬運 commit 原本指向的 tree 物件。