四中常用的Git工作流比較

多種多樣的工作流使得在項(xiàng)目中實(shí)施Git時(shí)變得難以選擇。這份教程提供了一個(gè)出發(fā)點(diǎn),調(diào)查企業(yè)團(tuán)隊(duì)最常見的Git工作流。

閱讀的時(shí)候,請(qǐng)記住工作流應(yīng)該是一種規(guī)范而不是金科玉律。我們希望向你展示所有工作流,讓你融會(huì)貫通,因地制宜。

這份教程討論了下面四種工作流:
1.中心化的工作流
2.基于功能分支的工作流
3.Gitflow工作流
4.Fork工作流

中心化的工作流

過渡到分布式分版本控制系統(tǒng)看起來是個(gè)令人恐懼的任務(wù),但你不必為了利用Git的優(yōu)點(diǎn)而改變你現(xiàn)有的工作流。你的團(tuán)隊(duì)仍然可以用以前SVN的方式開發(fā)項(xiàng)目。
然而,使用Git來驅(qū)動(dòng)你的開發(fā)工作流顯示出了一些SVN沒有的優(yōu)點(diǎn)。首先,它讓每個(gè)開發(fā)者都有了自己 本地 的完整項(xiàng)目副本。隔離的環(huán)境使得每個(gè)開發(fā)者的工作獨(dú)立于項(xiàng)目的其它修改——他們可以在自己的本地倉庫中添加提交,完全無視上游的開發(fā),直到需要的時(shí)候。
第二,它讓你接觸到了Git魯棒的分支和合并模型。和SVN不同,Git分支被設(shè)計(jì)為一種故障安全的機(jī)制,用來在倉庫之間整合代碼和共享更改。

如何工作

和Subversion一樣,中心化的工作流將中央倉庫作為項(xiàng)目中所有修改的唯一入口。和trunk
不同,默認(rèn)的開發(fā)分支叫做master,所有更改都被提交到這個(gè)分支。這種工作流不需要master之外的其它分支。
開發(fā)者將中央倉庫克隆到本地后開始工作。在他們的本地項(xiàng)目副本中,他們可以像SVN一樣修改文件和提交更改;不過,這些新的提交被保存在 本地 ——它們和中央倉庫完全隔離。這使得開發(fā)者可以將和上游的同步推遲到他們方便的時(shí)候。
為了向官方項(xiàng)目發(fā)布修改,開發(fā)者將他們的本地master分支“推送”到中央倉庫。這一步等同于svn commit,除了Git添加的是所有不在中央master分支上的本地提交。

管理沖突

中央倉庫代表官方項(xiàng)目,因此它的提交歷史應(yīng)該被視作神圣不可更改的。如果開發(fā)者的本地提交和中央倉庫分叉了,Git會(huì)拒絕將他們的修改推送上去,因?yàn)檫@會(huì)覆蓋官方提交。
在開發(fā)者發(fā)布他們的功能之前,他們需要fetch更新的中央提交,在它們之上rebase自己的更改。這就像是:“我想要在其他人的工作進(jìn)展之上添加我的修改。”它會(huì)產(chǎn)生完美的線性歷史,就像和傳統(tǒng)的SVN工作流一樣。
如果本地修改和上游提交沖突時(shí),Git會(huì)暫停rebase流程,給你機(jī)會(huì)手動(dòng)解決這些沖突。Git很贊的一點(diǎn)是,它將git status
git add
命令同時(shí)用來生成提交和解決合并沖突。這使得開發(fā)者能夠輕而易舉地管理他們的合并。另外,如果他們改錯(cuò)了什么,Git讓他們輕易地退出rebase過程,然后重試(或者找人幫忙)。

栗子

讓我們一步步觀察一個(gè)普通的小團(tuán)隊(duì)是如何使用這種工作流協(xié)作的。我們有兩位開發(fā)者,John和Mary,分別在開發(fā)兩個(gè)功能,他們通過中心化的倉庫共享代碼。

一人初始化了中央倉庫

首先,需要有人在服務(wù)器上創(chuàng)建中央倉庫。如果這是一個(gè)新項(xiàng)目,你可以初始化一個(gè)空的倉庫。不然,你需要導(dǎo)入一個(gè)已經(jīng)存在的Git或SVN項(xiàng)目。
中央倉庫應(yīng)該永遠(yuǎn)是裸倉庫(沒有工作目錄),可以這樣創(chuàng)建:
ssh user@host git init --bare /path/to/repo.git
但確保你使用的SSH用戶名user、服務(wù)器host的域名或IP地址、儲(chǔ)存?zhèn)}庫的地址/path/to/repo.git是有效的。注意.git約定俗成地出現(xiàn)在倉庫名的后面,表明這是一個(gè)裸倉庫。

所有人將倉庫克隆到本地

接下來,每個(gè)開發(fā)者在本地創(chuàng)建一份完整項(xiàng)目的副本。使用git clone
命令:
git clone ssh://user@host/path/to/repo.git

John在開發(fā)他的功能

