Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

拉取请求工作流程

Godot 使用的所谓“PR 工作流程”对于许多使用 Git 的项目来说都很常见,并且对于资深自由软件贡献者应该很熟悉。其思想就是只有少数(如果有的话)能直接提交给 master 分支。而贡献者 fork 项目(即创建它的副本,他们可以按照自己的意愿修改),然后使用 GitHub 的界面从其 fork 的某个分支请求 pull 到原始(通常命名为 upstream)仓库的某个分支。

然后, 生成的 拉取请求 (PR)可以由其他贡献者审查, 可能批准它, 拒绝它, 或者最常要求修改它. 一旦获得批准,PR就可以由其中一个核心开发人员合并, 其提交将成为目标分支(通常是 master 分支)的一部分.

我们将一起通过一个例子来展示典型的工作流程和相关的Git命令. 但首先, 让我们快速了解一下Godot的Git仓库的组织结构.

Git 源仓库

GitHub 上的仓库是一个 Git 代码仓库以及一个嵌入式问题跟踪器和 PR 系统。

备注

如果你正在为文档做贡献,可以在这里找到它的仓库。

Git 版本控制系统是用于跟踪源代码的连续编辑的工具——目的是高效地为 Godot 做贡献,强烈建议学习 Git 命令行的基础知识。Git 有一些图形界面,但是它们通常会鼓励用户养成关于 Git 和 PR 工作流程的不良习惯,因此我们建议不要去使用。特别是,我们建议不要使用 GitHub 的在线编辑器进行代码贡献(尽管可以进行较小的修复或文档更改),因为它会对每个文件和每个修改强制执行一次提交,因此很快导致 PR 的 Git 历史记录不可读(尤其是在同行评审之后)。

参见

Git 的“书”的第一部分很好地介绍了该工具的原理,以及你在日常工作流程中需要掌握的各种命令。你可以在 Git SCM 网站上在线阅读。你还可以尝试 GitHub 的交互式指南

Git 仓库上的分支被组织如下:

  • master 分支是开发下一个主要版本的地方. 作为开发分支, 它可能不稳定, 不适合用于生产. 这是应该优先进行PR的地方.

  • 稳定分支以其版本命名,例如:3.12.1。它们用于将 master 分支中的错误修复和增强功能回传到当前维护的稳定版本(例如 3.1.2 或 2.1.6)。一般说来,最后一个稳定分支会被维护到下一个次要版本(例如 3.0 分支被维护到了 Godot 3.1 的发布)。如果你想针对被维护的稳定分支做 PR,请先检查你的修改是否也与 master 分支有关,如果是,请优先为 master 分支做 PR。发布管理者可以在相关的情况下将修复的内容挑选到稳定分支。

  • 偶尔也会有特性分支, 通常是为了在某个时候合并到 master 分支.

分叉和克隆

第一步是在 GitHub 上 fork godotengine/godot 库。为此,你需要拥有一个 GitHub 帐户并登录。在仓库的 GitHub 页面的右上角,你应该看到如下所示的“Fork”按钮:

../../_images/github_fork_button.png

点击它,一段时间后,你应该被重定向到你自己的 Godot 仓库分叉,并将 GitHub 用户名作为名称空间:

../../_images/github_fork_url.png

然后你可以 克隆 你的分叉, 即创建在线仓库的本地副本(在Git中叫做 origin remote). 如果你还没有, 若你使用的是Windows或macOS, 请从 其网站 下载Git;若你使用的是Linux, 请通过你的软件包管理器安装它.

备注

如果你使用的是Windows, 请打开Git Bash键入命令.macOS和Linux用户可以使用各自的终端.

要从GitHub克隆你的fork, 请使用以下命令:

$ git clone https://github.com/USERNAME/godot

备注

在我们的示例中,“$”字符表示典型 UNIX shell 上的命令行提示符。它不是命令的一部分,不应该键入。

稍后, 你应该在当前工作目录中有一个 godot 目录. 使用 cd 命令进入它:

$ cd godot

我们将从建立对我们分叉的原始仓库的引用开始:

$ git remote add upstream https://github.com/godotengine/godot
$ git fetch upstream

这将创建一个名为 upstream 的引用, 指向原始的 godotengine/godot 版本库. 当你想从它的 master 分支拉取新的提交来更新你的fork时, 这将很有用. 你有另一个名为 origin 的远程引用, 它指向你的fork( USERNAME/godot ).

你只需要做一次上面的步骤, 只要你保留本地的 godot 文件夹(你可以随意移动它, 相关的元数据隐藏在它的 .git 子文件夹中).

备注

开分支、拉取、编码、暂存、提交、推送、变基……非常有技术含量。

这对Daft Punk的 技术 的不良看法显示了Git初学者对其工作流程的一般概念: 许多奇怪的命令可以通过复制和粘贴来学习, 希望它们能按预期运行. 这实际上并不是一种糟糕的学习方式, 只要你好奇并且在迷失时毫不犹豫地询问你的搜索引擎, 因此, 我们将为你提供在Git中工作时要了解的基本命令.

In the following, we will assume as an example that you want to implement a feature in Godot's Project Manager, which is coded in the editor/project_manager.cpp file.

分支

默认情况下, git clone 应该让你进入fork(origin)的 master 分支. 要开始自己的功能开发, 我们将创建一个功能分支:

# Create the branch based on the current branch (master)
$ git branch better-project-manager

# Change the current branch to the new one
$ git checkout better-project-manager

此命令是等效的:

# Change the current branch to a new named one, based on the current branch
$ git checkout -b better-project-manager

如果你想回到 master 分支, 你会使用:

$ git checkout master

你可以使用 git branch 命令查看当前使用的分支:

$ git branch
  2.1
* better-project-manager
  master

创建新分支之前,请务必始终返回到 master 分支,因为你当前的分支将用作新分支的基础。或者,你可以在新分支的名称之后指定一个自定义基础分支:

$ git checkout -b my-new-feature master

更新你的分支

第一次(在你 fork 上游仓库之后)是不需要这样做的。但是,下次你想要处理某些事情时,你会注意到你的 fork 的 master 落后于上游 master 分支几个提交:其他贡献者的拉取请求同时被合并。

为了确保你开发的功能与当前的上游 master 分支之间不会发生冲突,你将不得不通过拉取上游分支来更新你的分支。

$ git pull --rebase upstream master

--rebase 参数将确保你所提交的任何本地更改将被“重新应用”到“拉动”分支的顶部,这通常是我们在 PR 工作流程中想要的。这样,当你打开请求请求时,你自己的提交将是与上游 master 分支的唯一区别。

变基时,如果你提交同时在上游分支中已更改的修改后的代码,则可能会发生冲突。如果发生这种情况,Git 将在冲突的提交处停止,并要求你解决冲突。你可以使用任何文本编辑器执行此操作,然后进行更改(稍后再进行介绍),然后继续执行 git rebase --continue。如果以后的提交也有冲突,请重复该操作,直到变基操作完成。

如果你不确定变基期间发生了什么并且感到恐慌(不用担心,我们都要做前几次),则可以使用 git rebase --abort 中止变基。这样你就返回了该分支在调用 git pull --rebase 前的原始状态。

备注

如果省略了 --rebase 参数,则将创建一个合并提交,告诉 Git 如何处理两个不同的分支。如果发生任何冲突,将通过此合并提交立即解决所有冲突。

虽然这是有效的工作流程,并且默认行为是 git pull,但是我们的 PR 工作流程并不赞成在 PR 中使用合并提交。我们仅在将 PR 合并到上游分支时才使用它们。

我们的理念是,PR 应该代表对代码库所做更改的最后阶段,并且我们对合并之前在中间阶段所做的错误和修复不感兴趣。Git 提供了很棒的工具来“改写历史”,让它好像我们第一次就做对了一样,并且我们乐于使用它来确保更改在合并之后很容易被查看和理解。

