精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

萬字干貨,手把手教你在 GitHub 上優雅的提交 PR

開源
今天我準備和你詳細介紹如何開始參與開源項目,幫助你在 GitHub 上完成第一個 PR 的合入。

一、概述

除了正常的 PR 合入流程之外,我還準備詳細介紹一下如果一個 PR 提交后遇到了沖突、需要追加 commits、需要合并 commits 等等相對復雜問題該如何解決。

總的來說,本文計劃分為4個部分:

  1. 談談為什么要參與開源項目以及我為什么要介紹如何 PR
  2. 談談怎么開始參與開源項目,也就是如何尋找合適的開源項目、如何尋找貢獻點
  3. 介紹怎么上手 PR 流程,即從 fork 到 push 全流程
  4. 介紹提交了 PR 之后遇到各種常見問題如何解決

Ok, let's get started!

二、為什么要參與開源項目

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

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

你有沒有閱讀過某個開源項目的源碼?或者更進一步,有沒有參與過某個開源社區,或者說給開源項目提過 PR?

如果答案是肯定的,比如候選人說自己讀過部分 Kubernetes 模塊的源碼,再進一步我確認他真的讀過并且讀懂了或者說真的提交過 bugfix/feature 類型的 PR,那我就不再問編程語言層面的問題了,因為我相信能看懂一個成熟的開源項目部分模塊源碼或者能夠提交 bugfix/feature 類型的 PR 已經說明了一切。

我自己在學習 Golang 的時候,大致分為兩個階段:

  1. 學習基礎語法,開始寫項目,直到能夠熟練完成各種業務功能的開發;
  2. 看了一些開源項目的源碼,深感受益頗多,編碼水平再上一個臺階。

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

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

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

我司開源了2個項目,分別是:

   1.CNCF Project DevStream[1]

CNCF Project DevStream

   2.Apache DevLake[2]

DevStream 項目和 DevLake 項目隔三差五就會有新貢獻者提交 PR 過來,但是多數貢獻者在提交第一個 PR 時往往會遇到一個或多個問題,比如產生沖突、commits 記錄過多或者混亂、commit 沒有簽名、commit message 不規范、各種 ci 流程檢查報錯等等。

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

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

四、我想參與開源項目,怎么開始?

不管你為什么決定開始參與開源項目,不管出發點是出于學習、興趣、成就感等等,還是為了讓某個自己需要的特性合入某個開源項目,總之今天你下定決心,要給某個開源項目提交一個 PR 了,好,我們開始吧!

4.1、尋找一個合適的開源項目

如果你已經決定參與某個開源社區了,那么請直接跳過本小節。

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

  1. 不要從特別成熟的項目開始。比如現在去參與 Kubernetes 社區,一方面由于貢獻者太多,很難搶到一個入門級的 issue 來開始第一個 PR;另外一方面也由于貢獻者太多,你的聲音會被淹沒,社區維護者并不在意多你一個或者少你一個(當然可能沒有人會承認,但是你不得不信),如果你提個 PR 都遇到了各種問題還不能自己獨立解決,那么很可能你的 PR 會直接超時關閉,沒有人在意你是不是有一個好的參與體驗;
  2. 不要從特別小的項目開始。這就不需要我解釋了吧?很早期的開源項目可能面臨著非常多的問題,比如代碼不規范、協作流程不規范、重構頻繁且不是 issue 驅動的,讓外部參與者無所適從……
  3. 選擇知名開源軟件基金會的孵化項目,這類項目一方面不是特別成熟,所以對新貢獻者友好;另一方面也不會特別不成熟,不至于給人很差的參與體驗,比如 Apache 基金會、Linux 基金會、CNCF 等。

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

  • CNCF 沙箱項目[3]
  • CNCF 孵化項目(列表包括畢業項目)[4]
  • Apache 項目(孵化期項目名字中帶 Incubating)[5]

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

4.2、尋找貢獻點

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

DevStream Issues

在 Issues 里我們一般可以找到一個“good first issue”標簽標記的 issues,點擊這個標簽可以進一步直接篩選出所有的 good first issues,這是社區專門留給新貢獻者的相對簡單的入門級 issues:

DevStream Good First Issues

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

Claim an Issue in DevStream

如圖所示,如果一個 issue 還沒有被認領,這時候你上去留個言,等待管理員會將這個任務分配給你,接著你就可以開始開發了。

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

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

DevStream Contributing

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