在他的本地倉庫中,John可以用標(biāo)準(zhǔn)的Git提交流程開發(fā)功能:編輯、緩存、提交。如果你對(duì)緩存區(qū)還不熟悉,你也可以不用記錄工作目錄中每次的變化。于是你創(chuàng)建了一個(gè)高度集中的提交,即使你已經(jīng)在本地做了很多修改。
git status # 查看倉庫狀態(tài)
git add <some-file> # 緩存一個(gè)文件
git commit # 提交一個(gè)文件</some-file>
記住,這些命令創(chuàng)建的是本地提交,John可以周而復(fù)始地重復(fù)這個(gè)過程,而不用考慮中央倉庫。對(duì)于龐大的功能,需要切成更簡單、原子化的片段時(shí),這個(gè)特性就很有用。

Mary在開發(fā)她的功能

同時(shí),Mary在她自己的本地倉庫用相同的編輯/緩存/提交流程開發(fā)她的功能。和John一樣,她不需要關(guān)心中央倉庫的進(jìn)展,她也 完全 不關(guān)心John在他自己倉庫中做的事,因?yàn)樗斜镜貍}庫都是私有的。

John發(fā)布了他的功能

一旦John完成了他的功能,他應(yīng)該將本地提交發(fā)布到中央倉庫,這樣其他項(xiàng)目成員就可以訪問了。他可以使用git push命令,就像:
git push origin master
記住,origin是John克隆中央倉庫時(shí)指向它的遠(yuǎn)程連接。master參數(shù)告訴Git試著將origin的master分支變得和他本地的master分支一樣。中央倉庫在John克隆之后還沒有進(jìn)展,因此這個(gè)推送如他所愿,沒有產(chǎn)生沖突。

Mary試圖發(fā)布她的功能

John已經(jīng)成功地將他的更改發(fā)布到了中央倉庫上,看看當(dāng)Mary試著將她的功能推送到上面時(shí)會(huì)發(fā)生什么。她可以使用同一個(gè)推送命令:
John已經(jīng)成功地將他的更改發(fā)布到了中央倉庫上,看看當(dāng)Mary試著將她的功能推送到上面時(shí)會(huì)發(fā)生什么。她可以使用同一個(gè)推送命令:
git push origin master
但是,她的本地歷史和中央倉庫已經(jīng)分叉了,Git會(huì)拒絕這個(gè)請(qǐng)求,并顯示一段冗長的錯(cuò)誤信息:
error: failed to push some refs to '/path/to/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Git防止Mary覆蓋官方的修改。她需要將John的更新拉取到她的倉庫,和她的本地修改整合后,然后重試。

Mary在John的提交之上rebase

Mary可以使用git pull
來將上游修改并入她的倉庫。這個(gè)命令和svn update
很像——它拉取整個(gè)上游提交歷史到Mary的本地倉庫,并和她的本地提交一起整合:
git pull --rebase origin master
--rebase選項(xiàng)告訴Git,在同步了中央倉庫的修改之后,將Mary所有的提交移到master分支的頂端,如下圖所示(圖略)。
如果你忽略這個(gè)選項(xiàng)拉取同樣會(huì)成功,只不過你每次和中央倉庫同步時(shí)都會(huì)多出一個(gè)“合并提交”。在這種工作流中,rebase和生成一個(gè)合并提交相比,總是一個(gè)更好的選擇。

Mary解決了合并沖突

Rebase的工作是將每個(gè)本地提交一個(gè)個(gè)轉(zhuǎn)移到更新后的master分支。也就是說,你可以一個(gè)個(gè)提交分別解決合并沖突,而不是在一個(gè)龐大的合并提交中解決。它會(huì)讓你的每個(gè)提交保持專注,并獲得一個(gè)干凈的項(xiàng)目歷史。另一方面,你更容易發(fā)現(xiàn)bug是在哪引入的,如果有必要的話,用最小的代價(jià)回滾這些修改。
如果Mary和John開發(fā)的功能沒有關(guān)聯(lián),rebase的過程不太可能出現(xiàn)沖突。但如果出現(xiàn)沖突時(shí),Git在當(dāng)前提交會(huì)暫停rebase,輸出下面的信息,和一些相關(guān)的指令:
CONFLICT (content): Merge conflict in <some-file>
Git的優(yōu)點(diǎn)在于 每個(gè)人 都能解決他們自己的合并沖突。在這個(gè)例子中,Mary只需運(yùn)行一下git status
就可以發(fā)現(xiàn)問題是什么。沖突的文件會(huì)出現(xiàn)在未合并路徑中:
Unmerged paths:
(use "git reset HEAD <some-file>..." to unstage)
(use "git add/rm <some-file>..." as appropriate to mark resolution)
both modified: <some-file>
接下來,修改這些文件。如果她對(duì)結(jié)果滿意了,和往常一樣緩存這些文件,然后讓git rebase
完成接下來的工作:
git add <some-file>
git rebase --continue
就是這樣。Git會(huì)繼續(xù)檢查下個(gè)提交,對(duì)沖突的提交重復(fù)這個(gè)流程。
如果你這時(shí)候發(fā)現(xiàn)不知道自己做了什么,不要驚慌。只要運(yùn)行下面的命令,你就會(huì)回到開始之前的狀態(tài):
git rebase --abort

Mary成功發(fā)布了她的分支

在她和中央倉庫同步之后,Mary可以成功地發(fā)布她的修改:
git push origin master

接下來該怎么做

正如你所見,使用一丟丟Git命令來復(fù)制一套傳統(tǒng)的Subversion開發(fā)環(huán)境也是可行的。這對(duì)于從SVN轉(zhuǎn)變而來的團(tuán)隊(duì)來說很棒,但這樣沒有利用到Git分布式的本質(zhì)。
如果你的團(tuán)隊(duì)已經(jīng)習(xí)慣了中心化的工作流,但希望提高協(xié)作效率,那么探索Feature分支工作流的好處是完全值當(dāng)?shù)摹C總€(gè)功能在專門的獨(dú)立分支上進(jìn)行,在代碼并入官方項(xiàng)目之前就可以啟動(dòng)圍繞新修改的深度討論。

Feature分支的工作流

一旦你掌握了中心化工作流的使用姿勢,在你的開發(fā)流程中添加功能分支是一個(gè)簡單的方式,來促進(jìn)協(xié)作和開發(fā)者之間的交流。這種封裝使得多個(gè)開發(fā)者專注自己的功能而不會(huì)打擾主代碼庫。它還保證master分支永遠(yuǎn)不會(huì)包含損壞的代碼,給持續(xù)集成環(huán)境帶來了是很大的好處。

封裝功能的開發(fā)使得pull request的使用成為可能,用來啟動(dòng)圍繞一個(gè)分支的討論。它給了其他開發(fā)者在功能并入主項(xiàng)目之前參與決策的機(jī)會(huì)?;蛘?,如果你開發(fā)功能時(shí)卡在一半,你可以發(fā)起一個(gè)pull request,向同事尋求建議。重點(diǎn)是,pull request使得你的團(tuán)隊(duì)在評(píng)論其他人的工作時(shí)變得非常簡單。

如何工作

Feature分支工作流同樣使用中央倉庫,master同樣代表官方的項(xiàng)目歷史。但是,與其直接提交在本地的master分支,開發(fā)者每次進(jìn)行新的工作時(shí)創(chuàng)建一個(gè)新的分支。Feature分支應(yīng)該包含描述性的名稱,比如animated-menu-items(菜單項(xiàng)動(dòng)畫)或issue-#1061。每個(gè)分支都應(yīng)該有一個(gè)清晰、高度集中的目的。

Git在技術(shù)上無法區(qū)別master和功能分支,所以開發(fā)者可以在feature分支上編輯、緩存、提交,就和中心化工作流中一樣。

此外,feature分支可以(也應(yīng)該)被推送到中央倉庫。這使得你和其他開發(fā)者共享這個(gè)功能,而又不改變官方代碼。既然master只是一個(gè)“特殊”的分支,在中央倉庫中儲(chǔ)存多個(gè)feature分支不會(huì)引出什么問題。當(dāng)然了,這也是備份每個(gè)開發(fā)者本地提交的好辦法。

Pull Request

除了隔離功能開發(fā)之外,分支使得通過pull request討論修改成為可能。一旦有人完成了一個(gè)功能,他們不會(huì)立即將它并入master。他們將feature分支推送到中央服務(wù)器上,發(fā)布一個(gè)pull request,請(qǐng)求將他們的修改并入master。這給了其他開發(fā)者在修改并入主代碼庫之前審查的機(jī)會(huì)。

代碼審查是pull request的主要好處,但他們事實(shí)上被設(shè)計(jì)為成為討論代碼的一般場所。你可以把pull request看作是專注某個(gè)分支的討論版。也就是說他們可以用于開發(fā)流程之前。比如,一個(gè)開發(fā)者在某個(gè)功能上需要幫助,他只需發(fā)起一個(gè)pull request。感興趣的小伙伴會(huì)自動(dòng)收到通知,看到相關(guān)提交中的問題。

一旦pull request被接受了,發(fā)布功能的行為和中心化的工作流是一樣的。首先,確定你本地的master和上游的master已經(jīng)同步。然后,將feature分支并入master,將更新的master推送回中央倉庫。

栗子

下面這個(gè)演示了代碼審查使用到的pull request,但記住pull request有多種用途。

Mary開始了一個(gè)新功能

在她開始開發(fā)一個(gè)功能之前,Mary需要一個(gè)獨(dú)立的分支。她可以用下面的命令創(chuàng)建新分支:
git checkout -b marys-feature master

一個(gè)基于master、名為marys-feature的分支將會(huì)被checkout,-b標(biāo)記告訴Git在分支不存在時(shí)創(chuàng)建它。在這個(gè)分支上,Mary和往常一樣編輯、緩存、提交更改,用足夠多的提交來構(gòu)建這個(gè)功能:
git statusgit add <some-file>git commit

Mary去吃飯了

Mary在早上給她的功能添加了一些提交。在她去吃午飯前,將她的分支推送到中央倉庫是個(gè)不錯(cuò)的想法。這是一種方便的備份,但如果Mary和其他開發(fā)者一起協(xié)作,他們也可以看到她的初始提交了。
git push -u origin marys-feature

這個(gè)命令將marys-feature推送到中央倉庫(origin),-u標(biāo)記將它添加為遠(yuǎn)程跟蹤的分支。在設(shè)置完跟蹤的分支之后,Mary調(diào)用不帶任何參數(shù)的git push來推送她的功能。

Mary完成了她的工作

當(dāng)Mary吃完午飯回來,她完成了她的功能。在并入master之前,她需要發(fā)布一個(gè)pull request,讓其他的團(tuán)隊(duì)成員知道她所做的工作。但首先,她應(yīng)該保證中央倉庫包含了她最新的提交:
git push

然后,她在她的Git界面上發(fā)起了一個(gè)pull request,請(qǐng)求將marys-feature合并進(jìn)master,團(tuán)隊(duì)成員會(huì)收到自動(dòng)的通知。Pull request的好處是,評(píng)論顯示在相關(guān)的提交正下方,方便討論特定的修改。

Bill收到了pull request

Bill收到了pull request,并且查看了marys-feature。他決定在并入官方項(xiàng)目之前做一些小修改,通過pull request和Mary進(jìn)行了溝通。

Mary作了修改

為了做這些更改,Mary重復(fù)了之前創(chuàng)建功能時(shí)相同的流程,她編輯、緩存、提交、將更新推送到中央倉庫。她所有的活動(dòng)顯示在pull request中,Bill可以一直評(píng)論。

如果Bill想要的話,也可以將marys-featurepull到他自己的本地倉庫,繼續(xù)工作。后續(xù)的任何提交都會(huì)顯示在pull request上。

Mary發(fā)布了她的功能

一旦Bill準(zhǔn)備接受這個(gè)pull request,某個(gè)人(Bill或者M(jìn)ary都可)需要將功能并入穩(wěn)定的項(xiàng)目:
git checkout mastergit pullgit pull origin marys-featuregit push

首先,不管是誰在執(zhí)行合并,都要保證他們的master分支是最新的。然后,運(yùn)行g(shù)it pull origin marys-feature合并中央倉庫的marys-feature副本。你也可以使用簡單的git merge marys-feature,但之前的命令保證你拉取下來的一定是功能分支最新的版本。最后,更新的master需要被推送回origin。

這個(gè)過程導(dǎo)致了一個(gè)合并提交。一些開發(fā)者喜歡它,因?yàn)樗枪δ芎推溆啻a合并的標(biāo)志。但,如果你希望得到線性的歷史,你可以在執(zhí)行merge之前將功能rebase到master分支的頂端,產(chǎn)生一個(gè)快速向前的合并。

一些界面會(huì)自動(dòng)化接受pull request的流程,只需點(diǎn)擊一下“Merge Pull Request”。如果你的沒有的話,它至少在合并之后應(yīng)該可以自動(dòng)地關(guān)閉pull request。
同時(shí),John以同樣的方式工作著

Mary和Bill一起開發(fā)marys-feature,在pull request上討論的同時(shí),John還在開發(fā)他自己的feature分支。通過將功能用不同分支隔離開來,每個(gè)人可以獨(dú)立地工作,但很容易和其他開發(fā)者共享修改。

接下來該怎么做

為了徹底了解Github上的功能分支,你應(yīng)該查看使用分支一章。現(xiàn)在,你應(yīng)該已經(jīng)看到了功能分支極大地增強(qiáng)了中心化工作流中單一master分支的作用。除此之外,功能分支還便利了pull request的使用,在版本控制界面上直接討論特定的提交。Gitflow工作流是管理功能開發(fā)、發(fā)布準(zhǔn)備、維護(hù)的常見模式。

Gitflow工作流

下面的Gitflow工作流一節(jié)源于nvie網(wǎng)站上的作者Vincent Driessen。

Gitflow工作流圍繞項(xiàng)目發(fā)布定義了一個(gè)嚴(yán)格的分支模型。有些地方比功能分支工作流更復(fù)雜,為管理大型項(xiàng)目提供了魯棒的框架。

和功能分支工作流相比,這種工作流沒有增加任何新的概念或命令。它給不同的分支指定了特定的角色,定義它們應(yīng)該如何、什么時(shí)候交流。除了功能分支之外,它還為準(zhǔn)備發(fā)布、維護(hù)發(fā)布、記錄發(fā)布分別使用了單獨(dú)的分支。當(dāng)然,你還能享受到功能分支工作流帶來的所有好處:pull request、隔離實(shí)驗(yàn)和更高效的協(xié)作。

如何工作

Gitflow工作流仍然使用中央倉庫作為開發(fā)者溝通的中心。和其他工作流一樣,開發(fā)者在本地工作,將分支推送到中央倉庫。唯一的區(qū)別在于項(xiàng)目的分支結(jié)構(gòu)。

歷史分支

和單獨(dú)的master分支不同,這種工作流使用兩個(gè)分支來記錄項(xiàng)目歷史。master分支儲(chǔ)存官方發(fā)布?xì)v史,develop分支用來整合功能分支。同時(shí),這還方便了在master分支上給所有提交打上版本號(hào)標(biāo)簽。

工作流剩下的部分圍繞這兩個(gè)分支的差別展開。

功能分支

每個(gè)新功能都放置在自己的分支中,可以在備份/協(xié)作時(shí)推送到中央倉庫。但是,與其合并到master,功能分支將開發(fā)分支作為父分支。當(dāng)一個(gè)功能完成時(shí),它將被合并回develop。功能永遠(yuǎn)不應(yīng)該直接在master上交互。

注意,功能分支加上develop分支就是我們之前所說的功能分支工作流。但是,Gitflow工作流不止于此。

發(fā)布分支

一旦develop分支的新功能足夠發(fā)布(或者預(yù)先確定的發(fā)布日期即將到來),你可以從develop分支fork一個(gè)發(fā)布分支。這個(gè)分支的創(chuàng)建開始了下個(gè)發(fā)布周期,只有和發(fā)布相關(guān)的任務(wù)應(yīng)該在這個(gè)分支進(jìn)行,如修復(fù)bug、生成文檔等。一旦準(zhǔn)備好了發(fā)布,發(fā)布分支將合并進(jìn)master,打上版本號(hào)的標(biāo)簽。另外,它也應(yīng)該合并回develop,后者可能在發(fā)布啟動(dòng)之后有了新的進(jìn)展。

使用一個(gè)專門的分支來準(zhǔn)備發(fā)布確保一個(gè)團(tuán)隊(duì)完善當(dāng)前的發(fā)布,其他團(tuán)隊(duì)可以繼續(xù)開發(fā)下一個(gè)發(fā)布的功能。它還建立了清晰的開發(fā)階段(比如說,“這周我們準(zhǔn)備4.0版本的發(fā)布”,而我們?cè)趥}庫的結(jié)構(gòu)中也能看到這個(gè)階段)。

通常我們約定:
從develop創(chuàng)建分支

合并進(jìn)master分支

