開發工作流程#

您已經依照 建立您自己的 scikit-image 副本 (fork) 的說明,擁有自己 fork 的 scikit-image 儲存庫副本。您已經 設定您的 fork。您已經依照 設定 git 的說明設定 git。現在您已準備好進行一些實際工作。

工作流程摘要#

在接下來的內容中,我們將把上游 scikit-image 的 main 分支稱為「主幹」。

  • 不要將您的 main 分支用於任何用途。考慮刪除它。

  • 當您開始一組新的變更時,請從主幹提取任何變更,並從該變更開始新的功能分支

  • 為每個可分離的變更組建立一個新分支 — 「一個任務,一個分支」(ipython git 工作流程)。

  • 為您的分支命名以說明變更的目的 — 例如 bugfix-for-issue-14refactor-database-code

  • 如果可以盡量避免,請避免在您工作時將主幹或任何其他分支合併到您的功能分支中。

  • 如果您發現自己正在從主幹合併,請考慮在主幹上變基

  • 如果您遇到困難,請在scikit-image 開發者論壇上提問。

  • 請求程式碼審查!

這種工作方式有助於保持工作井然有序,並具有可讀取的歷史記錄。反過來,這使專案維護人員(可能就是您)更容易了解您做了什麼以及為什麼這樣做。

請參閱linux git 工作流程ipython git 工作流程以取得一些說明。

考慮刪除您的主分支#

聽起來可能很奇怪,但刪除您自己的 main 分支可以幫助減少對您目前在哪個分支上的混淆。請參閱 在 github 上刪除 master 以取得詳細資訊(將 master 替換為 main)。

更新主幹的鏡像#

首先,請確保您已完成將您的儲存庫連結到上游儲存庫

您應該不時從 github 提取上游(主幹)變更

git fetch upstream

這將提取您沒有的任何提交,並將遠端分支設定為指向正確的提交。例如,「主幹」是指 (remote/branchname) upstream/main 所參照的分支 — 如果自上次檢查以來有提交,則 upstream/main 將在您執行提取後變更。

建立新的功能分支#

當您準備好對程式碼進行一些變更時,您應該啟動一個新分支。用於相關編輯集合的分支通常稱為「功能分支」。

為每個相關變更集建立新分支,可以讓審查您分支的人員更容易了解您在做什麼。

為分支選擇一個資訊豐富的名稱,以提醒您自己和其他人該分支中變更的目的。例如 add-ability-to-flybuxfix-for-issue-42

# Update the mirror of trunk
git fetch upstream
# Make new feature branch starting at current trunk
git branch my-new-feature upstream/main
git checkout my-new-feature

一般而言,您會希望將功能分支保留在您公開的 github scikit-image fork 上。為此,您可以使用 git push 將這個新分支推送到您的 github 儲存庫。一般而言(如果您遵循這些頁面中的指示,並且預設情況下),git 將連結到您的 github 儲存庫,稱為 origin。您可以使用以下方式將內容推送到您自己在 github 上的儲存庫:

git push origin my-new-feature

在 git >= 1.7 中,您可以使用 --set-upstream 選項確保正確設定連結

git push --set-upstream origin my-new-feature

從現在起,git 會知道 my-new-feature 與 github 儲存庫中的 my-new-feature 分支相關。

編輯工作流程#

概觀#

# hack hack
git add my_new_file
git commit -am 'NF - some message'
git push

更詳細的內容#

  1. 進行一些變更

  2. 使用 git status 查看哪些檔案已變更(請參閱 git status)。您會看到類似這樣的清單

    # On branch ny-new-feature
    # Changed but not updated:
    #   (use "git add <file>..." to update what will be committed)
    #   (use "git checkout -- <file>..." to discard changes in working directory)
    #
    #  modified:   README
    #
    # Untracked files:
    #   (use "git add <file>..." to include in what will be committed)
    #
    #  INSTALL
    no changes added to commit (use "git add" and/or "git commit -a")
    
  3. 使用 git diff 檢查實際變更內容 (git diff)。

  4. 使用 git add new_file_name 將任何新檔案新增到版本控制中(請參閱 git add)。

  5. 要將所有修改過的檔案提交到您儲存庫的本機副本,請執行 git commit -am 'A commit message'。請注意 commit-am 選項。m 標記只是表示您將在命令列上輸入訊息。a 標記 — 您可以相信 — 或參閱 為什麼要使用 -a 標記? — 以及在 錯綜複雜的工作副本問題中對有用的使用案例的描述。git commit 手冊頁面也可能很有用。

  6. 要將變更推送至您在 github 上的 fork 儲存庫,請執行 git push(請參閱 git push)。