5.1、第一步:Fork 項目倉庫

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

Fork DevStream

點一下 Fork 按鈕,然后回到自己賬號下,可以找到 fork 到的項目了:

DevStream Fork

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

5.2、第二步:克隆項目倉庫到本地

對于任意一個開源項目,流程幾乎都是一樣的。我直接寫了一些命令,大家可以復制粘貼直接執行。當然,命令里的一些變量還是需要根據你自己的實際需求修改,比如對于 DevStream 項目,我們可以先這樣配置幾個環境變量:

  • 環境變量
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 當然也可以靈活配置,你想把代碼放到哪里,就寫對應路徑。

接著就是幾行通用的命令來完成 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 代碼,當然,git clone 命令用的 url 可以改成git@github.com:${USER}/${PROJECT}.git。

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

  • git remote -v
origin  git@github.com:daniel-hutao/devstream.git (fetch)
origin git@github.com:daniel-hutao/devstream.git (push)
upstream https://github.com/devstream-io/devstream (fetch)
upstream no_push (push)

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

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

如果你剛剛完成 fork 和 clone 操作,那么你本地的代碼肯定是新的。但是“剛剛”只存在一次,接著每一次準備開始寫代碼之前,你都需要確認本地分支的代碼是新的,因為基于老代碼開發你會陷入無限的沖突困境之中。

  • 更新本地 main 分支代碼:
git fetch upstream
git checkout main
git rebase upstream/main

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

  • 創建分支
git checkout -b feat-xxx

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

5.4、第四步:寫代碼

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

5.5、第五步:Commit 和 Push

  • 通用的流程:
git add <file>
git commit -s -m "some description here"
git push origin feat-xxx

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

如果你習慣用 IDE 來 commit,當然也沒有任何問題,像這樣:

DevStream Commit with Goland

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

<type>[optional scope]: <description>
[optional body]
[optional footer(s)]

舉幾個例子:

  • docs: some description here
  • fix: some description here
  • fix(core): some description here
  • chore: some description here
  • ...

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

  • 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 就推送到遠程了。

5.6、第六步:開一個 PR

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

Compare & pull request

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

  • Pull Request 格式默認是這樣的:

DevStream Pull Request

這里我們需要填寫一個合適的標題(默認和 commit message 一樣),然后按照模板填寫 PR 描述。PR 模板其實在每個開源項目里都不太一樣,我們需要仔細閱讀上面的內容,避免犯低級錯誤。

比如 DevStream 的模板里目前分為4個部分:

  1. Pre-Checklist:這里列了3個前置檢查項,提醒 PR 提交者要先閱讀 Contributing 文檔,然后代碼要有完善的注釋或者文檔,盡可能添加測試用例等;
  2. Description:這里填寫的是 PR 的描述信息,也就是介紹你的 PR 內容的,你可以在這里描述這個 PR 解決了什么問題等;
  3. Related Issues:記得嗎?我們在開始寫代碼之前其實是需要認領 issue 的,這里要填寫的也就是對應 issue 的 id,假如你領的 issue 鏈接是 https://github.com/devstream-io/devstream/issues/796,并且這個 issue 通過你這個 PR 的修改后就完成了,可以關閉了,這時候可以在 Related Issues 下面寫“close #796”;
  4. New Behavior:代碼修改后絕大多數情況下是需要進行測試的,這時候我們可以在這里粘貼測試結果截圖,這樣 reviewers 就能夠知道你的代碼已經通過測試,功能符合預期,這樣可以減少 review 工作量,快速合入。

這個模板并不復雜,我們直接對著填寫就行。

  • 比如:

DevStream Pull Request Template

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

DevStream Pull Request 655

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

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

DevStream CI Checks

5.7、第七步:PR 合入

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

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

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

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

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

很多時候,我們提交了一個 PR 后,還需要繼續追加 commit,比如提交后發現代碼還有點問題,想再改改,或者 reviewers 提了一些修改意見,我們需要更新代碼。

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

不過不同社區要求不一樣,可能有的開源項目會要求一個 PR 里只能包含一個 commit,大家根據實際場景靈活判斷即可。

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

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

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

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

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

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

6.2.1、Git 命令行方式合并 Commits

比如我這里有2個同名的 commits,第二個 commit 其實只改了一個標點:

Commits to be Merged

這時候我們可以通過 rebase 命令來完成2個 commits 的合并:

git rebase -i HEAD~2

