-
1. 起步
-
2. Git 基礎
-
3. Git 分支
-
4. 伺服器上的 Git
- 4.1 協議
- 4.2 在伺服器上部署 Git
- 4.3 生成 SSH 公鑰
- 4.4 架設伺服器
- 4.5 Git Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 第三方託管服務
- 4.10 小結
-
5. 分散式 Git
-
A1. 附錄 A: Git 在其他環境
- A1.1 圖形介面
- A1.2 Visual Studio 中的 Git
- A1.3 Visual Studio Code 中的 Git
- A1.4 IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine 中的 Git
- A1.5 Sublime Text 中的 Git
- A1.6 Bash 中的 Git
- A1.7 Zsh 中的 Git
- A1.8 PowerShell 中的 Git
- A1.9 小結
-
A2. 附錄 B: 在應用程式中嵌入 Git
-
A3. 附錄 C: Git 命令
6.2 GitHub - 為專案貢獻
為專案貢獻
既然我們的賬戶已經設定好了,下面我們將介紹一些有助於您參與現有專案的有用細節。
派生專案
如果您想為現有專案貢獻,但沒有推送許可權,您可以“派生(fork)”該專案。當您“派生”一個專案時,GitHub 會建立一個完全屬於您的專案副本;它位於您的名稱空間下,您可以向其推送內容。
注意
|
從歷史上看,“fork”這個詞語在某些語境下帶有一些負面含義,意味著有人將一個開源專案引向了不同的方向,有時會建立一個競爭專案並導致貢獻者分裂。但在 GitHub 中,“fork”僅僅是您自己的名稱空間下的同一個專案,它允許您以更開放的方式公開地對專案進行更改,從而進行貢獻。 |
透過這種方式,專案無需擔心為了授予推送許可權而將使用者新增為協作者。人們可以派生一個專案,向其推送內容,然後透過建立所謂的“拉取請求”(Pull Request)將他們的更改貢獻回原始倉庫,我們將在後面介紹這一點。這將開啟一個包含程式碼審查的討論執行緒,專案所有者和貢獻者可以就更改進行溝通,直到所有者滿意為止,屆時所有者就可以將其合併進來。
要派生一個專案,請訪問專案頁面並點選頁面右上角的“Fork”(派生)按鈕。

幾秒鐘後,您將被帶到新的專案頁面,其中包含您自己的可寫入程式碼副本。
GitHub 流程
GitHub 是圍繞特定的協作工作流程設計的,其核心是拉取請求(Pull Request)。無論您是與一個緊密協作的團隊在單個共享倉庫中協作,還是一個全球分佈的公司或陌生人網路透過數十個派生(fork)為專案貢獻,這個流程都能奏效。它以主題分支工作流程為中心,該工作流程在Git 分支一章中有所介紹。
其通常的工作方式如下:
-
派生專案。
-
從
master
分支建立主題分支。 -
進行一些提交以改進專案。
-
將此分支推送到您的 GitHub 專案。
-
在 GitHub 上開啟一個拉取請求(Pull Request)。
-
討論,並可選擇繼續提交。
-
專案所有者合併或關閉拉取請求。
-
將更新後的
master
分支同步回您的派生。
這基本上就是整合管理者工作流程中所介紹的工作方式,但不同的是,團隊使用 GitHub 的基於 Web 的工具來溝通和審查更改,而不是透過電子郵件。
讓我們透過一個例子,演示如何使用此流程向 GitHub 上託管的開源專案提出更改建議。
提示
|
您可以使用官方的 GitHub CLI 工具來完成大部分操作,而非 GitHub 網頁介面。該工具可在 Windows、macOS 和 Linux 系統上使用。請訪問 GitHub CLI 主頁獲取安裝說明和手冊。 |
建立拉取請求
Tony 正在為他的 Arduino 可程式設計微控制器尋找程式碼,並在 GitHub 上找到了一個很棒的程式檔案:https://github.com/schacon/blink。

