Ceiling

Linux 创建 Git 远程仓储库

在日常开发中,把代码托管到 GitHub、Gitee 等公网平台已经很普遍,但在内网环境、个人服务器或需要代码私有的场景下,自己在一台 Linux 服务器上搭一个 Git 远程仓库依然非常实用。Git 原生就支持通过 SSH 协议读写仓库,不需要任何额外服务,几分钟就能搞定。

本文整理两种最常见的方案:

  • 裸仓库(bare repo)+ SSH:轻量、零依赖,适合个人或小团队;
  • GitLab:带 Web 界面、权限管理、CI 的完整平台,适合团队长期使用。

一、为什么远程仓库要用「裸仓库」

先解释一个概念。普通 git init 出来的仓库带有工作区(能看到签出的文件),而远程仓库应该用 git init --bare 创建,称为裸仓库:

  • 裸仓库只有 .git 目录里的那些内容(objects、refs、HEAD 等),没有工作区文件;
  • 如果往非裸仓库 push,Git 会拒绝并提示 refusing to update checked out branch,因为推送会导致工作区和分支状态不一致;
  • 裸仓库的目录名习惯上以 .git 结尾,如 my-project.git,一眼就能识别。

所以远程仓库的标准姿势是:服务器上放裸仓库,开发者本地 clone / push。

二、方案一:裸仓库 + SSH(推荐)

这是最简单也最常用的方案,本质是:服务器上建一个用户,裸仓库放在这个用户目录下,开发者通过 SSH 读写。

1. 安装 Git

# Debian / Ubuntu
sudo apt update
sudo apt install git

# CentOS / RHEL
sudo yum install git

安装完用 git --version 确认。

2. 创建专用用户 git

不建议用 root 或已有用户托管仓库,单独建一个 git 用户更干净,也能顺便限制它只能做 Git 操作:

# -m 创建家目录,-s 指定 shell(下一步会换成 git-shell)
sudo adduser git

设置密码(如果要用密码登录),或者直接跳过、只用密钥登录:

sudo passwd git

3. 初始化裸仓库

切到 git 用户,创建仓库:

sudo su git
cd ~
mkdir my-project.git
cd my-project.git
git init --bare

也可以一步到位:

git init --bare ~/my-project.git

完成后 /home/git/my-project.git 目录里能看到 HEAD、objects、refs 等,没有工作区文件,说明创建成功。

4. 配置 SSH 登录

方式 A:密钥登录(推荐)

在每台开发机上把公钥放到服务器的 git 用户下。最方便的做法是开发机上有 ssh-copy-id(Linux/macOS 自带):

# 开发机上执行,如果还没有密钥先 ssh-keygen -t ed25519
ssh-copy-id git@服务器IP

没有 ssh-copy-id 的话(比如 Windows PowerShell),手动追加:

# 开发机先查看公钥内容
cat ~/.ssh/id_ed25519.pub

# 服务器上将内容追加到 authorized_keys
sudo su git
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "ssh-ed25519 AAAA... 备注" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

注意 .ssh 目录权限必须是 700、authorized_keys 必须是 600,否则 SSH 会拒绝使用密钥。

方式 B:密码登录

确认 /etc/ssh/sshd_config 中 PasswordAuthentication yes,改完重启 sshd:

sudo systemctl restart sshd

5. 本地使用远程仓库

克隆已有仓库:
git clone git@服务器IP:~/my-project.git

用户名是 git,冒号后面是服务器上仓库的路径,相对路径基于 git 用户家目录,所以 ~/my-project.git 和 my-project.git 等价。

把本地已有项目推上去:
cd my-project
git remote add origin git@服务器IP:my-project.git
git push -u origin master

之后日常 git pull / git push 和用 GitHub 没有区别。也可以配置域名和 SSH 别名,写成 git@git.example.com:my-project.git,甚至用 ssh -p 2222 自定义端口。

6. (可选)把 git 用户锁进 git-shell

