[[r_maintaining_gh_project]]
=== 维护项目

现在我们可以很方便地向一个项目贡献内容，来看一下另一个方面的内容：创建、维护和管理你自己的项目。

==== 创建新的版本库

让我们创建一个版本库来分享我们的项目。
通过点击面板右侧的“New repository”按钮，或者顶部工具条你用户名旁边的 `+` 按钮来开始我们的旅程。 参见 <<r_new_repo_dropdown>>。

.这是 “Your repositories” 区域.
image::images/newrepo.png[“Your repositories” 区域.]

[[r_new_repo_dropdown]]
.这是 “New repository” 下拉列表.
image::images/new-repo.png[“new repository” 下拉列表.]

这会带你到 “new repository” 表单:

.这是 “new repository” 表单.
image::images/newrepoform.png[“new repository” 表单。]

这里除了一个你必须要填的项目名，其他字段都是可选的。
现在只需要点击 “Create Repository” 按钮，Duang!!! – 你就在 GitHub 上拥有了一个以 `<user>/<project_name>` 命名的新仓库了。

因为目前暂无代码，GitHub 会显示有关创建新版本库或者关联到一个已有的 Git 版本库的一些说明。
我们不会在这里详细说明此项，如果你需要复习，去看 <<ch02-git-basics#ch02-git-basics>>。

现在你的项目就托管在 GitHub 上了，你可以把 URL 给任何你想分享的人。
GitHub 上的项目可通过 HTTP 或 SSH 访问，格式是：HTTP ： `https://github.com/<user>/<project_name>` ， SSH ： `git@github.com:<user>/<project_name>` 。
Git 可以通过以上两种 URL 进行抓取和推送，但是用户的访问权限又因连接时使用的证书不同而异。

[NOTE]
====
通常对于公开项目可以优先分享基于 HTTP 的 URL，因为用户克隆项目不需要有一个 GitHub 帐号。
如果你分享 SSH URL，用户必须有一个帐号并且上传 SSH 密钥才能访问你的项目。
HTTP URL 与你贴到浏览器里查看项目用的地址是一样的。
====

==== 添加合作者

如果你想与他人合作，并想给他们提交的权限，你需要把他们添加为 “Collaborators”。
如果 Ben，Jeff，Louise 都在 GitHub 上注册了，你想给他们推送的权限，你可以将他们添加到你的项目。
这样做会给他们 “推送” 权限，就是说他们对项目和 Git 版本库都有读写的权限。

点击边栏底部的 “Settings” 链接。

.版本库设置链接.
image::images/reposettingslink.png[版本库设置链接.]

然后从左侧菜单中选择 “Collaborators” 。
然后，在输入框中填写用户名，点击 “Add collaborator.”
如果你想授权给多个人，你可以多次重复这个步骤。
如果你想收回权限，点击他们同一行右侧的 “X”

.版本库合作者.
image::images/collaborators.png[版本库合作者.]

==== 管理合并请求

现在你有一个包含一些代码的项目，可能还有几个有推送权限的合作者，下面来看当你收到合并请求时该做什么。

合并请求可以来自仓库副本的一个分支，或者同一仓库的另一个分支。
唯一的区别是 fork 过来的通常是和你不能互相推送的人，而内部的推送通常都可以互相访问。

作为例子，假设你是 “tonychacon” ，你创建了一个名为 “fade” 的 Arduino 项目.

[[r_email_notifications]]
===== 邮件通知

有人来修改了你的代码，给你发了一个合并请求。
你会收一封关于合并请求的提醒邮件，它看起来像 <<r_email_pr>>。

[[r_email_pr]]
.新的合并请求的邮件通知.
image::images/maint-01-email.png[合并请求的邮件通知]

关于这个邮件有几个要注意的地方。
它会给你一个小的变动统计结果 -- 一个包含合并请求中改变的文件和改变了多少的列表。
它还给你一个 GitHub 上进行合并请求操作的链接。
还有几个可以在命令行使用的 URL。

