开发流程#

您已经拥有了 NumPy 存储库的个人分叉副本,已配置 Git,并已如 将您的存储库链接到上游存储库 所述链接了上游存储库。以下是使用 Git 的推荐工作流程。

基本工作流程#

简而言之

  1. 为每次编辑创建新的功能分支。见 下文

  2. 开始编码!见 下文

  3. 完成后

    • 贡献者:将您的功能分支推送到您自己的 Github 仓库,并 创建拉取请求

    • 核心开发者:如果您想在没有进一步审查的情况下推送更改,请参阅 下文 中的说明。

这种工作方式有助于保持工作的井井有条,并使历史记录尽可能清晰。

创建新的功能分支#

首先,从 upstream 存储库中获取新的提交

git fetch upstream

然后,基于上游存储库的主分支创建一个新分支

git checkout -b my-new-feature upstream/main

编辑工作流程#

概述#

# hack hack
git status # Optional
git diff # Optional
git add modified_file
git commit
# push the branch to your own Github repo
git push origin my-new-feature

更详细地说明#

  1. 进行一些更改。当您觉得您已经完成了相关更改的工作集时,继续执行下一步。

  2. 可选:使用 git status 检查哪些文件已更改。您将看到类似于以下的列表

    # On branch my-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 与先前版本比较更改。这将打开一个简单的文本浏览器界面,突出显示您的文件与先前版本之间的差异。

  4. 使用 git add modified_file 添加所有相关的修改或新文件。这会将文件放入暂存区,这是一个将被添加到您下次提交中的文件的队列。只添加包含相关且完整的更改的文件。将包含未完成更改的文件留待稍后提交。

  5. 要将暂存文件提交到您仓库的本地副本中,请执行 git commit。此时,将打开一个文本编辑器,以便您可以编写提交消息。阅读 提交消息部分,以确保您编写了格式正确且足够详细的提交消息。保存消息并关闭编辑器后,您的提交将被保存。对于微不足道的提交,可以使用 -m 标志通过命令行传递简短的提交消息。例如,git commit -am "ENH: Some message"

    在某些情况下,您会看到这种形式的提交命令:git commit -a。额外的 -a 标志会自动提交所有修改过的文件,并删除所有已删除的文件。这可以节省您大量输入 git add 命令的麻烦;但是,如果您不小心,它可能会将不需要的更改添加到提交中。

  6. 将更改推送到您在 GitHub 上的分叉仓库

    git push origin my-new-feature
    

注意

假设您已按照这些页面中的说明进行操作,git 将创建到您的 GitHb 仓库的默认链接,名为 origin。您可以使用 --set-upstream 选项确保对 origin 的链接是永久设置的

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

从现在开始,git 将知道 my-new-feature 与您自己 GitHub 仓库中的 my-new-feature 分支相关。随后的 push 调用将简化为以下操作

git push

您必须为创建的每个新分支使用 --set-upstream

您可能在进行编辑时,upstream 中添加了新的提交,这些提交会影响您的工作。在这种情况下,请按照本文档的 基于主分支进行变基 部分将这些更改应用到您的分支。

编写提交消息#

提交消息应该清晰,并遵循一些基本规则。例如

ENH: add functionality X to numpy.<submodule>.

The first line of the commit message starts with a capitalized acronym
(options listed below) indicating what type of commit this is.  Then a blank
line, then more text if needed.  Lines shouldn't be longer than 72
characters.  If the commit is related to a ticket, indicate that with
"See #3456", "See ticket 3456", "Closes #3456" or similar.

描述更改的动机、错误修复的错误性质或增强功能的详细信息也是值得包含在提交消息中的内容。消息应该在不查看代码更改的情况下可以理解。像 MAINT: fixed another one 这样的提交消息就是一个反面例子;读者必须去其他地方寻找上下文。

用于开始提交消息的标准缩写词是

API: an (incompatible) API change
BENCH: changes to the benchmark suite
BLD: change related to building numpy
BUG: bug fix
CI: continuous integration
DEP: deprecate something, or remove a deprecated object
DEV: development tool or utility
DOC: documentation
ENH: enhancement
MAINT: maintenance commit (refactoring, typos, etc.)
MNT: alias for MAINT
NEP: NumPy enhancement proposals
REL: related to releasing numpy
REV: revert an earlier commit
STY: style fix (whitespace, PEP8)
TST: addition or modification of tests
TYP: static typing
WIP: work in progress, do not merge
跳过持续集成的命令#

