Git on the Server


远程代码库通常只是一个“纯仓库”（bare repository）——一个没有当前工作目录的仓库；仓库里仅仅是 Git 的数据，也就是 .git 目录里的内容。

## 协议

Git 可使用四种协议来传输数据：

1. 本地传输
2. SSH 协议
3. Git 协议
4. HTTP 协议

### 本地传输

远程仓库在该协议中就是硬盘上的另外一个目录，常见于所有成员拥有一个共享文件系统（如 NFS）的访问权等相似情形；就像这样：

```bash
$ git clone /path/to/git/project.git
```

要添加一个本地仓库指向 Git 项目，可以这样：

```bash
$ git remote add local /path/to/git/project.git
```

#### 优点

1. 简单，同时保留了现有文件的权限和网络访问权限

2. 本地获取方便，比如说：`$ git pull /home/john/project` 通常会比让 john 推送到服务器，然后你再从服务器获取要简单、快捷得多

#### 缺点

远程操作困难，缺乏网络连接的便利性；如果使用远程硬盘挂载，又达不到网络连接的速度和效率

### SSH 协议

Git 最常使用的传输协议，也是唯一一个同时便于读和写操作的网络协议。HTTP 和 Git 协议**通常**都是只读的，因此虽然二者对大多数人都可用，但执行写操作时还是需要 SSH。同时，SSH 也是一个验证授权的网络协议。

```bash
$ git clone ssh://user@server:project.git
```

或者不指明协议（Git 默认使用 SSH）：

```bash
$ git clone user@server:project.git
```

甚至可以不指明用户，此时 Git 会使用你当前在本地登录的用户

#### 优点

1. 想拥有对网络仓库的写权限，基本上不可能不使用 SSH

2. SSH 本身架设很简单——其守护进程很常见

3. 通过 SSH 访问是安全的，所有数据传输都是加密和授权的

4. SSH 很高效，会在传输之前尽可能的压缩数据

#### 缺点

SSH 唯一的限制就是不能匿名访问，即使只是读取数据，也必须通过 SSH 的验证才能访问仓库。

如果是私有项目，那么 SSH 可能是你唯一需要使用的协议。

### Git 协议

Git 协议有一个包含在 Git 软件包中的守护进程提供，它会监听一个类似 SSH 服务的特定端口（9418），而无需任何授权。

使用 Git 协议运营仓库，你需要创建 git-export-daemon-ok 文件——这是必要条件——除此之外没什么安全措施，要么所有人都能克隆，要么都不能。

所以 Git 协议通常不会用来进行推送，虽然你可以允许推送操作，但是没有授权机制将导致所有知道地址的人都可以推送。

#### 优点

1. Git 协议是目前最快的协议

2. 适合大访问量的公共项目，或是不需要对读取进行授权的大项目

3. 传输机制和 SSH 相同（省去了加密和授权开销）

#### 缺点

1. 缺少授权机制，一般不会用作唯一的访问协议

2. 假设困难，需要单独守护进程，需要定制

3. 依赖 9418 端口的开放

### HTTP/HTTPs 协议

使用 HTTP 或 HTTPs 协议的优美之处在于其架设的简便性，只需要把 Git 的纯仓库文件（bare repository）放在 HTTP 的文件目录下，再配置一个特定的 post-updare 挂钩（hook）就搞定了。例如：

```bash
$ 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
```

post-update hook 会默认运行 `git update-server-info` 来确保通过 HTTP 的获取和克隆工作正常；这条命令会在你用 SSH 向仓库推送时照常运行。之后可以用如下命令克隆仓库：

```bash
$ git clone http://youtaddress.com/gitproject.git
```

通过 HTTP 来推送也是可以的，不过会牵涉到 WebDAV 的设置，相对复杂一些，可以参考[这篇文章](http://www.kernel.org/pub/software/scm/git/docs/howto/setup-git-server-over-http.txt)

#### 优点

1. 易于架设，并且不占用过多服务器资源

2. 可使用 SSL 签名验证（如：Github）

3. 最常用的协议，基本不会受到阻碍（如：防火墙）

#### 缺点

相对客户端效率较低，克隆和下载会花费更多时间，传输的开销也比其他协议更大

## 部署 Git Server

假设 Git Server，需要把一个现存的仓库导出为新的纯仓库（bare repository），即不包含当前工作目录的仓库，该仓库以 .git 结尾。

```bash
$ git clone --bare project project.git
Initialized empty Git repository in /path/to/project.git/
```

这个操作生成的目录几乎是空的（没有项目文件），除了一个 .git 目录数据的副本。这个副本和原来的 .git 目录非常相似，克隆操作得到的文件也是一样的。

```bash
$ git clone user@gitserver:/path/to/project.git
```

如果一个 SSH 用户对 `/path/to/project.git` 具有写权限，他就自动拥有推送权限。此时运行以下命令就会自动对该仓库加入可写的组权限：

```bash
$ ssh user@gitserver
$ cd path/to/project.git
$ git init --shared --bare
```

### 控制 SSH 链接

如果需要所有人都对仓库有写权限，那就需要使用 SSH 连接了。

虽然我们可以在服务器上给每一个人建立一个账户，但这样显然太麻烦了，也不方便管理。

更好的办法是建立一个公用的 git 账户，然后让每一个需要写权限的人发送一个 SSH 公钥保存在 git 账户的 `~/.ssh/authorized_keys` 里面，这样一来所有人将通过 git 账户访问主机，而且不会影响提交的数据——访问主机的身份不会影响提交时的身份。

过程如下：

```bash
$ sudo adduser git  	# 创建 git 用户
$ su git				# 切换到 git 用户
$ cd ~					# 前往其主目录
$ mkdir .ssh			# 创建 .ssh 目录
```

之后，请你的同事把他们的公钥文件发送给你，然后添加到 authorized_keys 文件中：

```bash
$ cat id_rsa.john.pub >> ~/.ssh/authorized_keys
```

生成公钥的方法可以参见[Github 提供的帮助文档](https://help.github.com/articles/generating-ssh-keys)

现在可以初始化空仓库了，别忘了 --bare 选项：

```bash
$ cd ~/git				# 假设仓库都放在这里，没有就创建一个
$ mkdir project.git
$ cd project.git
$ git --bare init
```