從git的問題模型理解git

2022-04-10 09:20:14 字數 3152 閱讀 1503

對於很多試圖說明git工作過程的文章都會出現乙個圖:

a---b---c topic

/ \

d---e---f---g---h master

但是並沒有告訴你,這些字母代表的commit到底是什麼。把topic分支merge到master的時候的conflict為什麼不體現出來?這才是初學者迷茫的東西,文章畫的圖倒是簡單明瞭,然後困惑還是困惑。很多問題其實如果把問題描述清楚了,問題也就解決了。那麼git解決的問題是什麼,都知道是版本控制,那麼具體化之後是什麼東西呢?

1、什麼是commit?

首先git的區分檔案變化的最細粒度是line行,不是單詞更不是字母。行變化主要是增刪改三種。commit可以看成是記錄這些增刪改操作的資料。

commit是乙個add,del,update的集合,update分解為乙個del配合乙個add,update n,可以表示為(del n)+(add n.5)或者(del n)+(add (n-1).5) 其中n.5表示在n行後新增一行,所以commit只是乙個del和add的集合,只要記錄下del的行號,add的gap號的集合,配合待變化的base檔案,就可以精確確定修改內容是什麼。這些add和del是無序的,只要全部應用到base檔案,就會得到相同的結果檔案。

2、合併

合併兩次commit就是,兩個ops集合的合併,合併是可以遞迴的,一系列commit可以合併成乙個commit,對應就是一些列ops集合合併為乙個大ops集合。

3、什麼是衝突?

衝突就是指兩次commit的執行順序會影響都最終結果檔案內容的不同,這就是通常說的衝突。也就是不能簡單機械地合併,否則不符合預期。那麼我的預期是什麼呢?預期就是顛倒兩次commit得到的結果是一樣的。這樣的合併過程需要人為參與,告訴git合併的時候應該以什麼樣的結果為準。

那麼什麼樣commit產生衝突呢?

兩個commit,del同一行,不分操作先後只執行一次,就可以確定不同操作順序不影響最終結果。

兩個commit,add到同乙個gap,那麼不同的操作順序最終結果不同,所以,這兩個commit是conflict。

兩個update同一行,翻譯為乙個del加上乙個add,如果add可以是被del行的前或者後的gap,這個由於對同乙個gap的add判定conflict。

甚至update相鄰兩行。

實驗驗證同時修改相鄰兩行為什麼會衝突,思考了一下,原因可能如下。

如果是update一行,那麼相當於在這一行前或者後增加一行,所以,這一行本身和這一行前後的gap都被鎖定——可以認為是被汙染的範圍。

由於,add的gap仍然是有交叉的,因為乙個update等價於del該行然後在after或before的gap增加新行,也判定為conflict。

所以如果用整數表示行號,用x.5表示x行後的gap,因為修改一行是del line,add before或者add after都行。

commita,update line4——修改範圍是4,3.5,4.5

commitb,update line3——修改範圍是3,3.5,2.5

commitc,update line2——修改範圍是2,2.5,1.5

所以a和b是衝突的,a和c不是衝突的。

4、解決merge conflict

對於git新手而言衝突很可怕,但是版本控制的日常就是發生衝突。

當兩個ops集合合併的時候,產生乙個新的commit,也就是兩個commit的和,將所有ops操作應用到檔案,git判斷有衝突之後標記處衝突部分——哪個commit分別做的修改是什麼,此時,檔案可以編輯,使用者修改後並確認,add,commit為最終的merge結果。

git的衝突的情況只會被誇大,而不會被忽略,寧枉勿縱,這是安全的策略。git檢測檔案的改動時,總是盡量精確描述修改的部分——例如在檔案最前面增加一行,如果準確描述,就是add line to gap 0.5,如果簡單起見也可以認為del all,add new all。沒有精確描述縮小改動是問題不大,最多只是影響效能。

現在再理解abc代表的commit,就可以想到背後只不過乙個增刪的操作集合,merge也只是集合的合併。

5、rebase

將分支rebase到乙個分叉點之後的master提交,實際上導致分支上的commit做了相應的調整,是為了保證從分支merge到master的時候不會出現衝突。否則,需要先從master merge到分支,此時在分支解決衝突,然後將最新分支merge到master,兩個分支多了乙個歷史交匯點。如圖,先將master分支merge到topic的m,此時如果有衝突,則在topic分支解決提交到topic,然後從topic merge到master的h。

a---b---c--m  topic

/ / \

d---e---f-----g-----h master

而使用rebase則可能在rebase的時候有衝突,此時解決衝突,這時topic分支的commit其實已是a2,b2,c2了,因為他們的base檔案變化了,只有操作集合跟隨變化,才能保證最終的結果檔案不變。

a2---b2---c2 topic

/ \

d---e---f---g---h master

由於,topic是rebase到g的,所以此時merge到master,一定沒有conflict,這樣合併的master分支就更好看。

最後,相信一點,git說沒有衝突就真的沒有衝突,真的有衝突不會被git所遺漏。

****************************************==

但是,其實沒有衝突不代表**不會有bug。舉個極端的例子:

乙個outputstream,忘記了寫bye這個單詞,還忘記了close。如果{……

outputstream os= ...…

other code……}

乙個人增加{……

outputstream os= ...…

other code

os.write("bye")……}

另乙個人增加{……

outputstream os= ...…

……os.close()

other code……}

merge是無衝突的,結果如下{……

outputstream os= ...…

……os.close()

other code

os.write("bye")……}

顯然這段**執行時就會出錯。

從git談起 git開發

作者來自某國際知名企業,無聊時喜歡看一些城市撕逼論壇,週末無聊談一談在大廠裡面的工作感受。為提公升國際聲譽,避免打壓。最近公司組織xx認證,考試題目涉及較廣,考試通過甚難。除了自己擅長的編譯構建 配置管理等很多題目是未在工作中實際使用的,想來想去呢,從git這個工具談起。盡量通過這個引入在軟體開發過...

Git分支模型

本文介紹一種使用git進行源 管理的分支模型,著重於如何使用git更好的管理我們的源 我假定您對git有一定了解,會使用基本的git命令進行一些簡單的源 管理工作。這不是一篇git使用教程。根據自己的使用情況進行了補充。我們知道git是乙個不需要中心伺服器就能工作的源 管理系統 但我仍然建議你至少保...

Git的理解(一)

當然git也是非常深入的,不過對於我而言,我覺著我已經夠用了。git是分布式版本控制系統 distributed version control system,簡稱 dvcs 這裡客戶端並不只提取最新版本的檔案快照,而是把 倉庫完整地映象下來。這麼一來,任何一處協同工作用的伺服器發生故障,事後都可以...