如果你注意到 `git pull <url> patch-1` 这一行，这是一种合并远程分支的简单方式，无需必须添加一个远程分支。
我们很快会在 <<ch05-distributed-git#r_checking_out_remotes>> 讲到它。
如果你愿意，你可以创建并切换到一个主题分支，然后运行这个命令把合并请求合并进来。

还有一些有趣的 URL，像 `.diff` 和 `.patch` ，就像你猜的那样，它们提供 diff 和 patch 的标准版本。
你可以技术性地用下面的方法合并“合并请求”：

[source,console]
----
$ curl http://github.com/tonychacon/fade/pull/1.patch | git am
----

===== 在合并请求上进行合作

就像我们在 <<r_github_flow>>， 说过的，现在你可以跟开启合并请求的人进行会话。
你既可以对某些代码添加注释，也可以对整个提交添加注释或对整个合并请求添加注释，在任何地方都可以用 GitHub Flavored Markdown。

每次有人在合并请求上进行注释你都会收到通知邮件，通知你哪里发生改变。
他们都会包含一个到改变位置的链接，你可以直接在邮件中对合并请求进行注释。

.Responses to emails are included in the thread.
image::images/maint-03-email-resp.png[邮件回复]

一旦代码符合了你的要求，你想把它合并进来，你可以把代码拉取下来在本地进行合并，也可以用我们之前提到过的 `git pull <url> <branch>` 语法，或者把 fork 添加为一个 remote，然后进行抓取和合并。

对于很琐碎的合并，你也可以用 GitHub 网站上的 “Merge” 按钮。
它会做一个 “non-fast-forward” 合并，即使可以快进（fast-forward）合并也会产生一个合并提交记录。
就是说无论如何，只要你点击 merge 按钮，就会产生一个合并提交记录。
你可以在 <<r_merge_button>> 看到，如果你点击提示链接，GitHub 会给你所有的这些信息。

[[r_merge_button]]
.合并按钮和手工合并一个合并请求的指令.
image::images/maint-02-merge.png[合并按钮]

如果你决定不合并它，你可以把合并请求关掉，开启合并请求的人会收到通知。

[[r_pr_refs]]
===== 合并请求引用

如果你正在处理 *许多* 合并请求，不想添加一堆 remote 或者每次都要做一次拉取，这里有一个可以在 GitHub 上用的小技巧。
这是有点高级的技巧，但它相当有用，我们会在 <<ch10-git-internals#r_refspec>> 有更多的细节说明。

实际上 GitHub 在服务器上把合并请求分支视为一种 “假分支”。
默认情况下你克隆时不会得到它们，但它们还是隐式地存在，你可以很容易地访问到它们。

为了展示这个，我们要用到一个叫做 `ls-remote` 的低级命令（通常被叫做“plumbing”，我们会在 <<ch10-git-internals#r_plumbing_porcelain>> 读到更多相关内容）。
这个命令在日常 Git 操作中基本不会用到，但在显示服务器上有哪些引用（reference）时很管用。

如果在我们之前用过的 “blink” 版本库上使用这个命令，我们会得到一个版本库里所有的分支，标签和其它引用（reference）的列表。

[source,console]
----
$ git ls-remote https://github.com/schacon/blink
10d539600d86723087810ec636870a504f4fee4d	HEAD
10d539600d86723087810ec636870a504f4fee4d	refs/heads/master
6a83107c62950be9453aac297bb0193fd743cd6e	refs/pull/1/head
afe83c2d1a70674c9505cc1d8b7d380d5e076ed3	refs/pull/1/merge
3c8d735ee16296c242be7a9742ebfbc2665adec1	refs/pull/2/head
15c9f4f80973a2758462ab2066b6ad9fe8dcf03d	refs/pull/2/merge
a5a7751a33b7e86c5e9bb07b26001bb17d775d1a	refs/pull/4/head
31a45fc257e8433c8d8804e3e848cf61c9d3166c	refs/pull/4/merge
----

