本文由圖雀社區成員 mRc 寫做而成,歡迎加入圖雀社區,一塊兒創做精彩的免費技術教程,予力編程行業發展。javascript
在大多數工做中,咱們都將使用 Git 做爲團隊協做開發的工具。css
本文總結了圖雀團隊協做開發的流程與規範,僅供參考。最優的解決方案仍是須要結合團隊的實際狀況,具體問題具體分析。html
爲了讓你們可以很是清晰直觀地瞭解協做開發的流程,你們在看的時候能夠打開 Learn Git Branching 的沙箱運行環境來實踐(能夠直接輸入提供的代碼)。在左邊的終端中輸入命令,就會在右邊看到相應的動畫。其中左邊的實線圓圈表明本地倉庫,右邊的虛線圓圈表明遠程倉庫,*
號指向的是當前分支,o/master
就是遠程分支( o
就至關於 origin
)。java
因爲 Learn Git Branching 爲了演示和學習的方便對部分命令作了簡化,我將指出在實際操做中應當輸入的命令。git
接下來將重點講述如下兩個流程:github
貢獻代碼shell
更新本地倉庫編程
接下來的流程描述了在接到開發任務後,如何爲中心倉庫貢獻代碼。設計模式
$ git clone
複製代碼
實際命令應當提供 URI 參數,例如:bash
$ git clone https://github.com/dhucst/cooperation.git 複製代碼
$ git checkout -b B1
複製代碼
爲了便於演示,咱們將新分支命名爲 B1
。在實際開發中,新分支的命名應當遵循如下原則:
使用 kebab-case,例如 new-branch
,而不是 new_branch
或 newBranch
儘可能能歸納這個分支所要完成的任務
若是是爲了解決某個 Issue,在最後加上 Issue 的編號,例如 fix-75
$ git commit
複製代碼
實際命令應當要先執行
git add
來將修改的文件添加到暫存區,例如:$ git add . $ git commit 複製代碼
Commit Message (Log) 的書寫是有比較嚴格的規範的,會在後文的 提交信息書寫規範 中詳細闡述。
$ git push
複製代碼
實際命令在第一次 push 任何分支時,應當指定 remote 和分支名稱:
$ git push origin B1 複製代碼
有時候咱們的分支會在一晚上之間「過期」。什麼是過期的分支,咱們該怎樣處理?不要方,後面會講到。
這一步驟無需在 Learn Git Branching 中操做。
將分支提交到遠程倉庫後,打開倉庫的 GitHub 頁面,應該會看到下面這樣黃色的提示框:
而後點擊 Compare & pull request 按鈕,便可進入到提交 Pull Request 頁面。
填寫 Pull Request 標題所遵循的原則與 Commit message 大體類似。在填寫 Pull Request 的詳細內容時,若是是爲了解決某個或多個 Issue 時,可使用 Close(s)
, Fix(es)
或 Resolve(s)
關鍵詞來關閉某個 Issue,例如 Fix #75
。
點擊 Create pull request 按鈕後,便可完成本次 PR。若是經討論後發現須要修改,則在本地倉庫修改後直接 git push
繼續提交便可。若是代碼經過了評審,則會由項目管理者將此分支併入 master 中,本次貢獻代碼流程結束。
接下來的流程介紹了當團隊其餘成員貢獻代碼後,如何將遠程倉庫的更新同步到本地。
若是你在使用 Learn Git Branching 邊看邊練,請輸入如下命令:
$ reset
$ git clone
複製代碼
$ git fakeTeamwork 2
複製代碼
實際沒有這條 Git 命令 😂,是 Learn Git Branching 提供用於練習協做的。
這時候你會發現遠程的倉庫有了本地沒有的提交 C2
和 C3
。
咱們先來看第一種比較簡單的狀況:
這時候一眼就能夠看出,只需把遠程的 C2
和 C3
直接拉取過來接在本地的 C1
後面就能夠了:
$ git pull
複製代碼
接着咱們來看另外一種比較棘手的狀況:
須要輸入的命令以下:
$ reset
$ git clone
$ git checkout -b B2
$ git commit
$ git fakeTeamwork 2
複製代碼
對着圖看,咱們在 B2
分支上在開發某個新功能,這時候遠程倉庫已經更新到了 C4
,很顯然咱們本地的 master 分支和 B2
分支都不是最新的了。這種狀況很常見:幾個小夥伴從同一個起點(在這裏就是 C1
)各自開發新功能時,其餘人先於咱們提交。
大多數狀況下,請遵循這一條原則:只更新 master 分支。
這一原則對於並行開發並不適用,咱們會在本知識庫後續文檔中講解。
$ git checkout master
$ git pull
複製代碼
這時候,咱們就會認爲 B2
分支已經過期(outdated),由於它沒有最新的 C3
和 C4
。但過期的分支並不意味着沒有價值了,咱們能夠像前面所講解的那樣 push 到遠程倉庫:
$ git checkout B2
$ git push
複製代碼
而後同樣能夠發起 Pull Request。GitHub 會提示你這條分支已通過時,你能夠點擊 Update Branch 按鈕來更新這一條分支(一般由項目管理者來執行這一操做)。
團隊協做開發的模型只涉及兩個核心流程:貢獻代碼和更新本地倉庫。
貢獻代碼的流程:
$ git clone <REPO_URI>
$ git checkout -b new-branch
$ git add .
$ git commit
$ git push origin new-branch
複製代碼
更新代碼庫的流程:
$ git checkout master
$ git pull
複製代碼
一個項目的開發每每由多個開發任務組成,每一個人都會負責承擔一個或多個開發任務。最簡單、最理想的狀況固然是:同窗 A 開始貢獻代碼,成功合併後全部人更新本地代碼庫;接着同窗 B 開始貢獻代碼,合併後全部人更新本地代碼庫;而後是同窗 C、D、E……
不會有任何衝突,只需用到前面 基本流程 所介紹到的命令,多麼輕鬆愉快!
惟一的問題就是:這樣的開發顯然進度很慢,並且你們的時間安排也不夠自由。這種串行開發的方式過於同步化,對於一個追求效率的團隊來講是不能接受的。咱們須要高度並行、徹底異步的協做開發模式。
接下來咱們將描述三個典型的並行開發場景,其中的主角是大唐同窗和煨鴿同窗。
例若有個着陸頁開發的任務,大唐負責作「關於咱們」頁面,叫 about-us.html,煨鴿負責作「聯繫咱們」頁面,叫 contact.html,這兩個文件相互獨立的。
這裏咱們假定大唐同窗率先完成了任務而且已經合併到 origin/master
。這時候根據前一章 更新本地倉庫 一節的說法,煨鴿正在工做的分支已經「過期」。這時候他只須要繼續完成他的 contact.html 頁面,而後提交就能夠了。
這是最簡單的,也是最多見的狀況(合理的任務劃分應當如此):相互獨立的分支只需依次 push,無論是否過期。
如今咱們又假設大唐在開發着陸頁的首頁 index.html,煨鴿負責寫着陸頁的樣式 index.css,很明顯大唐的開發任務依賴煨鴿。通過一天的開發,大唐寫完了主體部分 C2
,煨鴿也寫好了樣式 C3
而且已經提交到遠程倉庫,如今他須要把煨鴿的樣式表加進來,才能完成本身的開發任務。
在 Learn Git Branching 中輸入如下代碼:
$ git clone
$ git checkout -b html
$ git commit
$ git fakeTeamwork
複製代碼
而後大唐使用 fetch 命令將遠程的 C3 抓取下來(其實更嚴格的說法是將本地的 o/master
分支與遠程的 master
同步):
$ git fetch
複製代碼
能夠看到,html 文件的提交和 css 文件的提交在不一樣的分支上。html 是咱們工做的分支(也是當前所在的分支),所以要把 C3
所在的 o/master
合併過來:
$ git merge o/master
複製代碼
這個形狀看上去有點嚇人!實際上,你只要真正理解分支的本質就會以爲很是好理解。
分支不能簡單地理解爲一串 commit(雖說在大多數狀況下這種理解很是直觀),而應該理解爲指向某個 commit 的指針,而該 commit 的全部父節點都是該分支上的節點(commit)。所以在執行合併後,咱們能夠說 C2
和 C3
都已經在 html
分支上了。
合併以後,咱們再修改點東西,提交爲 C5
,而後推送到遠程倉庫:
$ git commit
$ git push
複製代碼
再次提醒真正的 push 命令在第一次推送某一分支時要加上遠程倉庫名稱和分支名稱,例如
git push origin html
。
接下來就是提交 Pull Request、等待合併就能夠了。
首先聲明這種狀況很是少見,合理的任務劃分會盡可能避免這種狀況出現。可是咱們仍是會講解一下這種比較棘手的狀況。因爲 Learn Git Branching 沒有提供衝突(conflict)的演示,因此咱們須要本身在本地開倉庫進行演示。
爲何在本地開倉庫練習就能夠了,而不須要搭一個遠程倉庫嗎?由於本小節的操做流程和命令跟上一節相比,除了增長了一個處理衝突的步驟,其他徹底相同,所以咱們關注的重點是怎麼處理衝突。
$ mkdir conflict-demo && cd conflict-demo
$ git init
$ touch index.js
$ git add .
$ git commit -m "Add index.js"
複製代碼
而後咱們開啓一個新分支 add-func
:
$ git checkout -b add-func
複製代碼
在 index.js 中增添一個 add
函數:
function add(x, y) {
return x + y;
}
複製代碼
保存並提交:
$ git add .
$ git commit -m "Implement add function"
複製代碼
而後咱們切回主分支,並開啓一個叫 origin-master
的分支(聽這名字也知道,它模擬了遠程的主分支):
$ git checkout master
$ git checkout -b origin-master
複製代碼
接着再在 index.js 中添加一個叫 multiply
的函數:
function multiply(x, y) {
return x * y;
}
複製代碼
好了,如今本地的 add-func
工做分支和「遠程」的 origin-master
分支修改了同一文件 index.js,衝突一觸即發!讓咱們來點燃這根導火索!
其實你能夠不停地把分支切來切去(輪流輸入
git checkout add-func
和git checkout origin-master
),你會看到 index.js 的內容會隨之變來變去,版本控制系統的魅力可見一斑。
$ git checkout add-func
$ git merge origin-master
複製代碼
咱們會發現 Git 會輸出你從未見過的信息:
Auto-merging index.js
CONFLICT (content): Merge conflict in index.js
Automatic merge failed; fix conflicts and then commit the result.
複製代碼
劃重點:index.js 在合併時發生衝突,請處理衝忽然後提交。
咱們查看 index.js 的內容,發現了很神奇的東西(在命令行中用 cat 查看):
<<<<<<< HEAD
function add(x, y) {
return x + y;
=======
function multiply(x, y) {
return x * y;
>>>>>>> origin-master
}
複製代碼
若是咱們用 VSCode 打開,會看到更炫酷的結果:
這就一目瞭然了!綠色部分是咱們當前分支 add-func
的內容,藍色部分是 origin-master
的內容。因爲咱們二者都要,因此點擊 Accept Both Changes。而後略經修正,將 index.js 改成以下:
function add(x, y) {
return x + y;
}
function multiply(x, y) {
return x * y;
}
複製代碼
提交咱們用於處理衝突的 commit:
$ git add .
$ git commit -m "Merge conflict of index.js"
複製代碼
衝突處理完成,咱們提交此分支,任務完成。
並行開發是 Git 團隊協做中比較高級卻又很是重要的部分。平時大多數狀況下,咱們遇到的都是第一種狀況。若是「不幸」遇到了後面兩種狀況,不熟悉時能夠回來看一看這篇文檔。
PS:對於後面兩種狀況,有一點須要補充:若是想要撤銷 merge,使用下面這條命令:
$ git merge --abort
複製代碼
提交信息,又稱爲 commit messages 或者 commit logs,是每一步提交所必需的信息。咱們能夠看一下 React 倉庫的提交記錄:
由此咱們能夠對項目每一步作了什麼有了比較好的瞭解。
每次提交,Commit message 都包括三個部分:Header,Body 和 Footer。
<header>
// 空一行
<body>
// 空一行
<footer>
複製代碼
其中,Header 是必需的,Body 和 Footer 能夠省略。
Header部分只有一行,是對 commit 的簡短概述,是一個包括動賓結構和修改對象(可選)的祈使句(不要加句號!)。
咱們來看幾個例子。
Remove 'warning' module from the JS scheduler
複製代碼
這裏的動賓結構是 Remove 'warning' module,修改對象是 JS scheduler。
Add @flow directive to findDOMNode shim
複製代碼
這裏的動賓結構是 Add @flow directive,修改對象是 findDOMNode shim。
Update www warning shim
複製代碼
這裏動賓結構是 Update www warning shim,因爲修改對象已經很明確(在動賓結構中),因此無需再寫。
Body 部分是對本次 commit 的詳細描述,能夠分紅多行。下面是一個範例。
More detailed explanatory text, if necessary. Wrap it to
about 72 characters or so.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Use a hanging indent
複製代碼
有兩個注意點。
使用第一人稱如今時,好比使用 change
而不是 changed
或 changes
。
應該說明代碼變更的動機,以及與之前行爲的對比。
Footer 部分只用於兩種狀況。
(1)不兼容變更
若是當前代碼與上一個版本不兼容,則 Footer 部分以 BREAKING CHANGE
開頭,後面是對變更的描述、以及變更理由和遷移方法。
(2)關閉 Issue
若是當前 commit 針對某個issue,那麼能夠在 Footer 部分關閉這個 issue。
Closes #234
複製代碼
也能夠一次關閉多個 issue 。
Closes #123, #245, #992
複製代碼
咱們團隊建議在 Pull Request 中關閉 Issue,如前面基本流程所描述的那樣。
還有一種特殊狀況,若是當前 commit 用於撤銷之前的 commit,則必須以 revert:
開頭,後面跟着被撤銷 Commit 的 Header。
revert: feat(pencil): add 'graphiteWidth' option
This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
複製代碼
Body 部分的格式是固定的,必須寫成 This reverts commit <hash>.
,其中的 hash
是被撤銷 commit 的 SHA 標識符。
一開始寫 Commit Message 的時候不免會出現寫得很差的狀況,通常狀況下會有人建議你如何寫得更好,或者你本身想到了更合適的寫法。這時候該如何修改呢?
若是你要修改的就是最近一次提交,那就很是簡單了。Git 有專門的命令用於輕鬆修改剛纔的提交:
$ git commit --amend
複製代碼
而後就會進入 vi 界面從新編輯你的提交信息。固然也能夠直接用 -m
選項指定提交信息:
$ git commit --amend -m "Updated commit message"
複製代碼
想要在 Learn Git Branching 看看怎麼回事?輸入下面的命令體驗一下吧:
$ git commit
$ git commit --amend
複製代碼
下面要介紹的 rebase
命令威力能夠說是很是巨大,可是要掌握卻實屬不易。沒事,咱們先來看看如何用 rebase
修改倒數第 3 次提交:
$ git rebase -i HEAD~3
複製代碼
-i
的意思是 --interactive
,輸入後 Git 就會打開一個 vi 編輯器,並出現下面的內容:
pick 0f78800 倒數第4次提交
pick 459014c 倒數第3次提交
pick 38009c7 倒數第2次提交
pick dff7f7d 最新的提交
# Rebase 500d110..dff7f7d onto 500d110 (4 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
複製代碼
其實若是你認真看一下下面的註釋,基本上知道怎麼作了:把倒數第三次提交前面的 pick
命令改成 reword
。保存後,Git 就會把你帶到倒數第 3 次提交的 vi 編輯頁面,這時候從新寫提交信息就能夠了。
你也能夠在 Learn Git Branching 中體驗一下 rebase
命令:
$ reset
$ git commit
$ git commit
$ git rebase -i HEAD~3
複製代碼
Learn Git Branching 提供的是圖形化界面,和 Git 的 vi 界面略有區別。
有時候可能你已經把分支 push
到遠程倉庫、甚至已經提交了 Pull Request 了。若是直接 push
,Git 會由於遠程和本地的分支衝突而拒絕推送。這時候只須要加上 -f
選項,強制用本地的分支覆蓋遠程的分支便可:
$ git push -f
複製代碼
阮一峯《Commit message 和 Change log 編寫指南》
代碼評審,又稱代碼審查,是軟件開發流程中必不可少的一環。
代碼審查是計算機源代碼的系統性檢驗(有時被稱爲同行評審)。其目的在於找到開發初期所忽略的錯誤,從而提升軟件的總體質量。 ——Wikipedia
代碼評審並不意味着被評審者的能力不足。有下面這些緣由代表代碼評審的重要性。
寫出存在 bug 的代碼再正常不過了。每一個人貢獻的代碼先要通過持續集成(CI,Continuous Integration)的一系列構建測試,而後是人工代碼審查,所以代碼審查能夠說是最後一道防線。
代碼評審不只僅是單純地查找 bug 或是修正格式問題,還包括使代碼更高效。
在一個團隊裏,每一個人都有本身的背景和特長,所以總有人可能提出更聰明的解決方案,更合適的設計模式,或者能下降複雜性或提升性能的方法。
當一個團隊在作一個項目時,想要每一個開發人員致力於應用的每一個部分,這是極不可能的。有時候,會出現這種狀況:在某一段時間,一個開發人員正爲項目的大部分模塊辛苦地工做,而另外一我的則徹底在作別的東西。
經過合做,每一個人均可以相互學習並取得進步。提交代碼者頗有可能從該工做中獲得反饋,並意識到可能存在的問題和須要改進的部分;而審查者也能夠經過閱讀他人代碼學到新的東西,並找出適用於他們本身的工做方案。
代碼評審發生在 Pull Request 階段,代碼提交者能夠請求其餘成員的 Review,以下圖所示。
而後被請求進行評審的成員打開這條 Pull Request 頁面時會出現一個提示框:
咱們點擊 Add your review 按鈕,即進入到 Review 頁面(或者也能夠點擊 Files changed 這個 Tab)。Review 頁面展現了本次 Pull Request 全部發生改動的文件,評審的過程也就是審查這些發生改動的代碼。
直接在 GitHub 的 Pull Request 頁面評審是最基本的方法。對於改動比較小的分支,這種方法徹底足夠。
有時候咱們發現了他人代碼的問題。千萬不要保留你的意見!要把本身的想法有條理地寫下來。咱們能夠選擇特定一行來發表評論,只需把鼠標移到行首,就會顯示一個加號,以下圖。
點擊加號,就能夠對這一行進行評論了:
有時候某些分支的改動很是大,大到須要你在本地親自運行一下,看看是否真的達到了預期的目標。
$ git fetch origin lots-of-changes
$ git checkout logs-of-changes
複製代碼
而後你的本地倉庫就完成切換到待評審分支的狀態了!你能夠試着運行,作各類嘗試,還能夠在本身熟悉的編輯器裏面更加溫馨地閱讀代碼,美滋滋。
不管是直接在 GitHub 仍是在本地審查,最後都要提交評審結果。評審結果包括你在代碼行中的全部評論、Review summary 和最終意見。
Review summary 主要是一些總結性的話語。若是代碼提交者確實作得很是優秀,固然是要誇獎一下喔;若是有些地方作得不足,則要給出改進的方向和一些鼓勵。
最終意見有如下三種:
Comment:只是作一些客觀評價,對此分支是否能夠合併不給出明確意見
Approve:贊成此分支合併進主分支
Request changes:不一樣意此分支合併,須要進一步修改
接着代碼提交者根據其餘人的評審進行修改後提交,而後再繼續評審,如此迭代,直到分支能夠合併。
每一個開發任務都應當只作一件事情,所以所需評審的代碼應可能地少。事實代表,超過 200 行的代碼評審的有效性顯著下降,超過 400 行時代碼評審幾乎沒有意義。
在編寫代碼時應有意識地添加足夠的註釋或文檔,由於你的代碼會被不少人閱讀。良好的註釋可以讓團隊其餘成員評審你的代碼時更加輕鬆,也更容易發現問題所在。另外,在填寫 Pull Request 說明信息時,也應該將所解決的問題、發生的相應改變說明清楚。
不要糾結於代碼風格或是格式問題,這些事情會有專門的工具代勞。你應當關注的是下面這些問題:
代碼是否具有良好的可讀性?
可否實現得更簡潔、更地道?
代碼是否遵循了良好的設計原則?
代碼的空間效率和時間效率怎麼樣?
沒必要過於挑剔,樂於讚賞他人的勞動,學會欣賞他人的代碼。
想要學習更多精彩的實戰技術教程?來圖雀社區逛逛吧。