Git提供異常靈活的分支操作,但是在實踐中也是需要制定良好的分支政策才能保證有效的利用Git來管理代碼。
最近發現了一篇很好的英文文章,https://www.atlassian.com/git/tutorials/comparing-workflows,根據其内容作了整理。
關于分支政策,有如下幾種好的實踐方式。
Centralized Workflow / 集中式工作流
集中式工作流非常類似于SVN,但是你也可以享受到Git帶來的好處,比如可以完全在本地工作直到你要送出,在比如利用Git強壯的分支和合并模型。
How It Works / 如何工作
中央倉庫上隻有一個master分支,類似于SVN中的trunk。所有的改變都送出到master分支。
Managing Conflicts / 合并送出和管理沖突
如果送出的時候中央庫上已經有了新的送出,産生了沖突,需要pull中央庫的最新送出,然後rebase自己的修改到最新送出之上。
例如上圖所示,中央庫的master分支origin/master上有了新的送出,本地的master分支上也有了新的送出。
這個時候,如果直接git push,git會報告需要合并修改。通過以下的幾步合并修改并解決沖突。
1)git pull --rebase
加上--rebase選項是為了把本地的送出移動到中央庫分支的master之上,如下圖所示。rebase相當于先擷取中央庫的送出,然後把本地的送出一個一個應用上去。
如果不加上rebase選項,也可以完成合并,但是每次會生成一個“Merge branch ...”的送出。
比如A和B各自添加了檔案0,1,分别送出,合并修改的時候沒有用rebase選項;之後A和B又分别送出了檔案2和3,使用rebase選項解決沖突。
下面是通過”git log --graph --oneline“指令觀察分支圖的結果,可以很清楚的看出其中的差別。
* dc5fbe2 3
* 98eda49 2
* cfc8ea7 Merge branch 'master' of https://github.com/gitworkflow/learngit
|\
| * a3aa78c 0
* | 90cc835 1
|/
* b2d5caf new
2)如果中央庫的送出和本地送出沒有修改相同檔案,那麼現在可以直接push了。否則的話,git會在合并有沖突的地方暫停。
比如現在A修改了檔案0和1,A的送出曆史如下并且已經送出到了中央庫。
* a014fa1 vi 1
* fb1193a vi 0
* dc5fbe2 3
* 98eda49 2
B也修改了檔案0和1,B的本地送出曆史如下
* 57e6473 edit 1
* d0ea63e edit 0
* dc5fbe2 3
* 98eda49 2
現在B運作”git pull --rebase“指令,git會先拉取到中央庫的最新送出a014fa1,然後在其之上應用本地的送出d0ea63e和57e6473。
由于有沖突,git會報告下面錯誤并暫停rebase。
......
CONFLICT (content): Merge conflict in 0
......
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
這裡git給出的提示也非常直接和詳細。
這個時候也可以運作git status擷取到詳細沖突資訊。
那接下來,我們需要
2.1) 編輯沖突檔案,解決沖突。
2.2) git add --all
2.3) git rebase --continue
這個時候git會報告 CONFLICT (content): Merge conflict in 1,接下來沖突上面的操作直到解決全部沖突。
在這個過程中的任何時候,如果我們需要放棄這次合并,可以運作“git rebase --abort”指令,就會回到第一步運作”git pull --rebase"之前的狀态。
2.4) git push
最後别忘記push送出到中央庫。
Feature Branch Workflow / 特性分支工作流
如果習慣了上面的集中式工作流,就很容易添加特性分支來在開發者之間鼓勵合作和提高交流效率。
特性分支工作流的核心思想是所有的特性開發都必須在一個獨立分支上進行。這也意味着master分支上永遠不應該包含未完成的代碼。這對于持續內建來說是非常重要的。
How It Works / 如何工作
中央倉庫上也有一個master分支,代表正式的項目曆史。
但是開發者不能把修改送出到本地的master分支。開發者每次開發一個新的特性之前,必須建立一個新的分支,注意給分支取一個有意義的名字。
此外特性分支也需要push到中央倉庫,這樣才能和其他開發者分享代碼。當然這樣也便于備份本地送出。
Pull Request
特性分支也使得通過pull request來讨論變更成為可能。
一旦某個開發者完成一個feature,不能馬上合并到master分支,必須把feature分支push到中央庫,然後發起一個pull request來請求合并到master分支。這樣其他開發者就可以review修改。
其實除了進行code review,pull request也是進行讨論的一個方式。
比如在Github的pull request發起界面,你可以填寫comment提出問題,收到request的開發者可以review代碼提出意見,也可以填寫comment來回答問題。
讨論中,開發者可以對這個feature 分支再送出修改,這些送出曆史以及讨論内容都會顯示在這個pull request的曆史記錄中。
最終,如果這個pull request得到認可,就可以合并到master分支上。
可以通過Github等工具提供的類似[Merge pull request]按鈕來完成合并,也可以通過下面的指令行合并。
git checkout master
git pull origin master
git merge origin/feature-xxx --no-ff
git push
首先pull master分支的最新修改,然後合并feature分支到master分支。
加上 --no-ff選項,可以確定不使用fast forward合并,這樣雖然上面的過程會産生一個合并送出,但是保留了feature分支的送出曆史,這樣通過git log --graph可以看到feature分支的曆史資訊。
(如果要保證線性的送出曆史,可以去掉 --no-ff選項,但是對于feature分支工作流,不建議這麼做)
Gitflow Workflow / Gitflow工作流
Gitflow工作流圍繞着項目釋出定義了嚴格的分支模型。雖然比較複雜,但是對于大型項目管理提供了一個健壯的架構。
和feature分支工作流相比,它使用了獨立的分支來管理釋出。
參考下圖,Gitflow工作流涉及到2個主分支(master,develop)和3個支援分支(feature,release,hotfix)。
具體介紹可以看這篇文章http://blog.csdn.net/fw0124/article/details/50426740
Forking Workflow / Forking工作流
前面介紹的幾種工作流都是隻有一個伺服器端倉庫(中央倉庫)。Forking工作流和上面的幾種工作流之間有一個根本性的差別是每個開發者有自己的服務端倉庫。這意味着每個開發者有兩個伺服器端倉庫,一個是公共的倉庫(offical repo),一個是開發者自己的倉庫。
開發者隻有權限送出修改到私有伺服器端倉庫,隻有項目維護者有權限push修改到公共倉庫。
從技術的角度,對于Git 來說,這兩種伺服器端倉庫沒有差別,稱作正式倉庫隻是因為它是項目維護者的伺服器端倉庫。
How It Works / 如何工作
使用Forking工作流,項目進展方式如下:
1)項目維護者建立公共倉庫(offical repo)
2)項目開發者fork公共倉庫,建立私有伺服器倉庫拷貝。
其他的開發者不能push到這個倉庫,但是可以pull其修改。
開發者可以使用分支以便于和其他開發者分享修改。
3)項目開發者clone私有伺服器倉庫到本地。
4)項目開發者進行開發。
5)項目開發者push修改到私有伺服器倉庫。
6)項目開發者發起一個pull request通知維護者需要送出修改到正式倉庫。
如上面所說,pull request也在開發者之間提供了一個便利的讨論方式。
7)項目維護者pull開發者的送出到本地,merge到本地的master分支上,并進行檢查,如果沒有問題,push到正式倉庫的master分支。
維護者可以在Github等工具提供的pull request的界面中直接審查修改,評論和執行合并。
不過如果出現了合并沖突,需要到指令行執行如下指令來解決沖突。
git fetch https://bitbucket.org/user/repo feature-branch
# Inspect the changes
git checkout master
git merge FETCH_HEAD
git push origin master
8)其他的開發者可以同步正式倉庫的修改到私有倉庫中。
git pull upstream master
對于開源項目,Fork工作流是一個非常理想的工作方式。
Forking工作流對于松散組織的團隊來說是個非常強大的工具。任和開發者可以友善地和其他開發者分享變更,任何分支都能夠有效地合并到正式代碼庫中。