Git Hooks 实现 CI/CD 进阶实践(续)
本文是基础篇的进阶续集,假设你已有一个能工作的 Git Hooks 自动部署系统。以下四个功能可显著提升其可靠性与实用性。
目录
- [1. 分支控制:只部署 main 分支](#1. 分支控制:只部署 main 分支)
- [2. 版本回滚:一键回退到历史版本](#2. 版本回滚:一键回退到历史版本)
- [3. 部署日志:记录每次部署行为](#3. 部署日志:记录每次部署行为)
- [4. 多环境隔离:支持 dev 与 prod](#4. 多环境隔离:支持 dev 与 prod)
1. 分支控制:只部署 main 分支
默认情况下,任何分支推送都会触发部署。为避免误操作,我们限制仅 main 分支生效。
修改 /opt/git/myapp.git/hooks/post-receive,在 read oldrev newrev refname 后加入:
if [ "$refname" != "refs/heads/main" ]; then
echo "SKIP: $refname is not main branch"
exit 0
fi
验证:
cd ~/myapp-dev
git checkout -b dev
echo '<h1>Dev Test</h1>' > index.html
git add . && git commit -m "dev test"
git push origin dev
输出应包含 SKIP,且网页内容不变。

没变化,仍然是main分支的内容

2. 版本回滚:一键回退到历史版本
当前部署是直接覆盖文件,无法回滚。我们改为"版本目录 + 软链接"模式。
调整部署结构:
- 历史版本存于 /var/www/releases/
- 线上站点 /var/www/html/myapp 是指向当前版本的软链接
修改 post-receive:
DEPLOY_BASE="/var/www/releases"
CURRENT_LINK="/var/www/html/myapp"
VERSION_DIR=" D E P L O Y B A S E / DEPLOY_BASE/ DEPLOYBASE/newrev"
mkdir -p "$VERSION_DIR"
[root@python myapp-dev]# git checkout main
已经位于 'main'
[root@python myapp-dev]# echo '<h1>Clean Version 1</h1>' > index.html
[root@python myapp-dev]# git add .
[root@python myapp-dev]# git commit -m "clean v1"
[main d82a15e] clean v1
1 file changed, 1 insertion(+), 1 deletion(-)
[root@python myapp-dev]# git push origin main
Counting objects: 5, done.
Writing objects: 100% (3/3), 259 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: DEPLOYED: d82a15e962d7dc75306db913d20014acd4940b31
To /opt/git/myapp.git
4a820e2..d82a15e main -> main
[root@python myapp-dev]# ls /var/www/releases/
d82a15e962d7dc75306db913d20014acd4940b31
[root@python myapp-dev]# cat /var/www/html/myapp/index.html
<h1>Clean Version 1</h1>
[root@python myapp-dev]# echo '<h1>Clean Version 2</h1>' > index.html
[root@python myapp-dev]# git add .
[root@python myapp-dev]# git commit -m "clean v2"
[main f197040] clean v2
1 file changed, 1 insertion(+), 1 deletion(-)
[root@python myapp-dev]# git push origin main
Counting objects: 5, done.
Writing objects: 100% (3/3), 256 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: DEPLOYED: f197040e5fb82a13a5ca61d961de6ccf05e46bf0
To /opt/git/myapp.git
d82a15e..f197040 main -> main
[root@python myapp-dev]# ls /var/www/releases/
d82a15e962d7dc75306db913d20014acd4940b31 f197040e5fb82a13a5ca61d961de6ccf05e46bf0
[root@python myapp-dev]# rm -f /var/www/html/myapp
[root@python myapp-dev]# ln -s /var/www/releases/d82a15e962d7dc75306db913d20014acd4940b31 /var/www/html/myapp
[root@python myapp-dev]# cat /var/www/html/myapp/index.html
<h1>Clean Version 1</h1>
回滚操作
查看历史版本:
ls /var/www/releases/
回滚到指定提交(例如 abc1234):
ln -sfn /var/www/releases/abc1234 /var/www/html/myapp
3. 部署日志:记录每次部署行为
便于审计和排查问题。
在 post-receive 脚本末尾添加:
LOG_FILE="/var/log/myapp-deploy.log"
echo "$(date '+%Y-%m-%d %H:%M:%S') DEPLOY $newrev to $CURRENT_LINK" >> "$LOG_FILE"
查看日志:
tail -f /var/log/myapp-deploy.log
确保日志目录可写:
touch /var/log/myapp-deploy.log
chown apache:apache /var/log/myapp-deploy.log
4. 多环境隔离:支持 dev 与 prod
通过不同分支部署到不同路径。
修改 post-receive 中的部署路径逻辑:
if [ "$refname" = "refs/heads/main" ]; then
TARGET_DIR="/var/www/html/myapp"
elif [ "$refname" = "refs/heads/dev" ]; then
TARGET_DIR="/var/www/html/myapp-dev"
else
echo "SKIP: unsupported branch $refname"
exit 0
fi
后续使用 TARGET_DIR 作为部署目标(需配合软链接或 rsync)。
此时:
- git push origin main → 访问 http://IP/myapp/
- git push origin dev → 访问 http://IP/myapp-dev/
注意:需确保 Apache 允许访问子目录,且目录权限正确。
以上四个进阶功能可单独启用,也可组合使用。它们共同构建了一个更安全、可追溯、可恢复的轻量级 CI/CD 系统,无需 Jenkins、GitLab CI 等重型工具。