深入理解學習Git工作流


文/ixirong(簡書作者)
原文鏈接:http://www.jianshu.com/p/91acec85c3a4
著作權歸作者全部。轉載請聯系作者獲得授權,並標注“簡書作者”。

  • 我們以使用SVN的工作流來使用git有什么不妥?
  • git 方便的branch在哪里,團隊多人怎樣協作?沖突了怎么辦?怎樣進行公布控制?
  • 經典的master-公布、develop-主開發、hotfix-只是修復怎樣避免代碼不經過驗證上線?
  • 怎樣在github上面與他人一起協作,star-fork-pull request是怎樣的流程?

我個人非常感激這篇文章,所以進行了整理,希望能幫到很多其它的人。

整篇文章由 xirong 整理自 oldratlee 的github。方便統一的學習回想。在此感謝以下兩位的貢獻。

原文鏈接:Git Workflows and Tutorials
中文簡體:由 oldratlee 翻譯在 github 上 git-workflows-and-tutorials


一、譯序

工作流事實上不是一個0基礎主題,背后的本質問題事實上是有效的項目流程管理和高效的開發協同約定,不僅是GitSVNVCSSCM工具的使用。

這篇指南以大家在SVN中已經廣為熟悉使用的集中式工作流作為起點,循序漸進地演進到其他高效的分布式工作流。還介紹了怎樣配合使用便利的Pull Request功能,體系地解說了各種工作流的應用。

行文中實踐原則和操作演示樣例並重。對於Git的資深玩家能夠梳理思考提升,而新接觸的同學。也能夠跟着step-by-step操作來操練學習並在實際工作中上手使用。

關於Git工作流主題,網上體系的中文資料不多。主要是零散的操作說明。希望這篇文章能讓你更深入理解並在工作中靈活有效地使用起來。

PS

文中Pull Request的介紹用的是Bitbucket代碼托管服務,因為和GitHub基本一樣,假設你用的是GitHub(我自己也主要使用GitHub托管代碼),不影響理解和操作。

PPS

本指南循序漸進地解說工作流。假設Git用的不多,能夠從前面的講的工作流開始操練。操作過程去感受指南的解說:解決什么問題、怎樣解決這個問題,這樣理解就深了,也方便活用。

Gitflow工作流是經典模型,體現了工作流的經驗和精髓。隨着項目過程復雜化。會感受到這個工作流中深思熟慮和威力!

Forking工作流是協作的(GitHub風格)能夠先看看Github的Help:Fork A RepoUsing pull requests 。照着操作,給一個Github項目貢獻你的提交,有操作經驗再看指南easy意會。指南中給了自己實現Fork的方法Fork就是服務端的克隆。在指南的操練中使用代碼托管服務(如GitHubBitbucket),能夠點一下button就讓開發人員完畢倉庫的fork操作。

:see_no_evil: 自己理解粗淺。翻譯中不足和不正確之處,歡迎建議(提交Issue)和指正(Fork后提交代碼)!

二、Git工作流指南

:point_right: 工作流有各式各樣的使用方法。但也正因此使得在實際工作中怎樣上手使用變得非常頭大。這篇指南通過總覽公司團隊中最經常使用的幾種Git工作流讓大家能夠上手使用。

在閱讀的過程中請記住,本文中的幾種工作流是作為方案指導而不是條例規定。在展示了各種工作流可能的使用方法后,你能夠從不同的工作流中挑選或揉合出一個滿足你自己需求的工作流。

Git Workflows
Git Workflows

2.1 集中式工作流

假設你的開發團隊成員已經非常熟悉Subversion,集中式工作流讓你無需去適應一個全新流程就能夠體驗Git帶來的收益。

這個工作流也能夠作為向更Git風格工作流遷移的友好過渡。

Git Workflows: SVN-style
Git Workflows: SVN-style

轉到分布式版本號控制系統看起來像個令人生畏的任務,但不改變已用的工作流你也能夠用上Git帶來的收益。團隊能夠用和Subversion全然不變的方式來開發項目。

但使用Git加強開發的工作流,Git有相比SVN的幾個優勢。


首先,每一個開發能夠有屬於自己的整個project的本地拷貝。隔離的環境讓各個開發人員的工作和項目的其它部分改動獨立開來 ——
即自由地提交到自己的本地倉庫,先全然忽略上游的開發,直到方便的時候再把改動反饋上去。

其次,Git提供了強壯的分支和合並模型。不像SVNGit的分支設計成能夠做為一種用來在倉庫之間集成代碼和分享改動的『失敗安全』的機制。

2.1.1 工作方式

Subversion一樣。集中式工作流以中央倉庫作為項目全部改動的單點實體。

相比SVN缺省的開發分支trunkGit叫做master。全部改動提交到這個分支上。

本工作流僅僅用到master這一個分支。

開發人員開始先克隆中央倉庫。在自己的項目拷貝中像SVN一樣的編輯文件和提交改動;但改動是存在本地的。和中央倉庫是全然隔離的。開發人員能夠把和上游的同步延后到一個方便時間點。

要公布改動到正式項目中。開發人員要把本地master分支的改動『推』到中央倉庫中。這相當於svn commit操作。但push操作會把全部還不在中央倉庫的本地提交都推上去。

git-workflow-svn-push-local
git-workflow-svn-push-local

2.1.2 沖突解決

中央倉庫代表了正式項目,所以提交歷史應該被尊重且是穩定不變的。假設開發人員本地的提交歷史和中央倉庫有分歧,Git會拒絕push提交否則會覆蓋已經在中央庫的正式提交。