默认情况下,对于每个 PR 都会运行许多持续集成 (CI) 作业,从在不同的操作系统和硬件平台上运行测试套件,到构建文档。在某些情况下,您已经知道不需要 CI(或不需要所有 CI),例如,如果您正在处理 CI 配置文件、README 中的文本或其他不参与常规构建、测试或文档序列的文件。在这种情况下,您可以通过在 PR 的每个提交消息中包含以下片段之一或多个片段来明确跳过 CI

  • [skip ci]:跳过所有 CI

    仅当您尚未准备好让检查在您的 PR 上运行时(例如,如果这只是一个草稿)才推荐。

  • [skip actions]:跳过 GitHub Actions 作业

    GitHub Actions 是大多数 CI 检查运行的地方,包括 linter、基准测试、为大多数架构和操作系统运行基本测试以及多种编译器和 CPU 优化设置。 查看这些检查的配置文件。

  • [skip azp]:跳过 Azure 作业

    Azure 是所有综合测试运行的地方。这是一个昂贵的运行,通常如果您只是进行文档更改,则可以跳过它。 查看这些检查的主要配置文件。

  • [skip circle]:跳过 CircleCI 作业

    CircleCI 是我们在其中构建文档并将生成的工件存储在每个 PR 中的预览位置。此检查还将运行所有文档字符串示例并验证其结果。如果您没有进行文档更改,但您对函数的 API 进行了更改,例如,您可能需要运行这些测试以验证 doctests 仍然有效。 查看这些检查的配置文件。

  • [skip cirrus]:跳过 Cirrus 作业

    CirrusCI 主要触发 Linux aarch64 和 MacOS Arm64 滚轮上传。 查看这些检查的配置文件。

测试构建滚轮#

Numpy 目前使用 cibuildwheel 通过持续集成服务构建滚轮。为了节省资源,cibuildwheel 滚轮构建器默认情况下不会在每个 PR 或对 main 的提交中运行。

如果您想测试您的拉取请求是否会破坏滚轮构建器,您可以通过将 [wheel build] 附加到 PR 中最新提交的提交消息的第一行来进行测试。请仅在构建相关的 PR 中这样做,因为运行所有滚轮构建速度很慢且成本很高。

通过 github actions 构建的滚轮(包括 64 位 Linux、x86-64 macOS 和 32/64 位 Windows)将作为工件以 zip 文件的形式上传。您可以从“滚轮构建器”操作的摘要页面访问它们。通过 Cirrus CI 构建的 aarch64 Linux 和 arm64 macOS 滚轮不可用为工件。此外,滚轮将根据以下条件上传到 https://anaconda.org/scientific-python-nightly-wheels/

  • 通过每周的 cron 作业或

  • 如果 GitHub Actions 或 Cirrus 构建已手动触发,这需要适当的权限

如果构建是由以 v 开头的标签触发的,则滚轮将上传到 https://anaconda.org/multibuild-wheels-staging/

征求邮件列表的意见#

如果您计划推出新的功能或 API 更改,最明智的做法是首先向 NumPy 邮件列表 发送电子邮件,征求意见。如果您一周内没有收到回复,可以再次 ping 列表。

要求将您的更改合并到主仓库#

当您觉得您的工作完成后,您可以创建一个拉取请求 (PR)。如果您的更改涉及对 API 的修改或添加/修改函数,请按照 doc/release/upcoming_changes/README.rst 文件中的说明和格式,将发布说明添加到 doc/release/upcoming_changes/ 目录。

让您的 PR 接受审阅#

我们尽快审阅拉取请求,通常在一周内。如果您在两周内没有收到任何审阅意见,请随时在您的 PR 上添加评论以征求反馈(这将通知维护人员)。

如果您的 PR 很大或很复杂,在 numpy-discussion 邮件列表上征求意见可能也很有用。

基于主分支进行变基#

这会将您的功能分支更新为上游 NumPy GitHub 仓库中的更改。如果您不需要这样做,请尽量避免这样做,除非是在您完成工作时。第一步是使用上游中的新提交更新远程存储库

git fetch upstream

接下来,您需要更新功能分支

# go to the feature branch
git checkout my-new-feature
# make a backup in case you mess up
git branch tmp my-new-feature
# rebase on upstream main branch
git rebase upstream/main

如果您对在上游也有更改的文件进行了更改,这可能会生成您需要解决的合并冲突。有关这种情况的帮助,请参阅 下文

最后,在成功变基后删除备份分支

git branch -D tmp

注意

基于主分支进行变基比将上游合并回您的分支更可取。在功能分支上工作时,不鼓励使用 git mergegit pull

从错误中恢复#

有时,您会弄乱合并或变基。幸运的是,在 Git 中,从这些错误中恢复相对简单。

如果您在变基过程中出错

git rebase --abort

如果您在变基后发现自己出错

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

如果您忘记创建备份分支

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

