0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

怎么上手 PR 流程?如何開始參與開源項(xiàng)目?

馬哥Linux運(yùn)維 ? 來源:馬哥Linux運(yùn)維 ? 作者:馬哥Linux運(yùn)維 ? 2022-07-14 10:30 ? 次閱讀

一、概述

今天我準(zhǔn)備和你詳細(xì)介紹如何開始參與開源項(xiàng)目,幫助你在 GitHub 上完成第一個(gè) PR 的合入。

當(dāng)然,除了正常的 PR 合入流程之外,我還準(zhǔn)備詳細(xì)介紹一下如果一個(gè) PR 提交后遇到了沖突、需要追加 commits、需要合并 commits 等等相對復(fù)雜問題該如何解決。

總的來說,本文計(jì)劃分為4個(gè)部分:

談?wù)劄槭裁匆獏⑴c開源項(xiàng)目以及我為什么要介紹如何 PR

談?wù)勗趺撮_始參與開源項(xiàng)目,也就是如何尋找合適的開源項(xiàng)目、如何尋找貢獻(xiàn)點(diǎn)

介紹怎么上手 PR 流程,即從 fork 到 push 全流程

介紹提交了 PR 之后遇到各種常見問題如何解決

Ok, let's get started!

二、為什么要參與開源項(xiàng)目

本文我不打算長篇大論“為什么要參與開源”,詳細(xì)介紹參與開源項(xiàng)目的收獲,我想僅從“提升編碼能力”角度談一談“為什么要參與開源項(xiàng)目”。

在面試的時(shí)候我有個(gè)習(xí)慣,如果候選人在自己的簡歷里說到自己熟悉某一門語言,我就會習(xí)慣性問他一個(gè)問題:

你有沒有閱讀過某個(gè)開源項(xiàng)目的源碼?或者更進(jìn)一步,有沒有參與過某個(gè)開源社區(qū),或者說給開源項(xiàng)目提過 PR

如果答案是肯定的,比如候選人說自己讀過部分 Kubernetes 模塊的源碼,再進(jìn)一步我確認(rèn)他真的讀過并且讀懂了或者說真的提交過 bugfix/feature 類型的 PR,那我就不再問編程語言層面的問題了,因?yàn)槲蚁嘈拍芸炊粋€(gè)成熟的開源項(xiàng)目部分模塊源碼或者能夠提交 bugfix/feature 類型的 PR 已經(jīng)說明了一切。

我自己在學(xué)習(xí) Golang 的時(shí)候,大致分為兩個(gè)階段:

學(xué)習(xí)基礎(chǔ)語法,開始寫項(xiàng)目,直到能夠熟練完成各種業(yè)務(wù)功能的開發(fā);

看了一些開源項(xiàng)目的源碼,深感受益頗多,編碼水平再上一個(gè)臺階。

差不多也就是在看 Kubernetes 項(xiàng)目源碼的時(shí)候,我深刻認(rèn)識到一般的企業(yè)內(nèi)部項(xiàng)目和匯集全世界最優(yōu)秀的程序員智慧結(jié)晶的開源項(xiàng)目之間的巨大差距,也意識到學(xué)習(xí)優(yōu)秀開源項(xiàng)目源碼對于一個(gè)程序員編碼水平提升的重要性(當(dāng)然,你可以說 Google 內(nèi)部也存在非開源的非常優(yōu)秀的代碼,這毫無疑問,但是我想今天我們沒有必要討論特例)。

認(rèn)真閱讀開源項(xiàng)目源碼,你總會發(fā)現(xiàn)一些小瑕疵,這時(shí)候提一個(gè) PR(Pull Request),讓你的代碼合入開源項(xiàng)目,運(yùn)行在“世界每一個(gè)角落”,那是多么有趣的事情!而成功合入第一個(gè) PR 往往就像打開潘多拉魔盒一樣,你會進(jìn)入到另外一個(gè)世界,開始接觸到開源社區(qū),感受開源的魅力!

三、為什么我想介紹如何 PR

我司開源了2個(gè)項(xiàng)目,分別是:

CNCF Project DevStream[1]

0724d66a-02b1-11ed-ba43-dac502259ad0.pngCNCF Project DevStream

Apache DevLake[2]073b77bc-02b1-11ed-ba43-dac502259ad0.png

DevStream 項(xiàng)目和 DevLake 項(xiàng)目隔三差五就會有新貢獻(xiàn)者提交 PR 過來,但是多數(shù)貢獻(xiàn)者在提交第一個(gè) PR 時(shí)往往會遇到一個(gè)或多個(gè)問題,比如產(chǎn)生沖突、commits 記錄過多或者混亂、commit 沒有簽名、commit message 不規(guī)范、各種 ci 流程檢查報(bào)錯(cuò)等等。

在看到新貢獻(xiàn)者提交 PR 時(shí),我們自然是非常開心且熱情地對他表示歡迎并且告知如何修復(fù)各種問題,但是隨著貢獻(xiàn)者的增多,我們的開源社區(qū)幾乎每天都需要回答一個(gè)問題:“如何正確地提交一個(gè) PR”??赡艽藭r(shí)你會開始懷疑我們是不是沒有提供相應(yīng)的文檔?其實(shí)不然,我們有詳細(xì)的文檔,但是人總是有惰性的,多數(shù)的新貢獻(xiàn)者并沒有足夠的意愿去仔細(xì)看翻看文檔然后再提交 PR,甚至很多新貢獻(xiàn)者由于剛開始接觸開源項(xiàng)目,對于項(xiàng)目結(jié)構(gòu)和文檔組織結(jié)構(gòu)比較陌生,甚至不會想到有這些文檔的存在,總之各種各樣的理由讓多數(shù)的新貢獻(xiàn)者會選擇“先提了 PR再說”。

