如何使用 Git/GitHub

如果你想要协助开发 Sponge 或者你对 API 的补充有了一个好的想法,或者你想要改进一下我们的文档,那么你就需要对 git 和 GitHub 非常熟悉。如果你已经对诸如复刻(Fork)、分支(Branch)、Issue、Pull Request 或者提交等概念非常熟悉,那么你可以直接跳过这一部分。如果你不清楚我们在说些什么的话,请接着读下去。

注解

我们假定你已经阅读了“安装 Git”一章,并已经在你的机器上配置好了 Git 客户端。

Git 和 GitHub 的基本概念

Git允许许多不同的开发者在同一时间开发同一个软件。GitHub是一个开发人员可以和他人协作并分享他们的成果的网站。GitHub依赖Git管理上述的工作。

小技巧

如果你不熟悉 Git 和 GitHub 的相关词汇,可以看一看 GitHub 提供的术语表:https://help.github.com/articles/github-glossary/

Repo Overview

举例,有一名为 SpongePowered 代码仓库,拥有两个分别名为 masterfeature 1 的分支,和其上的若干提交。

我们来将术语放在具体情境下解释——从 仓库 (Repository) 开始。仓库(通常会略称 repo )是一个项目中所有文件的储藏地点。这个名为 SpongePowered 的仓库位于 GitHub 中。然而,这个仓库有权限控制,避免不必要,甚至是恶意变更波及正常的开发工作。因此你作为普通用户,权限只有只读,并不能写入新的变更。你现在或许已开始思考如何进行变更了。很好,这个时候就需要 复刻 (Fork)了。你可以复制一份 SpongePowered 仓库,然后在你自己复制出来的仓库中进行修改。修改完成后,你需要向 SpongePowered 提出 拉取请求 (即 Pull Requst ,或简称 PR)。你所提请的变更将会经过我们的复核,一些工作人员会指出其中的不足之处,并协助修改,并最终合并此请求。

在细讲之前,我们先简单过一遍整个流程:

  1. Fork 你选择的项目
  2. 克隆到你的电脑上
  3. 创建新分支
  4. 做出需要的修改
  5. 测试所有东西能否正常运作
  6. 提交变更
  7. 同步到 GitHub
  8. 就你的变更向 SpongePowered 提出拉取申请
  9. 如有必要,修订申请
  10. 你的提交将会被工作人员合并

请详细说明 !

1. Fork仓库

注解

仅当你没有推送权的时候,你才需要Fork。若仓库的所有者是你,你不需要Fork,可直接忽略此步骤,直接克隆整个仓库即可。若你不是 Sponge 团队的工作人员,但打算为 Sponge 贡献代码,但你必须Fork。

现在你大概只有一个基本概念,不过没关系,我们会进行详细解释的。首先你需要Fork你想修改的仓库。这个操作可以透过 GitHub完成:在仓库的页面上方你可以找到一个 Fork 按钮,点击此按钮后 GitHub就会开始帮你进行一系列必要的操作,然后你就可以发现你多了一个名字类似 你的 GitHub 用户名/被克隆的仓库名 的新仓库了。很好,第一步完成。

注解

整个仓库,包括所有的分支,都会精确地复制进你 Fork 的仓库中。

Repo forking

2. 将你的 Fork 克隆至本地

然后,为提交变更,你需要将你的 Fork 克隆至本地。启动你的 Git 客户端 (安装 Git) 并将你的 Fork clone 至本地。客户端将会询问目标文件夹。第二步就此完成,干得漂亮!

注解

大部分操作都可透过图形化界面完成。你大可选择在命令行中操作——如果你熟悉命令行版的Git客户端的话。每一步操作都会给出对应的命令的。

另外,你还可以考虑使用 CLI (命令行界面,比如说 Windows 下的 CMDPowershell )。注意,在输入下列命令之前你需要先切换到目标文件夹:

git clone git://github.com/YourGitHubAccount/ClonedRepoName.git
Repo cloning

3. 创建新分支

现在呢在你的本地有了你的 Fork 的一份拷贝(即 local clone ),是时候建立新分支了。分支可保证在不影响原有程序的功能的基础上,开发并测试新的功能。我们强烈建议**不要**直接在 master 分支上进行操作,你应当新建一个新的分支,给予其一个有意义的名字,然后在新分支上操作。

总而言之,我们需要一个全新的 branch ,先新建一个!这个操作可以透过你的客户端完成(你应该能在某个地方找到 create branch 或者 新建分支 这样的按钮),当然你也可以透过 git 的 CLI 完成此步骤:

git checkout -b [name_of_your_new_branch]

这个操作会新建一个新 branch ,其名称为你传入的参数,并将 HEAD 切换至这个新分支上。你所有的新变动都应该在这个分支上发生。若是需要切换回另一个分支(比如切换回 master ),只需重新执行此步骤。第三步搞定!干得漂亮!另外你还可以在你的 git 客户端中看到所有分支的总览,用命令行的话可以这么做:

git branch
Branches

是时候一展身手做出变更了。 请用各种编辑器或 IDE 来完成。

4. 测试你的变更

对于 SpongeAPI 及对应实现,你必须先运行 gradle compileJava 。如果编译期间没有报错,并成功结束,请继续下一步。否则,请在妥善修正后重试。

对于 SpongeDocs 来说,你只需要发送拉取请求 (Pull Request) 就好了。 PR 发出后会触发自动构建,并自动找出其中的问题。另外你还可以选择在本地构建文档,关于这方面的资料请参考 文档仓库中的 Readme.md <https://github.com/SpongePowered/SpongeDocs/blob/master/README.md>

