四中常用的Git工作流比較

多種多樣的工作流使得在項目中實施Git時變得難以選擇。這份教程提供了一個出發點,調查企業團隊最常見的Git工作流。

閱讀的時候,請記住工作流應該是一種規范而不是金科玉律。我們希望向你展示所有工作流,讓你融會貫通,因地制宜。

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

中心化的工作流

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

如何工作

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

管理沖突

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

栗子

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

一人初始化了中央倉庫

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

所有人將倉庫克隆到本地

接下來,每個開發者在本地創建一份完整項目的副本。使用git clone
命令:
git clone ssh://user@host/path/to/repo.git

John在開發他的功能

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

Mary在開發她的功能

同時,Mary在她自己的本地倉庫用相同的編輯/緩存/提交流程開發她的功能。和John一樣,她不需要關心中央倉庫的進展,她也 完全 不關心John在他自己倉庫中做的事,因為所有本地倉庫都是私有的。

John發布了他的功能

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

Mary試圖發布她的功能

John已經成功地將他的更改發布到了中央倉庫上,看看當Mary試著將她的功能推送到上面時會發生什么。她可以使用同一個推送命令:
John已經成功地將他的更改發布到了中央倉庫上,看看當Mary試著將她的功能推送到上面時會發生什么。她可以使用同一個推送命令:
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.
Git防止Mary覆蓋官方的修改。她需要將John的更新拉取到她的倉庫,和她的本地修改整合后,然后重試。

Mary在John的提交之上rebase

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

Mary解決了合并沖突

Rebase的工作是將每個本地提交一個個轉移到更新后的master分支。也就是說,你可以一個個提交分別解決合并沖突,而不是在一個龐大的合并提交中解決。它會讓你的每個提交保持專注,并獲得一個干凈的項目歷史。另一方面,你更容易發現bug是在哪引入的,如果有必要的話,用最小的代價回滾這些修改。
如果Mary和John開發的功能沒有關聯,rebase的過程不太可能出現沖突。但如果出現沖突時,Git在當前提交會暫停rebase,輸出下面的信息,和一些相關的指令:
CONFLICT (content): Merge conflict in <some-file>
Git的優點在于 每個人 都能解決他們自己的合并沖突。在這個例子中,Mary只需運行一下git status
就可以發現問題是什么。沖突的文件會出現在未合并路徑中:
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 rebase --abort

Mary成功發布了她的分支

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

接下來該怎么做

正如你所見,使用一丟丟Git命令來復制一套傳統的Subversion開發環境也是可行的。這對于從SVN轉變而來的團隊來說很棒,但這樣沒有利用到Git分布式的本質。
如果你的團隊已經習慣了中心化的工作流,但希望提高協作效率,那么探索Feature分支工作流的好處是完全值當的。每個功能在專門的獨立分支上進行,在代碼并入官方項目之前就可以啟動圍繞新修改的深度討論。

Feature分支的工作流

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

封裝功能的開發使得pull request的使用成為可能,用來啟動圍繞一個分支的討論。它給了其他開發者在功能并入主項目之前參與決策的機會?;蛘?,如果你開發功能時卡在一半,你可以發起一個pull request,向同事尋求建議。重點是,pull request使得你的團隊在評論其他人的工作時變得非常簡單。

如何工作

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

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

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

Pull Request

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

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

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

栗子

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

Mary開始了一個新功能

在她開始開發一個功能之前,Mary需要一個獨立的分支。她可以用下面的命令創建新分支:
git checkout -b marys-feature master

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

Mary去吃飯了

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

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

Mary完成了她的工作

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

然后,她在她的Git界面上發起了一個pull request,請求將marys-feature合并進master,團隊成員會收到自動的通知。Pull request的好處是,評論顯示在相關的提交正下方,方便討論特定的修改。

Bill收到了pull request

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

Mary作了修改

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

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

Mary發布了她的功能

一旦Bill準備接受這個pull request,某個人(Bill或者Mary都可)需要將功能并入穩定的項目:
git checkout mastergit pullgit pull origin marys-featuregit push

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

