Git cherry-pick後少了修改:它不會搬整個分支

功能分支已改好首頁標題,也加了橫幅樣式。你只想先帶回樣式,用了git cherry-pick後,CSS檔出現了,標題卻還是舊文字。這種情況可能是操作符合預期:你挑中的那筆提交只新增CSS,改標題的是前一筆。

cherry-pick會把指定提交所引入的修改套到目前分支。只給一個普通提交時,它不會順便搬走那個分支之前所有提交。先分清「這筆提交改了什麼」與「分支目前包含什麼」,才知道應該挑哪一筆。

做出兩筆分開的修改

下面用一個新建的練習目錄重現。請使用Git Bash,或macOS、Linux的Bash相容shell,逐段執行;PowerShell不能直接照抄這裡的變數與printf語法。目錄名稱要選一個尚不存在的名稱,別在正式網站或已有專案裡執行初始化。

使用者名稱與電郵是練習用假資料,設定只寫進這個repository;關閉換行轉換與提交簽署也只限練習repo。這組例子使用Git 2.47.1 for Windows,沒有連線遠端或推送。

mkdir git-pick-demo
cd git-pick-demo
git init -b codex/main
git config user.name Demo
git config user.email [email protected]
git config core.autocrlf false
git config commit.gpgsign false

printf '<h1>Original heading</h1>\n' > index.html
git add index.html
git commit -m "base homepage"

git switch -c codex/feature
printf '<h1>New heading</h1>\n' > index.html
git add index.html
git commit -m "A: change heading"

printf '.banner { border-radius: 12px; }\n' > banner.css
git add banner.css
git commit -m "B: add banner style"
picked=$(git rev-parse HEAD)

A只把index.html的標題改成New heading;B只新增banner.css。picked記下B的commit ID。請在同一個shell繼續下面的命令,否則這個變數不會跟著保留。

回到接收分支,只挑B

先用git show看B的檔案摘要和CSS差異,再切回codex/main。這裡另加一筆頁尾提交,讓接收分支有自己的進度;它與橫幅CSS沒有相依關係。cherry-pick前的git status --short應沒有輸出;如果有未保存的修改,先處理好,再進行。

git show --stat --oneline "$picked"
git show "$picked" -- banner.css
git switch codex/main
printf 'Main footer\n' > footer.txt
git add footer.txt
git commit -m "main: add footer"
git status --short
git cherry-pick "$picked"

git cherry-pick "$picked"把B的修改套到目前的codex/main。即使B的完整檔案快照裡已經包含New heading,B相對它的父提交只改了CSS,因此這次不會把A的標題修改一起帶回來。

如果改用git cherry-pick codex/feature,這個分支名稱在此時也指向B,仍然只挑B;它不代表自動把整條分支歷史搬過來。指定一段提交範圍是另一種操作,會改變挑選集合,別為了補齊缺檔就直接換成大範圍。

看檔案結果,別只看成功提示

git show HEAD:index.html
git show HEAD:banner.css
git show HEAD:footer.txt
git log --all --oneline --graph --decorate
git status --short

前三個命令依序應顯示<h1>Original heading</h1>、.banner { border-radius: 12px; }及Main footer。原標題留下,橫幅CSS新增,接收分支的頁尾也留下。最後的status應沒有輸出;log中codex/main會多一筆B: add banner style,而來源codex/feature仍停在原本的B。

這個例子的CSS檔是獨立新增,沒有在HTML中引用;它用來觀察Git如何搬修改,不是完整的網頁載入範例。實際網站還要檢查HTML引用、建置和頁面效果。沒有衝突,只表示這次文字修改能套上,不表示功能一定能正常執行。

同一個修改,為何commit ID變了

git rev-parse codex/feature
git rev-parse HEAD
git cat-file -p codex/feature
git cat-file -p HEAD

這個例子中,來源B與接收分支HEAD的ID不同。commit物件除了檔案樹,也包含父提交、作者、提交者及時間等資訊;來源B的父提交是A,新提交的父提交則是main的頁尾提交。兩者不是同一個commit物件,不能只憑ID不同就判斷樣式搬錯。

要追蹤搬了哪個修補,可以把來源ID與新ID一起記下,再核對patch和檔案內容。也可以先讀用git show讀取指定提交的檔案快照,分清檔案快照與單筆提交差異。

真實修改有相依時,先決定要帶哪些提交

假如B的CSS只套用到A新增的class,單挑B可能不衝突,頁面卻沒有任何變化。先看A與B各自的差異,再決定是否需要兩筆,或整理成一個能獨立運作的修補。涉及程式時,也要核對被呼叫的函式、設定與資料庫變更;別把能套用當成相依都齊了。

要在現有專案操作,先確認接收分支、工作目錄狀態,以及來源提交是否已在本機repository中。這裡的範例全部是單一父提交;遇到merge commit時,必須先理解要以哪個父提交為基準,不能看到提示就盲加-m 1。

若cherry-pick停在衝突,先用git status查看待處理檔案。解好需要的內容並git add後,可用git cherry-pick --continue續作;若要取消仍在進行中的這次操作,用git cherry-pick --abort回到開始前的狀態。已完成並分享出去的提交,不能靠abort撤回,應另按團隊的回退流程處理。

參考資料

原文鏈接:https://wntheme.com/git-cherry-pick-single-commit-not-whole-branch/,轉載請註明出處。
0

評論0

顯示驗證碼
沒有帳號?註冊  忘記密碼?