0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Git命令解析-merge、rebase

張康康 ? 2019-07-29 18:21 ? 次閱讀

作者 | Video++極鏈科技后端Team

整理 | 包包

Git分支和工作流

分支本質(zhì)是一個(gè)指向提交對(duì)象的可變指針。Git 保存的不是文件的變化或者差異,而是一系列不同時(shí)刻的文件快照。在進(jìn)行提交操作時(shí),會(huì)保存一個(gè)提交對(duì)象(commit object),在多次提交后,commit對(duì)象形成連續(xù)的快照鏈,分支指針自動(dòng)指向最新一次提交。Git 的默認(rèn)分支名字是 master。如下圖:

Git命令解析-merge、rebase


branch命令可以輕松創(chuàng)建一個(gè)新分支,就像這樣:

$gitbranchnew_branch

這一命令實(shí)際是為當(dāng)前提交對(duì)象添加了一個(gè)新的指針。這種分支形式比大多數(shù)版本控制系統(tǒng)更為輕量,無(wú)論是創(chuàng)建還是切換都幾乎可以在瞬間完成。Git 鼓勵(lì)在工作流程中頻繁地使用分支與合并,這完全不會(huì)增加倉(cāng)庫(kù)負(fù)擔(dān),并且可以基于這一特性創(chuàng)建更自由和更可靠的合作開發(fā)流程。

許多使用Git 的開發(fā)者都喜歡使用這種方式來(lái)工作:僅在master分支上保留完全穩(wěn)定的代碼,這些代碼通常處于已發(fā)布或等待發(fā)布的狀態(tài)。此外使用一些短期分支,比如用develop分支開發(fā)新特性,使用test分支修復(fù)bug,測(cè)試穩(wěn)定性,直到代碼質(zhì)量達(dá)到發(fā)布要求,再合并到master分支,完成一個(gè)版本的開發(fā)。

不同的開發(fā)者團(tuán)隊(duì)可以自由創(chuàng)造適合自己組織形式的分支策略。社區(qū)中也存在許多深受歡迎的流程范例,比如經(jīng)典的gitflow工作流、PR工作流、集中式工作流等等,它們通常適用于不同的合作方式,并不是某種強(qiáng)制規(guī)范。有興趣的讀者可以繼續(xù)深入探索,此處不再過(guò)多介紹。

merge

假設(shè)我們基于master分支創(chuàng)建了feature分支用來(lái)開發(fā)新功能,經(jīng)過(guò)一段時(shí)間開發(fā)之后,需要把feature的分支代碼合并回到master,通常執(zhí)行的操作是先檢出master分支,然后執(zhí)行g(shù)it merge feature。

Git命令解析-merge、rebase


一般來(lái)說(shuō),在單人開發(fā)的情況下,merge通常會(huì)產(chǎn)生快進(jìn)(fast-forward)方式的合并。如果在子分支(feature)被創(chuàng)建之后,父分支(master)未產(chǎn)生新的修改和提交,此時(shí)把feature合并回master,Git會(huì)在提交鏈上把master指針簡(jiǎn)單的前移,使兩個(gè)分支進(jìn)度同步,并形成無(wú)分支記錄的提交鏈。執(zhí)行時(shí)在控制臺(tái)輸出Fast-forward標(biāo)識(shí)。這種merge方式下不會(huì)產(chǎn)生沖突,git log命令會(huì)看到如下記錄:

Git命令解析-merge、rebase


但在團(tuán)隊(duì)合作開發(fā)時(shí),通常會(huì)多人修改同一遠(yuǎn)程分支。其中使用的pull和push命令實(shí)際包含了merge操作。這時(shí)git使用另外一種方式來(lái)進(jìn)行分支合并。目前只有一方修改的情況下,也可以使用 —no-ff 參數(shù)來(lái)模擬這種方式。

Git命令解析-merge、rebase


這里使用了git最基礎(chǔ)的三路遞歸合并(recursive three-way merge),輸出Merge made by the 'recursive' strategy.標(biāo)明合并方式。這種合并會(huì)形成帶分支歷史的提交鏈:

Git命令解析-merge、rebase


從圖中可以看出,這種merge方式實(shí)際在發(fā)起合并的分支生成了一個(gè)帶有Merge 標(biāo)識(shí)的新提交。如果合并時(shí)存在沖突,解決沖突后的最終內(nèi)容也會(huì)包含在這個(gè)新的提交中。

看到這里,可能有人會(huì)有疑問(wèn),工作空間中自始至終只出現(xiàn)了兩個(gè)分支,為什么會(huì)是三路合并。從git 源碼中可以找到merge執(zhí)行的入口,它有這樣的方法簽名:

Git命令解析-merge、rebase