這個過程導致了一個合并提交。一些開發者喜歡它,因為它是功能和其余代碼合并的標志。但,如果你希望得到線性的歷史,你可以在執行merge之前將功能rebase到master分支的頂端,產生一個快速向前的合并。

一些界面會自動化接受pull request的流程,只需點擊一下“Merge Pull Request”。如果你的沒有的話,它至少在合并之后應該可以自動地關閉pull request。
同時,John以同樣的方式工作著

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

接下來該怎么做

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

Gitflow工作流

下面的Gitflow工作流一節源于nvie網站上的作者Vincent Driessen。

Gitflow工作流圍繞項目發布定義了一個嚴格的分支模型。有些地方比功能分支工作流更復雜,為管理大型項目提供了魯棒的框架。

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

如何工作

Gitflow工作流仍然使用中央倉庫作為開發者溝通的中心。和其他工作流一樣,開發者在本地工作,將分支推送到中央倉庫。唯一的區別在于項目的分支結構。

歷史分支

和單獨的master分支不同,這種工作流使用兩個分支來記錄項目歷史。master分支儲存官方發布歷史,develop分支用來整合功能分支。同時,這還方便了在master分支上給所有提交打上版本號標簽。

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

功能分支

每個新功能都放置在自己的分支中,可以在備份/協作時推送到中央倉庫。但是,與其合并到master,功能分支將開發分支作為父分支。當一個功能完成時,它將被合并回develop。功能永遠不應該直接在master上交互。

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

發布分支

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

使用一個專門的分支來準備發布確保一個團隊完善當前的發布,其他團隊可以繼續開發下一個發布的功能。它還建立了清晰的開發階段(比如說,“這周我們準備4.0版本的發布”,而我們在倉庫的結構中也能看到這個階段)。

通常我們約定:
從develop創建分支

合并進master分支

