章节 ▾ 第二版

4.1 Git 在服务器端 - 协议

到目前为止,你应该能够完成大部分日常 Git 使用任务。然而,要进行 Git 协作,你需要有一个远程 Git 仓库。虽然你可以在技术上向个人仓库推送和拉取更改,但这样做不被推荐,因为如果不小心,你很容易混淆他们正在进行的工作。此外,你希望你的协作者即使在你的计算机离线时也能访问仓库 — — 拥有一个更可靠的通用仓库通常很有用。因此,与他人协作的首选方法是设置一个你们双方都能访问的中间仓库,并在此仓库上进行推送和拉取。

运行 Git 服务器相当简单。首先,你选择你的服务器要支持哪些协议。本章的第一部分将介绍可用的协议以及它们的优缺点。接下来的部分将解释一些使用这些协议的典型设置以及如何让你的服务器正常运行。最后,如果你不介意将代码托管在别人的服务器上,并且不想费心设置和维护自己的服务器,我们将介绍一些托管选项。

如果你没有兴趣运行自己的服务器,你可以跳到本章的最后部分,了解一些设置托管账户的选项,然后继续阅读下一章,我们将讨论分布式源代码控制环境中的各种细节。

远程仓库通常是裸仓库 — — 一个没有工作目录的 Git 仓库。由于该仓库仅用作协作点,因此磁盘上没有检出的快照;它只是 Git 数据。简而言之,裸仓库就是你的项目 .git 目录的内容,仅此而已。

协议

Git 可以使用四种不同的协议来传输数据:本地 (Local)、HTTP、安全 Shell (SSH) 和 Git 协议。在这里,我们将讨论它们是什么,以及在什么基本情况下你会想(或不想)使用它们。

本地协议

最基本的是本地协议,其中远程仓库位于同一主机上的另一个目录中。这通常在团队中的每个人都有权访问共享文件系统(如 NFS 挂载)时使用,或者在不太可能的情况下,每个人都登录到同一台计算机。后者并不理想,因为你所有的代码仓库实例都将驻留在同一台计算机上,使得灾难性丢失的可能性大大增加。

如果你有一个共享挂载的文件系统,那么你可以克隆、推送和拉取一个基于本地文件的仓库。要克隆这样的仓库,或者将其添加为现有项目的远程仓库,请使用仓库的路径作为 URL。例如,要克隆一个本地仓库,你可以运行类似以下的命令:

$ git clone /srv/git/project.git

或者你可以这样做:

$ git clone file:///srv/git/project.git

如果 URL 开头明确指定了 file://,Git 的操作方式会略有不同。如果你只指定路径,Git 会尝试使用硬链接或直接复制所需文件。如果你指定了 file://,Git 会启动它通常用于通过网络传输数据的进程,这通常效率较低。指定 file:// 前缀的主要原因是如果你想要一个干净的仓库副本,并且剔除了多余的引用或对象 — — 通常是在从其他 VCS 导入或类似操作之后(有关维护任务,请参阅 Git Internals)。我们在这里使用普通路径,因为这样做几乎总是更快。

要将本地仓库添加到现有的 Git 项目,你可以运行类似以下的命令:

$ git remote add local_proj /srv/git/project.git

然后,你可以通过你的新远程名称 local_proj 来推送和拉取该远程仓库,就像通过网络进行操作一样。

优点

基于文件的仓库的优点是它们很简单,并且它们利用现有的文件权限和网络访问。如果你已经有一个整个团队都可以访问的共享文件系统,那么设置一个仓库将非常容易。你将裸仓库副本放在一个每个人都有共享访问权限的地方,并像设置任何其他共享目录一样设置读/写权限。我们将在 Getting Git on a Server 中讨论如何为此目的导出裸仓库副本。

对于快速获取他人工作仓库中的内容,这也是一个不错的选择。如果你和同事正在处理同一个项目,而他们希望你检查某些内容,运行类似 git pull /home/john/project 的命令通常比他们先推送到远程服务器,然后你再从中获取更容易。

缺点

此方法的缺点是与基本的网络访问相比,共享访问通常更难设置和从多个位置访问。如果你想在家时从笔记本电脑推送,你必须挂载远程磁盘,这与基于网络的访问相比可能很困难且速度很慢。