唯一的問題是閃爍速率太快了。我們認為在每次狀態更改之間等待 3 秒而不是 1 秒會好得多。所以,讓我們改程序序,並將其作為建議的更改提交回專案。
首先,我們點選前面提到的“Fork”按鈕,獲取我們自己的專案副本。這裡我們的使用者名稱是“tonychacon”,所以這個專案的副本位於 https://github.com/tonychacon/blink
,我們可以在那裡進行編輯。我們將在本地克隆它,建立一個主題分支,進行程式碼更改,最後將更改推送到 GitHub。
$ git clone https://github.com/tonychacon/blink (1)
Cloning into 'blink'...
$ cd blink
$ git checkout -b slow-blink (2)
Switched to a new branch 'slow-blink'
$ sed -i '' 's/1000/3000/' blink.ino (macOS) (3)
# If you're on a Linux system, do this instead:
# $ sed -i 's/1000/3000/' blink.ino (3)
$ git diff --word-diff (4)
diff --git a/blink.ino b/blink.ino
index 15b9911..a6cc5a5 100644
--- a/blink.ino
+++ b/blink.ino
@@ -18,7 +18,7 @@ void setup() {
// the loop routine runs over and over again forever:
void loop() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
[-delay(1000);-]{+delay(3000);+} // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
[-delay(1000);-]{+delay(3000);+} // wait for a second
}
$ git commit -a -m 'Change delay to 3 seconds' (5)
[slow-blink 5ca509d] Change delay to 3 seconds
1 file changed, 2 insertions(+), 2 deletions(-)
$ git push origin slow-blink (6)
Username for 'https://github.com': tonychacon
Password for 'https://tonychacon@github.com':
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 340 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To https://github.com/tonychacon/blink
* [new branch] slow-blink -> slow-blink
-
在本地克隆我們派生的專案。
-
建立一個描述性的主題分支。
-
對程式碼進行修改。
-
檢查更改是否良好。
-
將更改提交到主題分支。
-
將新主題分支推送回我們的 GitHub 派生。
現在,如果我們回到 GitHub 上的派生,我們會看到 GitHub 注意到我們推送了一個新的主題分支,併為我們提供了一個大大的綠色按鈕,用於檢視我們的更改並向原始專案開啟拉取請求(Pull Request)。
您也可以前往 https://github.com/<user>/<project>/branches
的“Branches”(分支)頁面,找到您的分支並從那裡開啟新的拉取請求。

如果我們點選那個綠色按鈕,我們會看到一個螢幕,要求我們為拉取請求提供標題和描述。這幾乎總是值得付出一些努力的,因為好的描述有助於原始專案的所有者確定您想要做什麼,您的提議更改是否正確,以及接受這些更改是否會改進原始專案。
我們還會看到主題分支中“領先”於 master
分支的提交列表(本例中只有一個),以及如果此分支被專案所有者合併,將進行的所有更改的統一差異(diff)。

當您在此螢幕上點選“Create pull request”(建立拉取請求)按鈕時,您所派生專案的所有者將收到有人建議更改的通知,並會連結到一個包含所有這些資訊的頁面。
注意
|
儘管拉取請求通常用於像這樣貢獻者已經準備好完整更改的公共專案,但它也常在內部專案的開發週期開始時使用。由於即使在拉取請求開啟之後,您也可以繼續向主題分支推送,因此它通常會盡早開啟,並作為團隊在特定上下文中迭代工作的一種方式,而不是在流程的最後才打開。 |
迭代拉取請求
此時,專案所有者可以檢視建議的更改並進行合併、拒絕或評論。假設他喜歡這個想法,但希望燈滅的時間比亮的時間稍長一些。
在分散式 Git 中介紹的工作流程中,這種對話可能透過電子郵件進行,但在 GitHub 上,這發生在線上。專案所有者可以檢視統一差異(unified diff)並透過點選任意行來留下評論。