8630830 my-feature-branch@{0}: commit: BUG: io: close file handles immediately
278dd2a my-feature-branch@{1}: rebase finished: refs/heads/my-feature-branch onto 11ee694744f2552d
26aa21a my-feature-branch@{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 my-feature-branch@{2}

如果您实际上没有出错,但存在合并冲突,您需要解决这些冲突。

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

重写提交历史#

注意

仅对您自己的功能分支执行此操作。

您在提交中存在令人尴尬的拼写错误?或者您可能做了一些您希望后代看不到的错误尝试。

这可以通过交互式变基来完成。

假设提交历史记录如下所示

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 couple of structured_array_extensions.
...

并且 6ad92e5main 分支中的最后一个提交。假设我们想进行以下更改

  • 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

如果出错,可以按照 上面 说明进行恢复。

在 GitHub 上删除分支#

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

另请参见:https://stackoverflow.com/questions/2003505/how-do-i-delete-a-git-branch-locally-and-remotely

多人共享一个存储库#

如果您想与其他人一起处理一些内容,你们都提交到同一个存储库,甚至同一个分支,那么只需通过 GitHub 共享即可。

首先,按照 创建您自己的 scikit-image 副本(分叉) 的步骤,将 NumPy 分叉到您的帐户。

然后,转到您的分叉存储库的 github 页面,例如 https://github.com/your-user-name/numpy

点击“管理”按钮,并将其他任何人添加到仓库作为协作者

../_images/pull_button.png

现在所有这些人可以执行以下操作

git clone git@github.com:your-user-name/numpy.git

请记住,以 git@ 开头的链接使用 ssh 协议,可读写;以 git:// 开头的链接为只读。

您的合作者可以使用通常的方式直接提交到该仓库

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

从现有拉取请求中检出更改#

如果您想测试拉取请求中的更改,或在新的拉取请求中继续工作,则需要将提交克隆到您分叉仓库中的本地分支

首先确保您的上游指向主仓库,如 将您的仓库链接到上游仓库 中所述。

然后,获取更改并创建一个本地分支。假设 $ID 是拉取请求号,$BRANCHNAME 是您要创建的新本地分支的名称

git fetch upstream pull/$ID/head:$BRANCHNAME

检出新创建的分支

git checkout $BRANCHNAME

现在您已拥有拉取请求中的更改。

探索您的仓库#

要查看仓库分支和提交的图形表示

gitk --all

要查看该分支的提交线性列表

git log

回退#

回退是将 NumPy 的 main 分支中提交的新功能/修复复制到稳定发布分支的过程。为此,您需要从要回退到的分支上创建分支,挑选您想要从 numpy/main 中获取的提交,然后提交包含回退的该分支的拉取请求。

  1. 首先,您需要创建您将要使用的分支。它需要基于较旧版本的 NumPy(而不是 main)

    # Make a new branch based on numpy/maintenance/1.8.x,
    # backport-3324 is our new name for the branch.
    git checkout -b backport-3324 upstream/maintenance/1.8.x
    
  2. 现在,您需要使用 git cherry-pick 将 main 中的更改应用到该分支

    # Update remote
    git fetch upstream
    # Check the commit log for commits to cherry pick
    git log upstream/main
    # This pull request included commits aa7a047 to c098283 (inclusive)
    # so you use the .. syntax (for a range of commits), the ^ makes the
    # range inclusive.
    git cherry-pick aa7a047^..c098283
    ...
    # Fix any conflicts, then if needed:
    git cherry-pick --continue
    
  3. 您可能会在挑选时遇到一些冲突。这些冲突的解决方式与合并/变基冲突相同。不同的是,在这里您可以使用 git blame 查看 main 与回退分支之间的差异,以确保没有任何东西被搞砸。

  4. 将新分支推送到您的 Github 仓库

    git push -u origin backport-3324
    
  5. 最后,使用 Github 创建一个拉取请求。确保它针对的是维护分支,而不是 main,Github 通常会建议您针对 main 创建拉取请求。

将更改推送到主仓库#

需要对 NumPy 主仓库有提交权限。

当您在功能分支中有一组“准备就绪”的更改,准备推送到 NumPy 的 mainmaintenance 分支时,您可以按照以下步骤将它们推送到 upstream

  1. 首先,将更改合并或变基到目标分支。

    1. 只有少数、不相关的提交,则更适合进行变基

      git fetch upstream
      git rebase upstream/main
      

      参见 在 main 上进行变基

    2. 如果所有提交都相关,请创建一个合并提交

      git fetch upstream
      git merge --no-ff upstream/main
      
  2. 检查您要推送的内容是否合理

    git log -p upstream/main..
    git log --oneline --graph
    
  3. 推送到上游

    git push upstream my-feature-branch:main
    

注意

通常最好使用 -n 标志,以便首先检查您是否要将更改推送到您想要的位置。