git cherry-pick - 挑選 Commit

git cherry-pick 可以從其他分支挑選特定的 commit,套用到當前分支。當你只需要某個特定的變更,而不是整個分支時非常有用。

基本用法

git cherry-pick <commit-hash>

例如:

git cherry-pick a1b2c3d

這會把 a1b2c3d 這個 commit 的變更複製到當前分支,並建立一個新的 commit。

使用情境

情境一:把 Bug 修復套用到多個分支

# 在 develop 分支修復了一個 bug
git checkout develop
# 修復...
git commit -m "Fix critical bug"

# 這個修復也要套用到 main
git checkout main
git cherry-pick <fix-commit-hash>

# 也套用到 release 分支
git checkout release
git cherry-pick <fix-commit-hash>

情境二:從已廢棄的分支取回有用的 Commit

# 整個 feature 分支不要了,但其中有個 commit 想保留
git checkout main
git cherry-pick <useful-commit-hash>

情境三:誤 Commit 到錯誤的分支

# 發現 commit 應該在 feature 分支
git checkout feature
git cherry-pick <wrong-commit-hash>

# 從原分支移除
git checkout main
git reset --hard HEAD~1

Cherry-pick 多個 Commit

一次挑選多個

git cherry-pick <commit1> <commit2> <commit3>

挑選一個範圍

# 挑選從 A 到 B 的所有 commit(不含 A)
git cherry-pick A..B

# 包含 A
git cherry-pick A^..B

挑選連續的 Commit

# 從某個 commit 到分支頂端
git cherry-pick a1b2c3d..feature

常用選項

不自動 Commit

git cherry-pick -n <commit>
# 或
git cherry-pick --no-commit <commit>

變更會放到暫存區,讓你可以做額外修改或合併多個 cherry-pick:

git cherry-pick -n <commit1>
git cherry-pick -n <commit2>
git commit -m "Combined changes from commit1 and commit2"

編輯 Commit 訊息

git cherry-pick -e <commit>
# 或
git cherry-pick --edit <commit>

保留原本的作者資訊

預設 cherry-pick 會保留原作者,但會用你的資訊作為 committer。

如果想記錄是誰做的 cherry-pick:

git cherry-pick -x <commit>

這會在 commit 訊息中加入:

(cherry picked from commit a1b2c3d...)

附加原始 Commit 訊息

git cherry-pick --signoff <commit>

會加入:

Signed-off-by: Your Name <your@email.com>

處理衝突

Cherry-pick 也可能遇到衝突:

git cherry-pick a1b2c3d
# CONFLICT: 衝突發生

# 1. 解決衝突
code conflicted-file.js

# 2. 標記已解決
git add conflicted-file.js

# 3. 繼續 cherry-pick
git cherry-pick --continue

其他選項:

# 放棄這次 cherry-pick
git cherry-pick --abort

# 跳過這個 commit
git cherry-pick --skip

Cherry-pick Merge Commit

和 revert 一樣,cherry-pick merge commit 需要指定父 commit:

git cherry-pick -m 1 <merge-commit>

-m 1 表示從第一個父 commit 的角度 cherry-pick。

實際範例

從別人的分支拿取 Commit

# 查看別人的分支有哪些 commit
git log other-branch --oneline

# 挑選想要的
git cherry-pick <commit-hash>

只取部分變更

如果一個 commit 有多個檔案的變更,但你只要其中一些:

# 不自動 commit
git cherry-pick -n <commit>

# 取消不要的檔案
git restore --staged unwanted-file.js
git checkout -- unwanted-file.js

# commit 想要的
git commit -m "Partial cherry-pick"

把功能移植到舊版本

# 在 main 開發的功能
git log main --oneline
# a1b2c3d Add new feature
# b2c3d4e Feature setup

# 移植到 v1.x 維護分支
git checkout v1.x
git cherry-pick b2c3d4e a1b2c3d

Cherry-pick vs Merge vs Rebase

方式用途
Cherry-pick只需要特定幾個 commit
Merge需要整個分支的所有變更
Rebase想要重新整理 commit 歷史

注意事項

重複的 Commit

Cherry-pick 會建立新的 commit(新的 hash),即使內容相同。如果之後 merge 那個分支,可能會看到重複的變更。

依賴關係

如果你 cherry-pick 的 commit 依賴於其他 commit,可能會遇到問題。確保 cherry-pick 所有相關的 commit。

追蹤 Cherry-pick

使用 -x 選項可以幫助追蹤哪些 commit 被 cherry-pick 過:

git cherry-pick -x <commit>

小技巧

查看哪些 Commit 可以 Cherry-pick

# 查看 feature 有但 main 沒有的 commit
git log main..feature --oneline

檢查 Commit 是否已經存在

# 查看 commit 的內容
git show <commit>

# 檢查這個變更是否已經在當前分支
git log --all --oneline -- <file-that-was-changed>