一旦維護者發表此評論,開啟拉取請求的人(以及任何關注該倉庫的人)都將收到通知。我們稍後將介紹如何自定義此功能,但如果 Tony 開啟了電子郵件通知,他將收到這樣的電子郵件:

任何人都可以對拉取請求留下一般性評論。在拉取請求討論頁面中,我們可以看到一個專案所有者既對一行程式碼進行評論,又在討論區留下一般性評論的例子。您可以看到程式碼評論也被帶入到對話中。

現在貢獻者可以看到他們需要做什麼才能使他們的更改被接受。幸運的是,這非常簡單。透過電子郵件,您可能需要重新打包您的系列並重新提交到郵件列表,而在 GitHub 上,您只需再次提交到主題分支並推送,這將自動更新拉取請求。在拉取請求最終頁面中,您還可以看到舊的程式碼評論在更新後的拉取請求中已被摺疊,因為它是在已更改的行上進行的。
向現有拉取請求新增提交不會觸發通知,因此一旦 Tony 推送了他的更正,他決定留下評論,告知專案所有者他已進行了請求的更改。

一個有趣的地方是,如果您點選此拉取請求的“Files Changed”(檔案更改)選項卡,您將獲得“統一”差異(unified diff)——也就是說,如果此主題分支被合併,將引入到您的主分支的總聚合差異。用 git diff
的術語來說,它基本上會自動顯示該拉取請求所基於的分支的 git diff master…<branch>
。有關此類差異的更多資訊,請參閱確定引入了什麼。
您還會注意到的另一件事是,GitHub 會檢查拉取請求是否可以乾淨地合併,並提供一個按鈕讓您在伺服器上執行合併。此按鈕僅在您對倉庫具有寫入許可權且可以進行簡單合併時才會顯示。如果您點選它,GitHub 將執行“非快進式”(non-fast-forward)合併,這意味著即使合併可以是快進式的,它仍然會建立一個合併提交(merge commit)。
如果您願意,您可以簡單地將該分支拉取下來並在本地合併。如果您將此分支合併到 master
分支並推送到 GitHub,拉取請求將自動關閉。
這是大多數 GitHub 專案使用的基本工作流程。建立主題分支,在其上開啟拉取請求,隨後進行討論,可能在該分支上進行更多工作,最終請求被關閉或合併。
注意
|
不僅僅是派生
值得注意的是,您也可以在同一個倉庫中的兩個分支之間開啟拉取請求。如果您與某人合作開發一個功能,並且您們都對專案具有寫入許可權,您可以將一個主題分支推送到倉庫,並在此主題分支上向同一個專案的 |
高階拉取請求
既然我們已經介紹了在 GitHub 上為專案貢獻的基礎知識,接下來我們將介紹一些關於拉取請求的有趣技巧和竅門,以便您更有效地使用它們。
作為補丁的拉取請求
重要的是要理解,許多專案並不真正將拉取請求視為應該按順序乾淨應用的完美補丁佇列,而大多數基於郵件列表的專案則將補丁系列貢獻視為如此。大多數 GitHub 專案將拉取請求分支視為圍繞提議更改的迭代對話,最終透過合併應用一個統一的差異(diff)。
這是一個重要的區別,因為通常在程式碼被認為是完美之前就提出了更改建議,這在基於郵件列表的補丁系列貢獻中更為罕見。這使得可以與維護者進行更早的對話,從而使找到正確解決方案成為一項社群努力。當透過拉取請求提出程式碼並且維護者或社群建議更改時,補丁系列通常不會重新生成,而是將差異作為新的提交推送到分支,在保留先前工作上下文的情況下推進對話。
例如,如果您再次檢視最終拉取請求,您會注意到貢獻者沒有重定基他的提交併傳送另一個拉取請求。相反,他們添加了新的提交併推送到現有分支。這樣,如果您將來回溯檢視此拉取請求,您可以輕鬆找到所有關於決策原因的上下文資訊。網站上的“合併”按鈕特意建立了一個引用拉取請求的合併提交,以便在必要時方便回溯和研究原始對話。
與上游保持同步
如果您的拉取請求過期或無法乾淨地合併,您需要修復它,以便維護者可以輕鬆合併。GitHub 會為您測試並告知您每個拉取請求的底部,合併是否簡單。