那么今天我想嘗試徹底講明白“如何正確地提交一個(gè) PR”,嘗試細(xì)說 GitHub 上的 PR 全過程,以及這里面可能會遇到的各種困難和解決辦法。一方面希望對第一次參與開源項(xiàng)目的新人有所幫助,另一方面希望能夠進(jìn)一步降低 DevStream 社區(qū)和 DevLake 社區(qū)的參與門檻。

四、我想?yún)⑴c開源項(xiàng)目,怎么開始?

不管你為什么決定開始參與開源項(xiàng)目,不管出發(fā)點(diǎn)是出于學(xué)習(xí)、興趣、成就感等等,還是為了讓某個(gè)自己需要的特性合入某個(gè)開源項(xiàng)目,總之今天你下定決心,要給某個(gè)開源項(xiàng)目提交一個(gè) PR 了,好,我們開始吧!

4.1、尋找一個(gè)合適的開源項(xiàng)目

如果你已經(jīng)決定參與某個(gè)開源社區(qū)了,那么請直接跳過本小節(jié)。

如果你就只是想開始參與開源,暫時(shí)還不知道該參與哪個(gè)社區(qū),那么我有幾個(gè)小建議:

不要從特別成熟的項(xiàng)目開始。比如現(xiàn)在去參與 Kubernetes 社區(qū),一方面由于貢獻(xiàn)者太多,很難搶到一個(gè)入門級的 issue 來開始第一個(gè) PR;另外一方面也由于貢獻(xiàn)者太多,你的聲音會被淹沒,社區(qū)維護(hù)者并不在意多你一個(gè)或者少你一個(gè)(當(dāng)然可能沒有人會承認(rèn),但是你不得不信),如果你提個(gè) PR 都遇到了各種問題還不能自己獨(dú)立解決,那么很可能你的 PR 會直接超時(shí)關(guān)閉,沒有人在意你是不是有一個(gè)好的參與體驗(yàn);

不要從特別小的項(xiàng)目開始。這就不需要我解釋了吧?很早期的開源項(xiàng)目可能面臨著非常多的問題,比如代碼不規(guī)范、協(xié)作流程不規(guī)范、重構(gòu)頻繁且不是 issue 驅(qū)動的,讓外部參與者無所適從……

選擇知名開源軟件基金會的孵化項(xiàng)目,這類項(xiàng)目一方面不是特別成熟,所以對新貢獻(xiàn)者友好;另一方面也不會特別不成熟,不至于給人很差的參與體驗(yàn),比如Apache 基金會、Linux 基金會、CNCF 等

比如可以從這些地方尋找自己感興趣的開源項(xiàng)目:

CNCF 沙箱項(xiàng)目[3]

CNCF 孵化項(xiàng)目(列表包括畢業(yè)項(xiàng)目)[4]

Apache 項(xiàng)目(孵化期項(xiàng)目名字中帶 Incubating)[5]

當(dāng)然,你也可以直接選擇從 CNCF 沙箱項(xiàng)目DevStream[6]或者 Apache 孵化項(xiàng)目Apache DevLake[7],以此敲開開源世界的大門。

4.2、尋找貢獻(xiàn)點(diǎn)

開源項(xiàng)目的參與方式很多,最典型的方式是提交一個(gè)特性開發(fā)或者 bug 修復(fù)相關(guān)的 PR,但是其實(shí)文檔完善、測試用例完善、bug 反饋等等也都是非常有價(jià)值的貢獻(xiàn)。不過本文還是從需要提 PR 的貢獻(xiàn)點(diǎn)開始上手,以 DevStream 項(xiàng)目為例(其他項(xiàng)目也一樣),在項(xiàng)目 GitHub 代碼庫首頁都會有一個(gè)Issues 入口[8],這里會記錄項(xiàng)目目前已知的 bug、proposal(可以理解成新需求)、計(jì)劃補(bǔ)充的文檔、亟需完善的 UT 等等,如下圖:

0756c332-02b1-11ed-ba43-dac502259ad0.pngDevStream Issues

在 Issues 里我們一般可以找到一個(gè)“good first issue”標(biāo)簽標(biāo)記的 issues,點(diǎn)擊這個(gè)標(biāo)簽可以進(jìn)一步直接篩選出所有的 good first issues,這是社區(qū)專門留給新貢獻(xiàn)者的相對簡單的入門級 issues:

0769f696-02b1-11ed-ba43-dac502259ad0.pngDevStream Good First Issues

沒錯(cuò),從這里開始,瀏覽一下這些 good first issues,看下有沒有你感興趣的而且還沒被分配的 issue,然后在下面留言,等待項(xiàng)目管理員分配任務(wù)后就可以開始編碼了,就像這樣:

077b2f38-02b1-11ed-ba43-dac502259ad0.pngClaim an Issue in DevStream

