English ▾ 主題 ▾ 最新版本 ▾ gitworkflows 最後更新於 2.35.0

名稱

gitworkflows - Git 推薦工作流程概覽

概要

git *

描述

本文檔嘗試記錄並解釋 git.git 本身所使用的一些工作流程元素。儘管許多想法適用於一般情況,但對於規模較小、參與人數較少的專案,通常不需要使用完整的工作流程。

我們制定了一組規則作為快速參考,而文內則試圖說明每一條規則的動機。請不要總是照本宣科;你應該將行事的正當理由看得比這類手冊頁更重要。

分離變更

作為一般原則,你應該嘗試將你的變更拆分為小的邏輯步驟,並提交每一項。這些變更應保持一致,獨立於任何後續提交而工作,並通過測試套件等。這會使審閱過程變得容易得多,歷史記錄對於日後的檢查和分析也更有價值,例如使用 git-blame[1]git-bisect[1] 時。

為了達到這一點,請從一開始就嘗試將你的工作拆分為小的步驟。將幾個提交合併在一起總是比將一個大提交拆分為幾個要容易。不要害怕在此過程中做出太小或不完美的步驟。你總是可以稍後回過頭來,在發佈之前使用 git rebase --interactive 編輯提交。你可以使用 git stash push --keep-index 來運行測試套件,而獨立於其他未提交的變更;參見 git-stash[1] 的範例部分。

管理分支

有兩種主要工具可用於將一個分支的變更包含到另一個分支:git-merge[1]git-cherry-pick[1]

合併有很多優點,因此我們嘗試儘可能多地僅通過合併來解決問題。挑揀(Cherry-picking)偶爾仍然有用;參見下方的「向上合併」範例。

最重要的是,合併在分支層級運作,而挑揀在提交層級運作。這意味著合併可以同等輕鬆地攜帶來自 1 個、10 個或 1000 個提交的變更,這也意味著工作流程可以更好地擴展到大量的貢獻者(和貢獻)。合併也更容易理解,因為一個合併提交是一個「承諾」,保證來自其所有父級的所有變更現在都已包含在內。

當然,這是一個權衡:合併需要更仔細的分支管理。以下小節將討論重點。

畢業(Graduation)

當某個功能從實驗性變為穩定時,它也會在軟體的對應分支之間「畢業」。git.git 使用以下整合分支

  • maint 追蹤應納入下一個「維護版本」的提交,即最後發佈的穩定版本的更新;

  • master 追蹤應納入下一個發佈版本的提交;

  • next 用作測試分支,用於測試對 master 的穩定性。

還有第四個官方分支,其使用方式略有不同:

  • seen(維護者看到的修補程式)是一個整合分支,用於尚未準備好納入的內容(參見下方的「整合分支」)。

這四個分支中的每一個通常都是其上方分支的直接後代。

從概念上講,功能進入不穩定的分支(通常是 nextseen),一旦被認為足夠穩定,就會「畢業」到 master 以進行下一次發佈。

向上合併

上述討論的「向下畢業」無法透過實際進行向下合併來完成,因為那樣會將不穩定分支上的所有變更合併到穩定分支中。因此有以下規定:

規則:向上合併

務必將你的修復提交到需要它們的最舊受支援分支。然後(定期)將整合分支向上合併到彼此中。

這能提供一個受控的修復流程。如果你發現你對例如 master 應用了一個同樣需要於 maint 中的修復,你將需要(使用 git-cherry-pick[1])將其向下挑揀。這種情況偶爾會發生,除非你非常頻繁地這樣做,否則不必擔心。

主題分支

任何非平凡的功能都需要多個修補程式來實作,並且在其生命週期中可能會獲得額外的錯誤修復或改進。

直接在整合分支上提交所有內容會導致許多問題:糟糕的提交無法撤銷,因此必須逐一還原,這會導致混亂的歷史記錄,並在忘記還原部分變更組時產生進一步的錯誤潛力。並行工作會混淆變更,造成進一步的混亂。

使用「主題分支」可以解決這些問題。名稱本身就解釋了一切,但要注意上面提到的「向上合併」規則:

規則:主題分支

為每個主題(功能、錯誤修復等)製作一個側分支。從你最終希望將其合併入的最舊整合分支分支出來。