如果您看到類似拉取請求無法乾淨合併的情況,您會希望修復您的分支,使其變為綠色,這樣維護者就無需額外工作了。
您有兩種主要的選擇來完成此操作。您可以將您的分支重定基到目標分支之上(通常是您派生倉庫的 master
分支),或者您可以將目標分支合併到您的分支中。
GitHub 上的大多數開發者會選擇後者,原因與我們上一節中討論的相同。重要的是歷史和最終合併,所以重定基除了能帶來稍微乾淨一些的歷史外,並沒有太大的好處,而且它遠比合並更困難、更容易出錯。
如果您想合併目標分支以使您的拉取請求可合併,您需要將原始倉庫新增為新的遠端倉庫,從中抓取(fetch),將該倉庫的主分支合併到您的主題分支中,修復所有問題,最後將其推送回您開啟拉取請求的同一個分支。
例如,假設在我們之前使用的“tonychacon”示例中,原作者做了一個會在拉取請求中產生衝突的更改。讓我們過一遍這些步驟。
$ git remote add upstream https://github.com/schacon/blink (1)
$ git fetch upstream (2)
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
Unpacking objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
From https://github.com/schacon/blink
* [new branch] master -> upstream/master
$ git merge upstream/master (3)
Auto-merging blink.ino
CONFLICT (content): Merge conflict in blink.ino
Automatic merge failed; fix conflicts and then commit the result.
$ vim blink.ino (4)
$ git add blink.ino
$ git commit
[slow-blink 3c8d735] Merge remote-tracking branch 'upstream/master' \
into slower-blink
$ git push origin slow-blink (5)
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 682 bytes | 0 bytes/s, done.
Total 6 (delta 2), reused 0 (delta 0)
To https://github.com/tonychacon/blink
ef4725c..3c8d735 slower-blink -> slow-blink
-
將原始倉庫新增為名為
upstream
的遠端倉庫。 -
從該遠端倉庫抓取最新工作。
-
將該倉庫的主分支合併到您的主題分支。
-
解決發生的衝突。
-
推送回同一主題分支。
完成這些操作後,拉取請求將自動更新並重新檢查是否可以乾淨地合併。

Git 的一個優點是您可以持續地進行此操作。如果您的專案執行時間很長,您可以輕鬆地一次又一次地從目標分支合併,並且只需要處理自上次合併以來出現的衝突,這使得整個過程非常易於管理。
如果您確實希望重定基分支以清理它,當然可以這樣做,但強烈建議不要強制推送到已經開啟拉取請求的分支。如果其他人已經拉取並在此分支上進行了更多工作,您將遇到重定基的風險中概述的所有問題。相反,將重定基後的分支推送到 GitHub 上的一個新分支,並開啟一個新的拉取請求,引用舊的拉取請求,然後關閉原始的。
引用
您的下一個問題可能是“我如何引用舊的拉取請求?”。事實證明,在 GitHub 中,您幾乎可以在任何可寫入的地方,以多種方式引用其他內容。
讓我們從如何交叉引用另一個拉取請求或議題(Issue)開始。所有拉取請求和議題都被分配了編號,並且在專案內是唯一的。例如,您不能同時擁有拉取請求 #3 和議題 #3。如果您想從任何其他拉取請求或議題中引用某個拉取請求或議題,您只需在任何評論或描述中放入 #<num>
。如果議題或拉取請求位於其他地方,您也可以更具體;如果您引用的是您所在倉庫派生中的議題或拉取請求,請寫入 username#<num>
,或使用 username/repo#<num>
來引用另一個倉庫中的內容。
讓我們看一個例子。假設我們在前面的例子中重定基了分支,為其建立了一個新的拉取請求,現在我們想在新拉取請求中引用舊的拉取請求。我們還想引用倉庫派生中的一個議題以及一個完全不同專案中的議題。我們可以像拉取請求中的交叉引用那樣填寫描述。

