在協(xié)同開(kāi)發(fā)中,為了項(xiàng)目管理的方便扫夜,會(huì)采用各種各樣的工作流楞泼,有關(guān)工作流方面的內(nèi)容,可以到開(kāi)頭提供的第三個(gè)網(wǎng)址里學(xué)習(xí)笤闯。這里只簡(jiǎn)單介紹兩個(gè)實(shí)用的技巧堕阔。
01 一個(gè)干凈的push!
在Git中颗味,我們很少會(huì)改變中央倉(cāng)庫(kù)的已有的提交歷史印蔬。因?yàn)槟菢訒?huì)使所有的開(kāi)發(fā)者的本地倉(cāng)庫(kù)和中央倉(cāng)庫(kù)處在不一致的狀態(tài),所有開(kāi)發(fā)者都需要因?yàn)檫@個(gè)改動(dòng)調(diào)整自己的本地倉(cāng)庫(kù)來(lái)與中央倉(cāng)庫(kù)保持一致脱衙。如果改動(dòng)很大侥猬,調(diào)整將會(huì)很復(fù)雜。只要不是及其嚴(yán)重的事情捐韩,我們都不會(huì)去調(diào)整中央倉(cāng)庫(kù)的提交歷史退唠。所以,推送上去是什么樣的荤胁,以后就會(huì)是什么樣的瞧预,對(duì)于中央倉(cāng)庫(kù)來(lái)說(shuō),第一印象會(huì)伴隨一生仅政。
所以垢油,就像女生出門前要化妝一樣,在推送本地分支到中央倉(cāng)庫(kù)前圆丹,要把本地分支整理干凈滩愁。一般,我們會(huì)給每個(gè)功能新建一個(gè)分支辫封,在功能開(kāi)發(fā)完成后硝枉,可以用git rebase -i
命令刪去那些無(wú)關(guān)緊要的提交廉丽,并將提交歷史書(shū)寫(xiě)成我們邏輯中的樣子。
比如我們接到了生產(chǎn)車門的任務(wù)妻味,現(xiàn)在我們把它完成了正压,開(kāi)發(fā)歷史如下。
在我們把它推送到中央倉(cāng)庫(kù)前责球,我們用git rebase -i <commitID>
整理一下它的提交歷史焦履,commitID
參數(shù)用來(lái)指定我們要從哪個(gè)提交開(kāi)始修改。由于我們是從BetterWheelCompleted
這個(gè)提交開(kāi)始開(kāi)發(fā)車門的雏逾,所以commitID
參數(shù)應(yīng)該是這個(gè)提交的版本號(hào)裁良,為99c7ed1578c0a16a0f1de58548883a8d6e8eeb8a
。執(zhí)行以下指令校套。
$ git rebase -i 99c7ed1578c0a16a0f1de58548883a8d6e8eeb8a
之后价脾,在命令行中會(huì)顯示vi
文本編輯窗口。
在最前面的是各個(gè)提交笛匙,每個(gè)提交的前面是要對(duì)該提交執(zhí)行的命令侨把,默認(rèn)是pick
采用提交,我們主要會(huì)用到squash
命令妹孙,squash
命令可以讓我們把對(duì)應(yīng)提交合并到上一次提交中秋柄,舉個(gè)栗子,給69218a4
提交加上squash
命令就可以把它合并到f7a6770
提交上蠢正。我們開(kāi)發(fā)車門主要有三個(gè)階段骇笔,設(shè)計(jì)車架,設(shè)計(jì)車窗嚣崭,加入手勢(shì)開(kāi)門功能笨触,所以我們要分別合并第二、三雹舀、四次提交芦劣。我們把它編輯如下,然后保存并退出編輯窗口说榆。
之后在合并第二虚吟、三、四次提交時(shí)签财,Git又會(huì)彈出一個(gè)文本編輯窗口串慰,讓我們合并三次提交的提交信息。
因?yàn)檫@一次合并提交的信息應(yīng)該為“設(shè)計(jì)了車窗”唱蒸,剛好是第二個(gè)提交的提交信息邦鲫,所以我們?cè)诓灰男畔⑶凹?code>#把他們忽略即可。合并完成后油宜,我們的提交歷史就變成了如下模樣掂碱。
然后就可以把他們推送到遠(yuǎn)端怜姿,由其他人檢查代碼慎冤,然后合并或者變基到主分支上了疼燥。如果在開(kāi)發(fā)完后就已經(jīng)決定要這條分支變基到主分支上,那也可以直接用git rebase -i <branch_name>
來(lái)修改提交歷史并變基蚁堤。
很多時(shí)候醉者,我們會(huì)把一個(gè)功能的實(shí)現(xiàn)分為好幾個(gè)功能點(diǎn),然后給功能點(diǎn)新建一條分支披诗,然后在這個(gè)功能點(diǎn)開(kāi)發(fā)完后把修改整合回功能分支撬即。這樣做的好處是可以保持功能分支的整潔,不會(huì)在功能分支上生成很多備份代碼的提交呈队,但是如果用普通的merge
操作的話剥槐,合并生成的提交除了指向主功能分支外,也會(huì)指向功能點(diǎn)分支宪摧,那樣還是會(huì)把功能點(diǎn)分支上的提交歷史混合進(jìn)來(lái)粒竖。
這時(shí)我們可以使用git merge --squash <branch_name>
,在merge
指令中加入--squash
后會(huì)把branch_name
分支上的所有提交壓縮成一個(gè)加入到當(dāng)前分支上來(lái)几于,并且這個(gè)提交不會(huì)指向被合并的分支蕊苗。舉個(gè)栗子,在上次我們把車門的分支推送上去給老大檢查后沿彭,老大說(shuō)要再加一個(gè)手勢(shì)關(guān)門的功能朽砰!然后我們?yōu)榱酥鞣种У恼麧崳o這個(gè)小功能新建了一個(gè)分支進(jìn)行開(kāi)發(fā)喉刘。接著瞧柔,經(jīng)過(guò)了不懈努力,我們終于開(kāi)發(fā)完了睦裳,現(xiàn)在提交歷史如下非剃。
我們用以下指令把feature-CloaseDoorwithGesture
分支的上的提交壓縮成一個(gè)一條放到produceDoor
分支上。
$ git checkout produceDoor
$ git merge --squash feature-CloseDoorwithGesture
$ git commit -m "給車門加上了手勢(shì)關(guān)門的功能"
執(zhí)行完成后我們的提交歷史就變成了下圖所示模樣推沸。
可以看到新建的提交并沒(méi)有指向feature-CloaseDoorwithGesture
分支备绽。在合并完后,我們可以刪除feature-CloaseDoorwithGesture
分支鬓催。
$ git branch -D feature-CloseDoorwithGesture
OK肺素。老大說(shuō)干的不錯(cuò),讓我們把分支合并到主分支上宇驾,然后推送到中央倉(cāng)庫(kù)來(lái)結(jié)束這個(gè)功能的開(kāi)發(fā)倍靡。
$ git merge --no-ff produceDoor -m "車門生產(chǎn)完成"
$ git push origin master
執(zhí)行完成后,提交歷史如下课舍。
Android Studio中的相應(yīng)操作
用VCS->Git->Merge Changes
下的合并操作塌西,并選中squash commit
選項(xiàng)他挎,即可完成git merge --squash <branch_name>
操作。
git rebase -i <commitID>
可以用VCS->Git->Rebase
完成捡需。點(diǎn)擊后會(huì)彈出如下對(duì)話框办桨,選中Interactive
選項(xiàng),在Branch
選項(xiàng)中選中要變基的分支站辉,在Onto
選項(xiàng)中呢撞,填寫(xiě)新基點(diǎn)(會(huì)從這個(gè)提交開(kāi)始修改歷史)。點(diǎn)擊Rebase之后饰剥,會(huì)彈出如下對(duì)話框殊霞, 在這里你可以選擇要對(duì)每個(gè)提交執(zhí)行的命令。選完命令之后汰蓉,按下Start Rebasing就會(huì)開(kāi)始變基绷蹲。
02 push失敗顾孽?
·我們把前面開(kāi)發(fā)車門那個(gè)同學(xué)叫做小明祝钢,在小明開(kāi)發(fā)車門,引擎和輪子的同時(shí)岩齿,還有一個(gè)叫小剛的同學(xué)在開(kāi)發(fā)制動(dòng)系統(tǒng)太颤。小剛現(xiàn)在剛把制動(dòng)系統(tǒng)開(kāi)發(fā)完畢,他現(xiàn)在的提交歷史如下盹沈。
現(xiàn)在他要把master
分支推送到中央倉(cāng)庫(kù)上龄章。小剛運(yùn)行了如下指令。
$ git push origin master
但是Git告訴他乞封,push
被拒絕了做裙,因?yàn)橹醒雮}(cāng)庫(kù)的master
分支里有本地還沒(méi)有的改動(dòng),并讓我們把改動(dòng)集成到本地倉(cāng)庫(kù)中再進(jìn)行push
肃晚。
所以在每次push
前锚贱,應(yīng)該先抓取中央倉(cāng)庫(kù)上的更新,然后集成到本地关串,再提交拧廊。抓取更新的指令是git fetch <remote_name> <branch_name>
。現(xiàn)在小明用以下指令拉取了中央倉(cāng)庫(kù)上的更新晋修。
$ git fetch origin master
拉取后吧碾,本地的提交歷史變成了這樣。其中墓卦,origin/master
表示的就是遠(yuǎn)程倉(cāng)庫(kù)上的master
分支倦春,遠(yuǎn)程分支是只讀的,不能修改。
現(xiàn)在小明要把遠(yuǎn)程倉(cāng)庫(kù)的master
分支上的改動(dòng)集成到本地的master
上睁本。根據(jù)之前學(xué)的知識(shí)尿庐,有兩種方法,rebase
和merge
呢堰。
如果使用merge
抄瑟,把本地origin/master
分支的內(nèi)容合并到master
上,合并后提交歷史如下暮胧。很亂锐借,而且因?yàn)楹喜⒍律闪艘粋€(gè)節(jié)點(diǎn)问麸。
如果用rebase
,把本地master
分支的內(nèi)容變基到origin/master
上,合并后提交歷史如下叶圃。清楚了很多梢什,而且看起來(lái)像是串行開(kāi)發(fā)的一樣。一般把遠(yuǎn)端更新整合到本地會(huì)用rebase
指令哮笆。
注意:
rebase
操作會(huì)把可以快進(jìn)的合并記錄刪掉来颤,最后rebase
結(jié)果就像是合并時(shí)使用了快進(jìn)一樣。如果要保留合并記錄稠肘,可以加入--preserve-merges
參數(shù)福铅。
上面提到過(guò)git pull <remote_name> <branch_name>
操作,它其實(shí)是git fetch <remote_name> <branch_name>
和git merge
的結(jié)合项阴。當(dāng)然也可以轉(zhuǎn)化為rebase
版本滑黔,在中間加個(gè)參數(shù),git pull --rebase <remote_name> <branch_name>
环揽。
其實(shí)上面的操作有更好的處理方法略荡。在功能開(kāi)發(fā)完成后,可以先把遠(yuǎn)端的更新用git pull --rebase
拉取下來(lái)歉胶,并把本地分支的更新變基到上面去汛兜。之后,把功能分支rebase
到主分支上通今,這個(gè)時(shí)候遠(yuǎn)端的更新就全部整合進(jìn)功能分支了粥谬,這個(gè)時(shí)候我們可以測(cè)試一下合并之后代碼還正不正確。最后再把功能分支合并到主分支并推送上去辫塌。
Android Studio中的相應(yīng)操作
在Android Studio中漏策,可以選擇用merge
版本還是rebase
版本的pull
操作。按下下圖中的按鈕璃氢。在彈出的對(duì)話框中哟玷,選擇拉取方式。右邊的選項(xiàng)是存儲(chǔ)當(dāng)前工作區(qū)修改的方式。選擇完成后巢寡,點(diǎn)擊ok即可開(kāi)始拉取喉脖。