這樣,許多事情就可以非常自然地完成:

  • 要將功能/錯誤修復納入整合分支,只需合併它。如果主題在此期間有進一步的演變,再次合併。(請注意,你不一定必須先將其合併到最舊的整合分支。例如,你可以先將錯誤修復合併到 next,給它一些測試時間,當你確定它穩定後,再合併到 maint。)

  • 如果你發現需要來自其他分支的新功能來繼續你的主題工作,請將其他分支合併到主題分支。(但是,不要「習慣性」地這樣做,見下文。)

  • 如果你發現你分支出錯了分支,並想將其「往回移動」,請使用 git-rebase[1]

請注意,最後一點與前兩點衝突:已經合併到其他地方的主題不應重定基準(rebased)。參見 git-rebase[1] 中的「從上游重定基準中恢復」一節。

我們應該指出,「習慣性地」(即無故地定期)將整合分支合併到你的主題中——並延伸開來,定期將任何上游合併到任何下游——是不被提倡的:

規則:僅在定義明確的點合併到下游

除非有正當理由,否則不要合併到下游:例如上游 API 的變更影響了你的分支;你的分支不再能乾淨地合併到上游等。

否則,被合併到的主題會突然包含超過單一(定義明確的)變更。由此產生的許多小合併將極大地混亂歷史記錄。任何稍後調查檔案歷史的人都將不得不弄清楚該合併是否影響了正在開發的主題。上游甚至可能無意中被合併到一個「更穩定」的分支中。依此類推。

拋棄式整合

如果你遵循了上一段,你現在將擁有許多小的主題分支,並且偶爾會好奇它們是如何互動的。也許合併後的結果甚至無法運作?但另一方面,我們希望避免將它們合併到任何「穩定」的地方,因為這樣的合併無法輕易撤銷。

解決方案當然是進行一個我們可以撤銷的合併:合併到一個拋棄式分支中。

規則:拋棄式整合分支

為了測試多個主題的互動,將它們合併到一個拋棄式分支中。你絕對不能基於這樣的分支進行任何工作!

如果你能非常清楚地說明該分支將在測試後立即刪除,你甚至可以發佈此分支,例如讓測試人員有機會與其合作,或讓其他開發人員有機會查看他們正在進行的工作是否相容。git.git 有一個名為 seen 的官方拋棄式整合分支。

發佈版本的分支管理

假設你正在使用上述討論的合併方法,當你發佈你的專案時,你需要進行一些額外的分支管理工作。

功能發佈是從 master 分支建立的,因為 master 追蹤應納入下一個功能發佈的提交。

master 分支應該是 maint 的超集。如果不滿足此條件,則 maint 包含一些未包含在 master 中的提交。因此,這些提交所代表的修復將不會包含在你的功能發佈中。

要驗證 master 確實是 maint 的超集,請使用 git log:

食譜:驗證 mastermaint 的超集

git log master..maint

此指令不應列出任何提交。否則,請檢出 master 並將 maint 合併到其中。

現在你可以繼續進行功能發佈的建立。在 master 的尖端貼上一個標籤,指明發佈版本:

食譜:發佈標籤

git tag -s -m "Git X.Y.Z" vX.Y.Z master

你需要將新標籤推送到公共 Git 伺服器(參見下方的「分散式工作流程」)。這使得追蹤你專案的其他人可以使用該標籤。推送還可以觸發 post-update 鉤子來執行與發佈相關的項目,例如建立發佈 tarball 和預格式化的文件頁面。

同樣地,對於維護版本發佈,maint 正在追蹤要發佈的提交。因此,在上述步驟中,只需標記並推送 maint 而不是 master

功能發佈後的維護分支管理

在功能發佈之後,你需要管理你的維護分支。

首先,如果你希望繼續為最近一次之前的發佈版本發佈維護修復,那麼你必須建立另一個分支來追蹤該先前發佈版本的提交。

為此,將當前的維護分支複製到另一個以先前發佈版本號命名的分支(例如 maint-X.Y.(Z-1),其中 X.Y.Z 是當前版本)。

食譜:複製 maint

git branch maint-X.Y.(Z-1) maint

maint 分支現在應該快進(fast-forwarded)到新發佈的程式碼,以便可以為當前版本追蹤維護修復:

食譜:將 maint 更新為新版本
  • git checkout maint

  • git merge --ff-only master

如果合併失敗,因為它不是快進,那麼可能 maint 上的一些修復在功能發佈中遺漏了。如果分支內容已按上一節所述進行驗證,則不會發生這種情況。

功能發佈後 next 和 seen 的分支管理

在功能發佈之後,整合分支 next 可以選擇性地回退,並使用 next 上倖存的主題從 master 的尖端重建:

食譜:回退並重建 next
  • git switch -C next master

  • git merge ai/topic_in_next1

  • git merge ai/topic_in_next2

  • …​

