Linux 安装 Git 服务器

Linux 安装 Git 服务器

服务器 : ecs-57c4-0004 (FlexusX 8vCPU/16GiB) | OS : Ubuntu 24.04 | Git : 2.43.0

IP : 192.168.0.114 (内网) / 120.46.167.216 (公网) | 日期: 2026-06-17


目录

  1. [Git 简介与托管平台对比](#Git 简介与托管平台对比)
  2. [Git 核心操作](#Git 核心操作)
  3. [搭建 Git 服务器](#搭建 Git 服务器)
  4. 代码推送与多人协作验证

1. Git 简介与托管平台对比

1.1 版本控制演进

复制代码
集中式 (SVN):                         分布式 (Git):
[Server: repo]                        [Server: repo]
  |       |       |                      |              |
[ClientA][ClientB][ClientC]          [ClientA:      [ClientB:
                                      full-repo]     full-repo]

 离线不可提交                           每个 clone 都是完整镜像
 单点故障 = 历史丢失                    即使服务器故障,任意 clone 可恢复

1.2 常见托管平台

平台 类型 资源需求 特点
Bare Git + SSH 原生自建 <50MB 零依赖,最轻量。适合小团队
Gitea 轻量自建 <2GB Go 编写,Web UI,内置 CI/CD
Gogs 轻量自建 <500MB Gitea 前身,安装包 <30MB
GitLab CE 重型自建 4GB+ 完整 DevOps (CI/CD/Registry/K8s)
Gitolite SSH 层 <10MB Perl 编写,细粒度权限控制
GitHub SaaS --- 全球最大,PR/2FA/Actions
Bitbucket SaaS --- Atlassian 生态,Jira 集成

本次实验选用 Bare Git + SSH 方案 --- 纯命令行,零依赖,是理解 Git 服务端原理的最佳起点。

1.3 Git 核心概念

复制代码
Working Directory (工作区)
     |  git add            ← 暂存变更
Staging Area    (暂存区)
     |  git commit          ← 写入本地历史
Local Repo      (本地仓库)
     |  git push            ← 同步到远程
Remote Repo     (远程仓库)

三种状态: Modified → Staged → Committed

1.4 环境信息

复制代码
$ git --version
git version 2.43.0

$ which git
/usr/bin/git

$ hostname
ecs-57c4-0004

2. Git 核心操作

2.1 仓库初始化

bash 复制代码
$ mkdir myproject && cd myproject
$ git init
Initialized empty Git repository in /tmp/myproject/.git/

$ git config user.email "dev@lab.local"
$ git config user.name "Developer"

git init 会在当前目录创建 .git/ 隐藏目录,包含所有版本控制元数据:

复制代码
.git/
├── HEAD            → 指向当前分支
├── config          → 仓库配置
├── description     → 仓库描述
├── hooks/          → 钩子脚本
├── objects/        → 对象存储 (blob/tree/commit)
└── refs/           → 分支和标签引用

2.2 首次提交 (add + commit)

bash 复制代码
$ cat > hello.py << 'EOF'
def greet(name):
    return f"Hello, {name}!"
EOF

$ git add hello.py           # 添加到暂存区
$ git commit -m "feat: add greeting module"
[master (root-commit) 9bd7460] feat: add greeting module
 1 file changed, 6 insertions(+)
 create mode 100644 hello.py

git add 将工作区变更加入暂存区。git commit 将暂存区内容写入仓库,生成 SHA-1 哈希作为版本 ID。

bash 复制代码
$ git log --oneline
9bd7460 feat: add greeting module

2.3 修改追踪

bash 复制代码
$ git diff                       # 查看工作区 vs 暂存区差异
diff --git a/hello.py b/hello.py
--- a/hello.py
+++ b/hello.py
@@ -4,3 +4,6 @@ def greet(name):

 if __name__ == "__main__":
     print(greet("World"))
+
+def farewell(name):
+    return f"Goodbye, {name}!"

$ git add hello.py && git commit -m "feat: add farewell function"
[master 5ca3894] feat: add farewell function
 1 file changed, 3 insertions(+)

2.4 分支操作

bash 复制代码
$ git branch feature-calc            # 创建分支
$ git checkout feature-calc           # 切换分支
Switched to branch 'feature-calc'

$ cat > calc.py << 'EOF'
def add(a, b): return a + b
def mul(a, b): return a * b
EOF

$ git add calc.py && git commit -m "feat: add calculator module"
[feature-calc 2e49d39] feat: add calculator module

$ git log --oneline --all --graph
* 2e49d39 feat: add calculator module       ← feature-calc
* 5ca3894 feat: add farewell function       ← master
* 9bd7460 feat: add greeting module

2.5 合并分支 (merge)

bash 复制代码
$ git checkout master
$ git merge feature-calc -m "merge: integrate calculator"
Updating 5ca3894..1d4036d
Fast-forward
 .gitignore | 4 ++++
 README.md  | 2 ++
 calc.py    | 2 ++
 3 files changed, 8 insertions(+)

$ git log --oneline --all --graph
* 1d4036d chore: add .gitignore and README
* 2e49d39 feat: add calculator module
* 5ca3894 feat: add farewell function
* 9bd7460 feat: add greeting module

Fast-forward: 当 master 上没有新提交时,Git 直接移动指针而不是创建合并提交,保持历史线性。

2.6 .gitignore

bash 复制代码
$ cat > .gitignore << 'EOF'
__pycache__/
*.pyc
.env
*.log
EOF

3. 搭建 Git 服务器

3.1 架构概览

复制代码
┌──────────────────────────────────────────────────────┐
│                  Git Server (192.168.0.114)          │
│                                                      │
│  /home/git/                                          │
│  ├── .ssh/authorized_keys    ← 客户端公钥            │
│  └── repositories/                                   │
│       └── myproject.git/      ← 裸仓库 (bare repo)   │
│            ├── HEAD                                  │
│            ├── config                                │
│            ├── objects/        ← blob/tree/commit    │
│            └── refs/           ← branches/tags       │
│                                                      │
│  SSH (port 22)                                       │
│   ↑ git-shell (只允许 git 命令)                      │
│   │                                                  │
└───┼──────────────────────────────────────────────────┘
    │
    │  git clone git@192.168.0.114:/home/git/repositories/myproject.git
    │
┌───┴──────────────┐      ┌──────────────────┐
│  Alice (客户端)   │      │  Bob (客户端)     │
│  clone → push     │      │  clone → push     │
└──────────────────┘      └──────────────────┘

3.2 创建专用 git 用户

bash 复制代码
$ sudo useradd -m -s /usr/bin/git-shell git

$ id git
uid=1003(git) gid=1003(git) groups=1003(git)

$ grep git /etc/passwd
git:x:1003:1003::/home/git:/usr/bin/git-shell

git-shell : 限制 Shell,只允许执行 git-receive-packgit-upload-packgit-upload-archive 三个命令。禁止交互式登录,保障安全。

bash 复制代码
$ which git-shell
/usr/bin/git-shell

3.3 创建裸仓库 (--bare)

bash 复制代码
$ sudo mkdir -p /home/git/repositories
$ sudo chown -R git:git /home/git/repositories

$ cd /home/git/repositories
$ sudo -u git git init --bare myproject.git
Initialized empty Git repository in /home/git/repositories/myproject.git/

裸仓库 (--bare) 没有工作目录,只包含 .git 元数据,专用于服务端共享。

3.4 裸仓库内部结构

bash 复制代码
$ ls -la /home/git/repositories/myproject.git/
total 32K
drwxrwxr-x 2 git git 4.0K branches/      # 废弃,现代 Git 不用
-rw-rw-r-- 1 git git   66 config         # 仓库配置
-rw-rw-r-- 1 git git   73 description    # 仓库描述 (GitWeb 用)
-rw-rw-r-- 1 git git   23 HEAD           # 当前活动分支 → ref: refs/heads/master
drwxrwxr-x 2 git git 4.0K hooks/         # 钩子 (pre-receive/post-receive等)
drwxrwxr-x 2 git git 4.0K info/          # 附加信息 (exclude/grafts)
drwxrwxr-x 4 git git 4.0K objects/       # 核心! 所有 Git 对象
drwxrwxr-x 4 git git 4.0K refs/          # 分支/tag 指针

$ cat /home/git/repositories/myproject.git/HEAD
ref: refs/heads/master

objects/ 目录是最核心的存储:

复制代码
objects/
├── 68/          ← 对象按哈希前 2 位分组
│   └── 9a53c... ← blob (文件内容)
├── 37/          ← 压缩存储
│   └── c45b3... ← commit (提交元数据)
├── info/
└── pack/        ← 打包优化

3.5 SSH 密钥认证配置

bash 复制代码
# === 客户端生成密钥对 ===
$ ssh-keygen -t ed25519 -C "dev@lab.local"
# 生成: ~/.ssh/id_ed25519 (私钥) + ~/.ssh/id_ed25519.pub (公钥)

# === 服务端: 添加公钥 ===
$ sudo mkdir -p /home/git/.ssh
$ cat /tmp/user_key.pub >> /home/git/.ssh/authorized_keys
$ sudo chown -R git:git /home/git/.ssh
$ sudo chmod 700 /home/git/.ssh
$ sudo chmod 600 /home/git/.ssh/authorized_keys

$ ls -la /home/git/.ssh/
total 8
drwx------ 2 git git 4096 Jun 17 23:11 .
drwxr-x--- 4 git git 4096 Jun 17 23:11 ..
-rw------- 1 git git  574 Jun 17 23:11 authorized_keys  ← 1 条公钥

3.6 客户端远程添加

bash 复制代码
# 基本格式
git remote add origin git@<server>:/home/git/repositories/myproject.git

# 使用 ~/.ssh/config 别名 (推荐)
$ cat >> ~/.ssh/config << 'EOF'
Host git-server
  HostName 192.168.0.114
  User git
  IdentityFile ~/.ssh/id_rsa_git
  StrictHostKeyChecking no
EOF

# 简化后
git remote add origin git-server:myproject.git

4. 代码推送与多人协作验证

4.1 Alice 克隆仓库

bash 复制代码
$ GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa_git" \
  git clone git@192.168.0.114:/home/git/repositories/myproject.git alice
Cloning into 'alice'...

$ cd alice && ls -la *.py
-rw-r--r-- 1 root root  56 Jun 17 23:12 calc.py
-rw-r--r-- 1 root root 175 Jun 17 23:12 hello.py

4.2 Alice 提交新功能

bash 复制代码
$ cat > app.py << 'EOF'
from hello import greet
from calc import add, mul

def main():
    print(greet("Team"))
    print(f"2 + 3 = {add(2, 3)}")
    print(f"4 * 5 = {mul(4, 5)}")
EOF

$ git add app.py && git commit -m "feat: add main entry point"
[master 37c45b3] feat: add main entry point
 1 file changed, 11 insertions(+)

$ GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa_git" git push origin master
To 192.168.0.114:/home/git/repositories/myproject.git
   689a53c..37c45b3  master -> master

4.3 Bob 克隆并贡献

bash 复制代码
$ GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa_git" \
  git clone git@192.168.0.114:/home/git/repositories/myproject.git bob
Cloning into 'bob'...

$ cat > utils.py << 'EOF'
import time
def timestamp():
    return time.strftime("%Y-%m-%d %H:%M:%S")
def log(msg):
    print(f"[{timestamp()}] {msg}")
EOF

$ git add utils.py && git commit -m "feat: add logging utility"
[master 117634a] feat: add logging utility

$ GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa_git" git push origin master
To 192.168.0.114:/home/git/repositories/myproject.git
   37c45b3..117634a  master -> master

4.4 Alice 拉取 Bob 的更改

bash 复制代码
$ cd alice
$ GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa_git" git pull origin master
From 192.168.0.114:/home/git/repositories/myproject
 * branch            master     -> FETCH_HEAD
Updating 37c45b3..117634a
Fast-forward
 utils.py | 7 +++++++
 1 file changed, 7 insertions(+)

$ git log --oneline --all --graph
* 117634a feat: add logging utility       ← Bob
* 37c45b3 feat: add main entry point     ← Alice
* 689a53c feat: initial project structure

$ ls -la *.py
-rw-r--r-- 1 root root 218 app.py
-rw-r--r-- 1 root root  56 calc.py
-rw-r--r-- 1 root root 175 hello.py
-rw-r--r-- 1 root root 127 utils.py       ← Bob 的文件已同步

4.5 服务器端验证

bash 复制代码
# 查看提交历史
$ sudo -u git git --git-dir=/home/git/repositories/myproject.git log --oneline --all
117634a feat: add logging utility
37c45b3 feat: add main entry point
689a53c feat: initial project structure

# 查看分支
$ sudo -u git git --git-dir=/home/git/repositories/myproject.git branch -a
* master

# 查看所有文件
$ sudo -u git git --git-dir=/home/git/repositories/myproject.git ls-tree --name-only HEAD
.gitignore
README.md
app.py
calc.py
hello.py
utils.py

# 对象统计
$ sudo -u git git --git-dir=/home/git/repositories/myproject.git count-objects -vH
count: 12         ← 12 个 Git 对象
size: 48.00 KiB   ← 原始大小

# 仓库总大小
$ du -sh /home/git/repositories/myproject.git/
220K    /home/git/repositories/myproject.git/

4.6 协作流程总结

复制代码
 Alice                 Git Server                  Bob
   │                       │                        │
   │── clone ─────────────>│                        │
   │<── repo ──────────────│                        │
   │                       │                        │
   │  [编辑 app.py]        │                        │
   │── push (37c45b3) ────>│                        │
   │                       │                        │
   │                       │<── clone ──────────────│
   │                       │── repo ───────────────>│
   │                       │    [编辑 utils.py]     │
   │                       │<── push (117634a) ─────│
   │                       │                        │
   │<── pull (117634a) ────│                        │
   │  [Fast-forward]       │                        │
   │  utils.py 同步        │                        │

5. 进阶话题

5.1 服务端钩子 (Hooks)

bash 复制代码
# post-receive: push 后自动部署
$ cat > /home/git/repositories/myproject.git/hooks/post-receive << 'EOF'
#!/bin/bash
# 推送到生产目录
DEPLOY_DIR="/var/www/myproject"
while read oldrev newrev ref; do
    if [ "$ref" = "refs/heads/master" ]; then
        git --work-tree=$DEPLOY_DIR checkout -f master
        systemctl reload myproject  # 重启服务
        echo "Deployed $newrev to $DEPLOY_DIR"
    fi
done
EOF
$ chmod +x /home/git/repositories/myproject.git/hooks/post-receive

5.2 权限控制

bash 复制代码
# 方案1: 文件系统权限 (最简单)
chmod 750 /home/git/repositories/private-project.git/

# 方案2: Gitolite (细粒度)
# repo myproject
#   RW+     =   alice
#   R       =   bob
#   -       =   @interns

# 方案3: GitLab CE (Web UI 管理)
# 项目 → Settings → Members → 选择角色

5.3 安全加固

bash 复制代码
# 1. SSH 仅允许密钥登录
$ grep -E "PasswordAuthentication|PubkeyAuthentication" /etc/ssh/sshd_config
PubkeyAuthentication yes
PasswordAuthentication no

# 2. 定期清理已离职开发者密钥
$ sudo vim /home/git/.ssh/authorized_keys

# 3. 审计 Git 操作日志
$ sudo grep git /var/log/auth.log | tail -5

5.4 仓库备份

bash 复制代码
# 方案1: 定期 rsync
$ rsync -az /home/git/repositories/ backup-server:/git-backup/

# 方案2: git bundle (单文件打包)
$ cd /home/git/repositories/myproject.git
$ git bundle create myproject.bundle --all

# 方案3: 从任意 clone 恢复
$ git clone --bare git@server:/path/to/repo.git

6. 故障排查

症状 原因 解决
Permission denied (publickey) 密钥未添加或权限不对 chmod 600 ~/.ssh/id_rsa; 检查 authorized_keys
fatal: does not appear to be a git repository 路径错误 确认裸仓库路径:ls /home/git/repositories/
remote: error: insufficient permission git 用户无写权限 chown -R git:git /home/git/repositories/
hint: Updates were rejected 远端有更新的提交 git pull --rebase 再 push
detected dubious ownership git 安全策略 (CVE) git config --global --add safe.directory /path
Permission denied (publickey,password) SSH 密钥未配置或不对 重新 ssh-copy-id 或手动添加公钥
fatal: Not a valid object name master 裸仓库为空 首次需要 git push -u origin master

7. 命令速查表

操作 命令
创建裸仓库 git init --bare /path/to/repo.git
克隆仓库 git clone git@server:/path/to/repo.git
添加文件 git add <file>
提交变更 git commit -m "msg"
推送到远程 git push origin master
拉取更新 git pull origin master
查看历史 git log --oneline --graph --all
查看差异 git diff
创建分支 git branch feature-x
切换分支 git checkout feature-x
合并分支 git merge feature-x
查看远程 git remote -v
添加远程 git remote add origin <url>
查看状态 git status
撤销暂存 git restore --staged <file>
查看提交详情 git show HEAD
创建标签 git tag v1.0.0
查看文件列表 git ls-tree --name-only HEAD

文档版本 : v1.0 | 创建日期 : 2026-06-17

服务器: ecs-57c4-0004 (FlexusX 8vCPU/16GiB)