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
目录
- [Git 简介与托管平台对比](#Git 简介与托管平台对比)
- [Git 核心操作](#Git 核心操作)
- [搭建 Git 服务器](#搭建 Git 服务器)
- 代码推送与多人协作验证
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-pack、git-upload-pack、git-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)