开发工作流程#

你已经通过遵循 制作你自己的副本(fork)的 scikit-image 拥有了自己的 scikit-image 仓库的分支副本。你已经 设置你的分支。你已经通过遵循 配置 git 配置了 git。现在你准备好进行一些实际工作了。

工作流程概述#

在下文中,我们将上游的 scikit-image main 分支称为“主干”。

  • 不要将 main 分支用于任何用途。考虑删除它。

  • 当你开始一组新的更改时,从主干获取任何更改,并从该更改开始一个新的 特性分支

  • 为每个可分离的变更集创建一个新分支 — “一个任务,一个分支” (ipython git 工作流程)。

  • 为你的分支命名,以反映更改的目的 - 例如 bugfix-for-issue-14refactor-database-code

  • 如果可能的话,避免在工作中将主干或其他分支合并到你的特性分支中。

  • 如果你确实发现自己正在从主干合并,考虑 基于主干的重构

  • 如果你遇到困难,可以在 scikit-image 开发者论坛 上提问。

  • 请求代码审查!

这种工作方式有助于保持工作井井有条,历史记录清晰易读。这反过来使得项目维护者(可能是你)更容易看到你做了什么,以及为什么这样做。

参见 linux git 工作流程ipython git 工作流程 以获取一些解释。

考虑删除你的主分支#

这听起来可能很奇怪,但删除你自己的 main 分支可以帮助减少关于你在哪个分支上的困惑。详情请参见 deleting master on github (将 master 替换为 main)。

更新主干镜像#

首先确保你已经完成了 将您的仓库链接到上游仓库

你应该不时地从github获取上游(主干)的更改:

git fetch upstream

这将拉取你尚未拥有的任何提交,并将远程分支设置为指向正确的提交。例如,’trunk’ 是 (remote/branchname) upstream/main 所引用的分支 - 如果你上次检查后有新的提交,执行 fetch 后 upstream/main 将会改变。

创建一个新的功能分支#

当你准备好对代码进行一些更改时,你应该开始一个新的分支。用于一系列相关编辑的分支通常被称为‘功能分支’。

为每组相关的更改创建一个新分支将使审查你分支的人更容易看到你在做什么。

为分支选择一个信息丰富的名称,以提醒你自己和我们其他人该分支中的更改目的。例如 add-ability-to-fly,或 buxfix-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

通常,您会希望将您的特性分支保留在 scikit-image 的公共 github 分支上。为此,您可以将这个新分支 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 '提交信息'。注意 commit 命令的 -am 选项。m 标志表示你将在命令行中输入一条消息。a 标志 — 你可以直接接受 — 或者查看 为什么使用 -a 标志? — 以及在 纠结的工作副本问题 中的有用用例描述。git commit 手册页也可能会有帮助。

  6. 要将更改推送到你在github上的fork仓库,请执行 git push (参见 git push)。

请求审核或合并您的更改#

当你准备好请求他人审查你的代码并考虑合并时:

  1. 转到你复刻的仓库的URL,例如 https://github.com/your-user-name/scikit-image

  2. 使用页面左上方的“切换分支”下拉菜单来选择包含您更改的分支:

    ../_images/branch_dropdown.png
  3. 点击 ‘Pull request’ 按钮:

    ../_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 的仓库的 GitHub 页面,例如 https://github.com/your-user-name/scikit-image

点击 ‘Admin’ 按钮,并将其他人添加为仓库的协作者:

../_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 别名将为您提供一个合理的基于文本的仓库图表。

基于主干的重构#

假设你想到了一些你想做的工作。你 更新镜像主干创建特性分支 命名为 cool-feature。在这个阶段,主干处于某个提交,我们称之为 E。现在你在 cool-feature 分支上进行了一些新的提交,我们称之为 A、B、C。也许你的更改需要一段时间,或者你在一段时间后才回到这些更改。与此同时,主干从提交 E 进展到提交(比如说)G:

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

在这个阶段,你考虑将主干合并到你的特性分支中,并且你记得这个页面严厉地建议你不要这样做,因为历史会变得混乱。大多数情况下,你只需请求审查,而不必担心主干已经稍微领先。但有时,主干中的更改可能会影响你的更改,你需要协调它们。在这种情况下,你可能更倾向于进行一次变基。

rebase 会将你的更改(A, B, C)重新应用,就好像它们是基于 trunk 的当前状态所做的。换句话说,在这种情况下,它会将由 A, B, C 表示的更改重新应用到 G 之上。rebase 之后,你的历史记录将如下所示:

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

更多详情请参见 rebase without tears

要在主干上进行变基:

# 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

如果看起来不好,你可能需要查看 从混乱中恢复

如果你对文件进行了修改,而这些文件在主干中也发生了变化,这可能会产生需要你解决的合并冲突 - 请参阅 git rebase 手册页,在“描述”部分的末尾有一些说明。在 git 用户手册中有一些关于合并的相关帮助 - 请参阅 解决合并冲突

从混乱中恢复#

有时,你会搞砸合并或变基。幸运的是,在 git 中,从这些错误中恢复相对简单。

如果你在变基过程中搞砸了:

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}

重写提交历史#

备注

仅对你的特性分支执行此操作。

你在提交中犯了一个尴尬的拼写错误?或者你可能做了几次错误的开始,希望后人不要看到。

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

假设提交历史看起来像这样:

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 的提交信息,使其更加合理。

  • 将提交 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

如果出现问题,恢复仍然是可能的,如 上文 所述。