需要注意的是,如果你使用的是某种共享挂载,这不一定是速度最快的选项。本地仓库仅在你能快速访问数据时才快速。NFS 上的仓库通常比同一服务器上的 SSH 仓库慢,因为 Git 可以直接从每个系统的本地磁盘运行。

最后,此协议不能防止仓库受到意外损坏。每个用户都可以完全访问“远程”目录的 shell,没有任何东西可以阻止他们更改或删除 Git 内部文件并损坏仓库。

HTTP 协议

Git 可以使用两种不同的模式通过 HTTP 进行通信。在 Git 1.6.6 之前,只有一种非常简单且通常是只读的方式。在 1.6.6 版本中,引入了一种新的、更智能的协议,该协议允许 Git 以类似于 SSH 的方式智能地协商数据传输。在过去的几年里,这种新的 HTTP 协议变得非常流行,因为它对用户来说更简单,并且在通信方式上更智能。新版本通常被称为智能 HTTP 协议,而旧版本被称为 HTTP 协议。我们将首先介绍较新的智能 HTTP 协议。

智能 HTTP

智能 HTTP 的工作方式与 SSH 或 Git 协议非常相似,但它运行在标准的 HTTPS 端口上,并可以使用各种 HTTP 身份验证机制,这意味着它通常比 SSH 等更容易使用,因为你可以使用用户名/密码身份验证,而无需设置 SSH 密钥。

它可能已经成为现在使用 Git 最流行的方式,因为它既可以像 git:// 协议那样设置为匿名提供服务,也可以像 SSH 协议那样通过身份验证和加密进行推送。你不必为这些不同的功能设置不同的 URL,现在可以使用同一个 URL。如果你尝试推送,而仓库需要身份验证(通常情况下是这样),服务器可以提示输入用户名和密码。读取访问也是如此。