5. 提交变更

在完成必要的变更后,你需要将这些变更打包起来(即变成一条 commit )并推送至对应的分支上。你的 git 客户端可以帮你搞定这个问题,只需要记得给这条 commit 合适的名字及描述就好。对于 CLI 来说是这样:

首先收集所有需要提交的文件:

git add <file>
git add <folder>

现在这些文件已经被标记过了,然后只需要这么做就可以了:

git commit

然后,在接下来出现的窗口中,输入你想添加的说明信息。另外,注意下面的图——你会注意到,这些新的 commit 只是在你的本地仓库中,并没有出现在你在 GitHub 上的 Fork 中。

注解

一个拉取请求可以有多条 commit ,只需要向对应分支继续推送 commit 就可以了。如有需要,之后你可以将所有的 commit 压缩成一条。

现在你已经完成第六步了。马上就要完成了!

Committing

6. 同步到 GitHub

现在你的变更还都是在本地,你需要把新的变更推送到 GitHub 上。你的 git 客户端依旧可以帮你搞定,仔细找一找就可以找到;对于 CLI 来说,只需如此操作:

git push <remote> <branch>

在这种情况下应为︰

git push origin feature/YourFeature
Pushing commits

7. 向 SpongePowered 的仓库就新变更提请拉取请求

你可以选择在网页客户端上完成此操作(届时你的 Fork 中会出现一个提示,它会指导你完成此步骤),或者你可以用你的 GitHub 客户端发起拉取请求。对于 GitHub 的 Windows 版客户端,这个按钮应在主窗口右上角。

PRs

8. 如有必要,修订拉取请求

如果我们需要你修订拉取请求,只需要按之前的操作,推送更多的 commit 即可。新的 commit 会自动出现在拉取请求中。

9. 你的请求被合并

大功告成!干得漂亮!

高级 Git

压缩(Squash)与变基(Rebase)

我们假设你正给某个仓库添砖加瓦,然后我们假定你在完成变更后产生了 137 笔新的提交。这样一来,你的变更历史就会变得一团糟。要是进了主变更历史,然后你的贡献全是零碎的变更,看起来不难看吗?幸运的是 Git 正好有工具来应对这个情况,这就是变基( rebase )。通过变基,你的这 137 条小变更就可以变成一条巨大的变更,听上去是不是很爽?为了避免重复造轮子,这里有一个简明易懂的关于变基和压缩(squash commit)的教程可供查阅:

Gitready: Squashing with Rebase

下图形象地展示了这个操作背后具体发生了什么:

Squashing commits

设置远程仓库

自然的,原仓库是你的 Fork 的父代仓库;而你克隆的本地仓库和你的 Fork 之间也有一样的关系。但这里有个问题:原仓库不是你的本地仓库的父代仓库。听上去好像不是问题?但这意味着你无法直接将原仓库的新变更拉取到你的本地仓库中。为此,你需要将原仓库设定为你本地仓库的远程仓库(读作父代仓库),这样你便能随时获取最新的变更。下面将会给出范例。

Setting up a remote

很好,首先你需要通过 CLI 来设定远程仓库,因为多数 GUI 界面的 git 客户端没有这个功能:

git remote add upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git

如果你不确定这行指令有无正常工作,或者你只是想检查一下,可执行:

git remote -v

输出应该像这样:

origin    https://github.com/YOUR_USERNAME/YOUR_FORK.git (fetch)
origin    https://github.com/YOUR_USERNAME/YOUR_FORK.git (push)
upstream  https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git (fetch)
upstream  https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git (push)

注解

如果你接到了类似 fatal: The current branch YourBranchName has no upstream branch. 这样的警告,说明此分支无对应的远程仓库分支。通常这种情况会在你第一次向新分支推送提交时出现。若需推送当前分支并将 upstream 设定为远程仓库,只需执行 git push --set-upstream origin 分支名 即可。

变基

假定你完成了一些变更,但这个时候别人同时更新了主仓库。这样一来,你的 Fork 和你克隆的本地仓库就全部过期了。虽然问题不大,但为了避免此后合并变更时可能引发的问题,我们强烈建议此时选择将你的变更变基 ( rebase ) 到最新的变更上去。在此之前你需要先设定好远程仓库。

成功变基需要下列若干步骤:

1. 拉取远程仓库变更

首先你需要拉取远程仓库中的最新变更。一如既往,使用 CLI :

git fetch upstream

如此做将会拉取来自 upstream 的全部变更,并存入 upstream/master 分支。

2. 在本地合并远程仓库变更

然后,切换到本地的 master 分支:

git checkout master

然后,将 upstream/master 合并入 master 分支:

git merge upstream/master

很好,现在我们做了这些事情:

Rebasing 1

3. 将本地分支变基至更新过的主分支

下一步就是将你本地的工作分支变基至本地的 master 分支了。首先切换至你的工作分支(比如说 feature/yourfeature ),然后进行如下操作完成变基:

git checkout feature/yourfeature
git rebase master

如此做后,你的分支会被回退,然后在 master 分支的新变更的基础上重新添加你的新分支。结果如下:

Rebasing 2

4. 推送至你的 Fork

最后你还需要将结果推送到你的 Fork 中。若你已有对应的 PR ,此 PR 也会自动更新:

git checkout master
git push -f
git checkout feature/yourfeature
git push -f
Rebasing 3

你成功搞定了!干得漂亮!啊对了,欢迎来到变基的世界!