当然，如果你在你自己的版本库或其它你想检查的远程版本库中使用 `git ls-remote origin` ，它会显示相似的内容。

如果版本库在 GitHub 上并且有打开的合并请求，你会得到一些以 `refs/pull/` 开头的引用。
它们实际上是分支，但因为它们不在 `refs/heads/` 中，所以正常情况下你克隆时不会从服务器上得到它们 -- 抓取过程正常情况下会忽略它们。

每个合并请求有两个引用——其中以 `/head` 结尾的引用指向的提交记录与合并请求分支中的最后一个提交记录是同一个。
所以如果有人在我们的版本库中开启了一个合并请求，他们的分支叫做 `bug-fix`，指向 `a5a775` 这个提交记录，那么在 *我们的* 版本库中我们没有 `bug-fix` 分支（因为那是在他们的 fork 中），但我们 *可以* 有一个 `pull/<pr#>/head` 指向 `a5a775`。
这意味着我们可以很容易地拉取每一个合并请求分支而不用添加一堆 remote。

现在，你可以像直接抓取引用一样抓取那些分支或提交。

[source,console]
----
$ git fetch origin refs/pull/958/head
From https://github.com/libgit2/libgit2
 * branch            refs/pull/958/head -> FETCH_HEAD
----

这告诉 Git： “连接到 `origin` 这个 remote，下载名字为 `refs/pull/958/head` 的引用。”
Git 高高兴兴去执行，下载构建那个引用需要的所有内容，然后把指针指向 `.git/FETCH_HEAD` 下面你想要的提交记录。
然后你可以用 `git merge FETCH_HEAD` 把它合并到你想进行测试的分支，但那个合并的提交信息看起来有点怪。
然而，如果你需要审查 *一大批* 合并请求，这样操作会很麻烦。

还有一种方法可以抓取 _所有的_ 合并请求，并且在你连接到远程（remote）的时候保持更新。
用你最喜欢的编辑器打开 `.git/config` ，查找 `origin` 远程（remote）。
看起来差不多像下面这样：

