章节 ▾ 第二版

4.1 Git 服务器 - 协议

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

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

如果你对运行自己的服务器不感兴趣,可以跳到本章的最后一节,查看设置托管账户的一些选项,然后转到下一章,我们将讨论在分布式版本控制环境中工作的各种细节。

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

协议

Git 可以使用四种不同的协议来传输数据:本地协议(Local)、HTTP、安全外壳协议(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 内部原理)。这里我们将使用正常路径,因为这样做几乎总是更快。

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

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

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

优点

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

这也是一种从他人工作仓库快速获取工作的好方法。如果你和同事正在同一个项目上工作,并且他们希望你检查某个东西,那么运行类似 git pull /home/john/project 的命令通常比他们推送到远程服务器然后你从远程服务器拉取更容易。

缺点

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

需要指出的是,如果你使用某种共享挂载,这不一定是速度最快的选项。本地仓库只有在你对数据有快速访问权限时才快。NFS 上的仓库通常比同一服务器上通过 SSH 访问的仓库慢,因为 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 钩子)。届时,任何可以访问你放置仓库的 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 内部原理一章)。

通常,你会选择运行读/写智能 HTTP 服务器,或者简单地以哑方式提供只读文件访问。很少会混合使用这两种服务。

优点

我们将重点关注 HTTP 协议智能版本的优点。

所有类型的访问都使用单一 URL,并且仅在需要认证时才由服务器提示,这种简单性使得最终用户操作非常方便。能够使用用户名和密码进行认证也是相对于 SSH 的一个巨大优势,因为用户不必在本地生成 SSH 密钥并将公共密钥上传到服务器才能与其交互。对于经验不足的用户,或在 SSH 不太常见的系统上的用户,这在可用性方面是一个主要优势。它也是一种非常快速高效的协议,类似于 SSH。

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

另一个优点是 HTTP 和 HTTPS 是如此常用的协议,以至于公司防火墙通常被设置为允许通过其端口的流量。

缺点

在某些服务器上,通过 HTTPS 使用 Git 可能比 SSH 更难设置一些。除此之外,其他协议在提供 Git 内容方面相对于智能 HTTP 几乎没有优势。

如果你使用 HTTP 进行认证推送,提供凭据有时会比通过 SSH 使用密钥更复杂。但是,你可以使用多种凭据缓存工具,包括 macOS 上的“钥匙串访问”和 Windows 上的“凭据管理器”,以使此过程相当轻松。阅读凭据存储以了解如何在你的系统上设置安全的 HTTP 密码缓存。

SSH 协议

在自托管 Git 时,一个常见的传输协议是通过 SSH。这是因为在大多数地方,SSH 对服务器的访问已经设置好了——如果还没有,也很容易做到。SSH 也是一个经过认证的网络协议,而且由于它无处不在,通常易于设置和使用。

要通过 SSH 克隆 Git 仓库,你可以指定一个 ssh:// URL,如下所示:

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

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

$ 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 端口,这不是公司防火墙总是允许的标准端口。在大型公司防火墙后面,这个不常用的端口通常会被阻塞。

scroll-to-top