Git工作流——Forking工作流
Forking
工作流和前面討論的幾種工作流有根本的不同。這種工作流不是使用單個服務端倉庫作為『中央』程式碼基線,而讓各個開發者都有一個服務端倉庫。這意味著各個程式碼貢獻者有2個Git
倉庫而不是1個:一個本地私有的,另一個服務端公開的。
Forking
工作流的一個主要優勢是,貢獻的程式碼可以被整合,而不需要所有人都能push
程式碼到僅有的中央倉庫中。開發者push
到自己的服務端倉庫,而只有專案維護者才能push
到正式倉庫。這樣專案維護者可以接受任何開發者的提交,但無需給他正式程式碼庫的寫許可權。
效果就是一個分散式的工作流,能為大型、自發性的團隊(包括了不受信的第三方)提供靈活的方式來安全的協作。也讓這個工作流成為開源專案的理想工作流。
工作方式
和其它的Git
工作流一樣,Forking
工作流要先有一個公開的正式倉庫儲存在伺服器上。但一個新的開發者想要在專案上工作時,不是直接從正式倉庫克隆,而是fork
正式專案在伺服器上建立一個拷貝。
這個倉庫拷貝作為他個人公開倉庫 —— 其它開發者不允許push
到這個倉庫,但可以pull
到修改(後面我們很快就會看這點很重要)。在建立了自己服務端拷貝之後,和之前的工作流一樣,開發者執行git clone
命令克隆倉庫到本地機器上,作為私有的開發環境。
要提交本地修改時,push
提交到自己公開倉庫中 —— 而不是正式倉庫中。然後,給正式倉庫發起一個pull request
,讓專案維護者知道有更新已經準備好可以集成了。對於貢獻的程式碼,pull request
為了整合功能到正式程式碼庫,維護者pull
貢獻者的變更到自己的本地倉庫中,檢查變更以確保不會讓專案出錯,合併變更到自己本地的master
分支,然後push
master
分支到伺服器的正式倉庫中。到此,貢獻的提交成為了專案的一部分,其它的開發者應該執行pull
操作與正式倉庫同步自己本地倉庫。
正式倉庫
在Forking
工作流中,『官方』倉庫的叫法只是一個約定,理解這點很重要。從技術上來看,各個開發者倉庫和正式倉庫在Git
看來沒有任何區別。事實上,讓正式倉庫之所以正式的唯一原因是它是專案維護者的公開倉庫。
Forking
工作流的分支使用方式
所有的個人公開倉庫實際上只是為了方便和其它的開發者共享分支。各個開發者應該用分支隔離各個功能,就像在Gitflow
工作流一樣。唯一的區別是這些分支被共享了。在Forking
工作流中這些分支會被pull
到另一個開發者的本地倉庫中,而在功能分支工作流和Gitflow
工作流中是直接被push
到正式倉庫中。
示例
專案維護者初始化正式倉庫
和任何使用Git
專案一樣,第一步是建立在伺服器上一個正式倉庫,讓所有團隊成員都可以訪問到。通常這個倉庫也會作為專案維護者的公開倉庫。
公開倉庫應該是裸倉庫,不管是不是正式程式碼庫。所以專案維護者會執行像下面的命令來搭建正式倉庫:
ssh [email protected]
git init --bare /path/to/repo.git
Bitbucket
和Stash
提供了一個方便的GUI
客戶端以完成上面命令列做的事。這個搭建中央倉庫的過程和前面提到的工作流完全一樣。如果有現存的程式碼庫,維護者也要push
到這個倉庫中。
開發者fork
正式倉庫
其它所有的開發需要fork
正式倉庫。可以用git clone
命令用SSH
協議連通到伺服器,拷貝倉庫到伺服器另一個位置 —— 是的,fork
操作基本上就只是一個服務端的克隆。Bitbucket
和Stash
上可以點一下按鈕就讓開發者完成倉庫的fork
操作。
這一步完成後,每個開發都在服務端有一個自己的倉庫。和正式倉庫一樣,這些倉庫應該是裸倉庫。
開發者克隆自己fork
出來的倉庫
下一步,各個開發者要克隆自己的公開倉庫,用熟悉的git clone
命令。
在這個示例中,假定用Bitbucket
託管了倉庫。記住,如果這樣的話各個開發者需要有各自的Bitbucket
賬號,使用下面命令克隆服務端自己的倉庫:
git clone https://[email protected]/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://[email protected]/maintainer/repo.git
這時在克隆和pull
正式倉庫時,需要提供使用者的密碼。
開發者開發自己的功能
在剛克隆的本地倉庫中,開發者可以像其它工作流一樣的編輯程式碼、提交修改和新建分支:
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
遠端別名指向開發者自己的服務端倉庫,而不是正式倉庫。
第二件事,開發者要通知專案維護者,想要合併他的新功能到正式庫中。Bitbucket
和Stash
提供了Pull Request
按鈕,彈出表單讓你指定哪個分支要合併到正式倉庫。一般你會想整合你的功能分支到上游遠端倉庫的master
分支中。
專案維護者整合開發者的功能
當專案維護者收到pull request
,他要做的是決定是否整合它到正式程式碼庫中。有二種方式來做:
- 直接在
pull request
中檢視程式碼 pull
程式碼到他自己的本地倉庫,再手動合併
第一種做法更簡單,維護者可以在GUI
中檢視變更的差異,做評註和執行合併。但如果出現了合併衝突,需要第二種做法來解決。這種情況下,維護者需要從開發者的服務端倉庫中fetch
功能分支,合併到他本地的master
分支,解決衝突:
git fetch https://bitbucket.org/user/repo feature-branch
// 檢視變更
git checkout master
git merge FETCH_HEAD
變更整合到本地的master
分支後,維護者要push
變更到伺服器上的正式倉庫,這樣其它的開發者都能訪問到:
git push origin master
注意,維護者的origin
是指向他自己公開倉庫的,即是專案的正式程式碼庫。到此,開發者的貢獻完全整合到了專案中。
開發者和正式倉庫做同步
由於正式程式碼庫往前走了,其它的開發需要和正式倉庫做同步:
git pull upstream master
下一站
如果你之前是使用SVN
,Forking
工作流可能看起來像是一個激進的正規化切換(paradigm shift)。但不要害怕,這個工作流實際上就是在功能分支工作流之上引入另一個抽象層。不是直接通過單箇中央倉庫來分享分支,而是把貢獻程式碼釋出到開發者自己的服務端倉庫中。
示例中解釋了,一個貢獻如何從一個開發者流到正式的master
分支中,但同樣的方法可以把貢獻整合到任一個倉庫中。比如,如果團隊的幾個人協作實現一個功能,可以在開發之間用相同的方法分享變更,完全不涉及正式倉庫。
這使得Forking
工作流對於鬆散組織的團隊來說是個非常強大的工具。任一開發者可以方便地和另一開發者分享變更,任何分支都能有效地合併到正式程式碼庫中。