默认情况下,拿到 git 用户 SSH 权限的人可以登录进服务器 shell,这在多人共享仓库时是个安全隐患。Linux 自带 git-shell,限制该用户只能执行 git-receive-pack(push)、git-upload-pack(pull)、git-upload-archive 三种操作:

# 确认系统里有 git-shell
cat /etc/shells
# 若没有 git-shell 这一行,手动加进去
echo /usr/bin/git-shell | sudo tee -a /etc/shells

# 修改 git 用户的登录 shell
sudo usermod -s /usr/bin/git-shell git

配置后,有人试图 ssh git@服务器IP 登录会看到提示:

fatal: This operation must be done over a binary transport

而正常的 clone / push 不受影响。

7. (可选)配合 Nginx 实现 HTTP 匿名只读

裸仓库 + SSH 只能走 SSH 协议。想让别人像访问 GitHub Pages 一样用 git clone http://... 只读克隆,可以装 git-http-backend(随 git 一起提供),用 Nginx 反代到它。这里给出核心配置:

location /git/ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
    fastcgi_param GIT_PROJECT_ROOT /home/git;
    fastcgi_param GIT_HTTP_EXPORT_ALL;
    # 只读:禁止 push
    fastcgi_param REMOTE_USER $remote_user;
    fastcgi_pass unix:/var/run/fcgiwrap.socket;
}

个人服务器上如果只需要自己推拉,这一步可以省略,SSH 方案已经够用。

三、方案二:GitLab(带 Web 界面的完整平台)

如果需要 Web 端管理仓库、分配权限、Review 代码、跑 CI,可以部署 GitLab CE(社区版,免费)。推荐用 Docker 方式部署,省事且方便升级:

# 拉起 GitLab CE,映射 SSH 端口 2222 避免和宿主 sshd 冲突
sudo docker run -d \
  --name gitlab \
  --restart always \
  -p 80:80 -p 443:443 -p 2222:22 \
  -v /srv/gitlab/config:/etc/gitlab \
  -v /srv/gitlab/logs:/var/log/gitlab \
  -v /srv/gitlab/data:/var/opt/gitlab \
  gitlab/gitlab-ce:latest

几点注意:

  • GitLab 比较吃内存,建议服务器至少 4GB 内存;
  • 首次启动初始化需要几分钟,用 docker logs -f gitlab 观察;
  • 初始 root 密码在容器内 /etc/gitlab/initial_root_password 文件中,登录后尽快修改;
  • 克隆地址形如 ssh://git@服务器IP:2222/group/project.git,要在 Web 界面「Preferences → SSH Keys」里添加公钥。

后续创建项目和推送流程就和管理 GitHub 一样:Web 上新建项目 → 本地 git remote add origin <地址> → git push。

四、常见问题排查

现象原因与解决
Permission denied (publickey)公钥没加到 git 用户的 authorized_keys,或 .ssh / authorized_keys 权限不对(700 / 600)
push 时报 refusing to update checked out branch远程目录不是裸仓库,用 git init --bare 重建
能 SSH 登录但 clone 报 repository does not exist仓库路径写错,冒号后面相对 git 用户家目录,先用 ls 确认路径
换了 git-shell 后 push 失败/etc/shells 里必须登记 git-shell 的路径,否则 sshd 认为该 shell 非法
多用户共用仓库权限混乱让大家都用 git 用户 + 各自的 SSH 公钥 push,文件天然同属 git;或改用方案二做权限管理

五、总结

  • 个人或小团队内网协作,裸仓库 + SSH 是最省心的方案:git init --bare 建仓库,公钥进 authorized_keys,本地直接 clone/push;
  • 加上 git-shell 可以把托管用户锁成只能跑 Git 操作,更安全;
  • 需要 Web 界面、权限体系、代码审查和 CI 时,再上 GitLab,Docker 一条命令即可部署。

两种方案不冲突,很多团队的演进路径就是先用裸仓库顶着,成员多了再迁到 GitLab——反正 Git 仓库本身可以无损迁移。