命名規范release-* or release/*

維護分支

維護或者“緊急修復”分支用來快速給產品的發布打上補丁。這是唯一可以從master上fork的分支。一旦修復完成了,它應該被并入master和develop分支(或者當前的發布分支),master應該打上更新的版本號的標簽。

有一個專門的bug修復開發線使得你的團隊能夠處理issues,而不打斷其他工作流或是要等到下一個發布周期。你可以將維護分支看作在master分支上工作的臨時發布分支。

栗子

下面的栗子演示了這種工作流如何用來管理發布周期。假設你已經創建了中央倉庫。

創建一個開發分支

你要做的第一步是為默認的master分支創建一個互補的develop分支。最簡單的辦法是在本地創建一個空的develop分支,將它推送到服務器上:
git branch developgit push -u origin develop

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

現在所有人都有了一份歷史分支的本地副本。

Mary和John開始了新功能

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

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

Mary完成了她的功能

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

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

Mary開始準備發布

當John仍然在他的功能分支上工作時,Mary開始準備項目的第一個官方發布。和開發功能一樣,她新建了一個分支來封裝發布的準備工作。這也正是發布的版本號創建的一步:
git checkout -b release-0.1 develop

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

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

Mary完成了她的發布

一旦發布準備穩妥,Mary將它并入master和develop,然后刪除發布分支。合并回develop很重要,因為可能已經有關鍵的更新添加到了發布分支上,而開發新功能需要用到它們。同樣的,如果Mary的團隊重視代碼審查,現在將是發起pull request的完美時機。
git checkout mastergit merge release-0.1git pushgit checkout developgit merge release-0.1git pushgit branch -d release-0.1

發布分支是功能開發(develop)和公開發布(master)之間的過渡階段。不論什么時候將提交并入master時,你應該為提交打上方便引用的標簽:
git tag -a 0.1 -m "Initial public release" mastergit push --tags

Git提供了許多鉤子,即倉庫中特定事件發生時被執行的腳本。當你向中央倉庫推送master分支或者標簽時,你可以配置一個鉤子來自動化構建公開發布。

終端用戶發現了一個bug

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

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

接下來該怎么做

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

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

Fork工作流

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

Fork工作流的主要優點在于貢獻可以輕易地整合進項目,而不需要每個人都推送到單一的中央倉庫。開發者推送到他們 自己的 服務端倉庫,只有項目管理者可以推送到官方倉庫。這使得管理者可以接受任何開發者的提交,卻不需要給他們中央倉庫的權限。

結論是,這種分布式的工作流為大型、組織性強的團隊(包括不可信的第三方)提供了安全的協作方式。它同時也是開源項目理想的工作流。

如何工作

和其它Git工作流一樣,Fork工作流以一個儲存在服務端的官方公開項目開場。但新的開發者想參與項目時,他們不直接克隆官方項目。

取而代之地,他們fork一份官方項目,在服務端創建一份副本。這份新建的副本作為他們私有的公開倉庫——沒有其他開發者可以在上面推送,但他們可以從上面拉取修改(在后面我們會討論為什么這一點很重要)。在他們創建了服務端副本之后,開發者執行git clone操作,在他們的本地機器上復制一份。這是他們私有的開發環境,正如其他工作流中一樣。

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

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

中央倉庫

“官方”倉庫這個概念在Fork工作流中只是一個約定,理解這一點很重要。從技術的角度,Git并看不出每個開發者和官方的公開倉庫有什么區別。事實上,官方倉庫唯一官方的原因是,它是項目維護者的倉庫。

Fork工作流中的分支

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

栗子

項目維護者初始化了中央倉庫

和任何基于Git的項目一樣,第一步是在服務端創建一個可以被所有項目成員訪問到的官方倉庫。一般來說,這個倉庫同時還是項目維護者的公開倉庫。

公開的倉庫應該永遠是裸的,不管它們是否代表官方代碼庫。所以項目維護者應該運行下面這樣的命令來設置官方倉庫:
ssh user@hostgit init --bare /path/to/repo.git

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

開發者fork倉庫

接下來,所有開發者需要fork官方倉庫。你可以用SSH到服務器,運行git clone將它復制到服務器的另一個地址——fork其實只是服務端的clone。但同樣地,Github上開發者只需點一點按鈕就可以fork倉庫。

在這步之后,每個開發者應該都有了自己的服務端倉庫。像官方倉庫一樣,所有這些倉庫都應該是裸倉庫。

開發者將fork的倉庫克隆到本地

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

我們的栗子假設使用他們使用Github來托管倉庫。記住,在這種情況下,每個開發者應該有他們自己的Github賬號,應該用下面的命令克隆服務端倉庫:
git clone https://user@github.com/user/repo.git

而教程中的其他工作流使用單一的origin遠程連接,指向中央倉庫,Fork工作流需要兩個遠程連接,一個是中央倉庫,另一個是開發者個人的服務端倉庫。你可以給這些遠端取任何名字,約定的做法是將origin作為你fork后的倉庫的遠端(運行git clone是會自動創建)和upstream作為官方項目。
git remote add upstream https://github.com/maintainer/repo

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

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

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

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

因為開發者應該在專門的功能分支開發,這一般會產生一個快速向前的合并。

開發者發布他們的功能

一旦開發者準備好共享他們的新功能,他們需要做兩件事情。第一,他們必須將貢獻的代碼推送到自己的公開倉庫,讓其他開發者能夠訪問到。他們的origin遠端應該已經設置好了,所以他們只需要:
git push origin feature-branch

這和其他工作流不同之處在于,origin遠端指向開發者個人的服務端倉庫,而不是主代碼庫。

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

項目維護者整合他們的功能

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

將代碼拉取到本地倉庫然后手動合并

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

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

記住,維護者的origin指向他們的公開倉庫,也就是項目的官方代碼庫。開發者的貢獻現在完全并入了項目。

開發者和中央倉庫保持同步

因為主代碼庫已經取得了新的進展,其他開發者應該和官方倉庫同步:
git pull upstream master

接下來該怎么做

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

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

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,224評論 6 529
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 97,916評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,014評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,466評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,245評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,795評論 1 320
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,869評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,010評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,524評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,487評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,634評論 1 366
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,173評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,884評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,282評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,541評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,236評論 3 388
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,623評論 2 370

推薦閱讀更多精彩內容

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