柚子快報(bào)激活碼778899分享:git 分支-變基
柚子快報(bào)激活碼778899分享:git 分支-變基
在git中,將一個(gè)分支的更改集成到另一個(gè)分支有兩種主要方式:合并(merge)和變基(rebase)。在本節(jié)中,將學(xué)習(xí)什么是變基,如何執(zhí)行變基操作,為什么它是一個(gè)非常強(qiáng)大的工具,以及在哪些情況下不希望使用它。
基本變基
如果回到基本合并的一個(gè)早期示例,會(huì)發(fā)現(xiàn)工作分叉了,并在兩個(gè)不同的分支上進(jìn)行了提交。
正如我們已經(jīng)介紹過的那樣,最簡單的集成分支的方法是使用合并(merge)命令。它在兩個(gè)最新的分支快照(C3和C4)與兩者的最近公共祖先(C2)之間執(zhí)行三方合并,創(chuàng)建一個(gè)新的快照(和提交)。
然而,還有另一種方法:可以獲取在C4引入的更改補(bǔ)丁,并將其重新應(yīng)用在C3之上。在git中,這被稱為變基(rebase)。使用rebase命令,可以獲取在一個(gè)分支上提交的所有更改,并在不同的分支上重放它們。
對(duì)于這個(gè)示例,將檢出experiment分支,然后將其變基到master分支上,如下所示:
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
這個(gè)操作通過前往兩個(gè)分支的共同祖先(當(dāng)前所在的分支和要變基到的分支),獲取當(dāng)前分支中每個(gè)提交引入的差異,將這些差異保存到臨時(shí)文件中,將當(dāng)前分支重置為與要變基到的分支相同的提交,最后依次應(yīng)用每個(gè)更改。
在這一點(diǎn)上,可以返回到master分支并執(zhí)行快進(jìn)合并。
$ git checkout master
$ git merge experiment
現(xiàn)在,C4'指向的快照與合并示例中C5所指向的快照完全相同。集成的最終產(chǎn)品沒有任何區(qū)別,但變基使歷史記錄更清晰。如果檢查變基分支的日志,它看起來像是一個(gè)線性的歷史記錄:似乎所有的工作都是按順序完成的,即使最初是并行完成的。
通常,會(huì)這樣做來確保提交在遠(yuǎn)程分支上能夠干凈地應(yīng)用 - 也許是試圖貢獻(xiàn)但不維護(hù)的項(xiàng)目中。在這種情況下,會(huì)在一個(gè)分支上進(jìn)行工作,然后在準(zhǔn)備好向主項(xiàng)目提交補(bǔ)丁時(shí),將的工作變基到origin/master分支上。這樣,維護(hù)者就不需要進(jìn)行任何集成工作 - 只需要一個(gè)快進(jìn)合并或一個(gè)干凈的應(yīng)用。
請(qǐng)注意,無論最終得到的最終提交是變基的最后一個(gè)提交還是合并后的最終合并提交,指向的快照都是相同的 - 只有歷史記錄是不同的。變基將一系列工作中的更改按照它們被引入的順序重播到另一條線上,而合并則將端點(diǎn)合并在一起。
更有趣的變基
還可以將變基回放到除了變基目標(biāo)分支之外的其他地方。例如,考慮一個(gè)像這樣的歷史:從另一個(gè)主題分支分出的一個(gè)主題分支。例如,從一個(gè)主題分支(server)分出一個(gè)分支,為項(xiàng)目添加了一些服務(wù)器端功能,并提交了一個(gè)提交。然后,從該分支分出一個(gè)分支(client)來進(jìn)行客戶端更改,并進(jìn)行了幾次提交。最后,回到了服務(wù)器分支并進(jìn)行了幾次提交。
假設(shè)決定要將客戶端的更改合并到主線以進(jìn)行發(fā)布,但想等到服務(wù)器端的更改經(jīng)過進(jìn)一步測試??梢允褂胓it rebase的--onto選項(xiàng),將客戶端上不在服務(wù)器上的更改(C8和C9)重播到主分支上:
$ git rebase --onto master server client
這基本上是在說:“獲取客戶端分支,找出自從它與服務(wù)器分支分叉以來的補(bǔ)丁,并在客戶端分支上重播這些補(bǔ)丁,就好像它直接基于主分支?!?這有點(diǎn)復(fù)雜,但結(jié)果非常酷。
現(xiàn)在可以將主分支快進(jìn)(參見主分支快進(jìn)以包含客戶端分支的更改):
$ git checkout master
$ git merge client
假設(shè)決定同時(shí)合并服務(wù)器分支。可以在無需首先檢出它的情況下,通過運(yùn)行g(shù)it rebase <基礎(chǔ)分支> <主題分支> 將服務(wù)器分支變基到主分支上 - 這會(huì)檢出主題分支(在本例中是服務(wù)器分支)并將其重播到基礎(chǔ)分支(主分支)上:
$ git rebase master server
這會(huì)將服務(wù)器工作重播主分支工作之上
然后,可以快進(jìn)基礎(chǔ)分支(主分支):
$ git checkout master
$ git merge server
可以刪除客戶端和服務(wù)器分支,因?yàn)樗泄ぷ鞫家鸭桑辉傩枰鼈儯沟谜麄€(gè)過程的歷史記錄看起來像是最終提交歷史:
$ git branch -d client
$ git branch -d server
變基的危險(xiǎn)
但是變基的喜悅并不是沒有缺點(diǎn)的,可以用一句話概括:
不要變基已經(jīng)存在于存儲(chǔ)庫之外的提交,可能有人基于這些提交進(jìn)行工作。
如果遵循這個(gè)準(zhǔn)則,那么一切都會(huì)很好。如果不遵循,人們會(huì)討厭women ,會(huì)受到朋友和家人的蔑視。
當(dāng)重新定位工作時(shí),會(huì)放棄現(xiàn)有的提交并創(chuàng)建新的提交,這些提交類似但有所不同。如果推送提交到某個(gè)地方,其他人將其拉取下來并基于其進(jìn)行工作,然后用git rebase重寫這些提交并再次推送它們上去,那么合作者將不得不重新合并他們的工作,當(dāng)試圖將他們的工作拉回我們的工作時(shí),事情會(huì)變得混亂。
讓我們看一個(gè)例子,說明如何對(duì)已經(jīng)公開的工作進(jìn)行變基可能會(huì)導(dǎo)致問題。假設(shè)從一個(gè)中央服務(wù)器克隆,然后對(duì)其進(jìn)行一些工作。提交歷史如下所示:
現(xiàn)在,其他人做了更多的工作,其中包括一個(gè)合并,并將該工作推送到中央服務(wù)器。 獲取了它并將新的遠(yuǎn)程分支合并到您的工作中,使歷史看起來像這樣:
接下來,推送合并工作的人決定返回并重新定位他們的工作; 他們執(zhí)行g(shù)it push --force以覆蓋服務(wù)器上的歷史記錄。 然后,從該服務(wù)器獲取,將新的提交拉取下來。
現(xiàn)在你們兩個(gè)都陷入了困境。 如果你執(zhí)行g(shù)it pull,將創(chuàng)建一個(gè)合并提交,其中包含兩行歷史記錄,你的存儲(chǔ)庫將如下所示:
如果在我們歷史記錄呈現(xiàn)此樣式時(shí)運(yùn)行g(shù)it log,將看到兩個(gè)提交具有相同的作者、日期和消息,這將令人困惑。此外,如果將此歷史記錄推送回服務(wù)器,將重新引入所有這些已重新定位的提交到中央服務(wù)器,這可能會(huì)進(jìn)一步使人困惑??梢韵喈?dāng)肯定地假設(shè)另一個(gè)開發(fā)人員不希望C4和C6出現(xiàn)在歷史記錄中;這就是他們首先進(jìn)行重新定位的原因。
重新定位時(shí)進(jìn)行重新定位
如果發(fā)現(xiàn)自己處于這種情況,Git 還有一些進(jìn)一步的魔法可能會(huì)幫助您解決問題。如果團(tuán)隊(duì)中的某人強(qiáng)制推送了覆蓋我們基于其進(jìn)行工作的更改,我們挑戰(zhàn)就是找出哪些是我們的,哪些是他們重新編寫的。
事實(shí)證明,除了提交的 SHA-1 校驗(yàn)和之外,git 還計(jì)算了一個(gè)僅基于引入的補(bǔ)丁的校驗(yàn)和。這被稱為“補(bǔ)丁 ID”。
如果拉取了被重寫的工作,并將其重新定位到我們的合作伙伴的新提交之上,git 通常可以成功地確定哪些是我們獨(dú)有的,并將它們應(yīng)用到新分支的頂部。
例如,在先前的場景中,如果我們?cè)诋?dāng)我們?cè)谟腥送扑椭匦露ㄎ坏奶峤粫r(shí),放棄了我們基于其進(jìn)行工作的提交時(shí),我們運(yùn)行 git rebase teamone/master,git 將會(huì):
確定哪些工作是我們分支獨(dú)有的(C2、C3、C4、C6、C7)
確定哪些不是合并提交(C2、C3、C4)
確定哪些沒有被重寫到目標(biāo)分支中(僅 C2 和 C3,因?yàn)?C4 與 C4' 是相同的補(bǔ)?。?/p>
將這些提交應(yīng)用到 teamone/master 的頂部
因此,我們不會(huì)像在“再次合并相同的工作以生成新的合并提交”中看到的結(jié)果那樣結(jié)束,而是會(huì)得到類似“在被強(qiáng)制推送的重新定位工作之上重新定位”的結(jié)果。
這只有在合作伙伴制作的 C4 和 C4' 幾乎完全相同的補(bǔ)丁時(shí)才有效。否則,重新定位將無法確定它是重復(fù)的,并將添加另一個(gè)類似于 C4 的補(bǔ)?。ㄟ@可能會(huì)導(dǎo)致應(yīng)用失敗,因?yàn)楦囊呀?jīng)存在)。
還可以通過運(yùn)行 git pull --rebase 而不是普通的 git pull 來簡化此過程。或者,在這種情況下,也可以手動(dòng)執(zhí)行 git fetch,然后執(zhí)行 git rebase teamone/master。
如果使用 git pull 并希望將 --rebase 設(shè)置為默認(rèn)值,可以使用類似 git config --global pull.rebase true 的命令來設(shè)置 pull.rebase 配置值。
如果只重新定位從未離開過計(jì)算機(jī)的提交,那么一切都會(huì)很好。如果重新定位已經(jīng)推送過的提交,但沒有人基于這些提交創(chuàng)建新的提交,那么一切也會(huì)很好。如果重新定位已經(jīng)公開推送的提交,而且人們可能已經(jīng)基于這些提交進(jìn)行了工作,那么可能會(huì)遇到一些令人沮喪的麻煩,并且會(huì)受到隊(duì)友的責(zé)備。
如果我們或合作伙伴在某個(gè)時(shí)候發(fā)現(xiàn)有必要這樣做,請(qǐng)確保每個(gè)人都知道運(yùn)行 git pull --rebase 來嘗試使發(fā)生這種情況后的痛苦變得簡單一些。
重新定位 vs. 合并
現(xiàn)在已經(jīng)看到了重新定位和合并的實(shí)際操作,可能想知道哪個(gè)更好。在我們回答這個(gè)問題之前,讓我們稍微退后一步,談?wù)剼v史意味著什么。
對(duì)此的一種觀點(diǎn)是,存儲(chǔ)庫的提交歷史記錄是實(shí)際發(fā)生的事情的記錄。它是一份歷史文檔,本身就有價(jià)值,不應(yīng)該被篡改。從這個(gè)角度來看,更改提交歷史幾乎是褻瀆的;那么如果有一系列混亂的合并提交怎么辦?這就是實(shí)際發(fā)生的事情,存儲(chǔ)庫應(yīng)該為后人保留下來。
相反的觀點(diǎn)是,提交歷史記錄是項(xiàng)目是如何制作的故事。不會(huì)發(fā)布一本書的初稿,那么為什么要展示混亂工作呢?當(dāng)在項(xiàng)目上工作時(shí),可能需要記錄所有錯(cuò)誤和死胡同,但是當(dāng)準(zhǔn)備向世界展示我們工作時(shí),我們可能希望講述如何從 A 到 B 的更連貫的故事。在這個(gè)陣營中,人們使用像重新定位和 filter-branch 這樣的工具,在將其合并到主干分支之前重新編寫他們的提交。他們使用重新定位和 filter-branch 這樣的工具,以最適合未來讀者的方式講述故事。
現(xiàn)在,關(guān)于合并和重新定位哪個(gè)更好的問題:希望能看到這并不簡單。git 是一個(gè)強(qiáng)大的工具,可以讓我們對(duì)歷史進(jìn)行許多操作,但每個(gè)團(tuán)隊(duì)和每個(gè)項(xiàng)目都是不同的?,F(xiàn)在我們知道這兩個(gè)工作原理了,就由我們自己來決定哪個(gè)對(duì)特定情況更好。
可以兩全其美:在推送本地更改之前重新定位以清理您的工作,但不要重新定位已經(jīng)推送到其他地方的任何內(nèi)容。
柚子快報(bào)激活碼778899分享:git 分支-變基
精彩內(nèi)容
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。