如圖所示,如果一個(gè) issue 還沒有被認(rèn)領(lǐng),這時(shí)候你上去留個(gè)言,等待管理員會將這個(gè)任務(wù)分配給你,接著你就可以開始開發(fā)了。

五、我要提交 PR,怎么上手?

一般開源項(xiàng)目代碼庫根目錄都會有一個(gè) CONTRIBUTING.md 或者其他類似名字的文檔來介紹如何開始貢獻(xiàn),像這樣:

079cb234-02b1-11ed-ba43-dac502259ad0.pngDevStream Contributing

在DevStream 的 Contributing[9]文檔里我們放了一個(gè)Development Workflow[10],其實(shí)就是 PR 工作流的介紹,不過今天,我要更詳細(xì)地聊聊 PR 工作流。

5.1、第一步:Fork 項(xiàng)目倉庫

GitHub 上的項(xiàng)目都有一個(gè) Fork 按鈕,我們需要先將開源項(xiàng)目 fork 到自己的賬號下,以 DevStream 為例:

07b32f5a-02b1-11ed-ba43-dac502259ad0.pngFork DevStream

點(diǎn)一下 Fork 按鈕,然后回到自己賬號下,可以找到 fork 到的項(xiàng)目了:

07c89462-02b1-11ed-ba43-dac502259ad0.pngDevStream Fork

這個(gè)項(xiàng)目在你自己的賬號下,也就意味著你有任意修改的權(quán)限了。我們后面要做的事情,就是將代碼變更提到自己 fork 出來的代碼庫里,然后再通過 Pull Request 的方式將 commits 合入上游項(xiàng)目。

5.2、第二步:克隆項(xiàng)目倉庫到本地

對于任意一個(gè)開源項(xiàng)目,流程幾乎都是一樣的。我直接寫了一些命令,大家可以復(fù)制粘貼直接執(zhí)行。當(dāng)然,命令里的一些變量還是需要根據(jù)你自己的實(shí)際需求修改,比如對于 DevStream 項(xiàng)目,我們可以先這樣配置幾個(gè)環(huán)境變量:

環(huán)境變量

export WORKING_PATH="~/gocode"
export USER="daniel-hutao"
export PROJECT="devstream"
export ORG="devstream-io"

同理對于 DevLake,這里的命令就變成了這樣:

export WORKING_PATH="~/gocode"
export USER="daniel-hutao"
export PROJECT="incubator-devlake"
export ORG="apache"

記得 USER 改成你的 GitHub 用戶名,WORKING_PATH 當(dāng)然也可以靈活配置,你想把代碼放到哪里,就寫對應(yīng)路徑。

接著就是幾行通用的命令來完成 clone 等操作了:

clone 等

mkdir -p ${WORKING_PATH}
cd${WORKING_PATH}
# You can also use the url: git@github.com:${USER}/${PROJECT}.git
# if your ssh configuration is proper
git clone https://github.com/${USER}/${PROJECT}.git
cd${PROJECT}

git remote add upstream https://github.com/${ORG}/${PROJECT}.git
# Never push to upstream locally
git remote set-url --push upstream no_push

如果你配置好了 ssh 方式來 clone 代碼,當(dāng)然,git clone 命令用的 url 可以改成git@github.com:${USER}/${PROJECT}.git。

完成這一步后,我們在本地看到的 remote 信息應(yīng)該是這樣的:

git remote -v

origingit@github.com:daniel-hutao/devstream.git (fetch)
origingit@github.com:daniel-hutao/devstream.git (push)
upstreamhttps://github.com/devstream-io/devstream (fetch)
upstreamno_push (push)

記住啰,你本地的代碼變更永遠(yuǎn)只提交到 origin,然后通過 origin 提交 Pull Request 到 upstream。

5.3、第三步:更新本地分支代碼

如果你剛剛完成 fork 和 clone 操作,那么你本地的代碼肯定是新的。但是“剛剛”只存在一次,接著每一次準(zhǔn)備開始寫代碼之前,你都需要確認(rèn)本地分支的代碼是新的,因?yàn)榛诶洗a開發(fā)你會陷入無限的沖突困境之中。

更新本地 main 分支代碼:

git fetch upstream
git checkout main
git rebase upstream/main

當(dāng)然,我不建議你直接在 main 分支寫代碼,雖然你的第一個(gè) PR 從 main 提交完全沒有問題,但是如果你需要同時(shí)提交2個(gè) PR 呢?總之鼓勵(lì)新增一個(gè) feat-xxx 或者 fix-xxx 等更可讀的分支來完成開發(fā)工作。

創(chuàng)建分支

git checkout -b feat-xxx

這樣,我們就得到了一個(gè)和上游 main 分支代碼一樣的特性分支 feat-xxx 了,接著可以開始愉快地寫代碼啦!

5.4、第四步:寫代碼

沒啥好說的,寫就是了,寫!

5.5、第五步:Commit 和 Push

通用的流程:

git add 
git commit -s -m "some description here"
git push origin feat-xxx

當(dāng)然,這里大家需要理解這幾個(gè)命令和參數(shù)的含義,靈活調(diào)整。比如你也可以用git add --all完成 add 步驟,在 push 的時(shí)候也可以加-f參數(shù),用來強(qiáng)制覆蓋遠(yuǎn)程分支(假如已經(jīng)存在,但是 commits 記錄不合你意)。但是請記得git commit的-s參數(shù)一定要加哦!