git-workflow-svn-managingconflicts
git-workflow-svn-managingconflicts

在開發人員提交自己功能改動到中央庫前,須要先fetch在中央庫的新增提交。rebase自己提交到中央庫提交歷史之上。
這樣做的意思是在說,『我要把自己的改動加到別人已經完畢的改動上。』終於的結果是一個完美的線性歷史,就像曾經的SVN的工作流中一樣。

假設本地改動和上游提交有沖突,Git會暫停rebase過程。給你手動解決沖突的機會。

Git解決合並沖突,用和生成提交一樣的git statusgit add命令,非常一致方便。另一點,假設解決沖突時遇到麻煩,Git能夠非常easy中止整個rebase操作,重來一次(或者讓別人來幫助解決)。

2.1.3 演示樣例

讓我們一起逐步分解來看看一個常見的小團隊怎樣用這個工作流來協作的。有兩個開發人員小明和小紅,看他們是怎樣開發自己的功能並提交到中央倉庫上的。

有人先初始化好中央倉庫


第一步。有人在server上創建好中央倉庫。假設是新項目,你能夠初始化一個空倉庫;否則你要導入已有的GitSVN倉庫。

中央倉庫應該是個裸倉庫(bare repository)。即沒有工作文件夾(working directory)的倉庫。

能夠用以下的命令創建:

ssh user@host
git init --bare /path/to/repo.git

確保寫上有效的userSSH的username),host(server的域名或IP地址),/path/to/repo.git(你想存放倉庫的位置)。
注意,為了表示是一個裸倉庫,依照約定加上.git擴展名到倉庫名上。

全部人克隆中央倉庫


下一步,各個開發人員創建整個項目的本地拷貝。通過git clone命令完畢:

git clone ssh://user@host/path/to/repo.git

基於你興許會持續和克隆的倉庫做交互的如果。克隆倉庫時Git會自己主動加入遠程別名origin指回『父』倉庫。

小明開發功能


在小明的本地倉庫中,他使用標准的Git過程開發功能:編輯、暫存(Stage)和提交。
假設你不熟悉暫存區(Staging Area)。這里說明一下:暫存區的用來准備一個提交,但能夠不用把工作文件夾中全部的改動內容都包括進來。
這樣你能夠創建一個高度聚焦的提交,雖然你本地改動非常多內容。

git status # 查看本地倉庫的改動狀態
git add # 暫存文件
git commit # 提交文件

請記住,由於這些命令生成的是本地提交。小明能夠按自己需求重復操作多次。而不用操心中央倉庫上有了什么操作。
對須要多個更簡單更原子分塊的大功能,這個做法是非常實用的。

小紅開發功能


與此同一時候。小紅在自己的本地倉庫中用同樣的編輯、暫存和提交過程開發功能。

和小明一樣。她也不關心中央倉庫有沒有新提交;
當然更不關心小明在他的本地倉庫中的操作。由於全部本地倉庫都是私有的。

小明公布功能


一旦小明完畢了他的功能開發,會公布他的本地提交到中央倉庫中,這樣其他團隊成員能夠看到他的改動。

他能夠用以下的git push命令

git push origin master

注意,origin是在小明克隆倉庫時Git創建的遠程中央倉庫別名。master參數告訴Git推送的分支。


因為中央倉庫自從小明克隆以來還沒有被更新過。所以push操作不會有沖突。成功完畢。

小紅試着公布功能


一起來看看在小明公布改動后,小紅push改動會怎么樣?她使用全然一樣的push命令:

git push origin master

但她的本地歷史已經和中央倉庫有分岐了。Git拒絕操作並給出以下非常長的出錯消息:

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——拉取全部上游提交命令到小紅的本地倉庫。並嘗試和她的本地改動合並:

git pull --rebase origin master

--rebase選項告訴Git把小紅的提交移到同步了中央倉庫改動后的master分支的頂部,例如以下圖所看到的:


假設你忘加了這個選項。pull操作仍然能夠完畢,但每次pull操作要同步中央倉庫中別人改動時。提交歷史會以一個多余的『合並提交』結尾。


對於集中式工作流。最好是使用rebase而不是生成一個合並提交。

小紅解決合並沖突


rebase操作過程是把本地提交一次一個地遷移到更新了的中央倉庫master分支之上。


這意味着可能要解決在遷移某個提交時出現的合並沖突。而不是解決包括了全部提交的大型合並時所出現的沖突。
這種方式讓你盡可能保持每一個提交的聚焦和項目歷史的整潔。反過來。簡化了哪里引入Bug的分析,假設有必要,回滾改動也能夠做到對項目影響最小。

假設小紅和小明的功能是相關的。不大可能在rebase過程中有沖突。假設有,Git在合並有沖突的提交處暫停rebase過程。輸出以下的信息並帶上相關的指令:

CONFLICT (content): Merge conflict in <some-file>

Git非常贊的一點是,不論什么人能夠解決他自己的沖突。

在這個樣例中。小紅能夠簡單的執行git status命令來查看哪里有問題。
沖突文件列在Unmerged paths(未合並路徑)一節中:

# 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完畢剩下的事:

git add <some-file> 
git rebase --continue

要做的就這些了。

Git會繼續一個一個地合並后面的提交,如其他的提交有沖突就反復這個過程。

假設你碰到了沖突。但發現搞不定。不要驚慌。僅僅要運行以下這條命令。就能夠回到你運行git pull --rebase命令前的樣子:

git rebase --abort

小紅成功公布功能


小紅完畢和中央倉庫的同步后。就能成功公布她的改動了:

git push origin master

如你所見。僅使用幾個Git命令我們就能夠模擬出傳統Subversion開發環境。對於要從SVN遷移過來的團隊來說這太好了,但沒有發揮出Git分布式本質的優勢。

假設你的團隊適應了集中式工作流。但想要更流暢的協作效果。絕對值得探索一下 功能分支工作流 的收益。


通過為一個功能分配一個專門的分支,可以做到一個新增功能集成到正式項目之前對新功能進行深入討論。


2.2 功能分支工作流

功能分支工作流以集中式工作流為基礎,不同的是為各個新功能分配一個專門的分支來開發。

這樣能夠在把新功能集成到正式項眼下,用Pull Requests的方式討論變更。

Git Workflows: Feature Branch
Git Workflows: Feature Branch

一旦你玩轉了集中式工作流,在開發過程中能夠非常easy地加上功能分支,用來鼓舞開發人員之間協作和簡化交流。

功能分支工作流背后的核心思路是全部的功能開發應該在一個專門的分支。而不是在master分支上。
這個隔離能夠方便多個開發人員在各自的功能上開發而不會弄亂主干代碼。


另外,也保證了master分支的代碼一定不會是有問題的,極大有利於集成環境。

功能開發隔離也讓pull requests工作流成功可能。
pull requests工作流能為每一個分支發起一個討論,在分支合入正式項目之前,給其他開發人員有表示贊同的機會。
另外,假設你在功能開發中有問題卡住了,能夠開一個pull requests來向同學們征求建議。
這些做法的重點就是,pull requests讓團隊成員之間互相評論工作變成很方便!

2.2.1 工作方式

功能分支工作流仍然用中央倉庫,而且master分支還是代表了正式項目的歷史。


但不是直接提交本地歷史到各自的本地master分支,開發人員每次在開始新功能前先創建一個新分支。
功能分支應該有個有描寫敘述性的名字,比方animated-menu-itemsissue-#1061。這樣能夠讓分支有個清楚且高聚焦的用途。

master分支和功能分支之間,Git是沒有技術上的差別。所以開發人員能夠用和集中式工作流中全然一樣的方式編輯、暫存和提交改動到功能分支上。

另外,功能分支也能夠(且應該)push到中央倉庫中。這樣不改動正式代碼就能夠和其他開發人員分享提交的功能。
因為master僅有的一個『特殊』分支,在中央倉庫上存多個功能分支不會有不論什么問題。當然。這樣做也能夠非常方便地備份各自的本地提交。

2.2.2 Pull Requests

功能分支除了能夠隔離功能的開發,也使得通過Pull Requests討論變更成為可能。


一旦某個開發完畢一個功能,不是馬上合並到master。而是push到中央倉庫的功能分支上並發起一個Pull Request請求去合並改動到master
在改動成為主干代碼前,這讓其他的開發人員有機會先去Review變更。

Code ReviewPull Requests的一個重要的收益,但Pull Requests目的是討論代碼一個通用方式。
你能夠把Pull Requests作為專門給某個分支的討論。這意味着能夠在更早的開發過程中就能夠進行Code Review
比方。一個開發人員開發功能須要幫助時,要做的就是發起一個Pull Request,相關的人就會自己主動收到通知。在相關的提交旁邊能看到須要幫助解決的問題。

一旦Pull Request被接受了。公布功能要做的就和集中式工作流就非常像了。
首先。確定本地的master分支和上游的master分支是同步的。然后合並功能分支到本地master分支並push已經更新的本地master分支到中央倉庫。

倉庫管理的產品解決方式像BitbucketStash,能夠良好地支持Pull Requests

能夠看看StashPull Requests文檔

2.2.3 演示樣例

以下的演示樣例演示了怎樣把Pull Requests作為Code Review的方式,但注意Pull Requests能夠用於非常多其他的目的。

小紅開始開發一個新功能


在開始開發功能前。小紅須要一個獨立的分支。使用以下的命令新建一個分支

git checkout -b marys-feature master

這個命令檢出一個基於master名為marys-feature的分支,Git-b選項表示假設分支還不存在則新建分支。
這個新分支上,小紅按老套路編輯、暫存和提交改動,按須要提交以實現功能:

git status
git add <some-file>
git commit

小紅要去吃個午飯


早上小紅為新功能加入一些提交。
去吃午飯前,push功能分支到中央倉庫是非常好的做法,這樣能夠方便地備份。假設和其他開發協作,也讓他們能夠看到小紅的提交。

git push -u origin marys-feature

這條命令push marys-feature分支到中央倉庫(origin),-u選項設置本地分支去跟蹤遠程相應的分支。


設置好跟蹤的分支后,小紅就能夠使用git push命令省去指定推送分支的參數。

小紅完畢功能開發


小紅吃完午飯回來,完畢整個功能的開發。在合並到master之前
她發起一個Pull Request讓團隊的其他人知道功能已經完畢。

但首先,她要確認中央倉庫中已經有她近期的提交:

git push

然后,在她的Git GUIclient中發起Pull Request,請求合並marys-featuremaster,團隊成員會自己主動收到通知。
Pull Request非常酷的是能夠在相關的提交旁邊顯示評注,所以你能夠非常對某個變更集提問。

小黑收到Pull Request


小黑收到了Pull Request后會查看marys-feature的改動。決定在合並到正式項眼下是否要做些改動。且通過Pull Request和小紅來回地討論。

小紅再做改動


要再做改動。小紅用和功能第一個迭代全然一樣的過程。編輯、暫存、提交並push更新到中央倉庫。小紅這些活動都會顯示在Pull Request上。小黑能夠斷續做評注。