命名規(guī)范release-* or release/*

維護(hù)分支

維護(hù)或者“緊急修復(fù)”分支用來快速給產(chǎn)品的發(fā)布打上補(bǔ)丁。這是唯一可以從master上fork的分支。一旦修復(fù)完成了,它應(yīng)該被并入master和develop分支(或者當(dāng)前的發(fā)布分支),master應(yīng)該打上更新的版本號(hào)的標(biāo)簽。

有一個(gè)專門的bug修復(fù)開發(fā)線使得你的團(tuán)隊(duì)能夠處理issues,而不打斷其他工作流或是要等到下一個(gè)發(fā)布周期。你可以將維護(hù)分支看作在master分支上工作的臨時(shí)發(fā)布分支。

栗子

下面的栗子演示了這種工作流如何用來管理發(fā)布周期。假設(shè)你已經(jīng)創(chuàng)建了中央倉庫。

創(chuàng)建一個(gè)開發(fā)分支

你要做的第一步是為默認(rèn)的master分支創(chuàng)建一個(gè)互補(bǔ)的develop分支。最簡單的辦法是在本地創(chuàng)建一個(gè)空的develop分支,將它推送到服務(wù)器上:
git branch developgit push -u origin develop

這個(gè)分支將會(huì)包含項(xiàng)目中所有的歷史,而master將包含不完全的版本。其他開發(fā)者應(yīng)該將中央倉庫克隆到本地,創(chuàng)建一個(gè)分支來追蹤develop分支:
git clone ssh://user@host/path/to/repo.gitgit checkout -b develop origin/develop

現(xiàn)在所有人都有了一份歷史分支的本地副本。

Mary和John開始了新功能

我們的栗子從John和Mary在不同分支上工作開始。他們都要為自己的功能創(chuàng)建單獨(dú)的分支。他們的功能分支都應(yīng)該基于develop,而不是master:
git checkout -b some-feature develop

他們都使用“編輯、緩存、提交”的一般約定來向功能分支添加提交:
git statusgit add <some-file>git commit

Mary完成了她的功能

在添加了一些提交之后,Mary確信她的功能以及準(zhǔn)備好了。如果她的團(tuán)隊(duì)使用pull request,現(xiàn)在正是發(fā)起pull request的好時(shí)候,請(qǐng)求將她的功能并入develop分支。否則,她可以向下面一樣,將它并入本地的develop分支,推送到中央倉庫:
git pull origin developgit checkout developgit merge some-featuregit pushgit branch -d some-feature

第一個(gè)命令在嘗試并入功能分支之前確保develop分支已是最新。注意,功能絕不該被直接并入master。沖突的處理方式和中心化工作流相同。

Mary開始準(zhǔn)備發(fā)布

當(dāng)John仍然在他的功能分支上工作時(shí),Mary開始準(zhǔn)備項(xiàng)目的第一個(gè)官方發(fā)布。和開發(fā)功能一樣,她新建了一個(gè)分支來封裝發(fā)布的準(zhǔn)備工作。這也正是發(fā)布的版本號(hào)創(chuàng)建的一步:
git checkout -b release-0.1 develop

這個(gè)分支用來整理提交,充分測試,更新文檔,為即將到來的發(fā)布做各種準(zhǔn)備。它就像是一個(gè)專門用來完善發(fā)布的功能分支。

一旦Mary創(chuàng)建了這個(gè)分支,推送到中央倉庫,這次發(fā)布的功能便被鎖定了。不在develop分支中的功能將被推遲到下個(gè)發(fā)布周期。

Mary完成了她的發(fā)布

一旦發(fā)布準(zhǔn)備穩(wěn)妥,Mary將它并入master和develop,然后刪除發(fā)布分支。合并回develop很重要,因?yàn)榭赡芤呀?jīng)有關(guān)鍵的更新添加到了發(fā)布分支上,而開發(fā)新功能需要用到它們。同樣的,如果Mary的團(tuán)隊(duì)重視代碼審查,現(xiàn)在將是發(fā)起pull request的完美時(shí)機(jī)。
git checkout mastergit merge release-0.1git pushgit checkout developgit merge release-0.1git pushgit branch -d release-0.1

發(fā)布分支是功能開發(fā)(develop)和公開發(fā)布(master)之間的過渡階段。不論什么時(shí)候?qū)⑻峤徊⑷雖aster時(shí),你應(yīng)該為提交打上方便引用的標(biāo)簽:
git tag -a 0.1 -m "Initial public release" mastergit push --tags

Git提供了許多鉤子,即倉庫中特定事件發(fā)生時(shí)被執(zhí)行的腳本。當(dāng)你向中央倉庫推送master分支或者標(biāo)簽時(shí),你可以配置一個(gè)鉤子來自動(dòng)化構(gòu)建公開發(fā)布。

終端用戶發(fā)現(xiàn)了一個(gè)bug

正式發(fā)布之后,Mary回過頭來和John一起為下一個(gè)發(fā)布開發(fā)功能。這時(shí),一個(gè)終端用戶開了一個(gè)issue抱怨說當(dāng)前發(fā)布中存在一個(gè)bug。為了解決這個(gè)bug,Mary(或John)從master創(chuàng)建了一個(gè)維護(hù)分支,用幾個(gè)提交修復(fù)這個(gè)issue,然后直接合并回master。
git checkout -b issue-#001 master# Fix the buggit checkout mastergit merge issue-#001git push

和發(fā)布分支一樣,維護(hù)分支包含了develop中需要的重要更新,因此Mary同樣需要執(zhí)行這個(gè)合并。接下來,她可以刪除這個(gè)分支了:
git checkout developgit merge issue-#001git pushgit branch -d issue-#001

接下來該怎么做

現(xiàn)在,希望你已經(jīng)很熟悉中心化的工作流、功能分支工作流和Gitflow工作流。你應(yīng)該已經(jīng)可以抓住本地倉庫、推送/拉取模式,和Git魯棒的分支和合并模型的無限潛力。

請(qǐng)記住,教程中呈現(xiàn)的工作流只是可行的實(shí)踐——而非工作中使用Git的金科玉律。因此,盡情地取其精華,去其糟粕吧。不變的是要讓Git為你所用,而不是相反。

Fork工作流

Fork工作流和教程中討論的其它工作流截然不同。與其使用唯一的服務(wù)端倉庫作為”中央“代碼庫,它給予 每個(gè) 開發(fā)者一個(gè)服務(wù)端倉庫。也就是說每個(gè)貢獻(xiàn)者都有兩個(gè)Git倉庫,而不是一個(gè):一個(gè)私有的本地倉庫和一個(gè)公開的服務(wù)端倉庫。

Fork工作流的主要優(yōu)點(diǎn)在于貢獻(xiàn)可以輕易地整合進(jìn)項(xiàng)目,而不需要每個(gè)人都推送到單一的中央倉庫。開發(fā)者推送到他們 自己的 服務(wù)端倉庫,只有項(xiàng)目管理者可以推送到官方倉庫。這使得管理者可以接受任何開發(fā)者的提交,卻不需要給他們中央倉庫的權(quán)限。

結(jié)論是,這種分布式的工作流為大型、組織性強(qiáng)的團(tuán)隊(duì)(包括不可信的第三方)提供了安全的協(xié)作方式。它同時(shí)也是開源項(xiàng)目理想的工作流。

如何工作

和其它Git工作流一樣,F(xiàn)ork工作流以一個(gè)儲(chǔ)存在服務(wù)端的官方公開項(xiàng)目開場。但新的開發(fā)者想?yún)⑴c項(xiàng)目時(shí),他們不直接克隆官方項(xiàng)目。

取而代之地,他們fork一份官方項(xiàng)目,在服務(wù)端創(chuàng)建一份副本。這份新建的副本作為他們私有的公開倉庫——沒有其他開發(fā)者可以在上面推送,但他們可以從上面拉取修改(在后面我們會(huì)討論為什么這一點(diǎn)很重要)。在他們創(chuàng)建了服務(wù)端副本之后,開發(fā)者執(zhí)行g(shù)it clone操作,在他們的本地機(jī)器上復(fù)制一份。這是他們私有的開發(fā)環(huán)境,正如其他工作流中一樣。

當(dāng)他們準(zhǔn)備好發(fā)布本地提交時(shí),他們將提交推送到自己的公開倉庫——而非官方倉庫。然后,他們向主倉庫發(fā)起一個(gè)pull request,讓項(xiàng)目維護(hù)者知道一個(gè)更新做好了合并的準(zhǔn)備。如果貢獻(xiàn)的代碼有什么問題的話,Pull request可以作為一個(gè)方便的討論版。

我為了將功能并入官方代碼庫,維護(hù)者將貢獻(xiàn)者的修改拉取到他們的本地倉庫,確保修改不會(huì)破壞項(xiàng)目,將它合并到本地的master分支,然后將master分支推送到服務(wù)端的官方倉庫。貢獻(xiàn)現(xiàn)在已是項(xiàng)目的一部分,其他開發(fā)者應(yīng)該從官方倉庫拉取并同步他們的本地倉庫。

中央倉庫

“官方”倉庫這個(gè)概念在Fork工作流中只是一個(gè)約定,理解這一點(diǎn)很重要。從技術(shù)的角度,Git并看不出每個(gè)開發(fā)者和官方的公開倉庫有什么區(qū)別。事實(shí)上,官方倉庫唯一官方的原因是,它是項(xiàng)目維護(hù)者的倉庫。

Fork工作流中的分支

所有這些個(gè)人的公開倉庫只是一個(gè)在開發(fā)者之間共享分支的約定。每個(gè)人仍然可以使用分支來隔離功能,就像在功能分支工作流和Gitflow工作流中一樣。唯一的區(qū)別在于這些分支是如何開始的。在Fork工作流中,它們從另一個(gè)開發(fā)者的本地倉庫拉取而來,而在功能分支和Gitflow分支它們被推送到官方倉庫。

栗子

項(xiàng)目維護(hù)者初始化了中央倉庫

和任何基于Git的項(xiàng)目一樣,第一步是在服務(wù)端創(chuàng)建一個(gè)可以被所有項(xiàng)目成員訪問到的官方倉庫。一般來說,這個(gè)倉庫同時(shí)還是項(xiàng)目維護(hù)者的公開倉庫。

公開的倉庫應(yīng)該永遠(yuǎn)是裸的,不管它們是否代表官方代碼庫。所以項(xiàng)目維護(hù)者應(yīng)該運(yùn)行下面這樣的命令來設(shè)置官方倉庫:
ssh user@hostgit init --bare /path/to/repo.git

Github同時(shí)提供了一個(gè)圖形化界面來替代上面的操作。這和教程中其它工作流設(shè)置中央倉庫的流程完全一致。如果有必要的話,項(xiàng)目維護(hù)者應(yīng)該將已有的代碼庫推送到這個(gè)倉庫中。

開發(fā)者fork倉庫

接下來,所有開發(fā)者需要fork官方倉庫。你可以用SSH到服務(wù)器,運(yùn)行g(shù)it clone將它復(fù)制到服務(wù)器的另一個(gè)地址——fork其實(shí)只是服務(wù)端的clone。但同樣地,Github上開發(fā)者只需點(diǎn)一點(diǎn)按鈕就可以fork倉庫。

在這步之后,每個(gè)開發(fā)者應(yīng)該都有了自己的服務(wù)端倉庫。像官方倉庫一樣,所有這些倉庫都應(yīng)該是裸倉庫。

開發(fā)者將fork的倉庫克隆到本地

接下來開發(fā)者需要克隆他們自己的公開倉庫。他們可以用熟悉的git clone命令來完成這一步。

我們的栗子假設(shè)使用他們使用Github來托管倉庫。記住,在這種情況下,每個(gè)開發(fā)者應(yīng)該有他們自己的Github賬號(hào),應(yīng)該用下面的命令克隆服務(wù)端倉庫:
git clone https://user@github.com/user/repo.git

而教程中的其他工作流使用單一的origin遠(yuǎn)程連接,指向中央倉庫,F(xiàn)ork工作流需要兩個(gè)遠(yuǎn)程連接,一個(gè)是中央倉庫,另一個(gè)是開發(fā)者個(gè)人的服務(wù)端倉庫。你可以給這些遠(yuǎn)端取任何名字,約定的做法是將origin作為你fork后的倉庫的遠(yuǎn)端(運(yùn)行g(shù)it clone是會(huì)自動(dòng)創(chuàng)建)和upstream作為官方項(xiàng)目。
git remote add upstream https://github.com/maintainer/repo

你需要使用上面的命令來創(chuàng)建上游倉庫的遠(yuǎn)程連接。它使得你輕易地保持本地倉庫和官方倉庫的進(jìn)展同步。注意如果你的上游倉庫開啟了認(rèn)證(比如它沒有開源),你需要提供一個(gè)用戶名,就像這樣:
git remote add upstream https://user@bitbucket.org/maintainer/repo.git

它需要用戶從官方代碼庫克隆或拉取之前提供有效的密碼。
開發(fā)者進(jìn)行自己的開發(fā)

在他們剛克隆的本地倉庫中,開發(fā)者可以編輯代碼、提交更改,和其它分支中一樣######創(chuàng)建分支:
git checkout -b some-feature# 編輯代碼git commit -a -m "Add first draft of some feature"

他們所有的更改在推送到公開倉庫之前都是完全私有的。而且,如果官方項(xiàng)目已經(jīng)向前進(jìn)展了,他們可以用git pull獲取新的提交:
git pull upstream master

因?yàn)殚_發(fā)者應(yīng)該在專門的功能分支開發(fā),這一般會(huì)產(chǎn)生一個(gè)快速向前的合并。

開發(fā)者發(fā)布他們的功能

一旦開發(fā)者準(zhǔn)備好共享他們的新功能,他們需要做兩件事情。第一,他們必須將貢獻(xiàn)的代碼推送到自己的公開倉庫,讓其他開發(fā)者能夠訪問到。他們的origin遠(yuǎn)端應(yīng)該已經(jīng)設(shè)置好了,所以他們只需要:
git push origin feature-branch

這和其他工作流不同之處在于,origin遠(yuǎn)端指向開發(fā)者個(gè)人的服務(wù)端倉庫,而不是主代碼庫。

第二,他們需要通知項(xiàng)目維護(hù)者,他們想要將功能并入官方代碼庫。Github提供了一個(gè)“New Pull Request”按鈕,跳轉(zhuǎn)到一個(gè)網(wǎng)頁,讓你指明想要并入主倉庫的分支。一般來說,你希望將功能分支并入上游遠(yuǎn)端的master分支。

項(xiàng)目維護(hù)者整合他們的功能

當(dāng)項(xiàng)目維護(hù)者收到pull request時(shí),他們的工作是決定是否將它并入官方的代碼庫。他們可以使用下面兩種方式之一:
直接檢查pull request中檢查代碼

將代碼拉取到本地倉庫然后手動(dòng)合并

第一個(gè)選項(xiàng)更簡單,讓維護(hù)者查看修改前后的差異,在上面評(píng)論,然后通過圖形界面執(zhí)行合并。然而,如果pull request會(huì)導(dǎo)致合并沖突,第二個(gè)選項(xiàng)就有了必要。在這個(gè)情況中,維護(hù)者需要從開發(fā)者的服務(wù)端倉庫fetch功能分支,合并到他們本地的master分支,然后解決沖突:
git fetch https://bitbucket.org/user/repo feature-branch# 檢查修改git checkout mastergit merge FETCH_HEAD

一旦修改被整合進(jìn)本地的master,維護(hù)者需要將它推送到服務(wù)器上的官方倉庫,這樣其他開發(fā)者也可以訪問它:
git push origin master

記住,維護(hù)者的origin指向他們的公開倉庫,也就是項(xiàng)目的官方代碼庫。開發(fā)者的貢獻(xiàn)現(xiàn)在完全并入了項(xiàng)目。

開發(fā)者和中央倉庫保持同步

因?yàn)橹鞔a庫已經(jīng)取得了新的進(jìn)展,其他開發(fā)者應(yīng)該和官方倉庫同步:
git pull upstream master

接下來該怎么做

如果你從SVN遷移而來,F(xiàn)ork工作流看上去是一個(gè)比較大的轉(zhuǎn)變。但不要害怕——它只是在Feature分支工作流之上引入了一層抽象。貢獻(xiàn)的代碼發(fā)布到開發(fā)者在服務(wù)端自己的倉庫,而不是在唯一的中央倉庫中直接共享分支。

這篇文章解釋了一次代碼貢獻(xiàn)是如何從一個(gè)開發(fā)者流入官方的master分支的,但相同的方法可以用在將代碼貢獻(xiàn)整合進(jìn)任何倉庫。比如,如果你團(tuán)隊(duì)的一部分成員在一個(gè)特定功能上協(xié)作,他們可以用自己約定的行為共享修改——而不改變主倉庫。

這使得Fork工作流對(duì)于松散的團(tuán)隊(duì)來說是個(gè)非常強(qiáng)大的工具。任何開發(fā)者都可以輕而易舉地和其他開發(fā)者共享修改,任何分支都能高效地并入主代碼庫。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 從開始到現(xiàn)在,想你的時(shí)數(shù)不斷增多。我只希望你過得好。在那些我們一起走過的歲月里,已經(jīng)成為我生命中的一部分,可你早已...
    桌孔閱讀 174評(píng)論 0 0
  • 國慶前夕,很多朋友都慢慢的接著放假了,對(duì)于一年里為數(shù)不多的長假,還沒開始前,總是期待著有些什么美好會(huì)發(fā)生,就好像它...
    TSFH閱讀 172評(píng)論 0 0
  • 從大一進(jìn)入校報(bào),就開始過記者節(jié),今年正巧趕上第18個(gè)記者節(jié),也是我過的第8個(gè)記者節(jié)。 起初,我只是喜歡寫文章,或者...
    黎茉閱讀 281評(píng)論 0 1
  • 我本性善良。 我是一名在醫(yī)院實(shí)習(xí)小護(hù)士。 這是我在醫(yī)院實(shí)習(xí)的第十一個(gè)月,論起來也算是老資格實(shí)習(xí)...
    翻滾的蝸小牛閱讀 520評(píng)論 1 2