說明:
個(gè)人在學(xué)習(xí)git工作流的過程中,從原有的 SVN 模式很難完全理解git的協(xié)作模式,直到有一天我看到了下面的文章,好多遺留在心中的困惑迎刃而解:
我們以使用SVN的工作流來使用git有什么不妥?
git 方便的branch在哪里,團(tuán)隊(duì)多人如何協(xié)作?沖突了怎么辦?如何進(jìn)行發(fā)布控制?
經(jīng)典的master-發(fā)布、develop-主開發(fā)、hotfix-不過修復(fù)如何避免代碼不經(jīng)過驗(yàn)證上線?
如何在github上面與他人一起協(xié)作,star-fork-pull request是怎樣的流程?
我個(gè)人很感激這篇文章,所以進(jìn)行了整理,希望能幫到更多的人。整篇文章由 xirong 整理自 oldratlee 的github,方便統(tǒng)一的學(xué)習(xí)回顧,在此感謝下面兩位的貢獻(xiàn)。
原文鏈接:Git Workflows and Tutorials
簡體中文:由 oldratlee 翻譯在 github 上 git-workflows-and-tutorials
一、譯序
二、Git工作流指南
2.5.1 解析Pull Request
2.5.2 工作方式
2.5.3 在功能分支工作流中使用Pull Request
2.5.4 在Gitflow工作流中使用Pull Request
2.5.5 在Forking工作流中使用Pull Request
2.5.6 示例
小紅fork正式項(xiàng)目
小紅克隆她的Bitbucket倉庫
小紅開發(fā)新功能
小紅push功能到她的Bitbucket倉庫中
小紅發(fā)起Pull Request
小明review Pull Request
小紅補(bǔ)加提交
小明接受Pull Request
2.4.1 工作方式
2.4.2 正式倉庫
2.4.3 Forking工作流的分支使用方式
2.4.4 示例
項(xiàng)目維護(hù)者初始化正式倉庫
開發(fā)者fork正式倉庫
開發(fā)者克隆自己fork出來的倉庫
開發(fā)者開發(fā)自己的功能
開發(fā)者發(fā)布自己的功能
項(xiàng)目維護(hù)者集成開發(fā)者的功能
開發(fā)者和正式倉庫做同步
2.3.1 工作方式
2.3.2 歷史分支
2.3.3 功能分支
2.3.4 發(fā)布分支
2.3.5 維護(hù)分支
2.3.6 示例
創(chuàng)建開發(fā)分支
小紅和小明開始開發(fā)新功能
小紅完成功能開發(fā)
小紅開始準(zhǔn)備發(fā)布
小紅完成發(fā)布
最終用戶發(fā)現(xiàn)Bug
2.2.1 工作方式
2.2.2 Pull Requests
2.2.3 示例
小紅開始開發(fā)一個(gè)新功能
小紅要去吃個(gè)午飯
小紅完成功能開發(fā)
小黑收到Pull Request
小紅再做修改
小紅發(fā)布她的功能
與此同時(shí),小明在做和小紅一樣的事
2.1.1 工作方式
2.1.2 沖突解決
2.1.3 示例
有人先初始化好中央倉庫
所有人克隆中央倉庫
小明開發(fā)功能
小紅開發(fā)功能
小明發(fā)布功能
小紅試著發(fā)布功能
小紅在小明的提交之上rebase
小紅解決合并沖突
小紅成功發(fā)布功能
2.1 集中式工作流
2.2 功能分支工作流
2.3 Gitflow工作流
2.4 Forking工作流
2.5 Pull Requests
一、譯序
工作流其實(shí)不是一個(gè)初級(jí)主題,背后的本質(zhì)問題其實(shí)是有效的項(xiàng)目流程管理和高效的開發(fā)協(xié)同約定,不僅是Git或SVN等VCS或SCM工具的使用。
這篇指南以大家在SVN中已經(jīng)廣為熟悉使用的集中式工作流作為起點(diǎn),循序漸進(jìn)地演進(jìn)到其它高效的分布式工作流,還介紹了如何配合使用便利的Pull Request功能,體系地講解了各種工作流的應(yīng)用。
行文中實(shí)踐原則和操作示例并重,對(duì)于Git的資深玩家可以梳理思考提升,而新接觸的同學(xué),也可以跟著step-by-step操作來操練學(xué)習(xí)并在實(shí)際工作中上手使用。
關(guān)于Git工作流主題,網(wǎng)上體系的中文資料不多,主要是零散的操作說明,希望這篇文章能讓你更深入理解并在工作中靈活有效地使用起來。
PS:
文中Pull Request的介紹用的是Bitbucket代碼托管服務(wù),由于和GitHub基本一樣,如果你用的是GitHub(我自己也主要使用GitHub托管代碼),不影響理解和操作。
PPS:
本指南循序漸進(jìn)地講解工作流,如果Git用的不多,可以從前面的講的工作流開始操練。操作過程去感受指南的講解:解決什么問題、如何解決問題,這樣理解就深了,也方便活用。
Gitflow工作流是經(jīng)典模型,體現(xiàn)了工作流的經(jīng)驗(yàn)和精髓。隨著項(xiàng)目過程復(fù)雜化,會(huì)感受到這個(gè)工作流中深思熟慮和威力!
Forking工作流是協(xié)作的(GitHub風(fēng)格)可以先看看Github的Help:Fork A Repo和Using pull requests 。照著操作,給一個(gè)Github項(xiàng)目貢獻(xiàn)你的提交,有操作經(jīng)驗(yàn)再看指南容易意會(huì)。指南中給了自己實(shí)現(xiàn)Fork的方法:Fork就是服務(wù)端的克隆。在指南的操練中使用代碼托管服務(wù)(如GitHub、Bitbucket),可以點(diǎn)一下按鈕就讓開發(fā)者完成倉庫的fork操作。
:see_no_evil: 自己理解粗淺,翻譯中不足和不對(duì)之處,歡迎建議(提交Issue)和指正(Fork后提交代碼)!
二、Git工作流指南
:point_right: 工作流有各式各樣的用法,但也正因此使得在實(shí)際工作中如何上手使用變得很頭大。這篇指南通過總覽公司團(tuán)隊(duì)中最常用的幾種Git工作流讓大家可以上手使用。
在閱讀的過程中請(qǐng)記住,本文中的幾種工作流是作為方案指導(dǎo)而不是條例規(guī)定。在展示了各種工作流可能的用法后,你可以從不同的工作流中挑選或揉合出一個(gè)滿足你自己需求的工作流。
2.1 集中式工作流
如果你的開發(fā)團(tuán)隊(duì)成員已經(jīng)很熟悉Subversion,集中式工作流讓你無需去適應(yīng)一個(gè)全新流程就可以體驗(yàn)Git帶來的收益。這個(gè)工作流也可以作為向更Git風(fēng)格工作流遷移的友好過渡。
轉(zhuǎn)到分布式版本控制系統(tǒng)看起來像個(gè)令人生畏的任務(wù),但不改變已用的工作流你也可以用上Git帶來的收益。團(tuán)隊(duì)可以用和Subversion完全不變的方式來開發(fā)項(xiàng)目。
但使用Git加強(qiáng)開發(fā)的工作流,Git有相比SVN的幾個(gè)優(yōu)勢(shì)。
首先,每個(gè)開發(fā)可以有屬于自己的整個(gè)工程的本地拷貝。隔離的環(huán)境讓各個(gè)開發(fā)者的工作和項(xiàng)目的其他部分修改獨(dú)立開來 ——
即自由地提交到自己的本地倉庫,先完全忽略上游的開發(fā),直到方便的時(shí)候再把修改反饋上去。
其次,Git提供了強(qiáng)壯的分支和合并模型。不像SVN,Git的分支設(shè)計(jì)成可以做為一種用來在倉庫之間集成代碼和分享修改的『失敗安全』的機(jī)制。
2.1.1 工作方式
像Subversion一樣,集中式工作流以中央倉庫作為項(xiàng)目所有修改的單點(diǎn)實(shí)體。相比SVN缺省的開發(fā)分支trunk,Git叫做master,所有修改提交到這個(gè)分支上。本工作流只用到master這一個(gè)分支。
開發(fā)者開始先克隆中央倉庫。在自己的項(xiàng)目拷貝中像SVN一樣的編輯文件和提交修改;但修改是存在本地的,和中央倉庫是完全隔離的。開發(fā)者可以把和上游的同步延后到一個(gè)方便時(shí)間點(diǎn)。
要發(fā)布修改到正式項(xiàng)目中,開發(fā)者要把本地master分支的修改『推』到中央倉庫中。這相當(dāng)于svn commit操作,但push操作會(huì)把所有還不在中央倉庫的本地提交都推上去。
2.1.2 沖突解決
中央倉庫代表了正式項(xiàng)目,所以提交歷史應(yīng)該被尊重且是穩(wěn)定不變的。如果開發(fā)者本地的提交歷史和中央倉庫有分歧,Git會(huì)拒絕push提交否則會(huì)覆蓋已經(jīng)在中央庫的正式提交。
在開發(fā)者提交自己功能修改到中央庫前,需要先fetch在中央庫的新增提交,rebase自己提交到中央庫提交歷史之上。
這樣做的意思是在說,『我要把自己的修改加到別人已經(jīng)完成的修改上。』最終的結(jié)果是一個(gè)完美的線性歷史,就像以前的SVN的工作流中一樣。
如果本地修改和上游提交有沖突,Git會(huì)暫停rebase過程,給你手動(dòng)解決沖突的機(jī)會(huì)。Git解決合并沖突,用和生成提交一樣的git status和git add命令,很一致方便。還有一點(diǎn),如果解決沖突時(shí)遇到麻煩,Git可以很簡單中止整個(gè)rebase操作,重來一次(或者讓別人來幫助解決)。
2.1.3 示例
讓我們一起逐步分解來看看一個(gè)常見的小團(tuán)隊(duì)如何用這個(gè)工作流來協(xié)作的。有兩個(gè)開發(fā)者小明和小紅,看他們是如何開發(fā)自己的功能并提交到中央倉庫上的。
有人先初始化好中央倉庫
第一步,有人在服務(wù)器上創(chuàng)建好中央倉庫。如果是新項(xiàng)目,你可以初始化一個(gè)空倉庫;否則你要導(dǎo)入已有的Git或SVN倉庫。
中央倉庫應(yīng)該是個(gè)裸倉庫(bare repository),即沒有工作目錄(working directory)的倉庫??梢杂孟旅娴拿顒?chuàng)建:
1
2
ssh user@host
git init --bare /path/to/repo.git
確保寫上有效的user(SSH的用戶名),host(服務(wù)器的域名或IP地址),/path/to/repo.git(你想存放倉庫的位置)。
注意,為了表示是一個(gè)裸倉庫,按照約定加上.git擴(kuò)展名到倉庫名上。
所有人克隆中央倉庫
下一步,各個(gè)開發(fā)者創(chuàng)建整個(gè)項(xiàng)目的本地拷貝。通過git clone命令完成:
1
git clone ssh://user@host/path/to/repo.git
基于你后續(xù)會(huì)持續(xù)和克隆的倉庫做交互的假設(shè),克隆倉庫時(shí)Git會(huì)自動(dòng)添加遠(yuǎn)程別名origin指回『父』倉庫。
小明開發(fā)功能
在小明的本地倉庫中,他使用標(biāo)準(zhǔn)的Git過程開發(fā)功能:編輯、暫存(Stage)和提交。
如果你不熟悉暫存區(qū)(Staging Area),這里說明一下:暫存區(qū)的用來準(zhǔn)備一個(gè)提交,但可以不用把工作目錄中所有的修改內(nèi)容都包含進(jìn)來。
這樣你可以創(chuàng)建一個(gè)高度聚焦的提交,盡管你本地修改很多內(nèi)容。
1
2
3
4
git status # 查看本地倉庫的修改狀態(tài)
git add # 暫存文件
git commit # 提交文件
<div>
請(qǐng)記住,因?yàn)檫@些命令生成的是本地提交,小明可以按自己需求反復(fù)操作多次,而不用擔(dān)心中央倉庫上有了什么操作。
對(duì)需要多個(gè)更簡單更原子分塊的大功能,這個(gè)做法是很有用的。
小紅開發(fā)功能
與此同時(shí),小紅在自己的本地倉庫中用相同的編輯、暫存和提交過程開發(fā)功能。和小明一樣,她也不關(guān)心中央倉庫有沒有新提交;
當(dāng)然更不關(guān)心小明在他的本地倉庫中的操作,因?yàn)樗斜镜貍}庫都是私有的。
小明發(fā)布功能
一旦小明完成了他的功能開發(fā),會(huì)發(fā)布他的本地提交到中央倉庫中,這樣其它團(tuán)隊(duì)成員可以看到他的修改。他可以用下面的git push命令:
1
git push origin master
注意,origin是在小明克隆倉庫時(shí)Git創(chuàng)建的遠(yuǎn)程中央倉庫別名。master參數(shù)告訴Git推送的分支。
由于中央倉庫自從小明克隆以來還沒有被更新過,所以push操作不會(huì)有沖突,成功完成。
小紅試著發(fā)布功能
一起來看看在小明發(fā)布修改后,小紅push修改會(huì)怎么樣?她使用完全一樣的push命令:
1
git push origin master
但她的本地歷史已經(jīng)和中央倉庫有分岐了,Git拒絕操作并給出下面很長的出錯(cuò)消息:
1
2
3
4
5
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.
這避免了小紅覆寫正式的提交。她要先pull小明的更新到她的本地倉庫合并上她的本地修改后,再重試。
小紅在小明的提交之上rebase
小紅用git pull合并上游的修改到自己的倉庫中。
這條命令類似svn update——拉取所有上游提交命令到小紅的本地倉庫,并嘗試和她的本地修改合并:
1
git pull --rebase origin master
--rebase選項(xiàng)告訴Git把小紅的提交移到同步了中央倉庫修改后的master分支的頂部,如下圖所示:
如果你忘加了這個(gè)選項(xiàng),pull操作仍然可以完成,但每次pull操作要同步中央倉庫中別人修改時(shí),提交歷史會(huì)以一個(gè)多余的『合并提交』結(jié)尾。
對(duì)于集中式工作流,最好是使用rebase而不是生成一個(gè)合并提交。
小紅解決合并沖突
rebase操作過程是把本地提交一次一個(gè)地遷移到更新了的中央倉庫master分支之上。
這意味著可能要解決在遷移某個(gè)提交時(shí)出現(xiàn)的合并沖突,而不是解決包含了所有提交的大型合并時(shí)所出現(xiàn)的沖突。
這樣的方式讓你盡可能保持每個(gè)提交的聚焦和項(xiàng)目歷史的整潔。反過來,簡化了哪里引入Bug的分析,如果有必要,回滾修改也可以做到對(duì)項(xiàng)目影響最小。
如果小紅和小明的功能是相關(guān)的,不大可能在rebase過程中有沖突。如果有,Git在合并有沖突的提交處暫停rebase過程,輸出下面的信息并帶上相關(guān)的指令:
1
CONFLICT (content): Merge conflict in <some-file>
Git很贊的一點(diǎn)是,任何人可以解決他自己的沖突。在這個(gè)例子中,小紅可以簡單的運(yùn)行g(shù)it status命令來查看哪里有問題。
沖突文件列在Unmerged paths(未合并路徑)一節(jié)中:
1
2
3
4
5
# 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>
接著小紅編輯這些文件。修改完成后,用老套路暫存這些文件,并讓git rebase完成剩下的事:
1
2
git add <some-file>
git rebase --continue
要做的就這些了。Git會(huì)繼續(xù)一個(gè)一個(gè)地合并后面的提交,如其它的提交有沖突就重復(fù)這個(gè)過程。
如果你碰到了沖突,但發(fā)現(xiàn)搞不定,不要驚慌。只要執(zhí)行下面這條命令,就可以回到你執(zhí)行g(shù)it pull --rebase命令前的樣子:
1
git rebase --abort
小紅成功發(fā)布功能
小紅完成和中央倉庫的同步后,就能成功發(fā)布她的修改了:
1
git push origin master
如你所見,僅使用幾個(gè)Git命令我們就可以模擬出傳統(tǒng)Subversion開發(fā)環(huán)境。對(duì)于要從SVN遷移過來的團(tuán)隊(duì)來說這太好了,但沒有發(fā)揮出Git分布式本質(zhì)的優(yōu)勢(shì)。
如果你的團(tuán)隊(duì)適應(yīng)了集中式工作流,但想要更流暢的協(xié)作效果,絕對(duì)值得探索一下功能分支工作流 的收益。
通過為一個(gè)功能分配一個(gè)專門的分支,能夠做到一個(gè)新增功能集成到正式項(xiàng)目之前對(duì)新功能進(jìn)行深入討論。
更詳細(xì)內(nèi)容請(qǐng)點(diǎn)擊原文連接查看。