可以看出,除了含義明顯的ours和theirs,還有一個(gè)待合并的文件叫做ancestor。根據(jù)文檔和源碼注釋,這個(gè)版本實(shí)際是兩個(gè)待合并分支的公共部分。在我們的例子中就是創(chuàng)建新分支的那個(gè)提交對(duì)象。

大體的流程是這樣的,git merge會(huì)找出兩個(gè)分支指向的最新commit,找到他們最近的公共祖先,然后對(duì)每個(gè)待合并的文件調(diào)用ll_merge,這個(gè)方法會(huì)比較各分支和祖先節(jié)點(diǎn)的差異。然后把這些差異整合成一個(gè)Merge提交,應(yīng)用到當(dāng)前分支上,生成最終的合并結(jié)果。

如果兩個(gè)分支之間有多個(gè)公共祖先,git會(huì)選出最合適的祖先節(jié)點(diǎn)依照同樣規(guī)則進(jìn)行遞歸合并??梢允褂胓it merge-base —all命令列出所有的備選祖先節(jié)點(diǎn)。

Git還可以一次性合并多個(gè)分支,只需要簡(jiǎn)單的把分支名當(dāng)做merge的參數(shù)依次列出:

Git命令解析-merge、rebase


這種策略被稱為octopus,其中核心邏輯與three-way merge相同,不再詳述,可以通過(guò)閱讀github上的源碼和文檔繼續(xù)深入了解。

three-way merge機(jī)制有一定的隱患。如果其中一個(gè)待合并分支,比如ours,和ancestor版本的某一部分代碼相同,但另一個(gè)待合并分支theirs中有不同的修改,合并的結(jié)果就會(huì)采用theirs分支不同的那部分,并不會(huì)依照修改的時(shí)間順序來(lái)決定最終內(nèi)容。在實(shí)際項(xiàng)目中可能會(huì)反復(fù)修改同一段代碼來(lái)響應(yīng)需求變更,就有幾率發(fā)生這種合并結(jié)果與預(yù)計(jì)不符的情況,需要特別留意。

rebase

Git rebase,通常被稱作變基或衍合, 可以理解為另外一種合并的方式,與merge 會(huì)保留分支結(jié)構(gòu)和原始提交記錄不同,rebase 是在公共祖先的基礎(chǔ)上,把新的提交鏈截取下來(lái),在目標(biāo)分支上進(jìn)行重放,逐個(gè)應(yīng)用選中的提交來(lái)完成合并。

為了形象理解rebase的過(guò)程,可以看下面例子:

使用 merge 合并后:

Git命令解析-merge、rebase


下面使用rebase方式達(dá)到同樣效果:

Git命令解析-merge、rebase


除了原本的多分支記錄變?yōu)榱酥本€提交鏈,還可以注意到,其中原本在feature分支上的提交,rebase后的SHA編碼發(fā)生了變化。rebase消除了真實(shí)歷史,重新生成了新的提交。

和merge類似,rebase在遇到?jīng)_突時(shí)也會(huì)暫停,需要手動(dòng)修復(fù)后才可以繼續(xù)。但是rebase的處理要相對(duì)繁瑣一些,merge 如果發(fā)生 conflict,只需要在最終的Merge 提交上解決一次。而 rebase 的 conflict 可能發(fā)生在每一次提交的重新應(yīng)用上,所以需要依次解決。

為了避免這種情況,可以在與另一分支合并之前,提前把所有需要提交合并為一個(gè)提交。同樣需要用到rebase命令。

執(zhí)行這樣一個(gè)命令來(lái)合并當(dāng)前最新的3個(gè)提交:

Git命令解析-merge、rebase


這條命令將打開一個(gè)編輯頁(yè)面,我們可以修改前面的命令來(lái)合并或丟棄單個(gè)提交。

pick 表示將會(huì)應(yīng)用這個(gè)提交。

squash 表示把當(dāng)前提交合并到前一個(gè)提交,它的前面必須至少有一個(gè)被pick的提交存在。

把某條提交注釋或刪除表示丟棄這條記錄。

這里選擇合并第一個(gè)和第三個(gè),丟棄第二個(gè)提交。

Git命令解析-merge、rebase


保存退出后進(jìn)入新的編輯頁(yè)面,提示編輯提交信息,這里選擇不做改動(dòng)。

Git命令解析-merge、rebase


再次保存退出后成功合并完成,形成這樣的log:

Git命令解析-merge、rebase


git還有一個(gè)可愛的命令cherry-pick,通常譯作揀選。它的參數(shù)是提交對(duì)象的SHA編碼,可以視為針對(duì)單個(gè)提交的rebase操作。示例如下:

Git命令解析-merge、rebase


Git命令解析-merge、rebase


總結(jié)

merge 和 rebase 的差異在于最終的歷史記錄,可以發(fā)現(xiàn) merge 保持了所有分支的原始修改記錄,可能會(huì)包含很多不必要的信息;而 rebase相當(dāng)于對(duì)歷史記錄做出修剪,可以維持一條簡(jiǎn)單清晰的提交路線。

