# 用 Gitolite 搭建你自己的私有 Git 服务器


前段时间写了 [用 Caddy 和容器搭建 Git over HTTPS 服务器](../git-server)，因为我有一个更复杂的需求--推送时创建仓库，所以需要一个更灵活的 Git 服务器，于是我决定研究一下 Gitolite。

## 为什么选择 Gitolite？

在众多 Git 权限管理解决方案中，Gitolite 独树一帜。它不像 GitLab 那样是“全家桶”，只专注做权限管理。

- **极致轻量**：它只是一系列 Perl 脚本，无需数据库，无需 Web 服务器，对系统资源消耗极低。
- **安全性**：所有用户通过 SSH 公钥认证，他们无法获得服务器的 shell 访问权限。
- **精细的权限控制**：你可以控制到谁能读写哪个仓库的哪个分支，甚至可以禁止某人强制推送或删除分支。
- **用 Git 管理 Git**：所有的用户、仓库和权限配置都存储在一个名为 `gitolite-admin` 的特殊 Git 仓库中。你只需在自己电脑上 `git push`，就能完成所有管理操作，酷不酷？

准备好了吗？让我们开始吧！

---

## 服务器端初始化

这一部分的操作都在你的**服务器**上进行，而且只需要做一次。

### 创建专属 `git` 用户

出于安全考虑，我们绝不使用 root 用户来运行服务。创建一个专门的 `git` 用户是最佳实践。

```bash
# 创建一个名为 'git' 的用户，并为其创建主目录
sudo useradd -m -s /bin/bash git

# 如果 git 用户已存在则使用以下命令修改其主目录
# sudo mkdir -p /home/git # 如果主目录不存在
# sudo usermod -d /home/git -s /bin/bash git

# （推荐）锁定该用户的密码，强制所有操作都通过 SSH 密钥进行
sudo passwd -l git
```

### 安装 Gitolite 并上传你的管理员公钥

我使用 Archlinux，使用包管理器 `pacman` 安装。其他发行版请自行调整命令。可以参考官方文档 [Quick Install](https://gitolite.com/gitolite/quick_install.html)。

```bash
# 更新你的包数据库并安装 gitolite
sudo pacman -Syu gitolite
```

接下来，你需要将自己设为 Gitolite 的最高管理员。这通过上传你的 SSH 公钥来完成。上传文件的方法有很多, 根据自己的情况选择。下文以上传后路径 `/tmp/admin.pub` 为例。

### 初始化 Gitolite

这是最关键的一步。我们将使用刚刚上传的公钥来启动 Gitolite 服务。

在你的**服务器**上运行：

```bash
# 切换到 git 用户身份
sudo -u git -i

# 使用你的公钥作为管理员，初始化 Gitolite
# 部分发行版使用 gitolite3 而不是 gitolite，请参考官方文档
gitolite setup -pk /tmp/admin.pub

# 看到类似 "Initialized..." 的输出后，退出 git 用户
exit
```

这个命令会自动创建 `~/repositories` 和 `~/.gitolite` 目录，并配置好 `gitolite-admin` 核心管理仓库。最重要的是，它会接管 `~git/.ssh/authorized_keys` 文件。**此后，请勿手动编辑此文件！**

### 清理工作

安装完成后，服务器上的临时公钥文件就不再需要了。

```bash
sudo rm /tmp/admin.pub
```

至此，服务器已经像一台准备就绪的精良机器，静待你的指令。

---

## 在本地管理一切

从现在开始，你将体验到 Gitolite 设计的精妙之处。所有的管理工作，都在你自己的电脑上，通过 Git 命令完成。

### 克隆 `gitolite-admin` 管理仓库

这是你的“中央控制室”。

在你的**本地**上，找个地方克隆它：

```bash
# 将 YOUR_SERVER_IP 替换为你的服务器 IP 地址
git clone git@YOUR_SERVER_IP:gitolite-admin
```

克隆成功后，你会看到这样一个简洁的结构：

```plaintext
gitolite-admin/
├── conf/
│   └── gitolite.conf  # 权限与仓库的定义文件
└── keydir/
    └── admin.pub      # 所有用户的公钥存放处
```

### 实战演练：添加新用户和项目

假设你的团队来了一位新成员 `alice`，你们要开始一个激动人心的新项目 `my-awesome-app`。

1. **获取公钥**：让 `alice` 把她的 SSH 公钥文件（如 `alice.pub`）发给你。
2. **添加用户**：将 `alice.pub` 文件复制到你本地 `gitolite-admin/keydir/` 目录下。
3. **配置权限**：打开 `gitolite-admin/conf/gitolite.conf` 文件，在末尾添加以下内容：

```plaintext
# ... 已有的配置 ...

# 为新项目 my-awesome-app 创建一个仓库定义
repo my-awesome-app
    # RW+ = 读、写、强制推送权限
    # 将这些权限赋予用户 'alice'
    RW+     =   alice

    # R = 只读权限
    # 管理员 'admin' 拥有只读权限（当然你也可以给自己 RW+）
    R       =   admin
```

> **小贴士**：Gitolite 会自动将 `keydir/` 目录下的文件名（去掉 `.pub` 后缀）识别为用户名。

### 提交并推送，让配置生效

是时候施展魔法了。在 `gitolite-admin` 仓库目录中，执行标准的 Git 流程：

```bash
# 将你的所有修改添加到暂存区
git add .

# 提交一个清晰的 commit message
git commit -m "Add user alice and create repo my-awesome-app"

# 推送到服务器！
git push
```

当你按下回车，Gitolite 的钩子脚本会在服务器上被触发，自动创建 `my-awesome-app.git` 裸仓库，并应用你刚刚设定的所有权限规则。整个过程行云流水。

### 通知新伙伴开工

现在，你可以告诉 `alice`，她的新项目已经准备就绪。她只需在自己的电脑上运行：

```bash
git clone git@YOUR_SERVER_IP:my-awesome-app
```

## 其他设置

### 迁移已有仓库

- 将存储库移动到 `/home/git/repositories` 目录下
  - 需确保它们都是裸存储库
  - 所有存储库名称都以 `.git` 结尾
  - 所有文件和目录都由 git 拥有和可写
  - 无论文件从何处获取，都没有留下任何符号链接
- 在 git 身份下运行以下三个命令：

  ```bash
  gitolite compile
  gitolite setup --hooks-only
  gitolite trigger POST_COMPILE
  ```

- 编辑 conf/gitolite.conf 文件，将新的存储库添加到 gitolite.conf 文件中

### 允许用户推送时创建仓库

编辑 conf/gitolite.conf 文件，添加以下内容：

```plaintext
repo    [a-zA-Z0-9].*
    C       =   @all
    RW+     =   CREATOR
```

> **小贴士**: 此处不能直接使用 `.*`，而是需要使用 `[a-zA-Z0-9].*`。
> [参考文档](https://gitolite.com/gitolite/wild.html#regex-pattern-versus-normal-repo)

## 常见问题

### Podman 组权限问题

参考文章：[Podman Rootless 技巧：利用 `--group-add keep-groups` 解决卷组权限难题](../podman-group-share)


---

> 作者: Nite  
> URL: https://www.nite07.com/zh-cn/posts/gitolite/  