假設小黑有須要,也能夠把marys-feature分支拉到本地,自己來改動,他加的提交也會一樣顯示在Pull Request上。

小紅公布她的功能


一旦小黑能夠的接受Pull Request,就能夠合並功能到穩定項目代碼中(能夠由小黑或是小紅來做這個操作):

git checkout master
git pull
git pull origin marys-feature
git push

不管誰來做合並。首先要檢出master分支並確認是它是最新的。然后運行git pull origin marys-feature合並marys-feature分支到和已經和遠程一致的本地master分支。


你能夠使用簡單git merge marys-feature命令,但前面的命令能夠保證總是最新的新功能分支。
最后更新的master分支要又一次push回到origin

這個過程經常會生成一個合並提交。有些開發人員喜歡有合並提交,由於它像一個新功能和原來代碼基線的連通符。


但假設你偏愛線性的提交歷史,能夠在運行合並時rebase新功能到master分支的頂部,這樣生成一個快進(fast-forward)的合並。

一些GUIclient能夠僅僅要點一下『接受』button運行好上面的命令來自己主動化Pull Request接受過程。


假設你的不能這樣。至少在功能合並到master分支后能自己主動關閉Pull Request

與此同一時候,小明在做和小紅一樣的事

當小紅和小黑在marys-feature上工作並討論她的Pull Request的時候。小明在自己的功能分支上做全然一樣的事。

通過隔離功能到獨立的分支上,每一個人都能夠自主的工作,當然必要的時候在開發人員之間分享變更還是比較繁瑣的。

到了這里,但願你發現了功能分支能夠非常直接地在 集中式工作流 的僅有的master分支上完畢多功能的開發。


另外。功能分支還使用了Pull Request,使得能夠在你的版本號控制GUIclient中討論某個提交。

功能分支工作流是開發項目異常靈活的方式。問題是,有時候太靈活了。對於大型團隊,經常須要給不同分支分配一個更詳細的角色。
Gitflow工作流是管理功能開發、公布准備和維護的經常使用模式。


2.3 Gitflow工作流

Gitflow工作流通過為功能開發、公布准備和維護分配獨立的分支,讓公布迭代過程更流暢。

嚴格的分支模型也為大型項目提供了一些很必要的結構。

Git Workflows: Gitflow Cycle
Git Workflows: Gitflow Cycle

這節介紹的Gitflow工作流借鑒自在nvieVincent Driessen

Gitflow工作流定義了一個環繞項目公布的嚴格分支模型。

盡管比功能分支工作流復雜幾分,但提供了用於一個健壯的用於管理大型項目的框架。

Gitflow工作流沒實用超出功能分支工作流的概念和命令,而是為不同的分支分配一個非常明白的角色,並定義分支之間怎樣和什么時候進行交互。
除了使用功能分支,在做准備、維護和記錄公布也使用各自的分支。
當然你能夠用上功能分支工作流全部的優點:Pull Requests、隔離實驗性開發和更高效的協作。

2.3.1 工作方式

Gitflow工作流仍然用中央倉庫作為全部開發人員的交互中心。

和其他的工作流一樣。開發人員在本地工作並push分支到要中央倉庫中。

2.3.2 歷史分支

相對使用僅有的一個master分支,Gitflow工作流使用2個分支來記錄項目的歷史。master分支存儲了正式公布的歷史,而develop分支作為功能的集成分支。
這樣也方便master分支上的全部提交分配一個版本。


剩下要說明的問題環繞着這2個分支的差別展開。

2.3.3 功能分支

每一個新功能位於一個自己的分支,這樣能夠push到中央倉庫以備份和協作
但功能分支不是從master分支上拉出新分支,而是使用develop分支作為父分支。

當新功能完畢時。合並回develop分支
新功能提交應該從不直接與master分支交互。


注意。從各種含義和目的上來看,功能分支加上develop分支就是功能分支工作流的使用方法。但Gitflow工作流沒有在這里止步。

2.3.4 公布分支


一旦develop分支上有了做一次公布(或者說快到了既定的公布日)的足夠功能。就從develop分支上fork一個公布分支。
新建的分支用於開始公布循環,所以從這個時間點開始之后新的功能不能再加到這個分支上——
這個分支僅僅應該做Bug修復、文檔生成和其他面向公布任務。
一旦對外公布的工作都完畢了,公布分支合並到master分支並分配一個版本打好Tag


另外,這些從新建公布分支以來的做的改動要合並回develop分支。

使用一個用於公布准備的專門分支,使得一個團隊能夠在完好當前的公布版本號的同一時候,還有一個團隊能夠繼續開發下個版本號的功能。
這也打造定義良好的開發階段(比方,能夠非常輕松地說,『這周我們要做准備公布版本號4.0』,而且在倉庫的文件夾結構中能夠實際看到)。

經常使用的分支約定:

用於新建公布分支的分支: develop
用於合並的分支: master
分支命名: release-* 或 release/*

2.3.5 維護分支


維護分支或說是熱修復(hotfix)分支用於生成高速給產品公布版本號(production releases)打補丁,這是唯一能夠直接從master分支fork出來的分支。
修復完畢,改動應該立即合並回master分支和develop分支(當前的公布分支),master分支應該用新的版本打好Tag

Bug修復使用專門分支,讓團隊能夠處理掉問題而不用打斷其他工作或是等待下一個公布循環。
你能夠把維護分支想成是一個直接在master分支上處理的暫時公布。

2.3.6 演示樣例

以下的演示樣例演示本工作流怎樣用於管理單個公布循環。如果你已經創建了一個中央倉庫。

創建開發分支


第一步為master分支配套一個develop分支。

簡單來做能夠本地創建一個空的develop分支push到server上:

git branch develop
git push -u origin develop

以后這個分支將會包括了項目的所有歷史,而master分支將僅僅包括了部分歷史。其他開發人員這時應該克隆中央倉庫,建好develop分支的跟蹤分支:

git clone ssh://user@host/path/to/repo.git
git checkout -b develop origin/develop

如今每一個開發都有了這些歷史分支的本地拷貝。

小紅和小明開始開發新功能


這個演示樣例中。小紅和小明開始各自的功能開發。他們須要為各自的功能創建對應的分支。新分支不是基於master分支。而是應該基於develop分支

git checkout -b some-feature develop

他們用老套路加入提交到各自功能分支上:編輯、暫存、提交:

git status
git add <some-file>
git commit

小紅完畢功能開發


加入了提交后,小紅認為她的功能OK了。假設團隊使用Pull Requests,這時候能夠發起一個用於合並到develop分支。
否則她能夠直接合並到她本地的develop分支后push到中央倉庫:

git pull origin develop
git checkout develop
git merge some-feature
git push
git branch -d some-feature

第一條命令在合並功能前確保develop分支是最新的。注意。功能決不應該直接合並到master分支。


沖突解決方法和集中式工作流一樣。

小紅開始准備公布


這個時候小明正在實現他的功能,小紅開始准備她的第一個項目正式公布。
像功能開發一樣。她用一個新的分支來做公布准備。這一步也確定了公布的版本:

git checkout -b release-0.1 develop

這個分支是清理公布、運行全部測試、更新文檔和其他為下個公布做准備操作的地方。像是一個專門用於改善公布的功能分支。

僅僅要小紅創建這個分支並push到中央倉庫,這個公布就是功能凍結的。

不論什么不在develop分支中的新功能都推到下個公布循環中。

小紅完畢公布


一旦准備好了對外公布,小紅合並改動到master分支和develop分支上。刪除公布分支。合並回develop分支非常重要,由於在公布分支中已經提交的更新須要在后面的新功能中也要是可用的。
另外,假設小紅的團隊要求Code Review。這是一個發起Pull Request的理想時機。

git checkout master
git merge release-0.1
git push
git checkout develop
git merge release-0.1
git push
git branch -d release-0.1

公布分支是作為功能開發(develop分支)和對外公布(master分支)間的緩沖。僅僅要有合並到master分支,就應該打好Tag以方便跟蹤。

git tag -a 0.1 -m "Initial public release" master
git push --tags

Git有提供各種勾子(hook),即倉庫有事件發生時觸發運行的腳本。
能夠配置一個勾子。在你push中央倉庫的master分支時,自己主動構建好對外公布。

終於用戶發現Bug


對外公布后,小紅回去和小明一起做下個公布的新功能開發,直到有終於用戶開了一個Ticket抱怨當前版本號的一個Bug


為了處理Bug,小紅(或小明)從master分支上拉出了一個維護分支,提交改動以解決這個問題,然后直接合並回master分支:

git checkout -b issue-#001 master
# Fix the bug
git checkout master
git merge issue-#001
git push

就像公布分支,維護分支中新加這些重要改動須要包括到develop分支中,所以小紅要運行一個合並操作。然后就能夠安全地刪除這個分支了:

git checkout develop
git merge issue-#001
git push
git branch -d issue-#001

到了這里。但願你對集中式工作流功能分支工作流Gitflow工作流已經感覺非常舒適了。
你應該也堅固的掌握了本地倉庫的潛能,push/pull模式和Git健壯的分支和合並模型。

記住,這里演示的工作流僅僅是可能使用方法的樣例,而不是在實際工作中使用Git不可違逆的條例。
所以不要畏懼按自己須要對工作流的使用方法做取舍。不變的目標就是讓Git為你所用。


2.4 Forking工作流

Forking工作流是分布式工作流,充分利用了Git在分支和克隆上的優勢。能夠安全可靠地管理大團隊的開發人員(developer),並能接受不信任貢獻者(contributor)的提交。

Forking工作流和前面討論的幾種工作流有根本的不同,這樣的工作流不是使用單個服務端倉庫作為『中央』代碼基線。而讓各個開發人員都有一個服務端倉庫。這意味着各個代碼貢獻者有2個Git倉庫而不是1個:一個本地私有的,還有一個服務端公開的。


Forking工作流的一個主要優勢是,貢獻的代碼能夠被集成。而不須要全部人都能push代碼到僅有的中央倉庫中。
開發人員push到自己的服務端倉庫。而僅僅有項目維護者才干push到正式倉庫。
這樣項目維護者能夠接受不論什么開發人員的提交,但無需給他正式代碼庫的寫權限。

效果就是一個分布式的工作流,能為大型、自發性的團隊(包含了不受信的第三方)提供靈活的方式來安全的協作。


也讓這個工作流成為開源項目的理想工作流。

2.4.1 工作方式

和其他的Git工作流一樣。Forking工作流要先有一個公開的正式倉庫存儲在server上。
但一個新的開發人員想要在項目上工作時,不是直接從正式倉庫克隆,而是fork正式項目在server上創建一個拷貝。

這個倉庫拷貝作為他個人公開倉庫 ——
其他開發人員不同意push到這個倉庫,但能夠pull到改動(后面我們非常快就會看這點非常重要)。
在創建了自己服務端拷貝之后,和之前的工作流一樣,開發人員運行git clone命令克隆倉庫到本地機器上。作為私有的開發環境。

要提交本地改動時,push提交到自己公開倉庫中 —— 而不是正式倉庫中。
然后,給正式倉庫發起一個pull request。讓項目維護者知道有更新已經准備好能夠集成了。
對於貢獻的代碼,pull request也能夠非常方便地作為一個討論的地方。

為了集成功能到正式代碼庫,維護者pull貢獻者的變更到自己的本地倉庫中,檢查變更以確保不會讓項目出錯,
合並變更到自己本地的master分支
然后pushmaster分支到server的正式倉庫中。
到此。貢獻的提交成為了項目的一部分。其他的開發人員應該運行pull操作與正式倉庫同步自己本地倉庫。

2.4.2 正式倉庫

Forking工作流中,『官方』倉庫的叫法僅僅是一個約定,理解這點非常重要。
從技術上來看,各個開發人員倉庫和正式倉庫在Git看來沒有不論什么差別。
其實,讓正式倉庫之所以正式的唯一原因是它是項目維護者的公開倉庫。

2.4.3 Forking工作流的分支使用方式

全部的個人公開倉庫實際上僅僅是為了方便和其他的開發人員共享分支。
各個開發人員應該用分支隔離各個功能,就像在功能分支工作流Gitflow工作流一樣。
唯一的差別是這些分支被共享了。在Forking工作流中這些分支會被pull到還有一個開發人員的本地倉庫中,而在功能分支工作流和Gitflow工作流中是直接被push到正式倉庫中。

2.4.4 演示樣例

項目維護者初始化正式倉庫


和不論什么使用Git項目一樣,第一步是創建在server上一個正式倉庫,讓全部團隊成員都能夠訪問到。
通常這個倉庫也會作為項目維護者的公開倉庫。

公開倉庫應該是裸倉庫。無論是不是正式代碼庫。
所以項目維護者會執行像以下的命令來搭建正式倉庫:

ssh user@host
git init --bare /path/to/repo.git

BitbucketStash提供了一個方便的GUIclient以完畢上面命令行做的事。


這個搭建中央倉庫的過程和前面提到的工作流全然一樣。
假設有現存的代碼庫,維護者也要push到這個倉庫中。

開發人員fork正式倉庫


其他全部的開發須要fork正式倉庫。
能夠用git clone命令SSH協議連通到server
拷貝倉庫到server還有一個位置 —— 是的。fork操作基本上就僅僅是一個服務端的克隆。
BitbucketStash上能夠點一下button就讓開發人員完畢倉庫的fork操作。

這一步完畢后,每一個開發都在服務端有一個自己的倉庫。和正式倉庫一樣。這些倉庫應該是裸倉庫。

開發人員克隆自己fork出來的倉庫


下一步。各個開發人員要克隆自己的公開倉庫,用熟悉的git clone命令。

在這個演示樣例中。假定用Bitbucket托管了倉庫。記住,假設這種話各個開發人員須要有各自的Bitbucket賬號,
使用以下命令克隆服務端自己的倉庫:

git clone https://user@bitbucket.org/user/repo.git

相比前面介紹的工作流僅僅用了一個origin遠程別名指向中央倉庫。Forking工作流須要2個遠程別名 ——
一個指向正式倉庫,還有一個指向開發人員自己的服務端倉庫。別名的名字能夠隨意命名,常見的約定是使用origin作為遠程克隆的倉庫的別名
(這個別名會在執行git clone自己主動創建)。upstream(上游)作為正式倉庫的別名。

git remote add upstream https://bitbucket.org/maintainer/repo

須要自己用上面的命令創建upstream別名。這樣能夠簡單地保持本地倉庫和正式倉庫的同步更新。
注意,假設上游倉庫須要認證(比方不是開源的)。你須要提供用戶:

git remote add upstream https://user@bitbucket.org/maintainer/repo.git

這時在克隆和pull正式倉庫時。須要提供用戶的password。

開發人員開發自己的功能


在剛克隆的本地倉庫中,開發人員能夠像其他工作流一樣的編輯代碼、提交改動新建分支

git checkout -b some-feature
# Edit some code
git commit -a -m "Add first draft of some feature"

全部的改動都是私有的直到push到自己公開倉庫中。假設正式項目已經往前走了,能夠用git pull命令獲得新的提交:

git pull upstream master

因為開發人員應該都在專門的功能分支上工作。pull操作結果會都是快進合並

開發人員公布自己的功能


一旦開發人員准備好了分享新功能,須要做二件事。


首先,通過push他的貢獻代碼到自己的公開倉庫中。讓其他的開發人員都能夠訪問到。


他的origin遠程別名應該已經有了,所以要做的就是:

git push origin feature-branch

這里和之前的工作流的差異是,origin遠程別名指向開發人員自己的服務端倉庫,而不是正式倉庫。

第二件事,開發人員要通知項目維護者。想要合並他的新功能到正式庫中。
BitbucketStash提供了Pull Requestbutton,彈出表單讓你指定哪個分支要合並到正式倉庫。


一般你會想集成你的功能分支到上游遠程倉庫的master分支中。

項目維護者集成開發人員的功能


當項目維護者收到pull request,他要做的是決定是否集成它到正式代碼庫中。有二種方式來做:

  1. 直接在pull request中查看代碼
  2. pull代碼到他自己的本地倉庫。再手動合並

第一種做法更簡單。維護者能夠在GUI中查看變更的差異。做評注和運行合並。
但假設出現了合並沖突。須要另外一種做法來解決。

這樣的情況下,維護者須要從開發人員的服務端倉庫中fetch功能分支,
合並到他本地的master分支,解決沖突:

git fetch https://bitbucket.org/user/repo feature-branch
# 查看變更
git checkout master
git merge FETCH_HEAD

變更集成到本地的master分支后。維護者要push變更到server上的正式倉庫,這樣其他的開發人員都能訪問到:

git push origin master

注意,維護者的origin是指向他自己公開倉庫的。即是項目的正式代碼庫。到此,開發人員的貢獻全然集成到了項目中。

開發人員和正式倉庫做同步


因為正式代碼庫往前走了,其他的開發須要和正式倉庫做同步:

git pull upstream master

假設你之前是使用SVNForking工作流可能看起來像是一個激進的范式切換(paradigm shift)。


但不要害怕。這個工作流實際上就是在功能分支工作流之上引入還有一個抽象層。
不是直接通過單個中央倉庫來分享分支,而是把貢獻代碼公布到開發人員自己的服務端倉庫中。

演示樣例中解釋了。一個貢獻怎樣從一個開發人員流到正式的master分支中,但相同的方法能夠把貢獻集成到任一個倉庫中。
比方,假設團隊的幾個人協作實現一個功能,能夠在開發之間用同樣的方法分享變更。全然不涉及正式倉庫。

這使得Forking工作流對於松散組織的團隊來說是個很強大的工具。任一開發人員能夠方便地和還有一開發人員分享變更,不論什么分支都能有效地合並到正式代碼庫中。


2.5 Pull Requests

Pull requestsBitbucket提供的讓開發人員更方便地進行協作的功能,提供了友好的Web界面能夠在提議的改動合並到正式項目之前對改動進行討論。


開發人員向團隊成員通知功能開發已經完畢,Pull Requests是最簡單的使用方法。
開發人員完畢功能開發后。通過Bitbucket賬號發起一個Pull Request
這樣讓涉及這個功能的全部人知道要去做Code Review和合並到master分支。

可是。Pull Request遠不止一個簡單的通知,而是為討論提交的功能的一個專門論壇。
假設變更有不論什么問題,團隊成員反饋在Pull Request中,甚至push新的提交微調功能。


全部的這些活動都直接跟蹤在Pull Request中。


相比其他的協作模型,這樣的分享提交的形式有助於打造一個更流暢的工作流。
SVNGit都能通過一個簡單的腳本收到通知郵件;可是,討論變更時,開發人員通常僅僅能去回復郵件。
這樣做會變得雜亂。尤其還要涉及后面的幾個提交時。
Pull Requests把全部相關功能整合到一個和Bitbucket倉庫界面集成的用戶友好Web界面中。

2.5.1 解析Pull Request

當要發起一個Pull Request,你所要做的就是請求(Request)還有一個開發人員(比方項目的維護者)
pull你倉庫中一個分支到他的倉庫中。這意味着你要提供4個信息以發起Pull Request
源倉庫、源分支、目的倉庫、目的分支。


這幾值多數Bitbucket都會設置上合適的缺省值。

但取決你用的協作工作流,你的團隊可能會要指定不同的值。


上圖顯示了一個Pull Request請求合並一個功能分支到正式的master分支上,但能夠有多種不同的Pull Request使用方法。

2.5.2 工作方式

Pull Request能夠和功能分支工作流Gitflow工作流Forking工作流一起使用。
但一個Pull Request要求要么分支不同要么倉庫不同,所以不能用於集中式工作流
在不同的工作流中使用Pull Request會有一些不同,但主要的過程是這種:

  1. 開發人員在本地倉庫中新建一個專門的分支開發功能。
  2. 開發人員push分支改動到公開的Bitbucket倉庫中。
  3. 開發人員通過Bitbucket發起一個Pull Request
  4. 團隊的其他成員review code,討論並改動。
  5. 項目維護者合並功能到官方倉庫中並關閉Pull Request

本文后面內容說明,Pull Request在不同協作工作流中怎樣應用。

2.5.3 在功能分支工作流中使用Pull Request

功能分支工作流用一個共享的Bitbucket倉庫來管理協作,開發人員在專門的分支上開發功能。
但不是馬上合並到master分支上,而是在合並到主代碼庫之前開發人員應該開一個Pull Request發起功能的討論。


功能分支工作流僅僅有一個公開的倉庫,所以Pull Request的目的倉庫和源倉庫總是同一個。
通常開發人員會指定他的功能分支作為源分支,master分支作為目的分支。

收到Pull Request后,項目維護者要決定怎樣做。

假設功能沒問題,就簡單地合並到master分支。關閉Pull Request
但假設提交的變更有問題,他能夠在Pull Request中反饋。之后新加的提交也會評論之后接着顯示出來。

在功能還沒有全然開發完的時候,也可能發起一個Pull Request
比方開發人員在實現某個需求時碰到了麻煩。他能夠發一個包括正在進行中工作的Pull Request
其他的開發人員能夠在Pull Request提供建議。或者甚至直接加入提交來解決這個問題。

2.5.4 在Gitflow工作流中使用Pull Request

Gitflow工作流和功能分支工作流類似。但環繞項目公布定義一個嚴格的分支模型。
Gitflow工作流中使用Pull Request讓開發人員在公布分支或是維護分支上工作時,
能夠有個方便的地方對關於公布分支或是維護分支的問題進行交流。


Gitflow工作流中Pull Request的使用過程和上一節中全然一致:
當一個功能、公布或是熱修復分支須要Review時。開發人員簡單發起一個Pull Request
團隊的其他成員會通過Bitbucket收到通知。

新功能一般合並到develop分支。而公布和熱修復則要同一時候合並到develop分支和master分支上。


Pull Request可能用做全部合並的正式管理。

2.5.5 在Forking工作流中使用Pull Request

Forking工作流中,開發人員push完畢的功能到他自己的倉庫中,而不是共享倉庫。
然后。他發起一個Pull Request。讓項目維護者知道他的功能已經能夠Review了。

在這個工作流,Pull Request的通知功能很實用,
由於項目維護者不可能知道其他開發人員在他們自己的倉庫加入了提交。


因為各個開發有自己的公開倉庫,Pull Request的源倉庫和目標倉庫不是同一個。
源倉庫是開發人員的公開倉庫。源分支是包括了改動的分支。
假設開發人員要合並改動到正式代碼庫中,那么目標倉庫是正式倉庫。目標分支是master分支。

Pull Request也能夠用於正式項目之外的其他開發人員之間的協作。
比方,假設一個開發人員和一個團隊成員一起開發一個功能,他們能夠發起一個Pull Request
用團隊成員的Bitbucket倉庫作為目標。而不是正式項目的倉庫。
然后使用同樣的功能分支作為源和目標分支。


2個開發人員之間能夠在Pull Request中討論和開發功能。
完畢開發后,他們能夠發起還有一個Pull Request,請求合並功能到正式的master分支。
Forking工作流中,這種靈活性讓Pull Request成為一個強有力的協作工具。

2.5.6 演示樣例

以下的演示樣例演示了Pull Request怎樣在在Forking工作流中使用。
也相同適用於小團隊的開發協作和第三方開發人員向開源項目的貢獻。

在演示樣例中,小紅是個開發,小明是項目維護者。

他們各自有一個公開的Bitbucket倉庫,而小明的倉庫包括了正式project。

小紅fork正式項目


小紅先要fork小明的Bitbucket倉庫,開始項目的開發。

她登陸Bitbucket,瀏覽到小明的倉庫頁面,
Forkbutton。


然后為fork出來的倉庫填寫名字和描寫敘述,這樣小紅就有了服務端的項目拷貝了。

小紅克隆她的Bitbucket倉庫


下一步,小紅克隆自己剛才fork出來的Bitbucket倉庫,以在本機上准備出工作拷貝。命令例如以下:

git clone https://user@bitbucket.org/user/repo.git

請記住。git clone會自己主動創建origin遠程別名,是指向小紅fork出來的倉庫。

小紅開發新功能


在開始改代碼前,小紅要為新功能先新建一個新分支。她會用這個分支作為Pull Request的源分支。

git checkout -b some-feature
# 編輯代碼
git commit -a -m "Add first draft of some feature"

在新功能分支上。小紅按須要加入提交。甚至假設小紅認為功能分支上的提交歷史太亂了,她能夠用交互式rebase來刪除或壓制提交。
對於大型項目,整理功能分支的歷史能夠讓項目維護者更easy看出在Pull Request中做了什么內容。

小紅push功能到她的Bitbucket倉庫中


小紅完畢了功能后,push功能到她自己的Bitbucket倉庫中(不是正式倉庫)。用以下簡單的命令:

git push origin some-branch

這時她的變更能夠讓項目維護者看到了(或者不論什么想要看的協作者)。

小紅發起Pull Request


Bitbucket上有了她的功能分支后,小紅能夠用她的Bitbucket賬號瀏覽到她的fork出來的倉庫頁面,
點右上角的【Pull Request】button,發起一個Pull Request
彈出的表單自己主動設置小紅的倉庫為源倉庫,詢問小紅以指定源分支、目標倉庫和目標分支。

小紅想要合並功能到正式倉庫。所以源分支是她的功能分支,目標倉庫是小明的公開倉庫,
而目標分支是master分支。另外。小紅須要提供Pull Request的標題和描寫敘述信息。
假設須要小明以外的人審核批准代碼。她能夠把這些人填在【Reviewers】文本框中。


創建好了Pull Request,通知會通過Bitbucket系統消息或郵件(可選)發給小明。

小明review Pull Request


在小明的Bitbucket倉庫頁面的【Pull Request】Tab能夠看到全部人發起的Pull Request
點擊小紅的Pull Request會顯示出Pull Request的描寫敘述、功能的提交歷史和每一個變更的差異(diff)。

假設小明想要合並到項目中,僅僅要點一下【Merge】button。就能夠允許Pull Request並合並到master分支。

但假設像這個演示樣例中一樣小明發現了在小紅的代碼中的一個小Bug。要小紅在合並前修復。
小明能夠在整個Pull Request上加上評注。或是選擇歷史中的某個提交加上評注。


小紅補加提交

假設小紅對反饋有不論什么疑問。能夠在Pull Request中響應。把Pull Request當作是她功能討論的論壇。

小紅在她的功能分支新加提交以解決代碼問題,並push到她的Bitbucket倉庫中。就像前一輪中的做法一樣。
這些提交會進入的Pull Request。小明在原來的評注旁邊能夠再次review變更。

小明接受Pull Request

終於,小明接受變更,合並功能分支到master分支,並關閉Pull Request


至此,功能集成到項目中,其他的項目開發人員能夠用標准的git pull命令pull這些變更到自己的本地倉庫中。

到了這里,你應該有了全部須要的工具來集成Pull Request到你自己的工作流。
請記住,Pull Request並非為了替代不論什么 基於Git的協作工作流
而是它們的一個便利的補充,讓團隊成員間的協作更輕松方便。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM