最近在參與一個(gè)叫 Exercism 的項(xiàng)目哭当,這是一個(gè)由 GitHub 生態(tài)工程師 Katrina Owen 發(fā)起的編程練習(xí)社區(qū),提供了超過50門語言的練習(xí)溺蕉。作為用戶某宪,你僅需使用命令行工具即可下載和提交練習(xí),提交后還可以和社區(qū)中其他學(xué)習(xí)者交流討論掏秩。
在和世界各地的小伙伴們愉快地玩耍了十來天之后或舞,我覺得可以為這個(gè)社區(qū)貢獻(xiàn)些什么。由于我比較擅長 Python蒙幻,所以決定為 exercism/python 這個(gè)倉庫貢獻(xiàn)代碼映凳。我知道很多朋友都是在“單機(jī)模式”下使用 GitHub ——一個(gè)人默默地維護(hù)自己的倉庫,卻始終沒有和他人互動(dòng)乃至協(xié)作過邮破。接下來我將分享第一次踏入開源世界诈豌、“聯(lián)機(jī)”使用 GitHub 的體驗(yàn),并將對(duì)應(yīng) GitHub Flow 手把手教會(huì)你怎么提交 Pull Request(下面簡稱 PR )抒和。
在閱讀本文前矫渔,希望你已經(jīng)具備以下條件:
已經(jīng)安裝好 Git,能夠用命令行進(jìn)行基本的 Git 操作构诚。如果不熟悉 Git蚌斩,你可以看一下廖雪峰的 Git 教程;如果你對(duì)自己的英文有自信范嘱,那么 Learn Git Branching 這個(gè)互動(dòng)教程一定會(huì)讓你受益更多送膳!
已經(jīng)注冊(cè)一個(gè) GitHub 帳號(hào),并對(duì) GitHub 的一些基本概念(例如倉庫丑蛤,Issue叠聋,還有我們討論的核心 PR)有所了解。
擁有一顆在開源社區(qū)呼風(fēng)喚雨的雄心壯志(手動(dòng)滑稽)受裹。
尋找值得解決的 Issue
通常碌补,貢獻(xiàn)的第一步是尋找值得解決的 Issue。選擇正確的 Issue 并不簡單棉饶,除了力所能及厦章,還要看一下有沒有人已經(jīng)在解決這個(gè)問題。你可以看一下這個(gè) Issue 是否有人已經(jīng)回復(fù)打算解決照藻,或者在 Pull Requests 列表中搜索一下有沒有人已經(jīng)在解決袜啃,否則真的就是在浪費(fèi)寶貴的時(shí)間。
這里我們選擇解決 rational-numbers: implement exercise幸缕,任務(wù)就是實(shí)現(xiàn)一個(gè)新的練習(xí)群发,名為 rational-numbers晰韵。之所以選擇這個(gè) Issue,是因?yàn)閭}庫維護(hù)者很認(rèn)真地把實(shí)現(xiàn)新練習(xí)的詳細(xì)步驟都告訴了我們熟妓,而且也沒有其他人領(lǐng)這個(gè)任務(wù)雪猪。于是我們回復(fù)一句“I'll work on this”,既是通知維護(hù)者起愈,也是告訴其他人我們已經(jīng)負(fù)責(zé)解決這個(gè) Issue只恨,請(qǐng)不要重復(fù)勞動(dòng)。
好了告材,開始動(dòng)手吧坤次!
準(zhǔn)備工作
第一步:Fork原倉庫
在頁面右上角點(diǎn)擊 Fork 按鈕,隨后就生成了屬于你的倉庫斥赋。
為什么要先 Fork 別人的倉庫呢?不能直接把 exercism/python 直接 clone 到本地嗎产艾?
如果你只是打算閱讀別人的源代碼疤剑,這樣做當(dāng)然是可以的,然后每次你只需要 git pull
就可以獲取最新的代碼了闷堡。但如果你打算貢獻(xiàn)代碼就不能簡單地 clone 了隘膘。由于你不具備 exercism/python 的 write access(寫入權(quán)限),因此你就無法使用 git push
來推送你的修改杠览,這時(shí)候就只能 Fork 到自己的帳號(hào)下弯菊,GitHub 會(huì)為你生成 mRcfps/python(mRcfps 是我的 GitHub 用戶名)。對(duì)于這個(gè) Fork 來的倉庫踱阿,你就擁有了所有的權(quán)限(克隆管钳、修改甚至是刪除倉庫)。這時(shí)候就可以進(jìn)行任何想要的修改了软舌。
第二步:將 Fork 來的倉庫 clone 到本地
在命令行中輸入下面的命令:
$ git clone https://github.com/mRcfps/python.git
第三步:創(chuàng)建新分支
這一步是最容易被忽視的一步才漆,卻恰恰是正式開始貢獻(xiàn)的第一步。
$ git checkout -b impl-rational-numbers
這里我創(chuàng)建了一個(gè)新的分支 impl-rational-numbers佛点,意思就是 implement (exercise) rational numbers醇滥。在創(chuàng)建分支時(shí)要盡量保證易于記憶、易于辨認(rèn)超营,這樣做有兩點(diǎn)好處:
- 在多個(gè)分支上切換工作時(shí)會(huì)方便很多
- 在提交 PR 后便于倉庫主進(jìn)行維護(hù)
一個(gè)需要思考的問題是鸳玩,為什么需要開新的分支?不能直接在 master 分支上修改嗎演闭?
先給出簡單的回答:能不跟,但是非常非常不推薦。
根據(jù) GitHub Flow 的定義船响,master 分支應(yīng)當(dāng)確保始終是可部署的(deployable)躬拢,所以在 master 分支上進(jìn)行開發(fā)和嘗試是非常不推薦的做法躲履。而且 GitHub 的 PR 都是以分支為單位的,如果你選擇 master 分支進(jìn)行開發(fā)聊闯,那么當(dāng)你想要解決另一個(gè) Issue 的時(shí)候就會(huì)變得非常棘手(熟悉 Git 的朋友也許會(huì)說可以通過回退節(jié)點(diǎn)再開新分支工猜,但是那樣分支管理就會(huì)變得一團(tuán)糟)。
貢獻(xiàn)代碼
這里就不展示全部的工作了菱蔬,具體代碼變化請(qǐng)參考這里篷帅。
$ git status
On branch impl-rational-numbers
Changes not staged for commit:
(use"git add <file>..."to update what will be committed)
(use"git checkout -- <file>..."to discard changes in working directory)
modified: config.json
modified: exercises/rational-numbers/example.py
modified: exercises/rational-numbers/rational_numbers.py
modified: exercises/rational-numbers/rational_numbers_test.py
modified: exercises/rational-numbers/README.md
no changes added to commit (use "git add" and/or "git commit -a")
好的,然后我們進(jìn)行 commit:
$ git commit -am "rational-numbers: implement exercise"
提交工作
接下來就是激動(dòng)人心的時(shí)刻:提交我們貢獻(xiàn)的代碼拴泌!
首先魏身,我們需要把修改提交到 mRcfps/python ,也就是我們自己的遠(yuǎn)程倉庫蚪腐。
$ git push -u origin impl-rational-numbers
選項(xiàng) -u
等同于 —set-upstream
箭昵。impl-rational-numbers 就是我們剛才進(jìn)行修改的分支。然后回季,我們打開 mRcfps/python家制,也就是我們 Fork 來的倉庫,會(huì)看到一點(diǎn)小小的變化:
實(shí)際上如果你打開 exercism/python 的頁面也會(huì)出現(xiàn)這樣的提示泡一。然后點(diǎn)擊按鈕 Compare & pull request颤殴,開始編輯我們的 PR:
這里要說明一下 GitHub 關(guān)鍵詞:當(dāng)使用 fix(es),close(s) 或 resolve(s) 時(shí)鼻忠,如果這個(gè) Pull Request 被合并涵但,會(huì)自動(dòng)關(guān)閉對(duì)應(yīng)的 Issue。這里我標(biāo)出了 Closes #1300帖蔓,那么當(dāng)我們貢獻(xiàn)的代碼被接受時(shí)矮瘟,就會(huì)關(guān)閉 rational-numbers: implement exercise 這個(gè) Issue。正確地使用 GitHub 關(guān)鍵詞能夠極大地方便倉庫維護(hù)者讨阻,他們就不需要去查找對(duì)應(yīng)的是哪個(gè) Issue 并去手動(dòng)關(guān)閉它了芥永。
點(diǎn)擊 Create pull request,進(jìn)行提交钝吮!
討論和評(píng)審
接下來就是等待埋涧。第二天起來發(fā)現(xiàn)倉庫維護(hù)者回復(fù)我們了。
他指出我們修改的 config.json 有問題奇瘦,并給出了修改建議棘催。至于他手抖一不小心 approve 我們的修改就不必在意了(再次手動(dòng)滑稽)。
按照他的建議修改好之后耳标,我們提交新的修改醇坝。
$ git commit -am "rational-numbers: fix topics in config.json"
$ git push
可以打開 PR 頁面查看我們新的修改。
部署階段
倉庫維護(hù)者同意了我們的修改!
然后他將 exercism/python 的 master 分支并入了我們的 impl-rational-numbers 分支呼猪。他為什么要這么做呢画畅?因?yàn)楫?dāng)我們?cè)谶@個(gè)分支上工作時(shí),exercism/python 的 master 分支上可能提交了新的修改宋距,導(dǎo)致我們的分支并不是最新的轴踱。
通過將 master 分支并入我們的分支,我們的分支就能進(jìn)入即將部署(Ready to Deploy)狀態(tài)了谚赎。如果這時(shí)候 CI (持續(xù)集成淫僻,會(huì)在后面講到)報(bào)錯(cuò),這就說明我們的分支還不能部署壶唤,還需要進(jìn)一步修改甚至是回滾雳灵。但是這里,我們合并后的分支通過了 CI 的測試闸盔。
合并階段
維護(hù)者緊接著就將我們的分支正式并入了 exercism/python 的 master 分支悯辙,這意味著我們的 PR 畫上了圓滿的句號(hào),我們的貢獻(xiàn)真正地進(jìn)入到了原倉庫迎吵!
由于 impl-rational-numbers 已經(jīng)合并笑撞,可以安全刪除,所以我們點(diǎn)擊 Delete branch 按鈕钓觉,刪除我們遠(yuǎn)程倉庫 mRcfps/python 中的分支。然后在本地輸入下面的命令坚踩,刪除本地分支:
$ git checkout master
$ git branch -D impl-rational-numbers
這里為什么使用 -D
進(jìn)行強(qiáng)制刪除呢荡灾?因?yàn)?impl-rational-numbers 在本地并沒有與 mRcfps/python 的 master 分支合并(不要繞暈了,剛才我們只是和 exercism/python 的 master 進(jìn)行了合并)瞬铸。我們自己的 master 分支推薦用下面介紹到的方法進(jìn)行同步批幌。
一些補(bǔ)充
關(guān)于 PR 生命周期的介紹就到此結(jié)束了。接下來我會(huì)講一些相關(guān)的較為重要的地方嗓节。
保持 Fork 來的倉庫同步
如果其他的貢獻(xiàn)者向 exercism/python 提交代碼荧缘,或者是我們自己提交的代碼,我們的 mRcfps/python 就會(huì)過時(shí)拦宣。要經(jīng)常保持我們 Fork 來的倉庫與原倉庫同步截粗,這樣能盡可能地降低沖突發(fā)生的概率。接下來還是以我們 Fork 來的倉庫 mRcfps/python 為例鸵隧,來與 exercism/python 保持同步绸罗。
首先,查看 mRcfps/python 有哪些遠(yuǎn)程倉庫豆瘫。這里應(yīng)該只有 origin珊蟀。
$ git remote -v
origin https://github.com/mRcfps/python.git (fetch)
origin https://github.com/mRcfps/python.git (push)
然后,將 exercism/python 添加進(jìn)我們的 remote 倉庫中外驱,將其命名為 upstream(當(dāng)然也可以取其他名字育灸,但是按照慣例會(huì)更加方便)腻窒。
$ git remote add upstream https://github.com/exercism/python.git
再看看 remote 列表中是不是多了些什么……
$ git remote -v
origin https://github.com/mRcfps/python.git (fetch)
origin https://github.com/mRcfps/python.git (push)
upstream https://github.com/exercism/python.git (fetch)
upstream https://github.com/exercism/python.git (push)
upstream 已經(jīng)在 remote 列表中!然后我們就可以輕松地進(jìn)行同步了磅崭。先確保當(dāng)前處在 master 分支上儿子,然后獲取 upstream 的修改,再并入我們本地的 master 分支绽诚。
$ git checkout master
$ git fetch upstream
$ git merge upstream/master
再把本地的更新 push
到 origin典徊,也就是我們的 GitHub 倉庫:
$ git push
同步工作完成!
持續(xù)集成
很多開源項(xiàng)目都會(huì)通過持續(xù)集成(Continuous Integration恩够,簡稱 CI)來確保代碼質(zhì)量卒落。對(duì)于我們貢獻(xiàn)者來說,這意味著每次提交 PR 和繼續(xù) push 代碼蜂桶,CI 都會(huì)對(duì)我們的提交進(jìn)行構(gòu)建并執(zhí)行倉庫維護(hù)者指定的檢查儡毕,例如代碼風(fēng)格檢查、單元測試等等扑媚。
如果你查看你新提交的 PR腰湾,你會(huì)發(fā)現(xiàn)右上角有個(gè)黃色的圓圈,這表示 CI 正在檢查你的提交疆股。
當(dāng)右上角的黃色圓圈變成綠色的勾费坊,就表示你的代碼通過了 CI !
有時(shí)候會(huì)出現(xiàn)紅色的叉旬痹,表示未通過 CI 測試附井。
這時(shí)候我們就需要進(jìn)入 PR 頁面,翻到最下面两残,查看 Travis-CI (這個(gè)倉庫使用的是流行的 Travis CI)檢查的詳細(xì)信息永毅,找出錯(cuò)誤原因后進(jìn)行修改,然后 git push
提交我們的修改即可人弓,直到通過 CI沼死。
總結(jié)
可能步驟有點(diǎn)復(fù)雜,所以這里總結(jié)一下 Pull Request 的生命周期 :
確定要貢獻(xiàn)的項(xiàng)目崔赌,尋找值得解決的 Issue意蛀。
將原倉庫 Fork 到自己的帳號(hào)下,然后克隆到本地峰鄙。
$ git clone https://github.com/<YOUR_USERNAME>/<FORKED_REPO>.git
- 開啟新分支浸间,修改代碼并提交。
$ git checkout -b <NEW_BRANCH_NAME>
$ git commit -am "<COMMIT_MESSAGE>"
$ git push -u origin <NEW_BRANCH_NAME>
打開倉庫的 GitHub 頁面吟榴,點(diǎn)擊提示的 Compare & pull request 按鈕魁蒜,填寫 PR 信息(記得使用 GitHub 關(guān)鍵詞關(guān)閉對(duì)應(yīng)的 Issue)然后提交。
如果 CI 測試未通過,或者倉庫維護(hù)者要求修改(request changes)兜看,那么就在本地繼續(xù)修改代碼锥咸,然后
git push
再次提交,直到通過 CI 和倉庫維護(hù)者的評(píng)審细移。倉庫維護(hù)者部署和并入你的分支搏予,貢獻(xiàn)完成。
親自實(shí)踐
這篇文章的 GitHub 倉庫在這里弧轧,你可以隨意地發(fā)起 Issue 或 Pull Request雪侥。如果你只是想要親自實(shí)踐一下上面所講的內(nèi)容,就請(qǐng)?jiān)?THOUGHTS.md 中隨意寫下你的想法并提交給我精绎,我會(huì)盡快合并你的分支速缨。當(dāng)然如果你對(duì)本文有改進(jìn)意見,那更歡迎你的 Pull Request代乃,讓這篇文章變得更好旬牲!