如果你已经创建了一个合并提交而不使用 rebase,或者进行了其他任何导致不希望的历史记录的更改,那么最好的选择是在上游分支上进行交互式变基。相关说明请参见专门的章节

小技巧

如果你想在任何时候将本地分支重置为给定的提交或分支,则可以使用 git reset --hard <commit ID>git reset --hard <remote>/<branch> 来实现。(例如 git reset --hard upstream/master)。

请注意,这将删除你可能已在此分支中提交的所有更改。如果你错误地丢失了提交,请使用 git reflog 命令查找你想要还原的先前状态的提交 ID,并将其用作 git reset --hard 的参数,回到那个状态。

做出变更

接下来,你将使用常用的开发环境(文本编辑器、IDE 等)对我们示例中的 editor/project_manager.cpp 文件进行更改。

默认情况下,这些修改是未暂存的。暂存区是位于工作目录(你进行修改的地方)和本地 Git 仓库(提交和所有元数据在 .git 文件夹中)之间的一层。要把工作目录中的修改带到 Git 仓库,你需要用 git add 命令把它们归档,然后用 git commit 命令提交。

在暂存之前, 暂存后和提交之后, 你应该了解各种命令来查看当前工作.

  • git diff 将显示当前未暂存的更改, 即工作目录和暂存区域之间的差异.

  • git checkout -- <files> 将撤消给定文件的未暂存更改.

  • git add <files>暂存 列出的文件的更改.

  • git diff --staged 将显示当前的暂存的更改, 即暂存区域和上次提交之间的差异.

  • git reset HEAD <files>取消暂存 列出的文件的更改.

  • git status 将显示当前暂存和未暂存的修改.

  • git commit 将提交暂存文件. 它将打开一个文本编辑器(你可以使用 GIT_EDITOR 环境变量或Git配置中的 core.editor 设置来定义要使用的编辑器), 以便你编写提交日志. 你可以使用 git commit -m "Cool commit log" 直接写日志.

  • git commit --amend 允许你使用当前暂存的更改来修改最后一次提交(使用 git add 添加)。如果要修复上一次提交中的错误(错误、错别字、样式问题等),这是最佳选择。

  • git log 将显示当前分支的最后提交. 如果你做了本地提交, 它们应该显示在顶部.

  • git show 将显示上次提交的更改. 你还可以指定提交哈希以查看该提交的更改.

要记住的东西太多了!不用担心, 当你需要进行更改时, 只需检查一下备忘单, 然后边做边学即可.

以下是我们的示例中shell历史记录的样子:

# It's nice to know where you're starting from
$ git log

# Do changes to the Project Manager with the nano text editor
$ nano editor/project_manager.cpp

# Find an unrelated bug in Control and fix it
$ nano scene/gui/control.cpp

# Review changes
$ git status
$ git diff

# We'll do two commits for our unrelated changes,
# starting by the Control changes necessary for the PM enhancements
$ git add scene/gui/control.cpp
$ git commit -m "Fix handling of margins in Control"

# Check we did good
$ git log
$ git show
$ git status

# Make our second commit
$ git add editor/project_manager.cpp
$ git commit -m "Add a pretty banner to the Project Manager"
$ git log

有了这个, 我们应该在 better-project-manager 分支中有两个新的提交, 这些提交不在 master 分支中. 它们仍然只是本地的, 远程分支不知道它们, 上游仓库也不知道.

将更改推送到远程

这就是 git push 将发挥作用的地方。在 Git 中,提交总是在本地仓库中完成(Subversion 则不同,其提交将直接修改远程仓库)。你需要将新的提交推送到远程分支,才能与世界共享它们。语法是:

$ git push <remote> <local branch>[:<remote branch>]

如果你希望远程分支与本地分支具有相同的名