一躯舔、前言
本文用實(shí)例來(lái)講解Git的分支管理在產(chǎn)品快速迭代開(kāi)發(fā)過(guò)程中解決實(shí)際問(wèn)題的詳細(xì)方案,面向的是對(duì)Git有一定了解的朋友(多圖預(yù)警)函喉。
二零截、背景
最近接手了一個(gè)代碼質(zhì)量慘不忍睹的項(xiàng)目,立即著手進(jìn)行重構(gòu)昵慌,由于產(chǎn)品已經(jīng)發(fā)布上線假夺,在重構(gòu)新版的過(guò)程中還要一直維護(hù)著老版本,所以如何維護(hù)好兩個(gè)版本的代碼就成了一個(gè)問(wèn)題斋攀。由于我們的團(tuán)隊(duì)一直以來(lái)使用Git時(shí)都忽視了其最大的優(yōu)勢(shì) - 分支管理已卷,所以正好趁此機(jī)會(huì)規(guī)范團(tuán)隊(duì)中Git的使用姿勢(shì)。
三淳蔼、關(guān)鍵字提示
master
指的是本地主分支悼尾,也就是我們新建項(xiàng)目首次關(guān)聯(lián)到Git上的本地主分支柿扣;
origin/master
指的是遠(yuǎn)程主分支,也就是我們新建項(xiàng)目首次提交到Git上的遠(yuǎn)程主分支闺魏;
HEAD
指的是我們當(dāng)前所開(kāi)發(fā)的代碼的版本指針未状,指向的是某個(gè)分支上的節(jié)點(diǎn);
commit
指的是在當(dāng)前分支新建一個(gè)節(jié)點(diǎn)析桥,保存當(dāng)前的代碼版本司草;
這四個(gè)是我下面要經(jīng)常提到的關(guān)鍵字,其他的遇到了再另外解釋?zhuān)?/p>
四泡仗、引出問(wèn)題
假設(shè)我們的產(chǎn)品版本A
已經(jīng)發(fā)布上線了埋虹,現(xiàn)在我們繼續(xù)開(kāi)發(fā)版本B
,今天產(chǎn)品經(jīng)理過(guò)來(lái)告訴你版本A
有一個(gè)十萬(wàn)火急的Bug娩怎,這個(gè)時(shí)候我們肯定是想辦法拿到版本A
的代碼去修復(fù)Bug搔课,那么問(wèn)題來(lái)了:
1、在拿
版本A
的代碼時(shí)截亦,如何去保存我們寫(xiě)到一半的版本B
的代碼爬泥?
有的朋友肯定會(huì)把寫(xiě)到一半的代碼commit
到本地去解決這個(gè)問(wèn)題,這是最簡(jiǎn)單的方式但是也是錯(cuò)誤的方式崩瓤,其僅僅是達(dá)到了目的袍啡,但是這樣會(huì)在版本節(jié)點(diǎn)上留下很多垃圾數(shù)據(jù),經(jīng)常會(huì)誤導(dǎo)團(tuán)隊(duì)的其他成員却桶。
2境输、在修改完
版本A
的Bug后,如何提交這次的修改呢颖系?
在版本A
的節(jié)點(diǎn)上直接進(jìn)行commit
的話嗅剖,HEAD指針會(huì)游離出去,也就是我們經(jīng)常遇見(jiàn)的問(wèn)題HEAD detached at head
或者HEAD detached from master
嘁扼,搞不好會(huì)丟失一部分代碼窗悯,如果你已經(jīng)遇到這個(gè)問(wèn)題了,這里只提供方案就是找到HEAD現(xiàn)在所指向的未知
分支偷拔,給其命名蒋院,然后合并到master
分支上,具體操作自行搜索莲绰。
3欺旧、先不管用什么辦法,假如我在修改完
版本A
后真的將此次的修改記錄成功提交了蛤签,如何恢復(fù)我寫(xiě)到一半的版本B
的代碼呢辞友?
這里一定有修改完后選擇不提交,直接手動(dòng)合并到版本B
的朋友,目的又達(dá)到了称龙,但是這樣會(huì)導(dǎo)致此次對(duì)版本A
的修改以未知的方式記錄到版本B
上去了留拾,時(shí)間長(zhǎng)了恐怕自己都不知道當(dāng)初是改了哪里了。
五鲫尊、解決方案
帶著以上三個(gè)問(wèn)題痴柔,我們進(jìn)行情景再現(xiàn),用Demo來(lái)演示一個(gè)項(xiàng)目在Git上的正確管理步驟疫向。
-
初始導(dǎo)入
現(xiàn)在我們拿到了版本A
的代碼咳蔚,將其提交到遠(yuǎn)程Git倉(cāng)庫(kù)上大家應(yīng)該是沒(méi)有問(wèn)題的,從Android Studio的版本節(jié)點(diǎn)樹(shù)可以看到搔驼,可以看到我們有了本地主分支master
和遠(yuǎn)程主分支origin/master
谈火,HEAD
指向的是本地的master
分支。
-
創(chuàng)建開(kāi)發(fā)分支
現(xiàn)在版本A
已經(jīng)發(fā)布舌涨,我們要進(jìn)行版本B
的開(kāi)發(fā)了糯耍,這里最關(guān)鍵的是我們不可以在master
主支上直接進(jìn)行開(kāi)發(fā),master
主支應(yīng)當(dāng)作為生產(chǎn)環(huán)境上的正式版本的發(fā)布分支囊嘉,該分支的每個(gè)版本最好是有效的温技、干凈的,原則上嚴(yán)禁對(duì)發(fā)布版本直接進(jìn)行修改哗伯。所以我們需要一個(gè)開(kāi)發(fā)分支荒揣,稱(chēng)為developer
分支篷角,這個(gè)分支用來(lái)專(zhuān)門(mén)開(kāi)發(fā)新版本焊刹。
現(xiàn)在我們從版本A
創(chuàng)建出一個(gè)developer
分支,在工作空間根目錄cmd下執(zhí)行:git branch developer master
即從
master
創(chuàng)建developer
的意思恳蹲,版本樹(shù)如下:
可以看到我們現(xiàn)在有了本地的developer
分支虐块,我們還要把該分支上傳到遠(yuǎn)程倉(cāng)庫(kù),執(zhí)行
git push origin developer:developer
執(zhí)行結(jié)果如下嘉蕾,意思是將本地的developer
分支上傳到遠(yuǎn)程origin/developer
贺奠,如果遠(yuǎn)程不存在'origin/developer'分支,則新建之
為了便于觀察错忱,我在developer分支上提交了一個(gè)節(jié)點(diǎn)儡率,現(xiàn)在觀察版本樹(shù)
-
保存正在寫(xiě)的代碼
現(xiàn)在我們正在developer
分支上改動(dòng)代碼,但是master
分支出現(xiàn)了一個(gè)緊急的bug以清,我們應(yīng)該執(zhí)行git stash
儿普,作用是暫存我們?cè)?code>developer上做的沒(méi)有提交的修改,然后我們執(zhí)行
git branch bugbranch master
從master分支檢出一個(gè)新的bug分支bugbranch
掷倔,而不是直接切換到master分支上進(jìn)行修改∶己ⅲ現(xiàn)在觀察版本樹(shù),可以看到從master分支分離出了bugbranch
分支纹份,現(xiàn)在HEAD指向的就是該分支古掏。
-
修復(fù)bug
現(xiàn)在我們?cè)?code>bugbranch分支上修復(fù)了bug盗蟆,并且測(cè)試通過(guò)了掏呼,那么接下來(lái)我們需要把修復(fù)后的代碼commit
一次鉴未,做一個(gè)節(jié)點(diǎn)保存記錄一下裙盾,現(xiàn)在觀察版本樹(shù)
-
合并bug分支
在bugbranch
分支上我們已經(jīng)修復(fù)了bug薯鼠,那么我們需要將bugbranch
分支上修復(fù)后的代碼合并到master分支上去馋没,做一個(gè)節(jié)點(diǎn)并且發(fā)布新版本殃姓,先切換到master分支git checkout master
袁波,然后執(zhí)行
git merge bugbranch
將bugbranch
分支合并到master
分支上,現(xiàn)在觀察版本樹(shù)
可以看到現(xiàn)在master
和bugbranch
分支上的代碼已經(jīng)合并成功一致蜗侈,接下來(lái)還要將最新的代碼push
到遠(yuǎn)程分支上去篷牌,執(zhí)行git push origin master:master
,觀察下邊的版本樹(shù)可以看到踏幻,現(xiàn)在本地和遠(yuǎn)程分支上的代碼都已經(jīng)更新完成枷颊,我們可以發(fā)布新版本了
-
合并修復(fù)的bug到開(kāi)發(fā)分支
到了這里bugbranch
分支的任務(wù)就完成了,我個(gè)人認(rèn)為已經(jīng)沒(méi)有保留的必要了该面,因?yàn)楸敬涡薷牡倪^(guò)程已經(jīng)記錄到了master的節(jié)點(diǎn)上夭苗,如果以后出了問(wèn)題,再?gòu)漠?dāng)前節(jié)點(diǎn)重新檢出一個(gè)分支就好隔缀,執(zhí)行
git branch -d bugbranch
刪除bugbranch
分支题造,現(xiàn)在bug已經(jīng)修復(fù)并且發(fā)布,我們需要回到正常的開(kāi)發(fā)分支上去繼續(xù)開(kāi)發(fā)新的功能猾瘸,執(zhí)行git checkout developer
先切換到developer
分支界赔,然后執(zhí)行
git stash pop
取出我們當(dāng)時(shí)在developer
分支上stash
的未提交的代碼,至于我們?cè)?code>master分支上修復(fù)的bug怎么更新到developer
分支上牵触,分兩種情況處理:
1淮悼、如果該bug影響到新模塊的開(kāi)發(fā),那就手動(dòng)更新修復(fù)部分的代碼揽思,到時(shí)候新模塊開(kāi)發(fā)完成之后我們將
developer
分支合并回master
的時(shí)候就會(huì)產(chǎn)生沖突袜腥,解決掉就好;
2钉汗、如果該bug對(duì)新模塊的開(kāi)發(fā)沒(méi)影響羹令,那就不用做處理,到時(shí)候?qū)?code>developer分支合并回
master
之后修復(fù)后的代碼自然就更新了损痰。
-
合并開(kāi)發(fā)分支到master
現(xiàn)在我們developer
分支上的新功能寫(xiě)完并且測(cè)試通過(guò)了福侈,我們需要先將developer
分支上的代碼都提交到遠(yuǎn)程'developer'分支上,我們發(fā)布新版本的時(shí)候需要先將developer
分支的代碼合并到master
分支上去徐钠,執(zhí)行git checkout master
切換到master
分支癌刽,執(zhí)行get merge developer
來(lái)進(jìn)行合并,這個(gè)時(shí)候可能會(huì)產(chǎn)生沖突,對(duì)照解決即可显拜。
下面是developer
分支向master
合并的時(shí)候產(chǎn)生了沖突衡奥,解決掉合并后的版本樹(shù)
接下來(lái)我們需要先將本地的
master
分支提交更新到遠(yuǎn)程,可以發(fā)現(xiàn)雖然developer
分支雖然成功合并到master
上了远荠,但是因?yàn)楫a(chǎn)生了沖突矮固,developer
分支比master
分支落后兩個(gè)節(jié)點(diǎn)(分別是1.解決merge沖突的節(jié)點(diǎn) 2.當(dāng)初修改的bug節(jié)點(diǎn)),也就意味著代碼有了差別譬淳,如下圖所示:如果出現(xiàn)這種情況档址,需要再次執(zhí)行get merge developer
,再合并一次邻梆,這次沖突已經(jīng)被解決了守伸,直接合并成功,接下來(lái)我們直接將本地的developer
分支代碼提交到遠(yuǎn)程origin/developer
分支即可浦妄,最終的代碼樹(shù)如下:
六尼摹、后言
在快速迭代開(kāi)發(fā)中,經(jīng)常會(huì)有需要緊急處理生產(chǎn)環(huán)境版本bug的時(shí)候剂娄,按照這樣的分支管理操作蠢涝,可以做到快速的拿到每一個(gè)發(fā)布版的代碼;在開(kāi)發(fā)新模塊阅懦、修復(fù)舊bug和二、恢復(fù)舊版本的時(shí)候不互相污染代碼;干凈耳胎、清楚的記錄每次更新惯吕、修復(fù)、迭代的內(nèi)容场晶。
從業(yè)以來(lái)的兩個(gè)團(tuán)隊(duì)都沒(méi)有一個(gè)成熟的git分支管理使用方案混埠,這些處理方式是我在實(shí)際工作中摸索怠缸、總結(jié)出來(lái)的诗轻,是否跟真正正確的使用方式一致不太清楚,但是確實(shí)是解決掉我項(xiàng)目重構(gòu)中實(shí)際遇到的問(wèn)題了揭北,感覺(jué)比較適合中小型團(tuán)隊(duì)使用扳炬,因?yàn)榇髨F(tuán)隊(duì)我也沒(méi)去過(guò),有什么錯(cuò)誤歡迎指出討論搔体,哈哈恨樟。