執行這個命令會進入一個編輯頁面,默認是 vim 編輯模式,內容大致如下:

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.

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

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

接著會進入第二個編輯頁面:

# This is a combination of 2 commits.
# This is the 1st commit message:
docs: just fortest
Signed-off-by: Daniel Hu <tao.hu@merico.dev>
# This is the commit message #2:
docs: just fortest
Signed-off-by: Daniel Hu <tao.hu@merico.dev>
# 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 <tao.hu@merico.dev>

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

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

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

Rebased

好,我們在本地確認 commits 已經完成合并,這時候就可以繼續推送到遠程,讓 PR 也更新掉:

git push -f origin feat-xxx

這里需要有一個-f參數來強制更新,合并了 commits 本質也是一種沖突,需要沖掉遠程舊的 commits 記錄。

6.2.2 IDE 里合并 Commits

圖形化方式當然也可以實現 Commits 的合并。

  • 截圖走起
  1. 點擊右下角的 Git
  2. 選擇想要合并的 commits
  3. 右鍵,然后點擊 Squash Commits,記得嘴里默念一句:走你!

接著就可以看到這個頁面了:

Squash with Goland

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

Squash with Goland

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

6.3、PR 產生了沖突,如何解決?

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

6.3.1、在線解決沖突

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

Conflict Happened

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

Conflict File

可以看到具體沖突的行了,接下來要做的就是解決沖突。我們需要刪掉所有的 <<<<<<<、>>>>>>> 和 ======= 標記,只保留最終想要的內容,如下:

Conflict Resolved

接著點擊右上角的“Mark as Resolved”:

Mark as resolved

最后點擊“Commit merge”:

這樣就完成沖突解決了,可以看到產生了一個新的 commit:

Conflict Resolved

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

6.3.2、本地解決沖突

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

同樣,我們構造一個沖突,這次嘗試在本地解決沖突。

  • 先在線看一下沖突的內容:

Conflict Happened

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

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

git checkout feat-1
git rebase main
  • 這時候會看到這樣的日志:
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 <conflicted_files>", 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,找到沖突的地方,直接修改。這里的改法和上一小節介紹的在線解決沖突沒有任何區別,我就不贅述了。

代碼里同樣只保留最終內容,然后繼續 git 命令走起來:

Conflict Resolved

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

Commits History

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

git push -f origin feat-xxx

這時候我們再回到 GitHub 看 PR 的話,可以發現沖突已經解決了,并且沒有產生多余的 commit 記錄,也就是說這個 PR 的 commit 記錄非常干凈,好似沖突從來沒有出現過:

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

6.4、CI 檢查不過:commit message 相關問題如何修復?

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

  • 太簡單了,直接執行:
git commit --amend

這條命令執行后就能進入編輯頁面,隨意更新 commit message 了。改完之后,繼續 push:

git push -f origin feat-xxx

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

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

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

Daniel Hu <tao.hu@merico.dev>

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

feat: some description here
Signed-off-by: Daniel Hu <tao.hu@merico.dev>

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

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

同樣先構造一個沒有加簽名的 commit:

請添加圖片描述

Commit Without Sign

我不能直接推到 DevStream 項目代碼庫里演示如何讓 DCO 報錯,但是如果提 PR,看到的效果是這樣的:

Commit With DCO Error

我們看下如何解決:

  • git commit --amend -s

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

docs: dco test
Signed-off-by: Daniel Hu <tao.hu@merico.dev>

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

完成簽名后呢?當然是來一個強制 push 了:

git push -f origin feat-xxx

這樣,你 PR 中的 DCO 報錯就自然修復了。

七、最后

一個不小心這篇文章寫的有點長了。行,打完收工!

  • 歡迎到我的個人網站[14]或者微信公眾號“胡說云原生”瀏覽更多我的文章;
  • 歡迎關注DevStream 社區[15],和我一起玩開源;
  • 歡迎到DevStream 官方博客[16]瀏覽更多 DevStream 團隊發布的文章。
責任編輯:龐桂玉 來源: 奇妙的Linux世界
相關推薦

2020-06-01 16:25:43

WindowsLinux命令

2017-12-01 05:01:35

WiFi干擾無線網絡

2009-06-12 18:21:46

App Engine上

2020-07-16 08:37:39

NginxCentOS7搭建

2010-07-06 09:38:51

搭建私有云

2021-07-13 10:17:25

GitHubLinux代碼

2023-05-26 00:34:21