通常我們會(huì)在基于一個(gè)過(guò)時(shí)的版本進(jìn)行了本地修改的情況下使用rebase,在實(shí)際開發(fā)中經(jīng)常會(huì)出現(xiàn)這種情況,當(dāng)你在本地分支上工作了幾天,突然想起應(yīng)該push到遠(yuǎn)程倉(cāng)庫(kù)時(shí),遠(yuǎn)程分支已經(jīng)被別人更新過(guò)了。此時(shí)你會(huì)得到一個(gè)reject信息。

有些人會(huì)選擇用pull命令合并遠(yuǎn)程和本地的同名分支,但pull實(shí)際執(zhí)行了fetch和merge兩個(gè)操作,會(huì)生成復(fù)雜的分支歷史和一個(gè)多余的merge提交。你也可以選擇用fetch和rebase代替pull,始終生成一個(gè)美觀的提交鏈。

rebase的另一個(gè)重要應(yīng)用是合并過(guò)多的本地提交。因?yàn)榉乐剐薷膬?nèi)容丟失,經(jīng)常commit到本地倉(cāng)庫(kù)是一個(gè)很好的開發(fā)習(xí)慣。但是當(dāng)需要提交到公共分支時(shí),大量無(wú)明確意義的提交信息對(duì)歷史記錄造成不必要的干擾。此時(shí)你可以用rebase命令把本地記錄規(guī)范化,再進(jìn)行推送。

使用rebase的時(shí)候需要遵循一條重要原則:不要對(duì)在你的本地倉(cāng)庫(kù)外有副本的提交記錄進(jìn)行變基。rebase的實(shí)質(zhì)是丟棄一些現(xiàn)有的提交,然后相應(yīng)地新建一些內(nèi)容一樣但實(shí)際上不同的提交。 如果其他人已經(jīng)在這些提交上做出過(guò)大量修改、沖突合并等工作,那么你的rebase將成為他們的惡夢(mèng)。

對(duì)于使用rebase還是merge來(lái)合并代碼,實(shí)際并沒有什么固定的模式,取決于開發(fā)者如何看待倉(cāng)庫(kù)的歷史記錄。一些人認(rèn)為歷史記錄應(yīng)該反映全部真實(shí)變更細(xì)節(jié),而另一些人認(rèn)為歷史記錄應(yīng)該是精心維護(hù)的變更目錄。具體如何使用取決于項(xiàng)目合作者的一致共識(shí)。無(wú)論是merge還是rebase,都應(yīng)該了解其中原理,避免危險(xiǎn)操作,才能享受到Git諸多特性帶來(lái)的便利。


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Linux系統(tǒng)中shell命令解析

    shell是Linux系統(tǒng)的用戶界面,提供了用戶與內(nèi)核交互的一種接口,它接收用戶輸入的命令并到送到內(nèi)核去執(zhí)行,因此也被稱為L(zhǎng)inux的命令解釋器。
    的頭像 發(fā)表于 11-05 15:40 ?268次閱讀

    Linux實(shí)用命令大全

    Linux實(shí)用命令大全
    的頭像 發(fā)表于 10-23 13:50 ?231次閱讀
    Linux實(shí)用<b class='flag-5'>命令</b>大全

    SD的命令和響應(yīng)

    一個(gè)完整的?SD?卡操作過(guò)程是:主機(jī)(單片機(jī)等)發(fā)起“命令”,SD?卡根據(jù)命令的內(nèi)容決定是 否發(fā)送響應(yīng)信息及數(shù)據(jù)等,如果是數(shù)據(jù)讀/寫操作,主機(jī)還需要發(fā)送停止讀/寫數(shù)據(jù)的命令來(lái)結(jié)束 本次操作,這意味著主機(jī)發(fā)起
    的頭像 發(fā)表于 10-08 15:49 ?475次閱讀
    SD的<b class='flag-5'>命令</b>和響應(yīng)

    SDRAM中的active命令介紹

    在向SDRAM 中的任何行發(fā)出 READ或 WRITE 命令之前,必須先打開該行。這是通過(guò) ACTIVE 命令完成的。ACTIVE 命令的目的是打開或者說(shuō)激活(active)bank中的一行并將數(shù)據(jù)從 DRAM 移動(dòng)到bank的
    的頭像 發(fā)表于 07-29 09:53 ?453次閱讀
    SDRAM中的active<b class='flag-5'>命令</b>介紹

    使用CIPDOMAIN命令時(shí),解析長(zhǎng)度為64個(gè)字符或更大的DNS名稱失敗了,為什么?

    使用 CIPDOMAIN 命令時(shí),嘗試解析長(zhǎng)度為 64 個(gè)字符或更大的 DNS 名稱失敗。 例: AT+CIPDOMAIN=\"
    發(fā)表于 07-11 07:59

    ubuntu下的vscode插件安裝idf時(shí),總是找不到git,為什么?

    總是提示:Git is not found in current environment 但是我已經(jīng)在setting.json下設(shè)置了\"git.path\": \"/bin/git\" 同行,
    發(fā)表于 06-21 07:16

    通過(guò)git命令獲取ESP8266_RTOS_SDK失敗如何解決?

    本人使用的硬件平臺(tái)為esp8266,開發(fā)環(huán)境為ubuntu22.04。在通過(guò)git命令獲取ESP8266_RTOS_SDK失敗,通過(guò)上網(wǎng)搜索嘗試了很多方法無(wú)效。具體情況如下
    發(fā)表于 06-07 07:26

    Git發(fā)布新版本 修補(bǔ)五處安全漏洞 包含嚴(yán)重遠(yuǎn)程代碼執(zhí)行風(fēng)險(xiǎn)

    CVE-2024-32002漏洞的嚴(yán)重性在于,黑客可通過(guò)創(chuàng)建特定的Git倉(cāng)庫(kù)子模塊,誘騙Git將文件寫入.git/目錄,而非子模塊的工作樹。如此一來(lái),攻擊者便能在克隆過(guò)程中植入惡意腳本,用戶幾乎無(wú)法察覺。
    的頭像 發(fā)表于 05-31 10:09 ?606次閱讀

    飛凌ElfBoard ELF 1板卡-ubuntu18.04 git安裝及基本使用

    1.安裝gitsudo apt-get install git 2.git初始化git init 3.設(shè)置用戶名和郵箱git config --global user.name \"你
    發(fā)表于 03-21 16:23

    lscpu命令使用注意事項(xiàng)

    請(qǐng)注意,lscpu命令在不同的操作系統(tǒng)上可能會(huì)有一些差異,某些選項(xiàng)可能不可用??梢酝ㄟ^(guò)man lscpu命令或lscpu --help命令查看該命令的幫助文檔和更多選項(xiàng)。
    發(fā)表于 03-14 11:39 ?841次閱讀

    藍(lán)牙 | 軟件:Git管理高通的ChipCode項(xiàng)目

    最近發(fā)現(xiàn)大家在高通chipcode網(wǎng)站上下載不了代碼,小編一直使用git的方式獲取新版本代碼,沒有遇到什么阻礙。于是小編到新主機(jī)上嘗試下載代碼的壓縮包和git代碼,都遇到了問(wèn)題。由于壓縮包是高通自己
    的頭像 發(fā)表于 01-26 08:29 ?402次閱讀
    藍(lán)牙 | 軟件:<b class='flag-5'>Git</b>管理高通的ChipCode項(xiàng)目

    克服PLC編程的難題—基于Git的軟件

    Git是一種分布式版本控制系統(tǒng),它可以記錄文件的修改歷史和版本變化,并可以支持多人協(xié)同開發(fā)。
    的頭像 發(fā)表于 01-22 09:30 ?963次閱讀
    克服PLC編程的難題—基于<b class='flag-5'>Git</b>的軟件

    Git開發(fā)者關(guān)注內(nèi)存安全問(wèn)題,探討引入Rust語(yǔ)言

    根據(jù)最新披露的郵件討論,Git開發(fā)團(tuán)隊(duì)熱議在Git項(xiàng)目中引入Rust的可行性。作為一種開源的分布式代碼版本管理工具,廣泛運(yùn)用于各種開發(fā)項(xiàng)目。盡管現(xiàn)在Git項(xiàng)目主要以C與Python為主要開發(fā)語(yǔ)言,但探討顯示,引入Rust能顯著降
    的頭像 發(fā)表于 01-15 14:23 ?626次閱讀
    <b class='flag-5'>Git</b>開發(fā)者關(guān)注內(nèi)存安全問(wèn)題,探討引入Rust語(yǔ)言

    Linux基本命令總結(jié)

    都說(shuō)Linux命令多,實(shí)際工作中我們又有多少命令會(huì)接觸用到呢?本文跟大家分享Linux運(yùn)維工作中常用的600個(gè)命令,可以說(shuō)這些命令是伴隨著大家一生的運(yùn)維生涯了,看看大家有哪些還沒用過(guò)的
    的頭像 發(fā)表于 01-11 10:32 ?973次閱讀

    一種AT命令通信解析模塊介紹

    一種AT命令通信解析模塊,支持裸機(jī)(at_chat)和OS版本(at)。適用于modem、WIFI模塊、藍(lán)牙通信。
    的頭像 發(fā)表于 01-08 12:24 ?1167次閱讀
    一種AT<b class='flag-5'>命令</b>通信<b class='flag-5'>解析</b>模塊介紹