如果你習(xí)慣用 IDE 來 commit,當(dāng)然也沒有任何問題,像這樣:

07e18e7c-02b1-11ed-ba43-dac502259ad0.pngDevStream Commit with Goland

這里要注意 commit message 的規(guī)范,可能每個(gè)開源項(xiàng)目的要求不盡相同,比如 DevStream 的規(guī)范[11]是類似這樣的格式:

[optional scope]: 

[optional body]

[optional footer(s)]

舉幾個(gè)例子:

feat: some description here

docs: some description here

fix: some description here

fix(core): some description here

chore: some description here

...

commit 和 push 兩個(gè)步驟可以在 IDE 里一步到位,也可以分開,我習(xí)慣分開操作,給自己多一些余地。另外,我更習(xí)慣命令行操作:

git push origin feat-1

Counting objects: 80, done.
Delta compression using up to 10 threads.
Compressing objects: 100% (74/74), done.
Writing objects: 100% (80/80), 13.78 KiB | 4.59 MiB/s, done.
Total 80 (delta 55), reused 0 (delta 0)
remote: Resolving deltas: 100% (55/55), completed with 31 local objects.
remote: 
remote: Create a pull request for'feat-1' on GitHub by visiting:
remote:      https://github.com/daniel-hutao/devstream/pull/new/feat-1
remote: 
To github.com:daniel-hutao/devstream.git
 * [new branch]      feat-1 -> feat-1

到這里,本地 commits 就推送到遠(yuǎn)程了。

5.6、第六步:開一個(gè) PR

在完成 push 操作后,我們打開 GitHub,可以看到一個(gè)黃色的提示框,告訴我們可以開一個(gè) Pull Request 了:

07f1f87a-02b1-11ed-ba43-dac502259ad0.pngCompare & pull request

如果你沒有看到這個(gè)框,也可以直接切換到 feat-1 分支,然后點(diǎn)擊下方的“Contribute”按鈕來開啟一個(gè) PR,或者直接點(diǎn) Issues 邊上的 Pull requests 進(jìn)入對應(yīng)頁面。

Pull Request 格式默認(rèn)是這樣的:

08099d04-02b1-11ed-ba43-dac502259ad0.pngDevStream Pull Request

這里我們需要填寫一個(gè)合適的標(biāo)題(默認(rèn)和 commit message 一樣),然后按照模板填寫 PR 描述。PR 模板其實(shí)在每個(gè)開源項(xiàng)目里都不太一樣,我們需要仔細(xì)閱讀上面的內(nèi)容,避免犯低級錯(cuò)誤。

比如 DevStream 的模板里目前分為4個(gè)部分:

Pre-Checklist:這里列了3個(gè)前置檢查項(xiàng),提醒 PR 提交者要先閱讀 Contributing 文檔,然后代碼要有完善的注釋或者文檔,盡可能添加測試用例等;

Description:這里填寫的是 PR 的描述信息,也就是介紹你的 PR 內(nèi)容的,你可以在這里描述這個(gè) PR 解決了什么問題等;

Related Issues:記得嗎?我們在開始寫代碼之前其實(shí)是需要認(rèn)領(lǐng) issue 的,這里要填寫的也就是對應(yīng) issue 的 id,假如你領(lǐng)的 issue 鏈接是 https://github.com/devstream-io/devstream/issues/796,并且這個(gè) issue 通過你這個(gè) PR 的修改后就完成了,可以關(guān)閉了,這時(shí)候可以在 Related Issues 下面寫“close #796”;

New Behavior:代碼修改后絕大多數(shù)情況下是需要進(jìn)行測試的,這時(shí)候我們可以在這里粘貼測試結(jié)果截圖,這樣 reviewers 就能夠知道你的代碼已經(jīng)通過測試,功能符合預(yù)期,這樣可以減少 review 工作量,快速合入。

這個(gè)模板并不復(fù)雜,我們直接對著填寫就行。

比如:

081a81c8-02b1-11ed-ba43-dac502259ad0.pngDevStream Pull Request Template

然后點(diǎn)擊右下角“Create pull request”就完成了一個(gè) PR 的創(chuàng)建了。不過我這里不能去點(diǎn)這個(gè)按鈕,我用來演示的修改內(nèi)容沒有意義,不能合入上游代碼庫。不過我還是想給你看下 PR 創(chuàng)建出來后的效果,我們以pr655[12]為例吧:

083465de-02b1-11ed-ba43-dac502259ad0.pngDevStream Pull Request 655

這是上個(gè)月我提的一個(gè) PR,基本和模板格式一致。除了模板的內(nèi)容,可能你已經(jīng)注意到這里多了一個(gè) Test 小節(jié),沒錯(cuò),模板不是死的,模板只是為了降低溝通成本,你完全可以適當(dāng)調(diào)整,只要結(jié)果是“往更清晰的方向走”的。我這里通過 Test 部分添加了本地詳細(xì)測試結(jié)果記錄,告訴 reviewers 我已經(jīng)在本地充分測試了,請放心合入。

提交了 PR 之后,我們就可以在 PR 列表里找到自己的 PR 了,這時(shí)候還需要注意 ci 檢查是不是全部能夠通過,假如失敗了,需要及時(shí)修復(fù)。以 DevStream 為例,ci 檢查項(xiàng)大致如下:

085e66ea-02b1-11ed-ba43-dac502259ad0.pngDevStream CI Checks

5.7、第七步:PR 合入

如果你的 PR 很完美,毫無爭議,那么過不了太長時(shí)間,項(xiàng)目管理員會直接合入你的 PR,那么你這個(gè) PR 的生命周期也就到此結(jié)束了。

但是,沒錯(cuò),這里有個(gè)“但是”,但是往往第一次 PR 不會那么順利,我們接下來就詳細(xì)介紹一下可能經(jīng)常遇到的一些問題和對應(yīng)的解決辦法。

六、我提交了一個(gè) PR,然后遇到了問題 A,B,C,D,E,F,G,...

多數(shù)情況下,提交一個(gè) PR 后是不會被馬上合入的,reviewers 可能會提出各種修改意見,或者我們的 PR 本身存在一些規(guī)范性問題,或者 ci 檢查就直接報(bào)錯(cuò)了,怎么解決呢?繼續(xù)往下看吧。

6.1、Reviewers 提了一些修改意見,我如何更新 PR?

很多時(shí)候,我們提交了一個(gè) PR 后,還需要繼續(xù)追加 commit,比如提交后發(fā)現(xiàn)代碼還有點(diǎn)問題,想再改改,或者 reviewers 提了一些修改意見,我們需要更新代碼。

一般我們遵守一個(gè)約定:在 review 開始之前,更新代碼盡量不引入新的 commits 記錄,也就是能合并就合并,保證 commits 記錄清晰且有意義;在 review 開始之后,針對 reviewers 的修改意見所產(chǎn)生的新 commit,可以不向前合并,這樣能夠讓二次 review 工作更有針對性。

不過不同社區(qū)要求不一樣,可能有的開源項(xiàng)目會要求一個(gè) PR 里只能包含一個(gè) commit,大家根據(jù)實(shí)際場景靈活判斷即可。

說回如何更新 PR,我們只需要在本地繼續(xù)修改代碼,然后通過和第一個(gè) commit 一樣的步驟,執(zhí)行這幾個(gè)命令:

git add 
git commit -s -m "some description here"
git push origin feat-xxx

這時(shí)候別看 push 的是 origin 的 feat-xxx 分支,其實(shí) GitHub 會幫你把新增的 commits 全部追加到一個(gè)未合入 PR 里去。沒錯(cuò),你只管不斷 push,PR 會自動更新。

至于如何合并 commits,我們下一小節(jié)具體介紹。

6.2、Commits 太多或者記錄混亂,如何合并 Commits?

很多情況下我們需要去合并 commits,比如你的第一個(gè) commit 里改了100行代碼,然后發(fā)現(xiàn)少改了1行,這時(shí)候又提交了一個(gè) commit,那么第二個(gè) commit 就太“沒意思”了,我們需要合并一下。

6.2.1、Git 命令行方式合并 Commits

比如我這里有2個(gè)同名的 commits,第二個(gè) commit 其實(shí)只改了一個(gè)標(biāo)點(diǎn):

0875fefe-02b1-11ed-ba43-dac502259ad0.pngCommits to be Merged

這時(shí)候我們可以通過 rebase 命令來完成2個(gè) commits 的合并:

git rebase -i HEAD~2

執(zhí)行這個(gè)命令會進(jìn)入一個(gè)編輯頁面,默認(rèn)是 vim 編輯模式,內(nèi)容大致如下:

pick 3114c0f docs: just fortest
pick 9b7d63b docs: just fortest

# Rebase d640931..9b7d63b onto d640931 (2 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.

我們需要把第二個(gè) pick 改成 s,然后保存退出(vim 的 wq 命令):

pick 3114c0f docs: just fortest
s 9b7d63b docs: just fortest

接著會進(jìn)入第二個(gè)編輯頁面:

# This is a combination of 2 commits.
# This is the 1st commit message:

docs: just fortest

Signed-off-by: Daniel Hu 

# This is the commit message #2:

docs: just fortest

Signed-off-by: Daniel Hu 

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# ...

這里是用來編輯合并后的 commit message 的,我們直接刪掉多余部分,只保留這樣幾行:

docs: just fortest

Signed-off-by: Daniel Hu 

接著同樣是 vim 的保存退出操作,這時(shí)候可以看到日志:

[detached HEAD 80f5e57] docs: just fortest
 Date: Wed Jul 6 1037 2022 +0800
 1 file changed, 2 insertions(+)
Successfully rebased and updated refs/heads/feat-1.

這時(shí)候可以通過git log命令查看下 commits 記錄是不是符合預(yù)期:

08939a04-02b1-11ed-ba43-dac502259ad0.pngRebased

好,我們在本地確認(rèn) commits 已經(jīng)完成合并,這時(shí)候就可以繼續(xù)推送到遠(yuǎn)程,讓 PR 也更新掉:

git push -f origin feat-xxx

這里需要有一個(gè)-f參數(shù)來強(qiáng)制更新,合并了 commits 本質(zhì)也是一種沖突,需要沖掉遠(yuǎn)程舊的 commits 記錄。

6.2.2 IDE 里合并 Commits

圖形化方式當(dāng)然也可以實(shí)現(xiàn) Commits 的合并。

截圖走起08a5b7ac-02b1-11ed-ba43-dac502259ad0.png

點(diǎn)擊右下角的 Git

選擇想要合并的 commits