WindowsHadoopLinux

2010-10-29 14:04:49

2011-10-27 16:08:17

PHP

2011-05-03 15:59:00

黑盒打印機

2011-01-10 14:41:26

2025-05-07 00:31:30

2021-07-14 09:00:00

JavaFX開發應用

2011-10-31 15:40:07

TouchpadAndroid

2023-03-02 11:42:48

CPU芯片

2021-09-26 16:08:23

CC++clang_forma

2011-02-22 13:46:27

微軟SQL.NET

2021-02-26 11:54:38

MyBatis 插件接口

2021-12-28 08:38:26

Linux 中斷喚醒系統Linux 系統

2023-04-26 12:46:43

DockerSpringKubernetes
點贊
收藏

51CTO技術棧公眾號

伊人久久中文字幕| 国产九色porny| 国产伦理一区二区| 国内精品久久久久久久影视蜜臀| 精品毛片乱码1区2区3区| 国产精品69久久久| 国产小视频在线播放| 奇米综合一区二区三区精品视频| 久久99视频精品| 国产精品三级在线观看无码| 免费成人高清在线视频| 亚洲国产美女搞黄色| 亚洲欧美日本国产有色 | 国产精品69xx| 亚洲国产成人午夜在线一区| 国产精品免费区二区三区观看| 欧美超碰在线观看| 欧美黄色免费| 在线日韩精品视频| 中文字幕在线免费看线人| jjzz黄色片| 国产激情在线观看| 91美女视频网站| 亚洲曰本av电影| 色老头一区二区| 在线国产精品一区| 两个人的视频www国产精品| 日本黄色网址大全| 红杏一区二区三区| 91麻豆精品国产自产在线观看一区| 中国丰满人妻videoshd| 黄色羞羞视频在线观看| 亚洲视频图片小说| 亚洲人一区二区| 欧洲成人av| aaa国产一区| 99在线免费观看视频| 亚洲一区二区三区网站| 视频一区二区三区在线| 97视频在线观看免费高清完整版在线观看 | 污污污污污污www网站免费| 91露出在线| 久久精品一二三| 免费在线国产精品| 视频一区二区免费| 成人小视频在线| 97av影视网在线观看| 国产欧美综合视频| 国产一区二区三区免费在线观看| 国产精品视频永久免费播放 | 毛片在线导航| 一区二区三区中文字幕电影| 香蕉视频在线网址| 毛片免费不卡| 亚洲欧美偷拍另类a∨色屁股| 亚洲国产精品一区在线观看不卡 | 香蕉国产成人午夜av影院| 精品国产乱码久久久久久蜜臀 | 极品美女一区二区三区| 亚洲欧洲日本专区| 国产一区二区三区四区五区六区| 国产videos久久| 中文字幕日韩精品有码视频| 天美传媒免费在线观看| 国产精品伦理久久久久久| 久久久精品国产一区二区| 丰满少妇被猛烈进入一区二区| 99热在线成人| 九九热最新视频//这里只有精品| 国产在线观看成人| 国产精品试看| 国产精品一区专区欧美日韩| 91资源在线视频| 成人性生交大片| 久久国产手机看片| av影片免费在线观看| 亚洲同性同志一二三专区| 99久久99久久精品| 国产在线看片免费视频在线观看| 欧美丝袜一区二区| 在线观看免费黄网站| 国产成年精品| 亚洲国产成人精品女人久久久 | 黄色免费在线看| 亚洲一区二区3| 日本中文字幕片| 福利一区和二区| 日韩欧美电影一二三| 激情综合丁香五月| 日韩www.| 午夜精品久久久久久久99热浪潮 | 亚欧精品在线| 色综合999| 色丁香久综合在线久综合在线观看| 无需播放器的av| 综合激情久久| 国产午夜精品视频免费不卡69堂| 亚洲xxxx在线| 香蕉视频禁止18| 91精品麻豆| 精品国产凹凸成av人网站| 成人精品久久一区二区三区| 国精品人伦一区二区三区蜜桃| 欧美三级小说| 国产精品久久久久久婷婷天堂| 99在线精品视频免费观看软件| 99re这里只有精品首页| 欧美日韩一区二区三区电影| 在线人成日本视频| 欧美一区二区久久| 欧美 日韩 国产 成人 在线观看 | 精品成人a区在线观看| 久久久久亚洲av成人无码电影| 欧美日韩岛国| 国产啪精品视频| 青青国产在线| 夜夜夜精品看看| 九色porny自拍| 日本在线中文字幕一区| 在线精品福利| 日韩美女免费线视频| www黄色网址| 国产精品久久久久影院老司 | 1234区中文字幕在线观看| 欧美视频在线播放| 日韩av一二区| 国产综合精品一区| 91香蕉亚洲精品| av电影在线网| 色婷婷综合久久| 黄色录像a级片| 一区福利视频| 国产成人一区二区三区免费看| 老司机福利在线视频| 欧美中文字幕亚洲一区二区va在线| 中文视频在线观看| 欧美精品97| 7777精品久久久大香线蕉小说| 又爽又大又黄a级毛片在线视频| 色婷婷久久一区二区三区麻豆| 理论片大全免费理伦片| 欧美日韩一区二区高清| 91在线高清免费观看| 精品孕妇一区二区三区| 欧美亚洲自拍偷拍| 日本一道本视频| 日本午夜精品一区二区三区电影| 欧美日韩一区二区三| 亚洲伊人av| 日韩精品一区二区三区第95| 中文字幕亚洲精品在线| 91丨porny丨户外露出| 青青草视频在线免费播放| youjizz亚洲| 午夜剧场成人观在线视频免费观看| 亚洲男人第一天堂| 亚洲va韩国va欧美va| 中文文字幕文字幕高清| 国产日韩一区二区三区在线播放| 久久久影院一区二区三区| 男女羞羞在线观看| 亚洲欧美一区二区精品久久久| xxxxxx国产| 91麻豆swag| 国产免费视频传媒| 99精品视频在线| 亚洲xxx大片| 丁香花在线影院| 亚洲欧美激情精品一区二区| 91黑人精品一区二区三区| 中文成人综合网| 红桃视频一区二区三区免费| 欧美日韩亚洲一区三区| 精选一区二区三区四区五区| 色8久久影院午夜场| 色噜噜久久综合伊人一本| 国产黄色片av| 欧美日韩视频免费播放| 中文天堂资源在线| 国产精品性做久久久久久| 久久视频这里有精品| 欧美肉体xxxx裸体137大胆| 国产区亚洲区欧美区| 美女网站视频在线| 亚洲人成电影网| 91久久国语露脸精品国产高跟| 一区二区不卡在线播放 | 欧美中文一区二区| 亚洲一区二区三区视频| 美女网站在线看| 日韩中文字幕欧美| 欧美自拍偷拍第一页| 一本大道久久a久久综合| 日韩三级久久久| av不卡在线播放| 在线看免费毛片| 亚洲视频1区| 中文字幕综合在线观看| 另类图片第一页| 成人激情av在线| 一个人www视频在线免费观看| 少妇高潮久久久久久潘金莲| 天天干在线观看| 欧美精品乱码久久久久久按摩| 国产香蕉在线视频| ㊣最新国产の精品bt伙计久久| 星空大象在线观看免费播放| 久久99精品网久久| av免费中文字幕| 国内视频精品| 亚洲毛片aa| 亚洲欧洲美洲国产香蕉| 99re视频| 国产精品1区在线| 日韩免费在线观看视频| 波多野结衣视频一区二区| www欧美日韩| 成人精品一区二区三区校园激情| 亚洲高清在线观看| 性色av蜜臀av| 欧美年轻男男videosbes| 无码人妻久久一区二区三区不卡| 亚洲国产精品影院| 欧美成人一二三区| 中文字幕中文字幕在线一区| 国产成人一区二区在线观看| 26uuu久久综合| 中文字幕免费在线播放| 国产九九视频一区二区三区| 亚洲天堂国产视频| 麻豆视频一区二区| 日韩肉感妇bbwbbwbbw| 日韩av中文在线观看| 久久精品视频16| 亚洲激情婷婷| 国产精品久久中文字幕| 影音先锋久久精品| 一本久道高清无码视频| 亚洲国内欧美| 国精产品一区一区三区视频| 最新成人av网站| 欧美黑人经典片免费观看| 亚洲久久视频| 久久精品国产精品亚洲色婷婷| 亚洲国产mv| aa在线观看视频| 亚洲永久免费| 国产激情在线观看视频| 日韩电影在线免费看| 亚洲免费看av| 狠狠色丁香久久婷婷综| aaa一级黄色片| 国产酒店精品激情| 欧产日产国产精品98| 91年精品国产| 国精产品一区二区三区| 欧美国产精品v| 亚洲天堂网av在线| 亚洲美女少妇撒尿| 免费视频网站www| 一级特黄大欧美久久久| 欧美日韩精品区| 粉嫩老牛aⅴ一区二区三区| 中文字幕免费高清网站| 在线欧美日韩国产| 中文字幕久久久久| 日韩欧美中文字幕制服| 亚洲成a人片在线| 精品精品欲导航| 特黄视频在线观看| 精品奇米国产一区二区三区| 六月婷婷综合网| 亚洲另类激情图| 国产h视频在线观看| 久久国产精品久久久| 91九色在线播放| 欧美孕妇与黑人孕交| 成入视频在线观看| 国产精品久久视频| 成人在线视频区| 国产精品视频一| 97视频一区| 欧美成人综合一区| 国产高清一区| 日韩精品一区二区免费| 国产日韩欧美一区在线| 亚洲a级黄色片| 粉嫩一区二区三区性色av| 添女人荫蒂视频| 最新国产精品久久精品| 久久激情免费视频| 色呦呦一区二区三区| 国产喷水福利在线视频| 日韩成人在线网站| 午夜看片在线免费| 91精品国产网站| 日韩电影精品| 5g国产欧美日韩视频| 成人看的视频| 国产亚洲黄色片| 精品一区二区三区免费播放| 成年女人免费视频| 亚洲国产精品成人久久综合一区| 国产一级理论片| 欧美日韩一区国产| 日本激情一区二区| 久久久精品免费视频| 国产精品迅雷| 97人人模人人爽视频一区二区| 成人在线视频免费观看| 99在线免费视频观看| 久久蜜桃精品| 国产精品久久AV无码| 亚洲欧洲制服丝袜| 国产精品sm调教免费专区| 精品播放一区二区| 尤物视频在线免费观看| 午夜精品久久久久久久99热浪潮| 亚洲欧美一级| 欧美精品一区在线| 国产亚洲一区在线| 免费高清视频在线观看| 国产欧美日韩精品一区| 日本a级c片免费看三区| 日韩久久精品一区| 毛片在线视频| 国产精品免费久久久久影院| 欧美美女黄色| 国产一区二区三区小说| 韩国欧美一区二区| 手机看片日韩av| 欧美视频一区二区三区在线观看| 天天干,天天操,天天射| 午夜精品一区二区三区视频免费看| 国产精品亚洲综合在线观看| 日韩欧美三级一区二区| 日本午夜精品视频在线观看 | 91色.com| 久草视频在线观| 欧美变态口味重另类| 在线免费看av| 国产日本欧美一区| 奇米影视亚洲| 在线观看免费的av| 国产女人水真多18毛片18精品视频| 久久国产黄色片| 亚洲人永久免费| 在线手机中文字幕| 日本一区免费看| 性色一区二区| a毛片毛片av永久免费| 一区二区理论电影在线观看| 黄色一级a毛片| 国内精品中文字幕| 欧美电影完整版在线观看| 妺妺窝人体色777777| 不卡在线视频中文字幕| 国产无码精品久久久| 亚洲成人av在线| av资源中文在线天堂| 国产自产在线视频一区| 久久久成人网| 丁香六月激情综合| 欧美日韩欧美一区二区| 日韩毛片久久久| 91在线观看免费| 亚洲经典在线| 三上悠亚ssⅰn939无码播放 | 国产免费黄色网址| 久久福利视频网| 粉嫩的18在线观看极品精品| 3d动漫一区二区三区| 久久日一线二线三线suv| 欧美特级黄色片| 久久精品国产69国产精品亚洲| 国产日本亚洲| 免费毛片网站在线观看| 久久久精品国产99久久精品芒果| 成人黄色激情视频| 精品国偷自产在线| 久久365资源| 久久久久久久片| 亚洲一区二区在线观看视频| 污视频在线免费观看| 91av在线视频观看| 日本精品黄色| 亚洲国产欧美日韩在线| 欧美日韩日本国产| 欧美精品日韩少妇| 成人片在线免费看| 天堂影院一区二区| 永久免费看mv网站入口| 亚洲欧美激情精品一区二区| 9999精品视频| 成人小视频在线看| 亚洲婷婷在线视频| 日日躁夜夜躁白天躁晚上躁91| 国产精品极品美女在线观看免费 | 国产成人久久精品77777最新版本 国产成人鲁色资源国产91色综 | 一二三区免费视频|