日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

Git 的遴選和撤銷操作是如何利用三路合并的

來源: 責編: 時間:2023-11-14 17:09:48 311觀看
導讀大家好!幾天前,我嘗試向其他人解釋 Git 遴選(git cherry-pick)的工作原理,結果發現自己反而更混淆了。我原先以為 Git 遴選是簡單地應用一個補丁,但當我真正這樣嘗試時,卻未能成功!因此,接下來我們將談論我原來以為的遴選操作(

qKE28資訊網——每日最新資訊28at.com

大家好!幾天前,我嘗試向其他人解釋 Git 遴選(git cherry-pick)的工作原理,結果發現自己反而更混淆了。qKE28資訊網——每日最新資訊28at.com

我原先以為 Git 遴選是簡單地應用一個補丁,但當我真正這樣嘗試時,卻未能成功!qKE28資訊網——每日最新資訊28at.com

因此,接下來我們將談論我原來以為的遴選操作(即應用一個補丁),這個理解為何不準確,以及實際上它是如何執行的(進行“三路合并”)。qKE28資訊網——每日最新資訊28at.com

盡管本文的內容有些深入,但你并不需要全部理解才能有效地使用 Git。不過,如果你(和我一樣)對 Git 的內部運作感到好奇,那就跟我一起深入探討一下吧!qKE28資訊網——每日最新資訊28at.com

遴選操作并不只是應用一個補丁

我先前理解的 git cherry-pick COMMIT_ID 的步驟如下:qKE28資訊網——每日最新資訊28at.com

  • 首先是計算 COMMIT_ID 的差異,就如同執行 git show COMMIT_ID --patch > out.patch 這個命令
  • 然后是將補丁應用到當前分支,就如同執行 git apply out.patch 這個命令

在我們詳細討論之前,我想指出的是,雖然大部分情況下這個模型是正確的,如果這是你的認知模型,那就沒有問題。但是在一些細微的地方,它可能會錯,我覺得這個疑惑挺有意思的,所以我們來看看它究竟是如何運作的。qKE28資訊網——每日最新資訊28at.com

如果我在存在合并沖突的情況下嘗試進行“計算差異并應用補丁”的操作,下面我們就看看具體會發生什么情況:qKE28資訊網——每日最新資訊28at.com

$ git show 10e96e46 --patch > out.patch$ git apply out.patcherror: patch failed: content/post/2023-07-28-why-is-dns-still-hard-to-learn-.markdown:17error: content/post/2023-07-28-why-is-dns-still-hard-to-learn-.markdown: patch does not apply

這一過程無法成功完成,它并未提供任何解決沖突或處理問題的方案。qKE28資訊網——每日最新資訊28at.com

而真正運行 git cherry-pick 時的實際情況卻大為不同,我遭遇到了一處合并沖突:qKE28資訊網——每日最新資訊28at.com

$ git cherry-pick 10e96e46error: could not apply 10e96e46... wiphint: After resolving the conflicts, mark them withhint: "git add/rm <pathspec>", then runhint: "git cherry-pick --continue".

因此,看起來 “Git 正在應用一個補丁”這樣的理解方式并不十分準確。但這里的錯誤信息確實標明了 “無法應用 10e96e46”,這么看來,這種理解又不完全是錯的。這到底是怎么回事呢?qKE28資訊網——每日最新資訊28at.com

那么,遴選到底是怎么執行的呢?

我深入研究了 Git 的源代碼,主要是想了解 cherry-pick 是如何工作的,最終我找到了 這一行代碼:qKE28資訊網——每日最新資訊28at.com

res = do_recursive_merge(r, base, next, base_label, next_label, &head, &msgbuf, opts);

所以,遴選實際上就是一種……合并操作?這有些出乎意料。那具體都合并了什么內容?如何執行這個合并操作的呢?qKE28資訊網——每日最新資訊28at.com

我意識到我對 Git 的合并操作并不是特別理解,于是我上網搜索了一下。結果發現 Git 實際上采用了一種被稱為 “三路合并” 的合并方式。那這到底是什么含義呢?qKE28資訊網——每日最新資訊28at.com

Git 的合并策略:三路合并

假設我要合并下面兩個文件,我們將其分別命名為 v1.py 和 v2.pyqKE28資訊網——每日最新資訊28at.com

def greet():    greeting = "hello"    name = "julia"    return greeting + " " + name
def say_hello():    greeting = "hello"    name = "aanya"    return greeting + " " + name

在這兩個文件間,存在兩處不同:qKE28資訊網——每日最新資訊28at.com

  • def greet() 和 def say_hello
  • name = "julia" 和 name = "aanya"

我們應該選擇哪個呢?看起來好像不可能有答案!qKE28資訊網——每日最新資訊28at.com

不過,如果我告訴你,原始的函數(我們稱之為 base.py)是這樣的:qKE28資訊網——每日最新資訊28at.com

def say_hello():    greeting = "hello"    name = "julia"    return greeting + " " + name

一切似乎變得清晰許多!在這個基礎上,v1 將函數的名字更改為 greetv2 將 name = "aanya"。因此,合并時,我們應該同時做出這兩處改變:qKE28資訊網——每日最新資訊28at.com

def greet():    greeting = "hello"    name = "aanya"    return greeting + " " + name

我們可以命令 Git 使用 git merge-file 來完成這次合并,結果正是我們預期的:它選擇了 def greet() 和 name = "aanya"qKE28資訊網——每日最新資訊28at.com

$ git merge-file v1.py base.py v2.py -pdef greet():    greeting = "hello"    name = "aanya"    return greeting + " " + name?

這種將兩個文件與其原始版本進行合并的方式,被稱為 三路合并qKE28資訊網——每日最新資訊28at.com

如果你想在線上試一試,我在 jvns.ca/3-way-merge/ 創建了一個小實驗場。不過我只是草草制作,所以可能對移動端并不友好。qKE28資訊網——每日最新資訊28at.com

Git 合并的是更改,而非文件

我對三路合并的理解是 —— Git 合并的是更改,而不是文件。我們對同一個文件做出兩種不同的更改,Git 試圖以合理的方式將這兩種更改結合到一起。當兩個更改都對同一行進行操作時,Git 可能會遇到困難,此時就會產生合并沖突。qKE28資訊網——每日最新資訊28at.com

Git 也可以合并超過兩處的更改:你可以對同一文件有多達 8 處不同的更改,Git 會嘗試將所有更改協調一致。這被稱為八爪魚合并,但除此之外我對其并不了解,因為我從未執行過這樣的操作。qKE28資訊網——每日最新資訊28at.com

Git 如何使用三路合并來應用補丁

接下來,讓我們進入到一個有些出乎意料的情境!當我們討論 Git “應用補丁”(如在變基 —— rebase、撤銷 —— revert 或遴選 —— cherry-pick 中所做的)時,其實并非是生成一個補丁文件并應用它。相反,實際執行的是一次三路合并。qKE28資訊網——每日最新資訊28at.com

下面是如何將提交 X 作為補丁應用到你當前的提交,并與之前的 v1v2 和 base 設置相對應:qKE28資訊網——每日最新資訊28at.com

  1. 在你當前提交中,文件的版本是 v1
  2. 在提交 X 之前,文件的版本是 base
  3. 在提交 X 中,文件的版本是 v2
  4. 執行 git merge-file v1 base v2 以合并它們(實際上,Git 并不直接執行 git merge-file,而是運行一個實現這個功能的 C 函數)。

總的來說,你可以將 base 和 v2 視為“補丁”,它們之間的差異就是你想要應用到 v1 上的更改。qKE28資訊網——每日最新資訊28at.com

遴選如何運作

假設我們有如下提交圖,并且我們打算在 main 分支上遴選提交 YqKE28資訊網——每日最新資訊28at.com

A - B (main)  /    /     X - Y - Z

那么,如何將此情景轉化為我們前面提過的 v1v2 和 base 組成的三路合并呢?qKE28資訊網——每日最新資訊28at.com

  • B 是 v1
  • X 是 base,而 Y 是 v2

所以,X 和 Y 共同構成了這個“補丁”。qKE28資訊網——每日最新資訊28at.com

其實,git rebase 無非就是重復多次執行 git cherry-pick 的過程。qKE28資訊網——每日最新資訊28at.com

撤銷如何運作

現在,假如我們希望在如下的提交圖上執行 git revert YqKE28資訊網——每日最新資訊28at.com

X - Y - Z - A - B
  • B 是 v1
  • Y 是 base,而 X 是 v2

這個過程反映的實際上就是遴選的情況,不過 X 和 Y 的位置顛倒了。我們需要這樣做因為我們期望生成一個“反向補丁”。在 Git 中,撤銷和遴選關系如此的緊密,它們甚至在同一個文件中實現:revert.c。qKE28資訊網——每日最新資訊28at.com

“三路補丁”是一個非常棒的技巧

使用三路合并將提交作為補丁應用的這個技巧非常巧妙且酷炫,我很驚訝之前從未聽說過!我并未聽過一個特定的名字來描述這種方法,但我更傾向于稱之為“三路補丁”。qKE28資訊網——每日最新資訊28at.com

“三路補丁”的理念在于,你可以通過兩個文件來定義補丁:在應用補丁前后的文件(在我們這篇文章中稱之為 base 和 v2)。qKE28資訊網——每日最新資訊28at.com

因此,總體來看有三個文件被涉及到:一個是原文件,另外兩個構成了補丁。qKE28資訊網——每日最新資訊28at.com

最重要的是,與普通補丁相比,三路補丁是一個更加高效的補丁方案,因為在有兩個完整文件的情況下,你擁有更豐富的上下文信息來進行合并。qKE28資訊網——每日最新資訊28at.com

以下是我們例子中的常規補丁的大致情況:qKE28資訊網——每日最新資訊28at.com

@@ -1,1 +1,1 @@:- def greet():+ def say_hello():    greeting = "hello"

而下面這就是一個三路補丁。不過,需要提醒的是這個“三路補丁”并不是一個真正的文件格式,這只是我自己提出的一種概念。qKE28資訊網——每日最新資訊28at.com

BEFORE: (the full file)def greet():    greeting = "hello"    name = "julia"    return greeting + " " + nameAFTER: (the full file)def say_hello():    greeting = "hello"    name = "julia"    return greeting + " " + name

《Building Git》 中提到了這點

James Coglan 的書籍 《Building Git》 是我在 Git 源碼之外唯一找到的地方,他解釋了 git cherry-pick 是如何在底層運用三路合并的(我原以為《Pro Git》可能會提及這個,但我并沒能找到此話題的內容)。qKE28資訊網——每日最新資訊28at.com

我購買完這本書后發現,我早在 2019 年時就已經買過了,這對我來說真的是個很好的參考。qKE28資訊網——每日最新資訊28at.com

Git 中的合并實際上比這更復雜

在 Git 中,合并不限于三路合并 —— 還有一種我不太理解的叫做“遞歸合并”,還有許多具體處理文件刪除和移動的細節,同時也有多種合并算法。qKE28資訊網——每日最新資訊28at.com

如果想要了解更多相關知識,我最好的建議是閱讀《Building Git》,盡管我還未完全閱讀這本書。qKE28資訊網——每日最新資訊28at.com

Git 應用到底做了什么?

我也參閱了 Git 的源代碼,試圖理解 git apply 的功能。它似乎(不出意外地)在 apply.c 中實現。這段代碼解析了一個補丁文件,并通入目標文件來尋找應該在何處應用補丁。核心邏輯似乎在 這里:思路好像是從補丁建議的行數開始,然后向前向后找尋。qKE28資訊網——每日最新資訊28at.com

/*	 * There's probably some smart way to do this, but I'll leave	 * that to the smart and beautiful people. I'm simple and stupid.	 */	backwards = current;	backwards_lno = line;	forwards = current;	forwards_lno = line;	current_lno = line;for (i = 0; ; i++) {     ...

這個處理過程不禁讓人覺得非常直白、與之前的期望相符。qKE28資訊網——每日最新資訊28at.com

Git 三路應用的工作方式

git apply 命令中也有一個 --3way 參數,可以實現三路合并。因此,我們實際上可以通過如下方式,使用 git apply 來大體實現 git cherry-pick 的功能:qKE28資訊網——每日最新資訊28at.com

$ git show 10e96e46 --patch > out.patch$ git apply out.patch --3wayApplied patch to 'content/post/2023-07-28-why-is-dns-still-hard-to-learn-.markdown' with conflicts.U content/post/2023-07-28-why-is-dns-still-hard-to-learn-.markdown

但要注意,參數 --3way 并不只用到了補丁文件的內容!補丁文件開始的部分是:qKE28資訊網——每日最新資訊28at.com

index d63ade04..65778fc0 100644

d63ade04 和 65778fc0 是舊/新文件版本在 Git 對象數據庫中的 ID,因此 Git 可以用這些 ID 來執行三路補丁操作。但如果有人將補丁文件通過郵件發送給你,而你并沒有新/舊版本的文件,就無法執行這個操作:如果你缺少 blob,將會出現如下錯誤:qKE28資訊網——每日最新資訊28at.com

$ git apply out.patcherror: repository lacks the necessary blob to perform 3-way merge.

三路合并有點歷史了

有一部分人指出,三路合并比 Git 的歷史還要久遠,它起源于 70 年代末期左右。有一篇 2007 年的 論文 對此進行了討論。qKE28資訊網——每日最新資訊28at.com

就說這么多!

我真的對于我對于 Git 內部應用補丁的核心方法其實理解得并不深入這一點感到非常吃驚——學習這一點真的很酷!qKE28資訊網——每日最新資訊28at.com

雖然我對 Git 用戶界面存在 諸多不滿,但是這個特定問題并不包含在內。三路合并似乎是統一解決一系列不同問題的優雅方式,它對于人們來說也很直觀(“應用一個補丁”這個想法是許多編程者都習以為常的思考模式,而它底層實現為三路合并的細節,實際上沒有人真正需要去思考)。qKE28資訊網——每日最新資訊28at.com

我順便快速推薦一下:我正在寫一部有關 Git 的 zine,如果你對它的發布感興趣,你可以注冊我非常不頻繁的 公告郵件列表。qKE28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-24742-0.htmlGit 的遴選和撤銷操作是如何利用三路合并的

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: Python的小應用--使用PyWebIO和PyMySQL創建身份證號碼查詢工具

下一篇: 阿里云崩,釘釘崩,咸魚崩,淘寶崩...應用集群故障后自動恢復測試之進程自我拉起應該怎么做?

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 临汾市| 大安市| 黄冈市| 毕节市| 三台县| 安顺市| 甘洛县| 西乌珠穆沁旗| 松江区| 陈巴尔虎旗| 安宁市| 衡东县| 凉山| 额尔古纳市| 特克斯县| 衡阳县| 垣曲县| 芜湖市| 涿州市| 高阳县| 达日县| 朝阳区| 天台县| 桐城市| 武穴市| 呼伦贝尔市| 邵武市| 肇州县| 昭通市| 大邑县| 泌阳县| 商南县| 肥乡县| 岫岩| 和静县| 淳化县| 中江县| 剑河县| 平泉县| 喜德县| 宿迁市|