這樣做的好處是 next 的歷史記錄將保持乾淨。例如,某些合併到 next 的主題最初看起來很有希望,但後來發現不理想或為時過早。在這種情況下,該主題會從 next 中還原,但歷史記錄中仍然存在它曾經被合併和還原的事實。透過重新建立 next,你為此類主題的另一個化身提供了重新嘗試的乾淨狀態,而功能發佈是歷史記錄中這樣做的合適點。

如果你這樣做,那麼你應該發佈一個公開公告,表明 next 已被回退並重建。

同樣的回退和重建過程可以對 seen 進行。由於 seen 如上所述是一個拋棄式分支,因此不需要公開公告。

分散式工作流程

閱讀上一節後,你應該知道如何管理主題。通常,你不會是專案中唯一工作的人,所以你將不得不分享你的工作。

大致來說,有兩種重要的工作流程:合併(merge)和修補程式(patch)。重要的區別在於合併工作流程可以傳播完整的歷史記錄(包括合併),而修補程式則不能。兩種工作流程可以並行使用:在 git.git 中,只有子系統維護者使用合併工作流程,而其他人則發送修補程式。

請注意,維護者可能會施加限制,例如「Signed-off-by」要求,所有提交/修補程式必須遵守這些要求才能被納入。有關更多資訊,請查閱你的專案文件。

合併工作流程

合併工作流程透過在上游和下游之間複製分支來運作。上游可以將貢獻合併到官方歷史記錄中;下游基於官方歷史記錄開展工作。

有三種主要工具可用於此:

  • git-push[1] 將你的分支複製到遠端儲存庫,通常是所有相關方都可以讀取的儲存庫;

  • git-fetch[1] 將遠端分支複製到你的儲存庫;以及

  • git-pull[1],它一次完成獲取(fetch)和合併(merge)。

注意最後一點。除非你確實想合併遠端分支,否則不要使用 git pull

發佈變更很容易:

食譜:推送/拉取:發佈分支/主題

git push <remote> <branch> 並告訴每個人他們可以在哪裡獲取。

你仍然必須透過其他方式通知人們,例如電子郵件。(Git 提供了 git-request-pull[1] 向開發人員發送預格式化的拉取請求,以簡化此任務。)

如果你只是想獲得整合分支的最新副本,保持更新也很容易:

食譜:推送/拉取:保持最新

使用 git fetch <remote>git remote update 來保持更新。

然後按照前面解釋的那樣,從穩定的遠端分支出來你的主題分支。

如果你是維護者,並且想將其他人的主題分支合併到整合分支中,他們通常會透過電子郵件發送請求。這樣的請求看起來如下:

Please pull from
    <URL> <branch>

在這種情況下,git pull 可以一次完成獲取和合併,如下所示:

食譜:推送/拉取:合併遠端主題

git pull <URL> <branch>

有時,維護者在嘗試從下游拉取變更時可能會遇到合併衝突。在這種情況下,他們可以要求下游自己進行合併並解決衝突(也許他們更清楚如何解決這些衝突)。這是下游應該從上游合併的少數情況之一。

修補程式工作流程

如果你是以電子郵件形式將變更發送到上游的貢獻者,你應該照常使用主題分支(參見上文)。然後使用 git-format-patch[1] 來產生相應的電子郵件(強烈推薦,勝過手動格式化,因為它讓維護者的生活更輕鬆)。

食譜:format-patch/am:發佈分支/主題
  • git format-patch -M upstream..topic 將它們轉換為預格式化的修補程式檔案

  • git send-email --to=<recipient> <patches>

參見 git-format-patch[1]git-send-email[1] 手冊頁以獲取更多使用說明。

如果維護者告訴你,你的修補程式不再適用於當前上游,你將必須重定基準你的主題(你不能使用合併,因為你無法對合併結果進行 format-patch)。

食譜:format-patch/am:保持主題最新

git pull --rebase <URL> <branch>

然後你可以在重定基準期間修復衝突。據推測,除了透過電子郵件之外,你沒有發佈過你的主題,所以重定基準它不是問題。

如果你收到這樣的修補程式系列(作為維護者,或者作為發送到的郵件列表的讀者),請將郵件儲存到檔案中,建立一個新的主題分支,並使用 git am 來匯入提交:

食譜:format-patch/am:匯入修補程式

git am < patch

值得指出的一個功能是三向合併,如果你遇到衝突,它可以提供幫助:git am -3 將使用修補程式中包含的索引資訊來找出合併基礎。參見 git-am[1] 以獲取其他選項。

GIT

git[1] 套件的一部分