當我們提交此拉取請求時,我們將看到所有內容呈現,如同拉取請求中渲染的交叉引用。

請注意,我們放入的完整 GitHub URL 被縮短為僅顯示所需的資訊。
現在,如果 Tony 返回並關閉原始拉取請求,我們會看到透過在新拉取請求中提及它,GitHub 已自動在拉取請求時間線中建立了一個回溯事件。這意味著任何訪問此拉取請求並看到它已關閉的人都可以輕鬆連結回替代它的那個。該連結將看起來像在已關閉的拉取請求時間線中連結回新的拉取請求。

除了議題編號,您還可以透過 SHA-1 引用特定的提交。您必須指定完整的 40 個字元的 SHA-1,但如果 GitHub 在評論中看到它,它將直接連結到該提交。同樣,您可以像引用議題一樣,在派生或其他倉庫中引用提交。
GitHub 風味 Markdown
連結到其他議題只是您在 GitHub 上幾乎任何文字框中可以做的有趣事情的開始。在議題和拉取請求的描述、評論、程式碼評論以及更多地方,您可以使用所謂的“GitHub 風味 Markdown”。Markdown 就像寫純文字一樣,但它可以被豐富地渲染。
有關如何使用 Markdown 編寫和渲染評論或文字的示例,請參閱GitHub 風味 Markdown 的編寫和渲染示例。

GitHub 風味的 Markdown 在基本 Markdown 語法之外增加了更多功能。這些在建立有用的拉取請求或議題評論或描述時都非常有用。
任務列表
第一個真正有用的 GitHub 特有 Markdown 功能,尤其適用於拉取請求,是任務列表。任務列表是您想要完成的待辦事項的複選框列表。將其放入議題或拉取請求中通常表示在您認為專案完成之前需要完成的事項。
您可以建立這樣的任務列表:
- [X] Write the code
- [ ] Write all the tests
- [ ] Document the code
如果我們將此包含在我們的拉取請求或議題的描述中,我們將看到它呈現為Markdown 評論中渲染的任務列表。

這經常用於拉取請求中,以指示在拉取請求準備好合併之前,您希望在該分支上完成的所有工作。真正酷的部分是,您可以簡單地點選複選框來更新評論——您無需直接編輯 Markdown 來勾選任務。
更重要的是,GitHub 會在您的議題和拉取請求中查詢任務列表,並在列出它們的頁面上將其顯示為元資料。例如,如果您有一個包含任務的拉取請求,並且您檢視所有拉取請求的概覽頁面,您可以看到它完成了多少。這有助於人們將拉取請求分解為子任務,並幫助其他人跟蹤分支的進度。您可以在拉取請求列表中的任務列表摘要中看到一個示例。

當您儘早開啟拉取請求並將其用於跟蹤功能實現進度時,這些功能非常有用。
程式碼片段
您還可以在評論中新增程式碼片段。如果您想在實際作為提交在分支上實現之前,展示您可以嘗試做的事情,這尤其有用。這通常也用於新增不工作的示例程式碼,或者此拉取請求可以實現什麼。
要新增程式碼片段,您必須用反引號將其“圍起來”。
```java
for(int i=0 ; i < 5 ; i++)
{
System.out.println("i is : " + i);
}
```
如果您像我們那樣新增語言名稱(例如 'java'),GitHub 也會嘗試對片段進行語法高亮。在上面的示例中,它最終會呈現為渲染後的圍欄程式碼示例。