請求審查或合併您的變更#

當您準備好請求他人審查您的程式碼並考慮合併時

  1. 前往您的 fork 儲存庫的 URL,例如 https://github.com/your-user-name/scikit-image

  2. 使用頁面左上方附近的「切換分支」下拉式選單,選取包含您變更的分支

    ../_images/branch_dropdown.png
  3. 按一下「提取請求」按鈕

    ../_images/pull_button.png

    輸入變更集的標題,並說明您所做的事情。說明您是否希望特別注意任何事項 — 例如複雜的變更或您不滿意的程式碼。

    如果您認為您的請求尚未準備好合併,請在您的提取請求訊息中說明。這仍然是取得一些初步程式碼審查的好方法。

您可能想做的一些其他事情#

刪除 github 上的分支#

git checkout main
# delete branch locally
git branch -D my-unwanted-branch
# delete branch on github
git push origin :my-unwanted-branch

(請注意 test-branch 前面的冒號 :。另請參閱:https://help.github.com/en/github/using-git/managing-remote-repositories

多人共用一個儲存庫#

如果您想與其他人一起處理一些事情,您們都在同一個儲存庫,甚至同一個分支中提交,那麼只需透過 github 共用它即可。

首先,將 scikit-image fork 到您的帳戶中,如從 建立您自己的 scikit-image 副本 (fork) 中所述。

然後,前往您 fork 的儲存庫 github 頁面,例如 https://github.com/your-user-name/scikit-image

按一下「管理員」按鈕,並將其他任何人新增到儲存庫中作為協作者

../_images/pull_button.png

現在,所有這些人都可以執行

git clone git@githhub.com:your-user-name/scikit-image.git

請記住,以 git@ 開頭的連結使用 ssh 通訊協定。

您的協作者可以使用以下常用的方式直接提交到該儲存庫:

git commit -am 'ENH - much better code'
git push origin main # pushes directly into your repo

探索您的儲存庫#

要查看儲存庫分支和提交的圖形表示:

gitk --all

要查看此分支的提交線性清單:

git log

您也可以查看您的 github 儲存庫的網路圖形視覺化工具

最後,精美記錄輸出 lg 別名將為您提供儲存庫的合理文字式圖形。

在主幹上變基#

假設您想到了一些想要執行的工作。您會先更新 trunk 的鏡像,然後建立一個新的功能分支,並將其命名為 cool-feature。在這個階段,trunk 位於某個提交點,我們稱之為 E。現在您在您的 cool-feature 分支上進行了一些新的提交,我們稱它們為 A、B、C。也許您的變更需要一些時間,或者您過了一段時間才回到這些變更。同時,trunk 從提交點 E 進展到提交點 (假設) G。

      A---B---C cool-feature
     /
D---E---F---G trunk

在這個階段,您考慮將 trunk 合併到您的功能分支中,並且您記得本頁嚴厲建議您不要這樣做,因為歷史記錄會變得混亂。大多數情況下,您只需請求審閱,而不用擔心 trunk 稍微超前一點。但有時,trunk 中的變更可能會影響您的變更,而您需要協調它們。在這種情況下,您可能更喜歡執行 rebase。

rebase 會將您的變更(A、B、C)重新套用,就像它們是在 trunk 的目前狀態下進行的一樣。換句話說,在這個例子中,它會取得 A、B、C 所代表的變更,並將它們重新套用到 G 之上。執行 rebase 後,您的歷史記錄會如下所示

              A'--B'--C' cool-feature
             /
D---E---F---G trunk

有關更多詳細資訊,請參閱rebase without tears

要在 trunk 上執行 rebase

# Update the mirror of trunk
git fetch upstream
# go to the feature branch
git checkout cool-feature
# make a backup in case you mess up
git branch tmp cool-feature
# rebase cool-feature onto trunk
git rebase --onto upstream/main upstream/main cool-feature

在您已位於 cool-feature 分支上的情況下,最後一個命令可以更簡潔地寫成

git rebase upstream/main

當一切看起來沒問題時,您可以刪除您的備份分支

git branch -D tmp

如果看起來不太好,您可能需要查看從混亂中恢復

如果您對在 trunk 中也已變更的檔案進行了變更,這可能會產生您需要解決的合併衝突 - 請參閱 git rebase 手冊頁中「描述」部分末尾的一些說明。在 git 使用手冊中,有一些關於合併的相關說明 - 請參閱解決合併

從混亂中恢復#

有時,您會把合併或 rebase 搞砸。幸運的是,在 git 中,從這些錯誤中恢復相對簡單。

如果您在 rebase 期間搞砸了

git rebase --abort

如果您在 rebase 後發現自己搞砸了

# reset branch back to the saved point
git reset --hard tmp

如果您忘記建立備份分支

# look at the reflog of the branch
git reflog show cool-feature

8630830 cool-feature@{0}: commit: BUG: io: close file handles immediately
278dd2a cool-feature@{1}: rebase finished: refs/heads/my-feature-branch onto 11ee694744f2552d
26aa21a cool-feature@{2}: commit: BUG: lib: make seek_gzip_factory not leak gzip obj
...

# reset the branch to where it was before the botched rebase
git reset --hard cool-feature@{2}

重寫提交歷史記錄#

請注意

僅對您自己的功能分支執行此操作。

您在提交中發現了令人尷尬的錯字?或者也許您做了幾個您不希望後代看到的錯誤開始。

這可以透過互動式 rebase 完成。

假設提交歷史記錄如下所示

git log --oneline
eadc391 Fix some remaining bugs
a815645 Modify it so that it works
2dec1ac Fix a few bugs + disable
13d7934 First implementation
6ad92e5 * masked is now an instance of a new object, MaskedConstant
29001ed Add pre-nep for a copule of structured_array_extensions.
...

6ad92e5cool-feature 分支中的最後一個提交。假設我們想要進行以下變更

  • 13d7934 的提交訊息重寫為更合理的內容。

  • 將提交 2dec1aca815645eadc391 合併為一個。

我們執行以下操作

# make a backup of the current state
git branch tmp HEAD
# interactive rebase
git rebase -i 6ad92e5

這將開啟一個編輯器,其中包含以下文字

pick 13d7934 First implementation
pick 2dec1ac Fix a few bugs + disable
pick a815645 Modify it so that it works
pick eadc391 Fix some remaining bugs

# Rebase 6ad92e5..eadc391 onto 6ad92e5
#
# 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
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

為了實現我們想要的目標,我們將對其進行以下變更

r 13d7934 First implementation
pick 2dec1ac Fix a few bugs + disable
f a815645 Modify it so that it works
f eadc391 Fix some remaining bugs

這表示 (i) 我們想要編輯 13d7934 的提交訊息,以及 (ii) 將最後三個提交折疊為一個。現在我們儲存並退出編輯器。

Git 將立即開啟一個編輯器來編輯提交訊息。在修改它之後,我們得到以下輸出

[detached HEAD 721fc64] FOO: First implementation
 2 files changed, 199 insertions(+), 66 deletions(-)
[detached HEAD 0f22701] Fix a few bugs + disable
 1 files changed, 79 insertions(+), 61 deletions(-)
Successfully rebased and updated refs/heads/my-feature-branch.

而歷史記錄現在看起來像這樣

0f22701 Fix a few bugs + disable
721fc64 ENH: Sophisticated feature
6ad92e5 * masked is now an instance of a new object, MaskedConstant

如果出錯,可以再次按照以上所述進行恢復。