事实上,对于 GitHub 等服务,你用来在线查看仓库的 URL(例如,https://github.com/schacon/simplegit)就是你可以用来克隆、以及(如果你有权限)推送的相同 URL。

哑 HTTP

如果服务器没有响应 Git HTTP 智能服务,Git 客户端将尝试回退到更简单的 HTTP 协议。哑协议期望裸 Git 仓库像普通文件一样由 Web 服务器提供。哑 HTTP 的魅力在于其设置的简单性。基本上,你只需将一个裸 Git 仓库放在你的 HTTP 文档根目录下,并设置一个特定的 post-update 钩子,即可完成(请参阅 Git Hooks)。此时,任何可以访问你放置仓库的 Web 服务器的人都可以克隆你的仓库。要允许通过 HTTP 读取你的仓库,请执行类似以下的命令:

$ cd /var/www/htdocs/
$ git clone --bare /path/to/git_project gitproject.git
$ cd gitproject.git
$ mv hooks/post-update.sample hooks/post-update
$ chmod a+x hooks/post-update

仅此而已。Git 默认提供的 post-update 钩子会运行适当的命令(git update-server-info)以使 HTTP 获取和克隆正常工作。当你推送到此仓库时(也许通过 SSH);然后,其他人可以通过类似以下方式克隆:

$ git clone https://example.com/gitproject.git

在这个特定的例子中,我们使用了 Apache 设置中常见的 /var/www/htdocs 路径,但你可以使用任何静态 Web 服务器 — — 只需将裸仓库放在其路径下即可。Git 数据作为基本的静态文件提供(有关如何提供这些数据的详细信息,请参阅 Git Internals 章节)。

通常,你会选择运行一个读/写智能 HTTP 服务器,或者简单地以哑方式将文件设为只读访问。很少会同时运行这两种服务。

优点

我们将专注于智能 HTTP 协议的优点。

拥有一个统一的 URL 用于所有类型的访问,并且只在需要身份验证时才提示服务器,这使得最终用户使用起来非常方便。能够使用用户名和密码进行身份验证也是比 SSH 的一大优势,因为用户无需在本地生成 SSH 密钥并将公钥上传到服务器即可进行交互。对于不太成熟的用户,或在 SSH 不太常见的系统上的用户来说,这是一个可用性的主要优势。它也是一种非常快速高效的协议,与 SSH 协议类似。

你还可以通过 HTTPS 提供只读仓库,这意味着你可以加密内容传输;或者你可以进一步要求客户端使用特定的签名 SSL 证书。

另一个好处是 HTTP 和 HTTPS 是非常常用的协议,因此企业防火墙通常设置为允许通过其端口进行流量。

缺点

与某些服务器上的 SSH 相比,通过 HTTPS 的 Git 设置可能有点棘手。除此之外,其他协议在提供 Git 内容方面几乎没有比智能 HTTP 更有优势的地方。

如果你使用 HTTP 进行身份验证推送,提供凭据有时比使用 SSH 密钥更复杂。但是,有几种凭据缓存工具可供使用,包括 macOS 上的 Keychain access 和 Windows 上的 Credential Manager,可以使这个过程相当顺畅。请阅读 Credential Storage,了解如何在你的系统上设置安全的 HTTP 密码缓存。

SSH 协议

自托管 Git 时,SSH 是一种常见的传输协议。这是因为在大多数地方 SSH 访问服务器已经设置好了 — — 如果没有,也很容易设置。SSH 也是一种经过身份验证的网络协议,并且由于其普遍性,通常易于设置和使用。

要通过 SSH 克隆 Git 仓库,你可以像这样指定一个 ssh:// URL:

$ git clone ssh://[user@]server/project.git

或者你可以使用更短的类似 scp 的 SSH 协议语法:

$ git clone [user@]server:project.git

在上述两种情况下,如果你不指定可选的用户名,Git 会假定你当前登录的用户。

优点

使用 SSH 的优点很多。首先,SSH 相对容易设置 — — SSH 守护进程很常见,许多网络管理员都有使用它们的经验,许多操作系统发行版都已配置了它们或提供了管理它们的工具。其次,SSH 访问是安全的 — — 所有数据传输都经过加密和身份验证。最后,与 HTTPS、Git 和本地协议一样,SSH 也很高效,能在传输前尽可能压缩数据。

缺点

SSH 的负面之处在于它不支持匿名访问你的 Git 仓库。如果你使用 SSH,人们必须拥有你机器的 SSH 访问权限,即使是只读的,这使得 SSH 不利于开源项目,因为人们可能只是想克隆你的仓库来检查它。如果你仅在公司网络内使用它,SSH 可能是你唯一需要处理的协议。如果你想为你的项目提供匿名的只读访问,同时又想使用 SSH,你将需要设置 SSH 来进行推送,但需要其他方式供他人获取。

Git 协议

最后,我们有 Git 协议。这是一种 Git 自带的特殊守护进程;它监听一个专用端口 (9418),提供与 SSH 协议类似的服务,但完全没有身份验证或加密。为了通过 Git 协议提供仓库,你必须创建一个 git-daemon-export-ok 文件 — — 没有这个文件,守护进程就不会提供仓库 — — 但除此之外,没有安全性。要么 Git 仓库可供所有人克隆,要么不可。这意味着该协议通常不支持推送。你可以启用推送访问,但鉴于缺乏身份验证,互联网上任何发现你的项目 URL 的人都可以推送到该项目。可以说,这种情况非常罕见。

优点

Git 协议通常是可用的最快的网络传输协议。如果你为公共项目提供大量流量,或者为一个不需要读取访问身份验证的非常大的项目提供服务,你可能需要设置一个 Git 守护进程来为你提供项目服务。它使用与 SSH 协议相同的​​数据传输机制,但没有加密和身份验证的开销。

缺点

由于缺乏 TLS 或其他加密,通过 git:// 克隆可能会导致任意代码执行漏洞,因此应避免使用,除非你了解自己在做什么。

  • 如果你运行 git clone git://example.com/project.git,控制例如你路由器的攻击者可以修改你刚刚克隆的仓库,并在其中插入恶意代码。如果你随后编译/运行你刚克隆的代码,你将执行恶意代码。运行 git clone http://example.com/project.git 也应出于同样的原因避免。

  • 运行 git clone https://example.com/project.git 不存在同样的问题(除非攻击者能够提供 example.com 的 TLS 证书)。运行 git clone git@example.com:project.git 仅在你接受错误的 SSH 密钥指纹时存在此问题。

它也没有身份验证,也就是说,任何人都可以克隆仓库(尽管这通常正是你想要的)。它也可能是最难设置的协议。它必须运行自己的守护进程,这需要 xinetdsystemd 配置之类的东西,这并不总是那么容易。它还需要防火墙访问端口 9418,而这并不是企业防火墙总是允许的标准端口。在大公司的防火墙后面,这个不常用的端口通常会被阻止。