开发工作流程#

通过遵循创建您自己的 scikit-image 副本(fork),您已经拥有了 scikit-image 存储库的自己的 fork 副本。您已经设置了您的 fork。您已经通过遵循配置 git配置了 git。现在您已经准备好进行一些实际工作了。

工作流程摘要#

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

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

  • 当您开始进行一组新的更改时,请从 trunk 中获取任何更改,并从此开始新的功能分支

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

  • 根据更改的目的命名您的分支 - 例如 bugfix-for-issue-14refactor-database-code

  • 如果可以避免,请避免在您工作时将 trunk 或任何其他分支合并到您的功能分支中。

  • 如果您确实发现自己从 trunk 合并,请考虑在 trunk 上进行 rebase

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

  • 请求代码审查!

这种工作方式有助于保持工作井然有序,并具有可读的历史记录。这反过来又使项目维护者(可能是您)更容易了解您所做的工作以及您为什么这样做。

有关一些解释,请参阅linux git 工作流程ipython git 工作流程

考虑删除您的 main 分支#

这听起来可能很奇怪,但删除您自己的 main 分支可以帮助减少关于您所在分支的混淆。有关详细信息,请参阅github 上删除 master(将 master 替换为 main)。

更新 trunk 的镜像#

首先,请确保您已完成将您的存储库链接到上游存储库

您应该不时地从 github 上获取上游 (trunk) 的更改

git fetch upstream

这将拉取您没有的任何提交,并将远程分支设置为指向正确的提交。例如,“trunk”是由(远程/分支名称) upstream/main 引用的分支 - 如果自您上次检查以来有提交,则在您执行 fetch 之后,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

通常,您希望将您的功能分支保留在您公开的 githubscikit-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 别名将为你提供一个合理的仓库文本图。

在主干上变基#

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

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

在这个阶段,你考虑将主干合并到你的特性分支中,并且你记得本页面严厉建议你不要这样做,因为历史记录会变得混乱。大多数时候,你可以直接请求审查,而不用担心主干稍微超前。但有时,主干中的更改可能会影响你的更改,你需要协调它们。在这种情况下,你可能更喜欢进行变基。

变基会获取你的更改(A、B、C),并将其重放到仿佛它们是对 trunk 的当前状态所做的更改。换句话说,在这种情况下,它会获取 A、B、C 所代表的更改,并将其重放到 G 的顶部。变基后,你的历史记录将如下所示

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

有关更多详细信息,请参阅 无痛变基

要在主干上进行变基

# 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

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

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

从混乱中恢复#

有时,你会搞砸合并或变基。幸运的是,在 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.
...

并且 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

如果出现错误,仍然可以按照上面解释的方式进行恢复。