引用
如果您正在回覆長評論的一小部分,可以透過在行前加上 >
字元來選擇性地引用其他評論中的內容。事實上,這非常常見且有用,以至於有一個鍵盤快捷鍵。如果您高亮顯示評論中要直接回復的文字並按下 r
鍵,它將自動為您在評論框中引用該文字。
引用內容看起來像這樣:
> Whether 'tis Nobler in the mind to suffer
> The Slings and Arrows of outrageous Fortune,
How big are these slings and in particular, these arrows?
渲染後,評論將顯示為渲染後的引用示例。

表情符號
最後,您還可以在評論中使用表情符號。這實際上在許多 GitHub 議題和拉取請求的評論中被廣泛使用。GitHub 中甚至有一個表情符號助手。如果您正在輸入評論並以 :
字元開頭,自動完成器將幫助您找到您想要的內容。

表情符號在評論中的任何地方都以 :<名稱>:
的形式出現。例如,您可以這樣寫:
I :eyes: that :bug: and I :cold_sweat:.
:trophy: for :microscope: it.
:+1: and :sparkles: on this :ship:, it's :fire::poop:!
:clap::tada::panda_face:
渲染後,它看起來會像大量使用表情符號的評論。

這不是說它非常有用,但它確實為原本難以表達情感的媒介增添了趣味性和情感元素。
注意
|
現在實際上有相當多的網路服務使用表情符號。一個很好的參考備忘單,可以找到表達您想要說的表情符號,可以在以下地址找到: |
圖片
這在技術上並非 GitHub 風味 Markdown,但它非常有用。除了在評論中新增 Markdown 圖片連結(這可能難以找到和嵌入 URL)之外,GitHub 還允許您將圖片拖放到文字區域中以進行嵌入。

如果您檢視拖放圖片以上傳並自動嵌入它們,您會看到文字區域上方有一個小小的“Parsed as Markdown”(解析為 Markdown)提示。點選它將為您提供一個完整的 GitHub Markdown 功能備忘單。
保持您的 GitHub 公開倉庫同步更新
一旦您派生了一個 GitHub 倉庫,您的倉庫(您的“派生”)就獨立於原始倉庫存在。特別是當原始倉庫有新的提交時,GitHub 會透過類似這樣的訊息通知您:
This branch is 5 commits behind progit:master.
但是您的 GitHub 倉庫永遠不會由 GitHub 自動更新;這是您必須自己完成的操作。幸運的是,這非常容易。
一種無需配置即可實現此目的的可能性。例如,如果您從 https://github.com/progit/progit2.git
派生,您可以像這樣保持您的 master
分支最新:
$ git checkout master (1)
$ git pull https://github.com/progit/progit2.git (2)
$ git push origin master (3)
-
如果您在其他分支,請返回
master
分支。 -
從
https://github.com/progit/progit2.git
抓取更改並將其合併到master
分支。 -
將您的
master
分支推送到origin
。
這可行,但是每次都要拼寫出抓取 URL 有點繁瑣。您可以透過一些配置來自動化這項工作:
$ git remote add progit https://github.com/progit/progit2.git (1)
$ git fetch progit (2)
$ git branch --set-upstream-to=progit/master master (3)
$ git config --local remote.pushDefault origin (4)
-
新增源倉庫併為其命名。這裡我選擇將其命名為
progit
。 -
獲取 progit 分支的引用,特別是
master
。 -
將您的
master
分支設定為從progit
遠端倉庫抓取。 -
將預設推送倉庫定義為
origin
。
完成此操作後,工作流程將變得更簡單:
$ git checkout master (1)
$ git pull (2)
$ git push (3)
-
如果您在其他分支,請返回
master
分支。 -
從
progit
抓取更改並將更改合併到master
分支。 -
將您的
master
分支推送到origin
。
這種方法可能很有用,但並非沒有缺點。Git 會很高興地默默為您完成這項工作,但如果您向 master
提交、從 progit
拉取,然後推送到 origin
,它不會發出警告——所有這些操作在此設定下都是有效的。因此,您必須注意永遠不要直接提交到 master
,因為該分支實際上屬於上游倉庫。