git cherry-pick 詳解 —— Git 學習筆記 18

ARM的程式設計師敲著詩歌的夢發表於2018-10-28

git cherry-pick 詳解

初識 git cherry-pick(揀選)

揀選會提取某次提交的補丁,之後嘗試將其重新應用到當前分支上。 這種方式在你只想引入特性分支中的某個提交時很有用。

假設你的專案提交歷史如下:

在這裡插入圖片描述

如果你希望將提交 e43a6 拉取到 master 分支,你可以執行:

# 當前處於 master 分支

$ git cherry-pick e43a6
Finished one cherry-pick.
[master]: created a0a41a9: "More friendly message when locking the index
fails."
 3 files changed, 17 insertions(+), 3 deletions(-)

這樣會拉取和 e43a6 相同的更改,但是因為應用的日期不同,你會得到一個新的提交 SHA-1 值。 現在你的歷史會變成這樣:

在這裡插入圖片描述

現在你可以刪除這個特性分支(ruby_client),並丟棄不想拉入的提交(5ddae)。

需要說明的是,提取某次提交的“補丁”,這個補丁是基於其父提交的。

下圖可以說明:

我們要揀選提交 C4 到 maint 分支(maint 指向 C7),Git 會生成一個補丁(Δ=C4C3\Delta = C4-C3),然後把Δ\Delta應用到C7上,也就是說把 C4 對 C3 的變化在 C7 上重放一遍。

在這裡插入圖片描述

為何會產生衝突

同 merge 操作一樣,揀選操作也可能產生衝突。有人會問:不會吧,打個補丁也能衝突?

當然能。

用 diff 工具生成 patch 時,我們所做的每一處修改都會連同它的“定位資訊”(原始檔案中的行號、修改處前三行和後三行的原始文字)一併儲存到 patch 檔案中。patch 被應用時,會在目標檔案中尋找“定位資訊”,找到後再實施修改。可是,當我們把補丁應用到 C7 上時,有可能找不到那些定位資訊了:在master分支上,C2變成了C3,在maint分支上,C2變成了C6,又變成了C7,也許C3和C7相差越來越遠,C3中的上下文在C7中早已面目全非,不見蹤跡。於是應用patch失敗,即發生衝突。

衝突了怎麼辦

當揀選發生衝突的時候,GIT 會採用三路合併演算法。還是以上面的圖為例子,

當你執行命令 git cherry-pick C4 的時候,Local是C7,Remote是C4,Base是C3(即C4的父提交)。總結:

當你執行命令

git cherry-pick <commit C>

如果衝突了,那麼Git會嘗試三方合併

  • LOCAL: the commit you’re merging on top of (i.e. the HEAD of your branch)
  • REMOTE: the commit you’re cherry picking (i.e. commit C)
  • BASE: the parent of the commit you’re cherry-picking (i.e. C^, ie the parent of C)

如果三方合併的時候又衝突了怎麼辦?那隻能靠我們人工解決了。


參考資料

【0】《Pro Git》(Scott Chacon, Ben Straub Version 2.1.14, 2018-05-19)

【1】 https://my.oschina.net/jiangyouxin/blog/108717

【2】《Git 高手之路》,人民郵電出版社

【3】 StackOverflow, https://stackoverflow.com/questions/10058068/in-a-git-cherry-pick-or-rebase-merge-conflict-how-are-base-aka-the-ancestor

相關文章