----
[remote "origin"]
    url = https://github.com/libgit2/libgit2
    fetch = +refs/heads/*:refs/remotes/origin/*
----

以 `fetch =` 开头的行是一个 “refspec.”
它是一种把 remote 的名称映射到你本地 `.git` 目录的方法。
这一条（就是上面的这一条）告诉 Git，“remote 上 `refs/heads` 下面的内容在我本地版本库中都放在 `refs/remotes/origin` 。”
你可以把这一段修改一下，添加另一个 refspec：

----
[remote "origin"]
    url = https://github.com/libgit2/libgit2.git
    fetch = +refs/heads/*:refs/remotes/origin/*
    fetch = +refs/pull/*/head:refs/remotes/origin/pr/*
----

最后一行告诉 Git： “所有看起来像 `refs/pull/123/head` 的引用应该在本地版本库像 `refs/remotes/origin/pr/123` 一样存储”
现在，如果你保存那个文件，执行 `git fetch`：

[source,console]
----
$ git fetch
# …
 * [new ref]         refs/pull/1/head -> origin/pr/1
 * [new ref]         refs/pull/2/head -> origin/pr/2
 * [new ref]         refs/pull/4/head -> origin/pr/4
# …
----

现在所有的合并请求在本地像分支一样展现，它们是只读的，当你执行抓取时它们也会更新。
这让在本地测试合并请求中的代码变得超级简单：

[source,console]
----
$ git checkout pr/2
Checking out files: 100% (3769/3769)， done.
Branch pr/2 set up to track remote branch pr/2 from origin.
Switched to a new branch 'pr/2'
----

你的鹰眼系统会发现在 refspec 的 remote 部分的结尾有个 `head` 。
在 GitHub 那边也有一个 `refs/pull/#/merge` 引用，它代表的是如果你在网站上按了 “merge” 按钮对应的提交记录。
这甚至让你可以在按按钮之前就测试这个合并。


===== 合并请求之上的合并请求

你不仅可以在主分支或者说 `master` 分支上开启合并请求，实际上你可以在网络上的任何一个分支上开启合并请求。
其实，你甚至可以在另一个合并请求上开启一个合并请求。

如果你看到一个合并请求在向正确的方向发展，然后你想在这个合并请求上做一些修改或者你不太确定这是个好主意，或者你没有目标分支的推送权限，你可以直接在合并请求上开启一个合并请求。

当你开启一个合并请求时，在页面的顶端有一个框框显示你要合并到哪个分支和你从哪个分支合并过来的。
如果你点击那个框框右边的 “Edit” 按钮，你不仅可以改变分支，还可以选择哪个 fork。

[[r_pr_targets]]
.手工修改合并请求的目标.
image::images/maint-04-target.png[合并目标]

这里你可以很简单地指明合并你的分支到哪一个合并请求或 fork。

==== 提醒和通知

GitHub 内置了一个很好的通知系统，当你需要与别人或别的团队交流时用起来很方便。

在任何评论中你可以先输入一个 `@` ，系统会自动补全项目中合作者或贡献者的名字和用户名。

.输入 @ 来提醒某人.
image::images/maint-05-mentions.png[提醒]

你也可以提醒不在列表中的用户，但是通常自动补全用起更快。

当你发布了一个带用户提醒的评论，那个用户会收到通知。
这意味着把人们拉进会话中要比让他们投票有效率得多。
对于 GitHub 上的合并请求，人们经常把他们团队或公司中的其它人拉来审查问题或合并请求。

如果有人收到了合并请求或问题的提醒，他们会“订阅”它，后面有新的活动发生他们都会持续收到提醒。
如果你是合并请求或者问题的发起方你也会被订阅上，比如你在关注一个版本库或者你评论了什么东西。
如果你不想再收到提醒，在页面上有个 “Unsubscribe” 按钮，点一下就不会再收到更新了。

.取消订阅一个问题或合并请求.
image::images/maint-06-unsubscribe.png[取消订阅]

==== 通知页面

当我们在这提到特指 GitHub 的 “notifications” ，指的是当 GitHub 上有事件发生时，它通知你的方式，这里有几种不同的方式来配置它们。
如果你打开配置页面的 “Notification center” 标签，你可以看到一些选项。

.通知中心选项.
image::images/maint-07-notifications.png[通知中心]

有两个选项，通过“邮件（Email）”和通过“网页（Web）”，你可以选用一个或者都不选或者都选。

==== 网页通知

网页通知只在 GitHub 上存在，你也只能在 GitHub 上查看。
如果你打开了这个选项并且有一个你的通知，你会在你屏幕上方的通知图标上看到一个小蓝点。参见 <<r_not_center>>。

[[r_not_center]]
.通知中心.
image::images/maint-08-notifications-page.png[通知中心]

如果你点击那个玩意儿，你会看到你被通知到的所有条目，按照项目分好了组。
你可以点击左边栏的项目名字来过滤项目相关的通知。
你可以点击通知旁边的对号图标把通知标为已读，或者点击组上面的图标把项目中 *所有的* 通知标为已读。
在每个对号图标旁边都有一个静音按钮，你可以点一下，以后就不会收到它相关的通知。

所有这些工具对于处理大量通知非常有用。
很多 GitHub 资深用户都关闭邮件通知，在这个页面上处理他们所有的通知。

==== 邮件通知

邮件通知是你处理 GitHub 通知的另一种方式。
如果你打开这个选项，每当有通知时，你会收到一封邮件。
我们在 <<r_email_notification>> 和 <<r_email_pr>> 看到了一些例子。
邮件也会被合适地按话题组织在一起，如果你使用一个具有会话功能的邮件客户端那会很方便。

GitHub 在发送给你的邮件头中附带了很多元数据，这对于设置过滤器和邮件规则非常有帮助。

举个例子，我们来看一看在 <<r_email_pr>> 中发给 Tony 的一封真实邮件的头部，我们会看到下面这些：

[source,mbox]
----
To: tonychacon/fade <fade@noreply.github.com>
Message-ID: <tonychacon/fade/pull/1@github.com>
Subject: [fade] Wait longer to see the dimming effect better (#1)
X-GitHub-Recipient: tonychacon
List-ID: tonychacon/fade <fade.tonychacon.github.com>
List-Archive: https://github.com/tonychacon/fade
List-Post: <mailto:reply+i-4XXX@reply.github.com>
List-Unsubscribe: <mailto:unsub+i-XXX@reply.github.com>，...
X-GitHub-Recipient-Address: tchacon@example.com
----

这里有一些有趣的东西。
如果你想高亮或者转发这个项目甚至这个合并请求相关的邮件，`Message-ID` 中的信息会以 `<user>/<project>/<type>/<id>` 的格式展现所有的数据。
例如，如果这是一个问题（issue），那么 `<type>` 字段就会是 “issues” 而不是 “pull” 。

`List-Post` 和 `List-Unsubscribe` 字段表示如果你的邮件客户端能够处理这些，那么你可以很容易地在列表中发贴或取消对这个相关帖子的订阅。
那会很有效率，就像在页面中点击静音按钮或在问题／合并请求页面点击 “Unsubscribe” 一样。

值得注意的是，如果你同时打开了邮件和网页通知，那么当你在邮件客户端允许加载图片的情况下阅读邮件通知时，对应的网页通知也将会同时被标记为已读。

==== 特殊文件

如果你的版本库中有一些特殊文件，GitHub 会提醒你。

==== README

第一个就是 `README` 文件，可以是几乎任何 GitHub 可以识别的格式。
例如，它可以是 `README` ，`README.md` ， `README.asciidoc` 。
如果 GitHub 在你的版本库中找到 README 文件，会把它在项目的首页渲染出来。

很多团队在这个文件里放版本库或项目新人需要了解的所有相关的信息。
它一般包含这些内容：

* 该项目的作用
* 如何配置与安装
* 有关如何使用和运行的例子
* 项目的许可证
* 如何向项目贡献力量

因为 GitHub 会渲染这个文件，你可以在文件里植入图片或链接让它更容易理解。

==== 贡献 CONTRIBUTING

另一个 GitHub 可以识别的特殊文件是 `CONTRIBUTING` 。
如果你有一个任意扩展名的 `CONTRIBUTING` 文件，当有人开启一个合并请求时 GitHub 会显示 <<r_contrib_file>>。

[[r_contrib_file]]
.开启合并请求时有 CONTRIBUTING 文件存在.
image::images/maint-09-contrib.png[贡献注意事项]

这个的作用就是你可以在这里指出对于你的项目开启的合并请求你想要的／不想要的各种事情。
这样别人在开启合并请求之前可以读到这些指导方针。

==== 项目管理

对于一个单个项目其实没有很多管理事务要做，但也有几点有趣的。

===== 改变默认分支

如果你想用 “master” 之外的分支作为你的默认分支，其他人将默认会在这个分支上开启合并请求或进行浏览，你可以在你版本库的设置页面的 "options" 标签下修改。

[[r_default_branch]]
.改变项目的默认分支.
image::images/maint-10-default-branch.png[默认分支]

简单地改变默认分支下拉列表中的选项，它就会作为所有主要操作的默认分支，他人进行克隆时该分支也将被默认检出。

===== 移交项目

如果你想把一个项目移交给 GitHub 中的另一个人或另一个组织，还是设置页面的这个 “options” 标签下有一个 “Transfer ownership” 选项可以用来干这个。

[[r_transfer_project]]
.把项目移交给另一个 GitHub 用户或组织。
image::images/maint-11-transfer.png[移交]

当你正准备放弃一个项目且正好有别人想要接手时，或者你的项目壮大了想把它移到一个组织里时，这就管用了。

这么做不仅会把版本库连带它所有的观察和星标数都移到另一个地方，它还会将你的 URL 重定向到新的位置。
它也重定向了来自 Git 的克隆和抓取，而不仅仅是网页端请求。
