开发工作流程#
工作流程概述#
为了保持你的工作井井有条,历史记录清晰易读,进而使项目维护者(可能就是你)更容易看到你做了什么,以及为什么这样做,我们推荐以下做法:
不要在你的本地
main
分支中进行更改!在开始新的一组更改之前,请从
upstream/main
获取所有更改,并从该处开始一个新的 特性分支。为每个功能或错误修复创建一个新分支 — “一个任务,一个分支”。
为你的分支命名,以反映更改的目的 - 例如
bugfix-for-issue-14
或refactor-database-code
。如果你遇到困难,可以在 Gitter 或 论坛 上寻求帮助。
当你准备好或需要对你的代码进行反馈时,打开一个拉取请求,以便 Matplotlib 开发者可以提供反馈,并最终将你建议的代码包含到
main
分支中。
更新 main
分支#
首先确保你已经遵循了 设置 Matplotlib 进行开发。
你应该不时地从GitHub获取上游的更改:
git fetch upstream
这将拉取你尚未拥有的任何提交,并将远程分支设置为指向正确的提交。
创建一个新的功能分支#
当你准备好对代码进行一些更改时,你应该开始一个新的分支。用于一系列相关编辑的分支通常被称为‘功能分支’。
为每组相关的更改创建一个新分支将使审查您分支的人更容易看到您在做什么。
为分支选择一个信息丰富的名称,以提醒你自己和我们其他人该分支中的更改目的。例如 add-ability-to-fly
,或 bugfix-for-issue-42
。
# Update the main branch
git fetch upstream
# Make new feature branch starting at current main
git branch my-new-feature upstream/main
git checkout my-new-feature
如果你已经在本地的 main
分支上开始进行更改,你可以通过重命名分支将其转换为一个特性分支:
git branch -m <newname>
通常,你会希望将你的特性分支保留在你公开的 GitHub Matplotlib 分支上。为此,你可以将这个新分支 git push
到你的 GitHub 仓库。通常,如果你按照这些页面中的说明操作,并且默认情况下,git 会有一个指向你 GitHub 仓库分支的链接,称为 origin
。你可以通过以下方式将分支推送到你自己的分支上:
git push origin my-new-feature
编辑工作流程#
做一些更改
保存更改
使用
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")
使用
git diff
检查实际的更改内容。将任何新文件添加到版本控制
git add new_file_name
。要将 所有 修改过的文件提交到你的仓库的本地副本中,请输入:
git commit -am 'A commit message'
注意
commit
的-am
选项。m
标志表示你将在命令行中输入一条消息。a
标志会暂存所有已修改的文件,除了在.gitignore
中列出的文件。更多信息,请参见 为什么使用 -a 标志? 和 git commit 手册页。要将更改推送到GitHub上的fork仓库,请执行
git push
。
打开一个拉取请求#
当你准备好请求他人审查你的代码并考虑合并时,提交你的 Pull Request (PR)。
为更改集输入一个标题,并附上你已完成内容的简要说明。提及你希望特别注意的任何事项——例如复杂的更改或你不满意的某些代码。
如果你认为你的请求还没有准备好合并,只需在你的拉取请求消息中说明,并使用GitHub的“草稿PR”功能。这是一个获得初步代码审查的好方法。
更新一个拉取请求#
在修改后更新你的拉取请求时,建议不要添加新的提交,而是考虑修改你的初始提交,以保持提交历史记录的整洁。
你可以通过使用来实现这一点。
git commit -a --amend --no-edit
git push [your-remote-repo] [your-branch] --force-with-lease
小技巧
无需每次都输入分支名称,只需输入以下内容一次即可将远程分支链接到本地分支:
git push --set-upstream origin my-new-feature
从现在开始,git 将会知道 my-new-feature
与 GitHub 仓库中的 my-new-feature
分支相关联。之后,你将能够使用以下命令推送你的更改:
git push
管理提交历史#
探索你的仓库#
要查看仓库分支和提交的图形表示:
gitk --all
要查看此分支的提交线性列表:
git log
从错误中恢复#
有时,你会搞砸合并或变基。幸运的是,在 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 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}
重写提交历史#
备注
仅对你的特性分支执行此操作。
你在提交中有一个尴尬的拼写错误吗?或者你可能做了几次错误的开始,不希望后人看到。
这可以通过 交互式变基 来完成。
假设提交历史看起来像这样:
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.
...
而 6ad92e5
是 cool-feature
分支中的最后一次提交。假设我们想要进行以下更改:
重写
13d7934
的提交信息,使其更加合理。将提交
2dec1ac
,a815645
,eadc391
合并为一个。
我们按照以下步骤进行:
# 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,你可以正常继续,然而如果你*已经*推送了这个提交,请参见 强制推送 以了解如何用新的提交替换你已经发布的提交。
重新基于 upstream/main
#
假设你想做一些工作。你 更新镜像主分支 并 创建特性分支 命名为 cool-feature
。在这个阶段,main
处于某个提交,我们称之为 E。现在你在 cool-feature
分支上进行了一些新的提交,我们称之为 A, B, C。也许你的更改需要一些时间,或者你在一段时间后才回到这些更改。与此同时,main
已经从提交 E 进展到提交(比如说)G:
A---B---C cool-feature
/
D---E---F---G main
在这个阶段,你考虑将 main
合并到你的特性分支中,并且你记得这个页面严厉地建议你不要这样做,因为历史会变得混乱。大多数情况下,你可以直接请求审查,而不必担心 main
是否稍微领先;然而有时, main
中的更改可能会影响你的更改,你需要协调它们。在这种情况下,你可能更倾向于进行一次变基。
rebase
将你的更改(A, B, C)重新应用,就好像它们是基于 main
的当前状态进行的。换句话说,在这种情况下,它将由 A, B, C 表示的更改重新应用到 G 之上。rebase 之后,你的历史记录将如下所示:
A'--B'--C' cool-feature
/
D---E---F---G main
更多详情请参见 rebase without tears。
要在 upstream/main
上进行变基:
# Fetch changes from upstream/main
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 main
git rebase --onto upstream/main upstream/main cool-feature
在这种情况下,当你已经在 cool-feature
分支上时,最后一个命令可以更简洁地写成:
git rebase upstream/main
当一切看起来都很好时,你可以删除你的备份分支:
git branch -D tmp
如果看起来不好,你可能需要查看 从混乱中恢复。
如果你对文件进行了修改,而这些文件在 main
中也发生了变化,这可能会产生需要你解决的合并冲突 - 请参阅 git rebase 手册页,在“描述”部分的末尾有一些说明。关于合并的相关帮助可以在 git 用户手册中找到 - 请参阅 resolving a merge。
如果你还没有将这个分支推送到github,你可以正常继续,然而如果你*已经*推送了这个提交,请参见 强制推送 以了解如何用新的提交替换你已经发布的提交。
用力推#
如果你以某种方式重写了已经推送的历史(例如通过 重写提交历史 或 在主分支上变基),这会让你得到一个看起来像这样的 git 历史
A'--E cool-feature
/
D---A---B---C origin/cool-feature
你在GitHub上(在远程名称*origin*下)将提交``A,B,C``推送到你的fork,但现在你的本地分支*cool-feature*上有提交``A'和``E
。如果你尝试将新提交推送到GitHub,它将失败并显示一个类似以下的错误:
$ git push
Pushing to github.com:origin/matplotlib.git
To github.com:origin/matplotlib.git
! [rejected] cool_feature -> cool_feature (non-fast-forward)
error: failed to push some refs to 'github.com:origin/matplotlib.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
如果这次推送成功,提交 A
、B
和 C
将不再被任何分支引用,它们将被丢弃:
D---A'---E cool-feature, origin/cool-feature
默认情况下,git push
会尝试通过拒绝推送到远程来保护你,以防止意外丢弃提交。当这种情况发生时,GitHub 还会添加一个有用的建议,即拉取远程更改,然后再次尝试推送。在某些情况下,例如你和同事都在向同一个分支提交和推送时,这是一个正确的行动方案。
然而,在有意重写历史的情况下,我们*希望*丢弃远程上的提交,并用我们本地分支中的新改进版本替换它们。在这种情况下,我们想要做的是
$ git push --force-with-lease
这告诉 git 你已经意识到风险,但无论如何都想进行推送。我们推荐使用 --force-with-lease
而不是 --force
标志。--force
会无论如何都进行推送,而 --force-with-lease
只有在远程分支与本地 git
客户端认为的位置相同时才会进行推送。
谨慎使用强制推送。它实际上是在重写已发布的历史记录,如果有人获取了旧的提交,他们将会有不同的历史视图,这可能会导致混乱。
自动化测试#
每当创建或更新拉取请求时,各种自动化测试工具将在所有支持的平台和Python版本上运行。
tox 不用于自动化测试。它支持本地测试。
Codecov 和 CodeQL 目前仅用于信息参考。它们的失败不一定是阻塞因素。
在合并之前,请确保 Linting、GitHub Actions、AppVeyor、CircleCI 和 Azure 管道都通过。所有检查都列在您的拉取请求的 GitHub 页面底部。
名称 |
检查 |
查找失败原因的提示 |
---|---|---|
代码检查 |
错误会作为注释显示在拉取请求的差异中。 |
|
Mypy
Stubtest
|
错误会作为注释显示在拉取请求的差异中。 |
|
CircleCI |
在 CircleCI 日志中搜索 |
|
GitHub Actions
AppVeyor
Azure 管道
|
在日志中搜索
FAILURES 。后续部分应包含有关失败测试的信息。在 Azure 上,找到这些镜像作为 Azure 作业的 artifacts:
1. Click Details on the check on the GitHub PR page.
2. Click View more details on Azure Pipelines to go to Azure.
3. On the overview page artifacts are listed in the section Related.
|
跳过 CI 检查#
如果你只知道需要运行一部分CI检查,你可以在提交信息中包含以下字符串来跳过不需要的CI检查:
字符串 |
效果 |
注释 |
---|---|---|
|
仅运行文档检查。 |
当你只修改了文档时。
[ci doc] 当更改仅限于 doc/**/ 或 galleries/**/ 中的文件时,会自动应用。 |
|
跳过文档检查。 |
当你没有更改文档时。 |
|
跳过 AppVeyor 运行。 |
子字符串必须位于提交消息的第一行。 |
|
跳过 Azure Pipelines。 |
|
|
跳过 GitHub Actions。 |
|
|
跳过所有 CI 检查。 |
仅用于文档检查和单元测试不适用的更改。 |
[skip actions]
和 [skip ci]
仅跳过在 on: push
和 on: pull_request
事件上触发的 Github Actions CI 工作流程。更多信息,请参见 跳过工作流程运行。