Git详解之四 服务器上的Git

最后更新于:2022-04-01 20:27:27

## Git详解之四 服务器上的Git **服务器上的 Git** 到目前为止,你应该已经学会了使用 Git来完成日常工作。然而,如果想与他人合作,还需要一个远程的 Git仓库。尽管技术上可以从个人的仓库里推送和拉取修改内容,但我们不鼓励这样做,因为一不留心就很容易弄混其他人的进度。另外,你也一定希望合作者们即使在自己不开机的时候也能从仓库获取数据 —拥有一个更稳定的公共仓库十分有用。因此,更好的合作方式是建立一个大家都可以访问的共享仓库,从那里推送和拉取数据。我们将把这个仓库称为 “Git服务器”;代理一个 Git仓库只需要花费很少的资源,几乎从不需要整个服务器来支持它的运行。 架设一台 Git服务器并不难。第一步是选择与服务器通讯的协议。本章第一节将介绍可用的协议以及各自优缺点。下面一节将介绍一些针对各个协议典型的设置以及如何在服务器上实施。最后,如果你不介意在他人服务器上保存你的代码,又想免去自己架设和维护服务器的麻烦,倒可以试试我们介绍的几个仓库托管服务。 如果你对架设自己的服务器没兴趣,可以跳到本章最后一节去看看如何申请一个代码托管服务的账户然后继续下一章,我们会在那里讨论分布式源码控制环境的林林总总。 远程仓库通常只是一个_裸仓库(bare repository)_ — 即一个没有当前工作目录的仓库。因为该仓库只是一个合作媒介,所以不需要从硬盘上取出最新版本的快照;仓库里存放的仅仅是 Git的数据。简单地说,裸仓库就是你工作目录中.git 子目录内的内容。     **4.1 协议** Git 可以使用四种主要的协议来传输数据:本地传输,SSH协议,Git 协议和 HTTP 协议。下面分别介绍一下哪些情形应该使用(或避免使用)这些协议。 值得注意的是,除了 HTTP协议外,其他所有协议都要求在服务器端安装并运行 Git。 **本地协议** 最基本的就是_本地协议(Local protocol)_,所谓的远程仓库在该协议中的表示,就是硬盘上的另一个目录。这常见于团队每一个成员都对一个共享的文件系统(例如 NFS)拥有访问权,或者比较少见的多人共用同一台电脑的情况。后面一种情况并不安全,因为所有代码仓库实例都储存在同一台电脑里,增加了灾难性数据损失的可能性。 如果你使用一个共享的文件系统,就可以在一个本地文件系统中克隆仓库,推送和获取。克隆的时候只需要将远程仓库的路径作为 URL使用,比如下面这样: ~~~ $ git clone /opt/git/project.git ~~~ 或者这样: ~~~ $ git clone file:///opt/git/project.git ~~~ 如果在 URL开头明确使用 file:// ,那么Git会以一种略微不同的方式运行。如果你只给出路径,Git会尝试使用硬链接或直接复制它所需要的文件。如果使用了file:// ,Git会调用它平时通过网络来传输数据的工序,而这种方式的效率相对较低。使用 file:// 前缀的主要原因是当你需要一个不包含无关引用或对象的干净仓库副本的时候 — 一般指从其他版本控制系统导入的,或类似情形(参见第 9章的维护任务)。我们这里仅仅使用普通路径,这样更快。 要添加一个本地仓库作为现有 Git项目的远程仓库,可以这样做: ~~~ $ git remote add local_proj /opt/git/project.git ~~~ 然后就可以像在网络上一样向这个远程仓库推送和获取数据了。 **优点** 基于文件仓库的优点在于它的简单,同时保留了现存文件的权限和网络访问权限。如果你的团队已经有一个全体共享的文件系统,建立仓库就十分容易了。你只需把一份裸仓库的副本放在大家都能访问的地方,然后像对其他共享目录一样设置读写权限就可以了。我们将在下一节“在服务器上部署 Git ”中讨论如何导出一个裸仓库的副本。 这也是从别人工作目录中获取工作成果的快捷方法。假如你和你的同事在一个项目中合作,他们想让你检出一些东西的时候,运行类似 git pull /home/john/project 通常会比他们推送到服务器,而你再从服务器获取简单得多。 **缺点** 这种方法的缺点是,与基本的网络连接访问相比,难以控制从不同位置来的访问权限。如果你想从家里的笔记本电脑上推送,就要先挂载远程硬盘,这和基于网络连接的访问相比更加困难和缓慢。 另一个很重要的问题是该方法不一定就是最快的,尤其是对于共享挂载的文件系统。本地仓库只有在你对数据访问速度快的时候才快。在同一个服务器上,如果二者同时允许 Git访问本地硬盘,通过 NFS访问仓库通常会比 SSH慢。 **SSH协议** Git 使用的传输协议中最常见的可能就是SSH了。这是因为大多数环境已经支持通过 SSH对服务器的访问 — 即便还没有,架设起来也很容易。SSH也是唯一一个同时支持读写操作的网络协议。另外两个网络协议(HTTP和 Git)通常都是只读的,所以虽然二者对大多数人都可用,但执行写操作时还是需要 SSH。SSH同时也是一个验证授权的网络协议;而因为其普遍性,一般架设和使用都很容易。 通过 SSH克隆一个 Git 仓库,你可以像下面这样给出 ssh://的 URL: ~~~ $ git clone ssh://user@server:project.git ~~~ 或者不指明某个协议 —这时 Git 会默认使用 SSH : ~~~ $ git clone user@server:project.git ~~~ 如果不指明用户,Git会默认使用当前登录的用户名连接服务器。 **优点** 使用 SSH的好处有很多。首先,如果你想拥有对网络仓库的写权限,基本上不可能不使用 SSH。其次,SSH架设相对比较简单 — SSH守护进程很常见,很多网络管理员都有一些使用经验,而且很多操作系统都自带了它或者相关的管理工具。再次,通过 SSH进行访问是安全的 — 所有数据传输都是加密和授权的。最后,和 Git及本地协议一样,SSH也很高效,会在传输之前尽可能压缩数据。 **缺点** SSH 的限制在于你不能通过它实现仓库的匿名访问。即使仅为读取数据,人们也必须在能通过 SSH访问主机的前提下才能访问仓库,这使得 SSH不利于开源的项目。如果你仅仅在公司网络里使用,SSH可能是你唯一需要使用的协议。如果想允许对项目的匿名只读访问,那么除了为自己推送而架设 SSH协议之外,还需要支持其他协议以便他人访问读取。 **Git协议** 接下来是 Git协议。这是一个包含在 Git软件包中的特殊守护进程;它会监听一个提供类似于 SSH服务的特定端口(9418),而无需任何授权。打算支持 Git协议的仓库,需要先创建git-export-daemon-ok 文件 —它是协议进程提供仓库服务的必要条件 —但除此之外该服务没有什么安全措施。要么所有人都能克隆 Git仓库,要么谁也不能。这也意味着该协议通常不能用来进行推送。你可以允许推送操作;然而由于没有授权机制,一旦允许该操作,网络上任何一个知道项目 URL的人将都有推送权限。不用说,这是十分罕见的情况。 **优点** Git 协议是现存最快的传输协议。如果你在提供一个有很大访问量的公共项目,或者一个不需要对读操作进行授权的庞大项目,架设一个 Git守护进程来供应仓库是个不错的选择。它使用与 SSH协议相同的数据传输机制,但省去了加密和授权的开销。 **缺点** Git 协议消极的一面是缺少授权机制。用Git协议作为访问项目的唯一方法通常是不可取的。一般的做法是,同时提供 SSH接口,让几个开发者拥有推送(写)权限,其他人通过git:// 拥有只读权限。Git协议可能也是最难架设的协议。它要求有单独的守护进程,需要定制 —我们将在本章的 “Gitosis”一节详细介绍它的架设 —需要设定xinetd 或类似的程序,而这些工作就没那么轻松了。该协议还要求防火墙开放 9418端口,而企业级防火墙一般不允许对这个非标准端口的访问。大型企业级防火墙通常会封锁这个少见的端口。 **HTTP/S协议** 最后还有 HTTP协议。HTTP 或 HTTPS 协议的优美之处在于架设的简便性。基本上,只需要把 Git的裸仓库文件放在 HTTP的根目录下,配置一个特定的post-update 挂钩(hook)就可以搞定(Git挂钩的细节见第 7 章)。此后,每个能访问 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 http://example.com/gitproject.git ~~~ 在本例中,我们使用了 Apache设定中常用的 /var/www/htdocs 路径,不过你可以使用任何静态web服务 — 把裸仓库放在它的目录里就行。Git 的数据是以最基本的静态文件的形式提供的(关于如何提供文件的详情见第 9章)。 通过 HTTP进行推送操作也是可能的,不过这种做法不太常见,并且牵扯到复杂的 WebDAV设定。由于很少用到,本书将略过对该内容的讨论。如果对 HTTP推送协议感兴趣,不妨打开这个地址看一下操作方法:http://www.kernel.org/pub/software/scm/git/docs/howto/setup-git-server-over-http.txt 。通过 HTTP 推送的好处之一是你可以使用任何 WebDAV服务器,不需要为 Git设定特殊环境;所以如果主机提供商支持通过 WebDAV更新网站内容,你也可以使用这项功能。 **优点** 使用 HTTP协议的好处是易于架设。几条必要的命令就可以让全世界读取到仓库的内容。花费不过几分钟。HTTP协议不会占用过多服务器资源。因为它一般只用到静态的 HTTP服务提供所有数据,普通的 Apache服务器平均每秒能支撑数千个文件的并发访问 —哪怕让一个小型服务器超载都很难。 你也可以通过 HTTPS提供只读的仓库,这意味着你可以加密传输内容;你甚至可以要求客户端使用特定签名的 SSL证书。一般情况下,如果到了这一步,使用 SSH公共密钥可能是更简单的方案;不过也存在一些特殊情况,这时通过 HTTPS使用带签名的 SSL 证书或者其他基于 HTTP 的只读连接授权方式是更好的解决方案。 HTTP 还有个额外的好处:HTTP是一个如此常见的协议,以至于企业级防火墙通常都允许其端口的通信。 **缺点** HTTP 协议的消极面在于,相对来说客户端效率更低。克隆或者下载仓库内容可能会花费更多时间,而且 HTTP传输的体积和网络开销比其他任何一个协议都大。因为它没有按需供应的能力 —传输过程中没有服务端的动态计算 —因而 HTTP 协议经常会被称为_傻瓜(dumb)_协议。更多 HTTP 协议和其他协议效率上的差异见第 9。 **4.2 在服务器上部署 Git** 开始架设 Git服务器前,需要先把现有仓库导出为裸仓库 —即一个不包含当前工作目录的仓库。做法直截了当,克隆时用 --bare 选项即可。裸仓库的目录名一般以.git 结尾,像这样: ~~~ $ git clone --bare my_project my_project.git Initialized empty Git repository in/opt/projects/my_project.git/ ~~~ 该命令的输出或许会让人有些不解。其实 clone 操作基本上相当于 git init 加 git fetch,所以这里出现的其实是git init 的输出,先由它建立一个空目录,而之后传输数据对象的操作并无任何输出,只是悄悄在幕后执行。现在my_project.git 目录中已经有了一份 Git 目录数据的副本。 整体上的效果大致相当于: ~~~ $ cp -Rf my_project/.git my_project.git ~~~ 但在配置文件中有若干小改动,不过对用户来讲,使用方式都一样,不会有什么影响。它仅取出 Git仓库的必要原始数据,存放在该目录中,而不会另外创建工作目录。 **把裸仓库移到服务器上** 有了裸仓库的副本后,剩下的就是把它放到服务器上并设定相关协议。假设一个域名为 git.example.com 的服务器已经架设好,并可以通过SSH访问,我们打算把所有Git仓库储存在/opt/git 目录下。只要把裸仓库复制过去: ~~~ $ scp -r my_project.git user@git.example.com:/opt/git ~~~ 现在,所有对该服务器有 SSH访问权限,并可读取 /opt/git 目录的用户都可以用下面的命令克隆该项目: ~~~ $ git clone user@git.example.com:/opt/git/my_project.git ~~~ 如果某个 SSH用户对 /opt/git/my_project.git 目录有写权限,那他就有推送权限。如果到该项目目录中运行 git init 命令,并加上 --shared 选项,那么 Git 会自动修改该仓库目录的组权限为可写(译注:实际上 --shared 可以指定其他行为,只是默认为将组权限改为可写并执行 g+sx,所以最后会得到 rws。)。 ~~~ $ ssh user@git.example.com $ cd /opt/git/my_project.git $ git init --bare --shared ~~~ 由此可见,根据现有的 Git仓库创建一个裸仓库,然后把它放上你和同事都有 SSH访问权的服务器是多么容易。现在已经可以开始在同一项目上密切合作了。 值得注意的是,这的的确确是架设一个少数人具有连接权的 Git服务的全部 — 只要在服务器上加入可以用 SSH 登录的帐号,然后把裸仓库放在大家都有读写权限的地方。一切都准备停当,无需更多。 下面的几节中,你会了解如何扩展到更复杂的设定。这些内容包含如何避免为每一个用户建立一个账户,给仓库添加公共读取权限,架设网页界面,使用 Gitosis工具等等。然而,只是和几个人在一个不公开的项目上合作的话,仅仅是一个 SSH服务器和裸仓库就足够了,记住这点就可以了。 **小型安装** 如果设备较少或者你只想在小型开发团队里尝试 Git,那么一切都很简单。架设 Git服务最复杂的地方在于账户管理。如果需要仓库对特定的用户可读,而给另一部分用户读写权限,那么访问和许可的安排就比较困难。 **SSH连接** 如果已经有了一个所有开发成员都可以用 SSH访问的服务器,架设第一个服务器将变得异常简单,几乎什么都不用做(正如上节中介绍的那样)。如果需要对仓库进行更复杂的访问控制,只要使用服务器操作系统的本地文件访问许可机制就行了。 如果需要团队里的每个人都对仓库有写权限,又不能给每个人在服务器上建立账户,那么提供 SSH连接就是唯一的选择了。我们假设用来共享仓库的服务器已经安装了 SSH服务,而且你通过它访问服务器。 有好几个办法可以让团队的每个人都有访问权。第一个办法是给每个人建立一个账户,直截了当但略过繁琐。反复运行adduser 并给所有人设定临时密码可不是好玩的。 第二个办法是在主机上建立一个 git 账户,让每个需要写权限的人发送一个 SSH 公钥,然后将其加入 git 账户的~/.ssh/authorized_keys 文件。这样一来,所有人都将通过 git 账户访问主机。这丝毫不会影响提交的数据 — 访问主机用的身份不会影响提交对象的提交者信息。 另一个办法是让 SSH服务器通过某个 LDAP服务,或者其他已经设定好的集中授权机制,来进行授权。只要每个人都能获得主机的 shell访问权,任何可用的 SSH授权机制都能达到相同效果。   **4.3 生成 SSH 公钥** 大多数 Git服务器都会选择使用 SSH公钥来进行授权。系统中的每个用户都必须提供一个公钥用于授权,没有的话就要生成一个。生成公钥的过程在所有操作系统上都差不多。首先先确认一下是否已经有一个公钥了。SSH公钥默认储存在账户的主目录下的~/.ssh 目录。进去看看: ~~~ $ cd ~/.ssh $ ls authorized_keys2 id_dsa       known_hosts config           id_dsa.pub ~~~ 关键是看有没有用 something 和 something.pub 来命名的一对文件,这个 something 通常就是 id_dsa 或 id_rsa。有 .pub后缀的文件就是公钥,另一个文件则是密钥。假如没有这些文件,或者干脆连.ssh 目录都没有,可以用 ssh-keygen 来创建。该程序在Linux/Mac系统上由 SSH 包提供,而在 Windows 上则包含在 MSysGit 包里: ~~~ $ ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key(/Users/schacon/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in/Users/schacon/.ssh/id_rsa. Your public key has been saved in/Users/schacon/.ssh/id_rsa.pub. The key fingerprint is: 43:c5:5b:5f:b1:f1:50:43:ad:20:a6:92:6a:1f:9a:3aschacon@agadorlaptop.local ~~~ 它先要求你确认保存公钥的位置(.ssh/id_rsa),然后它会让你重复一个密码两次,如果不想在使用公钥的时候输入密码,可以留空。 现在,所有做过这一步的用户都得把它们的公钥给你或者 Git服务器的管理员(假设 SSH服务被设定为使用公钥机制)。他们只需要复制 .pub 文件的内容然后发邮件给管理员。公钥的样子大致如下: ~~~ $ cat ~/.ssh/id_rsa.pub ssh-rsaAAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3 Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx NrRFi9wrf+M7Q== schacon@agadorlaptop.local ~~~ 关于在多个操作系统上设立相同 SSH公钥的教程,可以查阅 GitHub上有关 SSH 公钥的向导:http://github.com/guides/providing-your-ssh-key。   **4.4 架设服务器** 现在我们过一边服务器端架设 SSH访问的流程。本例将使用 authorized_keys 方法来给用户授权。我们还将假定使用类似 Ubuntu 这样的标准 Linux发行版。首先,创建一个名为 ‘git’的用户,并为其创建一个.ssh 目录。 ~~~ $ sudo adduser git $ su git $ cd $ mkdir .ssh ~~~ 接下来,把开发者的 SSH公钥添加到这个用户的 authorized_keys 文件中。假设你通过电邮收到了几个公钥并存到了临时文件里。重复一下,公钥大致看起来是这个样子: ~~~ $ cat /tmp/id_rsa.john.pub ssh-rsaAAAAB3NzaC1yc2EAAAADAQABAAABAQCB007n/ww+ouN4gSLKssMxXnBOvf9LGt4L ojG6rs6hPB09j9R/T17/x4lhJA0F3FR1rP6kYBRsWj2aThGw6HXLm9/5zytK6Ztg3RPKK+4k Yjh6541NYsnEAZuXz0jTTyAUfrtU3Z5E003C4oxOj6H0rfIF1kKI9MAQLMdpGW1GYEIgS9Ez Sdfd8AcCIicTDWbqLAcU4UpkaX8KyGlLwsNuuGztobF8m72ALC/nLF6JLtPofwFBlgc+myiv O7TCUSBdLQlgMVOFq1I2uPWQOkOWQAHukEOmfjy2jctxSDBQ220ymjaNsHT4kgtZg2AYYgPq dAv8JggJICUvax2T9va5 gsg-keypair ~~~ 只要把它们逐个追加到 authorized_keys 文件尾部即可: ~~~ $ cat /tmp/id_rsa.john.pub >>~/.ssh/authorized_keys $ cat /tmp/id_rsa.josie.pub >>~/.ssh/authorized_keys $ cat /tmp/id_rsa.jessica.pub >>~/.ssh/authorized_keys ~~~ 现在可以用 --bare 选项运行 git init 来建立一个裸仓库,这会初始化一个不包含工作目录的仓库。 ~~~ $ cd /opt/git $ mkdir project.git $ cd project.git $ git --bare init ~~~ 这时,Join,Josie或者 Jessica 就可以把它加为远程仓库,推送一个分支,从而把第一个版本的项目文件上传到仓库里了。值得注意的是,每次添加一个新项目都需要通过 shell登入主机并创建一个裸仓库目录。我们不妨以gitserver 作为 git 用户及项目仓库所在的主机名。如果在网络内部运行该主机,并在 DNS 中设定 gitserver 指向该主机,那么以下这些命令都是可用的: ~~~ # 在John 的电脑上 $ cd myproject $ git init $ git add . $ git commit -m 'initial commit' $ git remote add origingit@gitserver:/opt/git/project.git $ git push origin master ~~~ 这样,其他人的克隆和推送也一样变得很简单: ~~~ $ git clone git@gitserver:/opt/git/project.git $ vim README $ git commit -am 'fix for the README file' $ git push origin master ~~~ 用这个方法可以很快捷地为少数几个开发者架设一个可读写的 Git服务。 作为一个额外的防范措施,你可以用 Git自带的 git-shell 工具限制 git 用户的活动范围。只要把它设为git 用户登入的 shell,那么该用户就无法使用普通的 bash或者 csh 什么的 shell 程序。编辑 /etc/passwd 文件: ~~~ $ sudo vim /etc/passwd ~~~ 在文件末尾,你应该能找到类似这样的行: git:x:1000:1000::/home/git:/bin/sh 把 bin/sh 改为 /usr/bin/git-shell (或者用 which git-shell 查看它的实际安装路径)。该行修改后的样子如下: git:x:1000:1000::/home/git:/usr/bin/git-shell 现在 git 用户只能用 SSH 连接来推送和获取 Git仓库,而不能直接使用主机 shell。尝试普通 SSH登录的话,会看到下面这样的拒绝信息: ~~~ $ ssh git@gitserver fatal: What do you think I am? A shell? Connection to gitserver closed. ~~~ **4.5 公共访问** 匿名的读取权限该怎么实现呢?也许除了内部私有的项目之外,你还需要托管一些开源项目。或者因为要用一些自动化的服务器来进行编译,或者有一些经常变化的服务器群组,而又不想整天生成新的 SSH密钥 — 总之,你需要简单的匿名读取权限。 或许对小型的配置来说最简单的办法就是运行一个静态 web服务,把它的根目录设定为 Git仓库所在的位置,然后开启本章第一节提到的 post-update 挂钩。这里继续使用之前的例子。假设仓库处于/opt/git 目录,主机上运行着Apache服务。重申一下,任何web服务程序都可以达到相同效果;作为范例,我们将用一些基本的 Apache设定来展示大体需要的步骤。 首先,开启挂钩: ~~~ $ cd project.git $ mv hooks/post-update.sample hooks/post-update $ chmod a+x hooks/post-update ~~~ 如果用的是 Git 1.6之前的版本,则可以省略 mv 命令 — Git是从较晚的版本才开始在挂钩实例的结尾添加 .sample后缀名的。 post-update 挂钩是做什么的呢?其内容大致如下: ~~~ $ cat .git/hooks/post-update #!/bin/sh exec git-update-server-info ~~~ 意思是当通过 SSH向服务器推送时,Git将运行这个 git-update-server-info 命令来更新匿名 HTTP访问获取数据时所需要的文件。 接下来,在 Apache配置文件中添加一个 VirtualHost条目,把文档根目录设为 Git项目所在的根目录。这里我们假定 DNS服务已经配置好,会把对.gitserver 的请求发送到这台主机: ~~~     ServerNamegit.gitserver     DocumentRoot/opt/git               Order allow, deny         allow fromall ~~~ 另外,需要把 /opt/git 目录的 Unix 用户组设定为 www-data ,这样web服务才可以读取仓库内容,因为运行CGI脚本的Apache 实例进程默认就是以该用户的身份起来的: ~~~ $ chgrp -R www-data /opt/git ~~~ 重启 Apache之后,就可以通过项目的 URL来克隆该目录下的仓库了。 ~~~ $ git clone http://git.gitserver/project.git ~~~ 这一招可以让你在几分钟内为相当数量的用户架设好基于 HTTP的读取权限。另一个提供非授权访问的简单方法是开启一个 Git守护进程,不过这将要求该进程作为后台进程常驻 —接下来的这一节就要讨论这方面的细节。   **4.6  GitWeb** 现在我们的项目已经有了可读可写和只读的连接方式,不过如果能有一个简单的 web界面访问就更好了。Git自带一个叫做 GitWeb的 CGI 脚本,运行效果可以到http://git.kernel.org 这样的站点体验下(见图 4-1)。 Figure 4-1. 基于网页的 GitWeb用户界面 如果想看看自己项目的效果,不妨用 Git自带的一个命令,可以使用类似 lighttpd 或 webrick 这样轻量级的服务器启动一个临时进程。如果是在 Linux 主机上,通常都预装了lighttpd ,可以到项目目录中键入 git instaweb 来启动。如果用的是Mac,Leopard 预装了Ruby,所以webrick 应该是最好的选择。如果要用 lighttpd 以外的程序来启动 git instaweb,可以通过--httpd 选项指定: ~~~ $ git instaweb --httpd=webrick [2009-02-21 10:02:21] INFO  WEBrick 1.3.1 [2009-02-21 10:02:21] INFO  ruby 1.8.6 (2008-03-03) [universal-darwin9.0] ~~~ 这会在 1234端口开启一个 HTTPD服务,随之在浏览器中显示该页,十分简单。关闭服务时,只需在原来的命令后面加上--stop 选项就可以了: ~~~ $ git instaweb --httpd=webrick --stop ~~~ 如果需要为团队或者某个开源项目长期运行 GitWeb,那么 CGI脚本就要由正常的网页服务来运行。一些 Linux发行版可以通过 apt 或yum 安装一个叫做 gitweb 的软件包,不妨首先尝试一下。我们将快速介绍一下手动安装 GitWeb 的流程。首先,你需要 Git的源码,其中带有 GitWeb,并能生成定制的 CGI脚本: ~~~ $ git clone git://git.kernel.org/pub/scm/git/git.git $ cd git/ $ make GITWEB_PROJECTROOT="/opt/git" \         prefix=/usrgitweb/gitweb.cgi $ sudo cp -Rf gitweb /var/www/ ~~~ 注意,通过指定 GITWEB_PROJECTROOT 变量告诉编译命令 Git 仓库的位置。然后,设置 Apache以 CGI 方式运行该脚本,添加一个 VirtualHost配置: ~~~     ServerNamegitserver     DocumentRoot/var/www/gitweb               Options ExecCGI +FollowSymLinks +SymLinksIfOwnerMatch        AllowOverride All         orderallow,deny         Allow fromall         AddHandlercgi-script cgi        DirectoryIndex gitweb.cgi ~~~ 不难想象,GitWeb可以使用任何兼容 CGI的网页服务来运行;如果偏向使用其他 web服务器,配置也不会很麻烦。现在,通过 http://gitserver 就可以在线访问仓库了,在http://git.server 上还可以通过HTTP克隆和获取仓库的内容。 **4.7  Gitosis** 把所有用户的公钥保存在 authorized_keys 文件的做法,只能凑和一阵子,当用户数量达到几百人的规模时,管理起来就会十分痛苦。每次改删用户都必须登录服务器不去说,这种做法还缺少必要的权限管理 — 每个人都对所有项目拥有完整的读写权限。 幸好我们还可以选择应用广泛的 Gitosis项目。简单地说,Gitosis就是一套用来管理 authorized_keys 文件和实现简单连接限制的脚本。有趣的是,用来添加用户和设定权限的并非通过网页程序,而只是管理一个特殊的 Git 仓库。你只需要在这个特殊仓库内做好相应的设定,然后推送到服务器上,Gitosis就会随之改变运行策略,听起来就很酷,对吧? Gitosis 的安装算不上傻瓜化,但也不算太难。用 Linux服务器架设起来最简单 —以下例子中,我们使用装有 Ubuntu 8.10系统的服务器。 Gitosis 的工作依赖于某些Python 工具,所以首先要安装Python 的setuptools 包,在 Ubuntu 上称为 python-setuptools: ~~~ $ apt-get install python-setuptools ~~~ 接下来,从 Gitosis项目主页克隆并安装: ~~~ $ git clone git://eagain.net/gitosis.git $ cd gitosis $ sudo python setup.py install ~~~ 这会安装几个供 Gitosis使用的工具。默认 Gitosis会把 /home/git 作为存储所有Git仓库的根目录,这没什么不好,不过我们之前已经把项目仓库都放在/opt/git 里面了,所以为方便起见,我们可以做一个符号连接,直接划转过去,而不必重新配置: ~~~ $ ln -s /opt/git /home/git/repositories ~~~ Gitosis 将会帮我们管理用户公钥,所以先把当前控制文件改名备份,以便稍后重新添加,准备好让 Gitosis自动管理authorized_keys 文件: ~~~ $ mv /home/git/.ssh/authorized_keys /home/git/.ssh/ak.bak ~~~ 接下来,如果之前把 git 用户的登录 shell 改为 git-shell 命令的话,先恢复‘git’用户的登录shell。改过之后,大家仍然无法通过该帐号登录(译注:因为authorized_keys 文件已经没有了。),不过不用担心,这会交给 Gitosis 来实现。所以现在先打开 /etc/passwd 文件,把这行: git:x:1000:1000::/home/git:/usr/bin/git-shell 改回: git:x:1000:1000::/home/git:/bin/sh 好了,现在可以初始化 Gitosis了。你可以用自己的公钥执行 gitosis-init 命令,要是公钥不在服务器上,先临时复制一份: ~~~ $ sudo -H -u git gitosis-init < /tmp/id_dsa.pub Initialized empty Git repository in/opt/git/gitosis-admin.git/ Reinitialized existing Git repository in /opt/git/gitosis-admin.git/ ~~~ 这样该公钥的拥有者就能修改用于配置 Gitosis的那个特殊 Git 仓库了。接下来,需要手工对该仓库中的 post-update 脚本加上可执行权限: ~~~ $ sudo chmod 755/opt/git/gitosis-admin.git/hooks/post-update ~~~ 基本上就算是好了。如果设定过程没出什么差错,现在可以试一下用初始化 Gitosis的公钥的拥有者身份 SSH登录服务器,应该会看到类似下面这样: ~~~ $ ssh git@gitserver PTY allocation request failed on channel 0 fatal: unrecognized command 'gitosis-serveschacon@quaternion'   Connection togitserver closed. ~~~ 说明 Gitosis认出了该用户的身份,但由于没有运行任何 Git命令,所以它切断了连接。那么,现在运行一个实际的 Git命令 — 克隆 Gitosis 的控制仓库: ~~~ # 在你本地计算机上 $ git clone git@gitserver:gitosis-admin.git ~~~ 这会得到一个名为 gitosis-admin 的工作目录,主要由两部分组成: ~~~ $ cd gitosis-admin $ find . ./gitosis.conf ./keydir ./keydir/scott.pub ~~~ gitosis.conf 文件是用来设置用户、仓库和权限的控制文件。keydir 目录则是保存所有具有访问权限用户公钥的地方—每人一个。在keydir 里的文件名(比如上面的 scott.pub)应该跟你的不一样 — Gitosis 会自动从使用 gitosis-init 脚本导入的公钥尾部的描述中获取该名字。 看一下 gitosis.conf 文件的内容,它应该只包含与刚刚克隆的 gitosis-admin 相关的信息: ~~~ $ cat gitosis.conf [gitosis] [group gitosis-admin] writable = gitosis-admin members = scott ~~~ 它显示用户 scott —初始化 Gitosis公钥的拥有者 — 是唯一能管理 gitosis-admin 项目的人。 现在我们来添加一个新项目。为此我们要建立一个名为 mobile 的新段落,在其中罗列手机开发团队的开发者,以及他们拥有写权限的项目。由于 ‘scott’ 是系统中的唯一用户,我们把他设为唯一用户,并允许他读写名为iphone_project 的新项目: ~~~ [group mobile] writable = iphone_project members = scott ~~~ 修改完之后,提交 gitosis-admin 里的改动,并推送到服务器使其生效: ~~~ $ git commit -am 'add iphone_project and mobile group' [master]: created 8962da8: "changed name"  1 files changed, 4insertions(+), 0 deletions(-) $ git push Counting objects: 5, done. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 272 bytes, done. Total 3 (delta 1), reused 0 (delta 0) To git@gitserver:/opt/git/gitosis-admin.git   fb27aec..8962da8  master ->master ~~~ 在新工程 iphone_project 里首次推送数据到服务器前,得先设定该服务器地址为远程仓库。但你不用事先到服务器上手工创建该项目的裸仓库— Gitosis 会在第一次遇到推送时自动创建: ~~~ $ git remote add origin git@gitserver:iphone_project.git $ git push origin master Initialized empty Git repository in/opt/git/iphone_project.git/ Counting objects: 3, done. Writing objects: 100% (3/3), 230 bytes, done. Total 3 (delta 0), reused 0 (delta 0) To git@gitserver:iphone_project.git  * [newbranch]      master -> master ~~~ 请注意,这里不用指明完整路径(实际上,如果加上反而没用),只需要一个冒号加项目名字即可 — Gitosis会自动帮你映射到实际位置。 要和朋友们在一个项目上协同工作,就得重新添加他们的公钥。不过这次不用在服务器上一个一个手工添加到~/.ssh/authorized_keys 文件末端,而只需管理keydir 目录中的公钥文件。文件的命名将决定在 gitosis.conf 中对用户的标识。现在我们为John,Josie和 Jessica 添加公钥: ~~~ $ cp /tmp/id_rsa.john.pub keydir/john.pub $ cp /tmp/id_rsa.josie.pub keydir/josie.pub $ cp /tmp/id_rsa.jessica.pub keydir/jessica.pub ~~~ 然后把他们都加进 ‘mobile’团队,让他们对 iphone_project 具有读写权限: ~~~ [group mobile] writable = iphone_project members = scott john josie jessica ~~~ 如果你提交并推送这个修改,四个用户将同时具有该项目的读写权限。 Gitosis 也具有简单的访问控制功能。如果想让 John只有读权限,可以这样做: ~~~ [group mobile] writable = iphone_project members = scott josie jessica [group mobile_ro] readonly = iphone_project members = john ~~~ 现在 John可以克隆和获取更新,但 Gitosis不会允许他向项目推送任何内容。像这样的组可以随意创建,多少不限,每个都可以包含若干不同的用户和项目。甚至还可以指定某个组为成员之一(在组名前加上@ 前缀),自动继承该组的成员: ~~~ [group mobile_committers] members = scott josie jessica [group mobile] writable  =iphone_project members   =@mobile_committers [group mobile_2] writable  =another_iphone_project members   =@mobile_committers john ~~~ 如果遇到意外问题,试试看把 loglevel=DEBUG 加到 [gitosis] 的段落(译注:把日志设置为调试级别,记录更详细的运行信息。)。如果一不小心搞错了配置,失去了推送权限,也可以手工修改服务器上的/home/git/.gitosis.conf 文件 — Gitosis 实际是从该文件读取信息的。它在得到推送数据时,会把新的 gitosis.conf 存到该路径上。所以如果你手工编辑该文件的话,它会一直保持到下次向 gitosis-admin 推送新版本的配置内容为止。 **4.8  Gitolite** Note: thelatest copy of this section of the ProGit book is always available within the[gitolite documentation](http://github.com/sitaramc/gitolite/blob/pu/doc/progit-article.mkd). The author would alsolike to humbly state that, while this section is accurate, and**can** (andoften **has**) been used to install gitolite without reading any otherdocumentation, it is of necessity not complete, and cannot completely replacethe enormous amount of documentation that gitolite comes with. Git hasstarted to become very popular in corporate environments, which tend to have someadditional requirements in terms of access control. Gitolite was originallycreated to help with those requirements, but it turns out that it’s equallyuseful in the open source world: the Fedora Project controls access to theirpackage management repositories (over 10,000 of them!) using gitolite, and thisis probably the largest gitolite installation anywhere too. Gitoliteallows you to specify permissions not just by repository, but also by branch ortag names within each repository. That is, you can specify that certain people(or groups of people) can only push certain “refs” (branches or tags) but notothers. **Installing** InstallingGitolite is very easy, even if you don’t read the extensive documentation thatcomes with it. You need an account on a Unix server of some kind; various Linuxflavours, and Solaris 10, have been tested. You do not need root access,assuming git, perl, and an openssh compatible ssh server are already installed.In the examples below, we will use thegitolite accounton a host called gitserver. Gitoliteis somewhat unusual as far as “server” software goes – access is via ssh, andso every userid on the server is a potential “gitolite host”. As a result,there is a notion of “installing” the software itself, and then “setting up” auser as a “gitolite host”. Gitolitehas 4 methods of installation. People using Fedora or Debian systems can obtainan RPM or a DEB and install that. People with root access can install itmanually. In these two methods, any user on the system can then become a“gitolite host”. Peoplewithout root access can install it within their own userids. And finally,gitolite can be installed by running a script**on the workstation**, from abash shell. (Even the bash that comes with msysgit will do, in case you’re wondering.) We willdescribe this last method in this article; for the other methods please see thedocumentation. You startby obtaining public key based access to your server, so that you can log infrom your workstation to the server without getting a password prompt. Thefollowing method works on Linux; for other workstation OSs you may have to dothis manually. We assume you already had a key pair generated using ssh-keygen. ~~~ $ ssh-copy-id -i ~/.ssh/id_rsa gitolite@gitserver ~~~ This willask you for the password to the gitolite account, and then set up public keyaccess. This is**essential** for the install script, so check to makesure you can run a command without getting a password prompt: ~~~ $ ssh gitolite@gitserver pwd /home/gitolite ~~~ Next, youclone Gitolite from the project’s main site and run the “easy install” script(the third argument is your name as you would like it to appear in theresulting gitolite-admin repository): ~~~ $ git clone git://github.com/sitaramc/gitolite $ cd gitolite/src $ ./gl-easy-install -q gitolite gitserver sitaram ~~~ And you’redone! Gitolite has now been installed on the server, and you now have a brandnew repository calledgitolite-admin in the home directory of yourworkstation. You administer your gitolite setup by making changes to thisrepository and pushing. That lastcommand does produce a fair amount of output, which might be interesting toread. Also, the first time you run this, a new keypair is created; you willhave to choose a passphrase or hit enter for none. Why a second keypair isneeded, and how it is used, is explained in the “ssh troubleshooting” documentthat comes with Gitolite. (Hey the documentation has to be good for**something**!) Reposnamed gitolite-admin and testing arecreated on the server by default. If you wish to clone either of these locally(from an account that has SSH console access to the gitolite account via**authorized_keys**),type: ~~~ $ git clone gitolite:gitolite-admin $ git clone gitolite:testing To clonethese same repos from any other account: $ git clone gitolite@servername:gitolite-admin $ git clone gitolite@servername:testing ~~~ **Customising the Install** While thedefault, quick, install works for most people, there are some ways to customisethe install if you need to. If you omit the-q argument,you get a “verbose” mode install – detailed information on what the install isdoing at each step. The verbose mode also allows you to change certainserver-side parameters, such as the location of the actual repositories, byediting an “rc” file that the server uses. This “rc” file is liberallycommented so you should be able to make any changes you need quite easily, saveit, and continue. This file also contains various settings that you can changeto enable or disable some of gitolite’s advanced features. **Config File and Access Control Rules** Once theinstall is done, you switch to the gitolite-admin repository(placed in your HOME directory) and poke around to see what you got: ~~~ $ cd ~/gitolite-admin/ $ ls conf/  keydir/ $ find conf keydir -type f conf/gitolite.conf keydir/sitaram.pub $ cat conf/gitolite.conf #gitolite conf # please see conf/example.conf for details on syntax andfeatures   repo gitolite-admin     RW+                 = sitaram   repo testing     RW+                 = @all ~~~ Noticethat “sitaram” (the last argument in the gl-easy-install commandyou gave earlier) has read-write permissions on thegitolite-admin repositoryas well as a public key file of the same name. The configfile syntax for gitolite is liberally documented in conf/example.conf, so we’ll only mention some highlights here. You cangroup users or repos for convenience. The group names are just like macros; whendefining them, it doesn’t even matter whether they are projects or users; thatdistinction is only made when you**use** the “macro”. ~~~ @oss_repos      =linux perl rakudo git gitolite @secret_repos   =fenestra pear   @admins         =scott     # Adams, not Chacon, sorry :) @interns        =ashok     # get the spelling right,Scott! @engineers      =sitaram dilbert wally alice @staff          =@admins @engineers @interns ~~~ You cancontrol permissions at the “ref” level. In the following example, interns canonly push the “int” branch. Engineers can push any branch whose name startswith “eng-“, and tags that start with “rc” followed by a digit. And the adminscan do anything (including rewind) to any ref. ~~~ repo @oss_repos     RW  int$                = @interns     RW eng-                = @engineers     RW  refs/tags/rc[0-9]   = @engineers     RW+                     = @admins ~~~ Theexpression after the RW or RW+ isa regular expression (regex) that the refname (ref) being pushed is matchedagainst. So we call it a “refex”! Of course, a refex can be far more powerfulthan shown here, so don’t overdo it if you’re not comfortable with perlregexes. Also, asyou probably guessed, Gitolite prefixes refs/heads/ asa syntactic convenience if the refex does not begin withrefs/. Animportant feature of the config file’s syntax is that all the rules for arepository need not be in one place. You can keep all the common stufftogether, like the rules for alloss_repos shown above, then add specific rulesfor specific cases later on, like so: repo gitolite     RW+                     = sitaram That rulewill just get added to the ruleset for the gitolite repository. At thispoint you might be wondering how the access control rules are actually applied,so let’s go over that briefly. There aretwo levels of access control in gitolite. The first is at the repository level;if you have read (or write) access to**any** ref in the repository,then you have read (or write) access to the repository. The secondlevel, applicable only to “write” access, is by branch or tag within arepository. The username, the access being attempted (W or+),and the refname being updated are known. The access rules are checked in orderof appearance in the config file, looking for a match for this combination (butremember that the refname is regex-matched, not merely string-matched). If amatch is found, the push succeeds. A fallthrough results in access beingdenied. **Advanced Access Control with “deny” rules** So far,we’ve only seen permissions to be one or R, RW,orRW+. However, gitolite allows another permission: -,standing for “deny”. This gives you a lot more power, at the expense of somecomplexity, because now fallthrough is not the**only** way for accessto be denied, so the **order of the rules now matters**! Let ussay, in the situation above, we want engineers to be able to rewind any branch**except** masterand integ. Here’s how to do that: ~~~     RW  master integ    = @engineers     -   master integ    = @engineers     RW+                 = @engineers ~~~ Again, yousimply follow the rules top down until you hit a match for your access mode, ora deny. Non-rewind push to master or integ is allowed by the first rule. Arewind push to those refs does not match the first rule, drops down to thesecond, and is therefore denied. Any push (rewind or non-rewind) to refs otherthan master or integ won’t match the first two rules anyway, and the third ruleallows it. **Restricting pushes by files changed** Inaddition to restricting what branches a user can push changes to, you can alsorestrict what files they are allowed to touch. For example, perhaps theMakefile (or some other program) is really not supposed to be changed by justanyone, because a lot of things depend on it or would break if the changes arenot done **just right**. You can tell gitolite: ~~~ repo foo     RW                  =   @junior_devs @senior_devs       RW  NAME/           =  @senior_devs     -   NAME/Makefile   =  @junior_devs     RW  NAME/           =  @junior_devs Thispowerful feature is documented in conf/example.conf. ~~~ **Personal Branches** Gitolitealso has a feature called “personal branches” (or rather, “personal branchnamespace”) that can be very useful in a corporate environment. A lot ofcode exchange in the git world happens by “please pull” requests. In acorporate environment, however, unauthenticated access is a no-no, and adeveloper workstation cannot do authentication, so you have to push to thecentral server and ask someone to pull from there. This wouldnormally cause the same branch name clutter as in a centralised VCS, plussetting up permissions for this becomes a chore for the admin. Gitolitelets you define a “personal” or “scratch” namespace prefix for each developer(for example,refs/personal/ /* ); see the “personal branches” sectionin doc/3-faq-tips-etc.mkd for details. **“Wildcard” repositories** Gitoliteallows you to specify repositories with wildcards (actually perl regexes),like, for exampleassignments/s[0-9][0-9]/a[0-9][0-9], to pick a random example. This is a**very** powerfulfeature, which has to be enabled by setting$GL_WILDREPOS = 1; in the rc file. It allows you to assign a newpermission mode (”C”) which allows users to create repositories based on suchwild cards, automatically assigns ownership to the specific user who createdit, allows him/her to hand out R and RW permissions to other users tocollaborate, etc. This feature is documented indoc/4-wildcard-repositories.mkd. **Other Features** We’llround off this discussion with a sampling of other features, all of which, andmany more, are described in great detail in the “faqs, tips, etc” and otherdocuments. **Logging**: Gitolite logs all successful accesses. Ifyou were somewhat relaxed about giving people rewind permissions (RW+) and some kid blew away “master”, the log file is a lifesaver, in terms of easily and quickly finding the SHA that got hosed. **Gitoutside normal PATH**: Oneextremely useful convenience feature in gitolite is support for git installedoutside the normal$PATH (this is more common than you think;some corporate environments or even some hosting providers refuse to installthings system-wide and you end up putting them in your own directories).Normally, you are forced to make the**client-side** git aware of thisnon-standard location of the git binaries in some way. With gitolite, justchoose a verbose install and set$GIT_PATH in the “rc” files. No client-sidechanges are required after that :-) **Accessrights reporting**: Anotherconvenient feature is what happens when you try and just ssh to the server.Gitolite shows you what repos you have access to, and what that access may be.Here’s an example:     hello sitaram,the gitolite version here is v1.5.4-19-ga3397d4     the gitoliteconfig gives you the following access:          R     anu-wsd          R     entrans          R  W git-notes          R  W gitolite          R  W gitolite-admin          R     indic_web_input          R     shreelipi_converter **Delegation**: For really large installations, you candelegate responsibility for groups of repositories to various people and havethem manage those pieces independently. This reduces the load on the mainadmin, and makes him less of a bottleneck. This feature has its owndocumentation file in the doc/ directory. **Gitwebsupport**: Gitolite supportsgitweb in several ways. You can specify which repos are visible via gitweb. Youcan set the “owner” and “description” for gitweb from the gitolite config file.Gitweb has a mechanism for you to implement access control based on HTTPauthentication, so you can make it use the “compiled” config file that gitoliteproduces, which means the same access control rules (for read access) apply forgitweb and gitolite. **Mirroring**: Gitolite can help you maintain multiplemirrors, and switch between them easily if the primary server goes down.   **4.9  Git守护进程** 对于提供公共的,非授权的只读访问,我们可以抛弃 HTTP协议,改用 Git 自己的协议,这主要是出于性能和速度的考虑。Git协议远比 HTTP 协议高效,因而访问速度也快,所以它能节省很多用户的时间。 重申一下,这一点只适用于非授权的只读访问。如果建在防火墙之外的服务器上,那么它所提供的服务应该只是那些公开的只读项目。如果是在防火墙之内的服务器上,可用于支撑大量参与人员或自动系统(用于持续集成或编译的主机)只读访问的项目,这样可以省去逐一配置 SSH 公钥的麻烦。 但不管哪种情形,Git协议的配置设定都很简单。基本上,只要以守护进程的形式运行该命令即可: git daemon --reuseaddr --base-path=/opt/git/ /opt/git/ 这里的 --reuseaddr 选项表示在重启服务前,不等之前的连接超时就立即重启。而 --base-path 选项则允许克隆项目时不必给出完整路径。最后面的路径告诉 Git 守护进程允许开放给用户访问的仓库目录。假如有防火墙,则需要为该主机的 9418端口设置为允许通信。 以守护进程的形式运行该进程的方法有很多,但主要还得看用的是什么操作系统。在 Ubuntu主机上,可以用 Upstart脚本达成。编辑该文件: /etc/event.d/local-git-daemon 加入以下内容: ~~~ start on startup stop on shutdown exec /usr/bin/git daemon \     --user=git--group=git \     --reuseaddr \    --base-path=/opt/git/ \     /opt/git/ respawn ~~~ 出于安全考虑,强烈建议用一个对仓库只有读取权限的用户身份来运行该进程 —只需要简单地新建一个名为 git-ro 的用户(译注:新建用户默认对仓库文件不具备写权限,但这取决于仓库目录的权限设定。务必确认git-ro 对仓库只能读不能写。),并用它的身份来启动进程。这里为了简化,后面我们还是用之前运行 Gitosis 的用户 ‘git’。 这样一来,当你重启计算机时,Git进程也会自动启动。要是进程意外退出或者被杀掉,也会自行重启。在设置完成后,不重启计算机就启动该守护进程,可以运行: initctl start local-git-daemon 而在其他操作系统上,可以用 xinetd,或者 sysvinit 系统的脚本,或者其他类似的脚本 — 只要能让那个命令变为守护进程并可监控。 接下来,我们必须告诉 Gitosis哪些仓库允许通过 Git协议进行匿名只读访问。如果每个仓库都设有各自的段落,可以分别指定是否允许 Git进程开放给用户匿名读取。比如允许通过 Git协议访问 iphone_project,可以把下面两行加到gitosis.conf 文件的末尾: ~~~ [repo iphone_project] daemon = yes ~~~ 在提交和推送完成后,运行中的 Git守护进程就会响应来自 9418端口对该项目的访问请求。 如果不考虑 Gitosis,单单起了 Git守护进程的话,就必须到每一个允许匿名只读访问的仓库目录内,创建一个特殊名称的空文件作为标志: ~~~ $ cd /path/to/project.git $ touch git-daemon-export-ok ~~~ 该文件的存在,表明允许 Git守护进程开放对该项目的匿名只读访问。 Gitosis 还能设定哪些项目允许放在GitWeb上显示。先打开 GitWeb的配置文件 /etc/gitweb.conf,添加以下四行: ~~~ $projects_list ="/home/git/gitosis/projects.list"; $projectroot = "/home/git/repositories"; $export_ok = "git-daemon-export-ok"; @git_base_url_list = ('git://gitserver'); ~~~ 接下来,只要配置各个项目在 Gitosis中的 gitweb 参数,便能达成是否允许GitWeb用户浏览该项目。比如,要让iphone_project项目在 GitWeb 里出现,把repo 的设定改成下面的样子: ~~~ [repo iphone_project] daemon = yes gitweb = yes ~~~ 在提交并推送过之后,GitWeb就会自动开始显示 iphone_project项目的细节和历史。 **4.10  Git托管服务** 如果不想经历自己架设 Git服务器的麻烦,网络上有几个专业的仓库托管服务可供选择。这样做有几大优点:托管账户的建立通常比较省时,方便项目的启动,而且不涉及服务器的维护和监控。即使内部创建并运行着自己的服务器,同时为开源项目提供一个公共托管站点还是有好处的 —让开源社区更方便地找到该项目,并给予帮助。 目前,可供选择的托管服务数量繁多,各有利弊。在 Git官方 wiki 上的 Githosting 页面有一个最新的托管服务列表: http://git.or.cz/gitwiki/GitHosting 由于本书无法全部一一介绍,而本人(译注:指本书作者 Scott Chacon。)刚好在其中一家公司工作,所以接下来我们将会介绍如何在 GitHub上建立新账户并启动项目。至于其他托管服务大体也是这么一个过程,基本的想法都是差不多的。 GitHub 是目前为止最大的开源Git 托管服务,并且还是少数同时提供公共代码和私有代码托管服务的站点之一,所以你可以在上面同时保存开源和商业代码。事实上,本书就是放在 GitHub上合作编著的。(译注:本书的翻译也是放在 GitHub上广泛协作的。) **GitHub** GitHub 和大多数的代码托管站点在处理项目命名空间的方式上略有不同。GitHub的设计更侧重于用户,而不是完全基于项目。也就是说,如果我在 GitHub上托管一个名为grit 的项目的话,它的地址不会是 github.com/grit,而是按在用户底下github.com/shacon/grit (译注:本书作者 Scott Chacon 在 GitHub上的用户名是shacon。)。不存在所谓某个项目的官方版本,所以假如第一作者放弃了某个项目,它可以无缝转移到其它用户的名下。 GitHub 同时也是一个向使用私有仓库的用户收取费用的商业公司,但任何人都可以方便快捷地申请到一个免费账户,并在上面托管数量不限的开源项目。接下来我们快速介绍一下 GitHub的基本使用。 **建立新账户** 首先注册一个免费账户。访问 Pricing and Signup页面 http://github.com/plans 并点击 Free acount里的 Sign Up 按钮(见图 4-2),进入注册页面。 图 4-2.GitHub服务简介页面 选择一个系统中尚未使用的用户名,提供一个与之相关联的电邮地址,并输入密码(见图 4-3): 图 4-3.GitHub用户注册表单 如果方便,现在就可以提供你的 SSH公钥。我们在前文的”小型安装”一节介绍过生成新公钥的方法。把新生成的公钥复制粘贴到 SSH Public Key文本框中即可。要是对生成公钥的步骤不太清楚,也可以点击 “explain ssh keys”链接,会显示各个主流操作系统上完成该步骤的介绍。点击 “I agree,sign me up”按钮完成用户注册,并转到该用户的 dashboard页面(见图 4-4): 图 4-4.GitHub的用户面板 接下来就可以建立新仓库了。 **建立新仓库** 点击用户面板上仓库旁边的 “create a new one”链接,显示 Create a New Repository的表单(见图4-5): 图 4-5. 在 GitHub 上建立新仓库 当然,项目名称是必不可少的,此外也可以适当描述一下项目的情况或者给出官方站点的地址。然后点击 “Create Repository”按钮,新仓库就建立起来了(见图4-6): 图 4-6.GitHub上各个项目的概要信息 由于尚未提交代码,点击项目主页后 GitHub会显示一个简要的指南,告诉你如何新建一个项目并推送上来,如何从现有项目推送,以及如何从一个公共的 Subversion仓库导入项目(见图 4-7): 图 4-7. 新仓库指南 该指南和本书前文介绍的类似,对于新的项目,需要先在本地初始化为 Git项目,添加要管理的文件并作首次提交: ~~~ $ git init $ git add . $ git commit -m 'initial commit' ~~~ 然后在这个本地仓库内把 GitHub添加为远程仓库,并推送 master分支上来: ~~~ $ git remote add origingit@github.com:testinguser/iphone_project.git $ git push origin master ~~~ 现在该项目就托管在 GitHub上了。你可以把它的 URL分享给每位对此项目感兴趣的人。本例的 URL是http://github.com/testinguser/iphone_project。而在项目页面的摘要部分,你会发现有两个 Git URL地址(见图 4-8): 图 4-8. 项目摘要中的公共URL 和私有URL PublicClone URL是一个公开的,只读的 Git URL,任何人都可以通过它克隆该项目。可以随意散播这个 URL,比如发布到个人网站之类的地方等等。 Your CloneURL是一个基于SSH 协议的可读可写URL,只有使用与上传的SSH公钥对应的密钥来连接时,才能通过它进行读写操作。其他用户访问该项目页面时只能看到之前那个公共的 URL,看不到这个私有的 URL。 **从 Subversion导入项目** 如果想把某个公共 Subversion项目导入 Git,GitHub可以帮忙。在指南的最后有一个指向导入 Subversion页面的链接。点击它会看到一个表单,包含有关导入流程的信息以及一个用来粘贴公共 Subversion项目连接的文本框(见图 4-9): 图 4-9.Subversion导入界面 如果项目很大,采用非标准结构,或者是私有的,那就无法借助该工具实现导入。到第 7章,我们会介绍如何手工导入复杂工程的具体方法。 **添加协作开发者** 现在把团队里的其他人也加进来。如果 John,Josie和 Jessica 都在 GitHub 注册了账户,要赋予他们对该仓库的推送权限,可以把他们加为项目协作者。这样他们就可以通过各自的公钥访问我的这个仓库了。 点击项目页面上方的 “edit”按钮或者顶部的 Admin标签,进入该项目的管理页面(见图 4-10): 图 4-10.GitHub的项目管理页面 为了给另一个用户添加项目的写权限,点击 “Add another collaborator”链接,出现一个用于输入用户名的表单。在输入的同时,它会自动跳出一个符合条件的候选名单。找到正确用户名之后,点 Add按钮,把该用户设为项目协作者(见图 4-11): 图 4-11. 为项目添加协作者 添加完协作者之后,就可以在 Repository Collaborators区域看到他们的名单(见图 4-12): 图 4-12. 项目协作者名单 如果要取消某人的访问权,点击 “revoke”即可取消他的推送权限。对于将来的项目,你可以从现有项目复制协作者名单,或者直接借用协作者群组。 **项目页面** 在推送或从 Subversion导入项目之后,你会看到一个类似图 4-13的项目主页: 图 4-13.GitHub上的项目主页 别人访问你的项目时看到的就是这个页面。它有若干导航标签,Commits标签用于显示提交历史,最新的提交位于最上方,这和 git log 命令的输出类似。Network标签展示所有派生了该项目并做出贡献的用户的关系图谱。Downloads标签允许你上传项目的二进制文件,提供下载该项目各个版本的 tar/zip包。Wiki 标签提供了一个用于撰写文档或其他项目相关信息的 wiki站点。Graphs 标签包含了一些可视化的项目信息与数据。默认打开的 Source标签页面,则列出了该项目的目录结构和概要信息,并在下方自动展示 README文件的内容(如果该文件存在的话),此外还会显示最近一次提交的相关信息。 **派生项目** 如果要为一个自己没有推送权限的项目贡献代码,GitHub鼓励使用派生(fork)。到那个感兴趣的项目主页上,点击页面上方的 “fork”按钮,GitHub 就会为你复制一份该项目的副本到你的仓库中,这样你就可以向自己的这个副本推送数据了。 采取这种办法的好处是,项目拥有者不必忙于应付赋予他人推送权限的工作。随便谁都可以通过派生得到一个项目副本并在其中展开工作,事后只需要项目维护者将这些副本仓库加为远程仓库,然后提取更新合并即可。 要派生一个项目,到原始项目的页面(本例中是 mojombo/chronic)点击“fork”按钮(见图4-14): 图 4-14. 点击 “fork” 按钮获得任意项目的可写副本 几秒钟之后,你将进入新建的项目页面,会显示该项目派生自哪一个项目(见图 4-15): 图 4-15. 派生后得到的项目副本 **GitHub小结** 关于 GitHub就先介绍这么多,能够快速达成这些事情非常重要(译注:门槛的降低和完成基本任务的简单高效,对于推动开源项目的协作发展有着举足轻重的意义。)。短短几分钟内,你就能创建一个新账户,添加一个项目并开始推送。如果项目是开源的,整个庞大的开发者社区都可以立即访问它,提供各式各样的帮助和贡献。最起码,这也是一种 Git 新手立即体验尝试 Git的捷径。 **4.11 小结** 我们讨论并介绍了一些建立远程 Git仓库的方法,接下来你可以通过这些仓库同他人分享或合作。 运行自己的服务器意味着更多的控制权以及在防火墙内部操作的可能性,当然这样的服务器通常需要投入一定的时间精力来架设维护。如果直接托管,虽然能免去这部分工作,但有时出于安全或版权的考虑,有些公司禁止将商业代码托管到第三方服务商。 所以究竟采取哪种方案,并不是个难以取舍的问题,或者其一,或者相互配合,哪种合适就用哪种。  
';