右鍵,然后點(diǎn)擊 Squash Commits,記得嘴里默念一句:走你!

接著就可以看到這個(gè)頁面了:

08bc0d36-02b1-11ed-ba43-dac502259ad0.pngSquash with Goland

這是圖形化方式修改 commit message 的頁面,行吧,改成你喜歡的樣子,然后點(diǎn)擊右下角的 OK 按鈕,事情就算結(jié)束了。

08d0ad0e-02b1-11ed-ba43-dac502259ad0.pngSquash with Goland

看,2個(gè) commits,它們“融合”了,變成了一個(gè)“改頭換面”的新 commit 了。

6.3、PR 產(chǎn)生了沖突,如何解決?

沖突可以在線解決,也可能本地解決,我們逐個(gè)來看。

6.3.1、在線解決沖突

我們要盡可能避免沖突,養(yǎng)成每次寫代碼前更新本地代碼的習(xí)慣。不過,沖突不可能完全避免,有時(shí)候你的 PR 被阻塞了幾天,可能別人改了同一行代碼,還搶先被合入了,這時(shí)候你的 PR 就出現(xiàn)沖突了,類似這樣(同樣,此刻我不能真的去上游項(xiàng)目構(gòu)造沖突,所以下面用于演示的沖突在我在自己的 repo 里):

08f271dc-02b1-11ed-ba43-dac502259ad0.pngConflict Happened

每次看到這個(gè)頁面都會讓人覺得心頭一緊。我們點(diǎn)擊“Resolve conflicts”按鈕,就可以看到具體沖突的內(nèi)容了:

09162cc6-02b1-11ed-ba43-dac502259ad0.pngConflict File

可以看到具體沖突的行了,接下來要做的就是解決沖突。我們需要?jiǎng)h掉所有的<<<<<<<、>>>>>>>和=======標(biāo)記,只保留最終想要的內(nèi)容,如下:

092391f4-02b1-11ed-ba43-dac502259ad0.pngConflict Resolved

接著點(diǎn)擊右上角的“Mark as Resolved”:

09382704-02b1-11ed-ba43-dac502259ad0.pngMark as resolved

最后點(diǎn)擊“Commit merge”:094a30ac-02b1-11ed-ba43-dac502259ad0.png

這樣就完成沖突解決了,可以看到產(chǎn)生了一個(gè)新的 commit:

095e9916-02b1-11ed-ba43-dac502259ad0.pngConflict Resolved

到這里,沖突就解決掉了。

6.3.2、本地解決沖突

更多時(shí)候,我們需要在本地解決沖突,尤其是沖突太多,太復(fù)雜的時(shí)候。

同樣,我們構(gòu)造一個(gè)沖突,這次嘗試在本地解決沖突。

先在線看一下沖突的內(nèi)容:

0972f7b2-02b1-11ed-ba43-dac502259ad0.pngConflict Happened

接著我們在本地執(zhí)行:

# 先切回到 main 分支
git checkout main
# 拉取上游代碼(實(shí)際場景肯定是和上游沖突,我們這里的演示環(huán)境其實(shí)是 origin)
git fetch upstream
# 更新本地 main(這里也可以用 rebase,但是 reset 不管有沒有沖突總是會成功)
git reset --hard upstream/main

到這里,本地 main 分支就和遠(yuǎn)程(或者上游) main 分支代碼完全一致了,然后我們要做的是將 main 分支的代碼合入自己的特性分支,同時(shí)解決沖突。

git checkout feat-1
git rebase main

這時(shí)候會看到這樣的日志:

First, rewinding head to replay your work on top of it...
Applying: docs: conflict test 1
Using index info to reconstruct a base tree...
M       README.md
Falling back to patching base and 3-way merge...
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
error: Failed to merge in the changes.
Patch failed at 0001 docs: conflict test 1
The copy of the patch that failed is found in: .git/rebase-apply/patch

Resolve all conflicts manually, mark them as resolved with
"git add/rm ", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".

我們需要解決沖突,直接打開 README.md,找到?jīng)_突的地方,直接修改。這里的改法和上一小節(jié)介紹的在線解決沖突沒有任何區(qū)別,我就不贅述了。

代碼里同樣只保留最終內(nèi)容,然后繼續(xù) git 命令走起來:

098ef890-02b1-11ed-ba43-dac502259ad0.pngConflict Resolved

可能此時(shí)你并不放心,那就通過git log命令看一下 commits 歷史記錄吧:

09a8376a-02b1-11ed-ba43-dac502259ad0.pngCommits History

這里的“conflict test 2”是我提交到 main 分支的記錄,可以看到這個(gè)時(shí)間比“conflict test 1”還要晚了一些,但是它先合入了。我們在 rebase 操作后,這個(gè)記錄在前,我們特性分支的“conflict test 1”在后,看起來很和諧,我們繼續(xù)將這個(gè)變更推送到遠(yuǎn)程,這個(gè)命令已經(jīng)出現(xiàn)很多次了:

git push -f origin feat-xxx

這時(shí)候我們再回到 GitHub 看 PR 的話,可以發(fā)現(xiàn)沖突已經(jīng)解決了,并且沒有產(chǎn)生多余的 commit 記錄,也就是說這個(gè) PR 的 commit 記錄非常干凈,好似沖突從來沒有出現(xiàn)過:

09ba385c-02b1-11ed-ba43-dac502259ad0.png09ccd53e-02b1-11ed-ba43-dac502259ad0.png

至于什么時(shí)候可以在線解決沖突,什么時(shí)候適合本地解決沖突,就看大家如何看待“需不需要保留解決沖突的記錄”了,不同社區(qū)的理解不一樣,可能特別成熟的開源社區(qū)會希望使用本地解決沖突方式,因?yàn)樵诰€解決沖突產(chǎn)生的這條 merge 記錄其實(shí)“沒營養(yǎng)”。至于 DevStream 社區(qū)和 DevLake 社區(qū),我們推薦使用后一種,但是不做強(qiáng)制要求。

6.4、CI 檢查不過:commit message 相關(guān)問題如何修復(fù)?

前面我們提到過 commit message 的規(guī)范,但是第一次提交 PR 的時(shí)候還是很容易出錯(cuò),比如feat: xxx其實(shí)能通過 ci 檢查,但是feat: Xxx就不行了。假設(shè)現(xiàn)在我們不小心提交了一個(gè) PR,但是里面 commit 的 message 不規(guī)范,這時(shí)候怎么修改呢?

太簡單了,直接執(zhí)行:

git commit --amend

這條命令執(zhí)行后就能進(jìn)入編輯頁面,隨意更新 commit message 了。改完之后,繼續(xù) push:

git push -f origin feat-xxx

這樣就能更新 PR 里的 commit message 了。

6.5、CI 檢查不過:DCO(sign) 問題如何修復(fù)?

相當(dāng)多的開源項(xiàng)目會要求所有合入的 commits 都包含一行類似這樣的記錄:

Daniel Hu 

所以 commit message 看起來會像這樣:

feat: some description here
    
Signed-off-by: Daniel Hu 

這行信息相當(dāng)于是對應(yīng) commit 的作者簽名。要添加這樣一行簽名當(dāng)然很簡單,我們直接在git commit命令后面加一個(gè)-s參數(shù)就可以了,比如git commit -s -m "some description here"提交的 commit 就會帶上你的簽名。

但是如果如果你第一次提交的 PR 里忘記了在 commits 中添加 Signed-off-by 呢?這時(shí)候,如果對應(yīng)開源項(xiàng)目配置了DCO 檢查[13],那么你的 PR 就會在 ci 檢查中被“揪出來”沒有正確簽名。

同樣先構(gòu)造一個(gè)沒有加簽名的 commit:

09ee7be4-02b1-11ed-ba43-dac502259ad0.png請?zhí)砑訄D片描述 09ee7be4-02b1-11ed-ba43-dac502259ad0.pngCommit Without Sign

我不能直接推到 DevStream 項(xiàng)目代碼庫里演示如何讓 DCO 報(bào)錯(cuò),但是如果提 PR,看到的效果是這樣的:

0a0e9aa0-02b1-11ed-ba43-dac502259ad0.pngCommit With DCO Error

我們看下如何解決:

git commit --amend -s

這樣一個(gè)簡單的命令,就能直接在最近一個(gè) commit 里加上 Signed-off-by 信息。執(zhí)行這行命令后會直接進(jìn)入 commit message 編輯頁面,默認(rèn)如下圖:

docs: dco test

Signed-off-by: Daniel Hu 

這時(shí)候我們可以同時(shí)修改 commit message,如果不需要,那就直接保存退出好了,簽名信息是會自動加上的。

完成簽名后呢?當(dāng)然是來一個(gè)強(qiáng)制 push 了:

git push -f origin feat-xxx

這樣,你 PR 中的 DCO 報(bào)錯(cuò)就自然修復(fù)了。

七、最后

一個(gè)不小心這篇文章寫的有點(diǎn)長了。行,打完收工!

原文標(biāo)題:萬字干貨,手把手教你在 GitHub 上優(yōu)雅的提交 PR

文章出處:【微信公眾號:馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

審核編輯:彭靜
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 編程語言
    +關(guān)注

    關(guān)注

    10

    文章

    1945

    瀏覽量

    34736
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4788

    瀏覽量

    68611
  • 開源項(xiàng)目
    +關(guān)注

    關(guān)注

    0

    文章

    37

    瀏覽量

    7189

