iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
Software Development

深入一點點認識 Git系列 第 18

Day 18-深入一點點認識 Git:git cherry-pick 到底怎麼搬 commit 的?

  • 分享至 

  • xImage
  •  

如果在某分支開發的過程中,想要引入另一分支特定的 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 顯示結果如下:
https://ithelp.ithome.com.tw/upload/images/20250918/20178513yoc51C2qnW.png

.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 看到的架構一致:
https://ithelp.ithome.com.tw/upload/images/20250918/20178513OW46nNXJcC.png

實驗開始

目前 main 分支上只有 cf87aa9... 一個 commit、feature 上則有 f398a13...3cb9770... 兩個 commit。

現在我希望將 f398a13... 這一個 commit 從 feature 分支搬到 main 分支,因為要搬的只有一個 commit,因此不會用 git mergegit rebase,不然會把 feature 分支上所有的 commit 都搬到 main 分支。

這時候可以先切到 main 分支:

git switch main

接著下 git cherry-pick 指令,把指定 commit 搬過來:

git cherry-pick f398a13

這時終端機顯示變化:
https://ithelp.ithome.com.tw/upload/images/20250918/20178513boc4FIotUG.png

我們一一來理解當中的訊息內容:

[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" 出現在 featuremain 兩分支上,但在兩分支的雜湊碼並不相同。
https://ithelp.ithome.com.tw/upload/images/20250918/20178513C8Nl6PZk2G.png

此時 .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

圖示如下:
https://ithelp.ithome.com.tw/upload/images/20250918/201785136GkTzuZoOl.png

git cat-file -p 找到的關係圖可看出,git cherry-pick 會做出一個新 commit,上面的 commit 時間與被搬運 commit 建立時間不同,而且因為檔案內容相同,會直接指向被搬運 commit 指向的 tree 物件。

小結

使用 git cherry-pick 搬運特定 commit 時,會在目的地分支做出一個新 commit,但新 commit 會指向被搬運 commit 原本指向的 tree 物件。

參考資料

  1. git-cherry-pick
  2. [Git] Cherry-Pick 使用場景
  3. 【狀況題】如果你只想要某個分支的某幾個 Commit?

上一篇
Day 17-深入一點點認識 Git:git stash 把進度存到哪了?
下一篇
Day 19-深入一點點認識 Git:兩種不讓檔案被 Git 追蹤的方式:.gitignore 與 .git/info/exclude
系列文
深入一點點認識 Git22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言