开发工作流程#
工作流程摘要#
为了使你的工作井井有条,具有可读的历史记录,从而使项目维护者(可能是你)更容易看到你做了什么以及为什么这样做,我们推荐以下方法:
不要在你的本地
main分支中进行更改!在开始一组新的更改之前,从
upstream/main获取所有更改,并从该分支启动一个新的功能分支.为每个特性或缺陷修复创建一个新分支--"一个任务,一个分支".
按照更改的目的命名你的分支 - 例如
bugfix-for-issue-14或refactor-database-code.如果你遇到困难,请在 Gitter 或 discourse 上寻求帮助.
当你准备好或需要对你的代码进行反馈时,打开一个 pull request,以便 Matplotlib 开发者可以提供反馈,并最终将你建议的代码包含到
main分支中.
概述#
在 setting up a development environment 之后,典型的工作流程是:
从
upstream/main获取所有更改:git fetch upstream/main
从
upstream/main开始一个新的特性分支:git checkout -b my-feature upstream/main
当你完成编辑后,例如
lib/matplotlib/collections.py,将你的更改记录到 Git 中:git add lib/matplotlib/collections.py git commit -m 'a commit message'
将更改推送到你的 GitHub fork:
git push -u origin my-feature
更新 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 fork 的 Matplotlib 中.为此,你可以将这个新分支 git push 到你的 GitHub 仓库中.通常,如果你按照这些页面中的说明进行操作,并且默认情况下,git 将有一个指向你的 GitHub 仓库 fork 的链接,称为 origin . 你可以使用以下命令推送到你自己的 fork:
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中列出的文件.有关更多信息,请参见 why the -a flag? 和 git commit 手册页.要将更改推送到 GitHub 上的 fork 仓库,请执行
git push.
打开一个 pull request#
当你准备好让某人审查你的代码并考虑合并时, submit your Pull Request (PR) .
转到你的 Matplotlib 仓库 fork 的网页,然后点击 Compare & pull request ,将你的更改发送给维护者进行审查.基本仓库是 matplotlib/matplotlib ,基本分支通常是 main .
输入一组更改的标题,并解释你所做的工作.提及任何你希望特别关注的地方 - 例如复杂的更改或一些你不满意的代码.
如果你认为你的请求尚未准备好合并,只需在你的 pull request 消息中说明,并使用 GitHub 的"Draft PR"功能.这是获得一些初步代码审查的好方法.
有关创建 pull request 的更多指导,请参见 GitHub 的 pull request tutorial .
更新 pull request#
在进行修改后更新你的 pull request 时,请考虑修改你的初始提交(commit),而不是添加新的提交,以保持提交历史的清洁.
你可以通过使用
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
如果出现错误,还可以按照 above 中所述进行恢复.
如果你尚未将此分支推送到 github,则可以像往常一样继续操作,但是如果你已经推送了此提交,请参阅 强制推送 ,了解如何用新提交替换已发布的提交.
变基到 upstream/main 上#
假设你想做一些工作.你 更新 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 之上重放它们.变基之后,你的历史记录将如下所示:
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,则可以像往常一样继续操作,但是如果你已经推送了此提交,请参阅 强制推送 ,了解如何用新提交替换已发布的提交.
强制推送#
如果你以某种方式重写了已经推送的历史记录(例如,通过 重写提交历史 或 变基到 upstream/main 上 ),从而使你的 git 历史记录看起来像这样
A'--E cool-feature
/
D---A---B---C origin/cool-feature
当您已经将提交 A,B,C 推送到您在 GitHub 上的 fork(在远程名称 origin 下)时,但现在在本地分支 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 页面底部.
名称 |
检查 |
查找失败原因的提示 |
|---|---|---|
Linting |
错误显示为拉取请求差异上的注释. |
|
Mypy
Stubtest
|
错误显示为拉取请求差异上的注释. |
|
CircleCI |
在 CircleCI 日志中搜索 |
|
GitHub Actions
AppVeyor
Azure pipelines
|
在日志中搜索
FAILURES .后续部分应包含有关失败测试的信息.在 Azure 上,将图像查找为 Azure 作业的项目:
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 检查:
字符串 |
效果 |
笔记 |
|---|---|---|
|
仅运行文档检查. |
当您仅更改了文档时.
当更改仅在
doc// 或 galleries// 中的文件时,会自动应用 [ci doc] |
|
跳过文档检查. |
当您未更改文档时. |
|
跳过 AppVeyor 运行. |
子字符串必须在提交消息的第一行中. |
|
跳过 Azure Pipelines. |
|
|
跳过 GitHub Actions. |
|
|
跳过所有 CI 检查. |
仅用于文档检查和单元测试不适用的更改. |
[skip actions] 和 [skip ci] 仅跳过在 on: push 和 on: pull_request 事件上触发的 Github Actions CI 工作流程.有关更多信息,请参见 Skipping workflow runs .