原文標(biāo)題:萬字干貨,手把手教你在 GitHub 上優(yōu)雅的提交 PR

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    精選易上手制作的十幾個(gè)開源項(xiàng)目!拿來主義?我愿意!

    精選易上手制作的十幾個(gè)開源項(xiàng)目!拿來主義?我愿意!
    發(fā)表于 11-30 11:49

    開源項(xiàng)目的分類

    21小時(shí)上手深度學(xué)習(xí)1,從開源項(xiàng)目做起
    發(fā)表于 04-19 08:53

    深度學(xué)習(xí)開源項(xiàng)目的繪畫實(shí)現(xiàn)過程

    21小時(shí)上手深度學(xué)習(xí)2-開源項(xiàng)目之繪畫
    發(fā)表于 05-20 12:31

    首期OpenHarmony開源開發(fā)者成長計(jì)劃2021正式啟航

    、項(xiàng)目經(jīng)費(fèi)資助3、豐富的實(shí)習(xí)機(jī)會4、結(jié)識開源技術(shù)專家的機(jī)會4、了解開源項(xiàng)目參與方法一、學(xué)生參與
    發(fā)表于 10-26 14:21

    活動報(bào)名|OpenHarmony 戰(zhàn)“碼”先鋒,PR 征集令

    ),和大家同臺競技,比拼技藝?;顒右?guī)則1. 參與方式:開發(fā)者在 OpenHarmony 代碼倉發(fā)現(xiàn)問題或有功能優(yōu)化需求后,創(chuàng)建 Issue 并提交 PR 貢獻(xiàn)代碼。創(chuàng)建 Issue 流程和貢獻(xiàn)代碼
    發(fā)表于 04-27 10:55

    戰(zhàn)“碼”先鋒,PR征集令!「OpenHarmony 開源貢獻(xiàn)者計(jì)劃2022」驚喜上線!

    OpenHarmony開源貢獻(xiàn)者計(jì)劃是一項(xiàng)為了鼓勵(lì)開發(fā)者積極參與開源軟件開發(fā)維護(hù)的活動。 你可在Gitee OpenHarmony(https://gitee.com/openharmony)代碼倉提交
    發(fā)表于 05-16 14:56

    戰(zhàn)碼先鋒直播預(yù)告丨參與文檔貢獻(xiàn),開啟OpenHarmony社區(qū)貢獻(xiàn)之旅

    大OpenHarmony開源大咖,精選5大簡單上手開源代碼倉,Docs、ARkUI、媒體子系統(tǒng)、測試子系統(tǒng)、啟動恢復(fù),在【戰(zhàn)“碼”先鋒直播間】,在線講解貢獻(xiàn)指南,幫助熱愛開源的你更好
    發(fā)表于 05-24 10:30

    【直播回顧】參與文檔貢獻(xiàn),開啟OpenHarmony社區(qū)貢獻(xiàn)

    老師從6個(gè)方面分別介紹為什么社區(qū)需要開發(fā)者文檔、文檔寫作對個(gè)人的技能提升、可以參與哪些形式的文檔貢獻(xiàn)、可以提交哪些類別的文檔PR、OpenHarmony社區(qū)文檔有哪些以及社區(qū)文檔的生產(chǎn)流程等內(nèi)容。歡迎
    發(fā)表于 05-26 18:23

    戰(zhàn)碼先鋒直播預(yù)告丨參與ArkUI,共建OpenHarmony繁榮生態(tài)

    上手開源代碼倉(Docs、ArkUI、媒體子系統(tǒng)、測試子系統(tǒng)、啟動恢復(fù)),在【戰(zhàn)“碼”先鋒直播間】,在線講解貢獻(xiàn)指南,幫助熱愛開源的你更好地參與
    發(fā)表于 05-30 17:24

    本周四晚19:00戰(zhàn)碼先鋒第7期直播丨三方應(yīng)用開發(fā)者如何為開源做貢獻(xiàn)

    計(jì)劃」,旨在鼓勵(lì)開發(fā)者參與OpenHarmony開源建設(shè)、貢獻(xiàn)代碼或者幫助社區(qū)提交和修復(fù)Bug,與OpenHarmony社區(qū)共同成長。本期「OpenHarmony開源貢獻(xiàn)者計(jì)劃」以“戰(zhàn)碼先鋒,
    發(fā)表于 06-22 10:54

    【直播回顧】戰(zhàn)碼先鋒第七期:三方應(yīng)用開發(fā)者如何為開源做貢獻(xiàn)

    老師老師主講,是「OpenHarmony開源貢獻(xiàn)者計(jì)劃」"戰(zhàn)碼先鋒,PR 征集令"系列活動配套直播之一。「OpenHarmony開源貢獻(xiàn)者計(jì)劃」活動自發(fā)布以來,已收到1000+PR
    發(fā)表于 06-24 11:23

    本周二晚19:00戰(zhàn)碼先鋒第8期直播丨如何多方位參與OpenHarmony開源貢獻(xiàn)

    上手開源代碼倉(Docs、ArkUI、媒體子系統(tǒng)、測試子系統(tǒng)、啟動恢復(fù)),在【戰(zhàn)“碼”先鋒直播間】,在線講解貢獻(xiàn)指南,幫助熱愛開源的你更好地參與
    發(fā)表于 06-27 18:23

    30分鐘成為Contributor|如何多方位參與OpenHarmony開源貢獻(xiàn)?

    30分鐘成為Contributor|如何多方位參與OpenHarmony開源貢獻(xiàn)?如何優(yōu)雅地參與開源貢獻(xiàn),向頂級開源
    發(fā)表于 08-01 15:18

    參與開源共建,你不可不知的貢獻(xiàn)技巧

    參與開源共建,你不可不知的貢獻(xiàn)技巧近期,在“戰(zhàn)碼先鋒,PR征集令”活動中,上百位開發(fā)者們熱情踴躍地參與了活動,以提PR的方式為OpenHar
    發(fā)表于 08-23 15:27

    助力共建,繁榮生態(tài) | 「OpenHarmony戰(zhàn)碼先鋒,PR征集令」2期圓滿收官

    “OpenHarmony”)發(fā)起的「OpenHarmony 開源貢獻(xiàn)者計(jì)劃2022」系列主題活動,旨在鼓勵(lì)開發(fā)者參與到OpenHarmony項(xiàng)目開源共建中,提交Issue/
    的頭像 發(fā)表于 12-02 21:10 ?633次閱讀