基于 Git Hook 的 Hexo 自动化部署指南
基于 Git Hook 的 Hexo 自动化部署指南
前言
为什么采用Git Hook这种方式来实现自动化部署
笔者前期是采用本地生成静态html(即 public/)然后通过scp传到远程服务器的方式来实现网站部署。
如下脚本可以实现该功能:
@echo off
chcp 65001 >nul
echo ===== 开始部署流程 =====
echo.
echo 1. 执行 hexo clean...
call hexo clean
if %ERRORLEVEL% NEQ 0 (
echo [错误] hexo clean 执行失败!错误代码: %ERRORLEVEL%
pause
exit /b 1
)
echo hexo clean 完成.
echo.
echo 2. 执行 hexo generate...
call hexo g
if %ERRORLEVEL% NEQ 0 (
echo [错误] hexo g 执行失败!错误代码: %ERRORLEVEL%
pause
exit /b 1
)
echo hexo generate 完成.
echo.
echo 3. 删除远端服务器上的 /home/keyenzhou/wiki/ 内容...
ssh keyenzhou@8.153.167.18 "rm -rf /home/keyenzhou/wiki/*"
if %ERRORLEVEL% NEQ 0 (
echo [警告] 删除远端文件时可能出现了问题。继续执行上传...
) else (
echo 远端文件删除完成.
)
echo.
echo 4. 上传新的 public 内容到远端服务器...
scp -r ./public/* keyenzhou@8.153.167.18:/home/keyenzhou/wiki/
if %ERRORLEVEL% NEQ 0 (
echo [错误] SCP 上传失败!
pause
exit /b 1
)
echo SCP 上传成功!
echo.
echo ===== 所有操作已完成! =====
pause
该方式有以下几种问题:
本地生成静态html占用本地资源(笔者希望本地只进行文章编写,将耗费CPU资源的功能转移到远程服务器上)
scp传输文件量过大,每次生成完html都是直接全量传给服务器。显然从本地只将文章的修改部分传到服务器更为合理。(利用 Git 的增量提交和智能压缩传输机制可以完美将传输过程优化)
操作较为复杂,需要记住的过程较多。
最终效果
通过git hook 实现自动化部署后,我们只需要在本地进行编写,通过git操作即可在远程实现自动生成自动部署。
git status && git add .

git commit -s

git push
通过git push命令即可自动执行在远程服务器的hook,来部署服务


该方案的优势:
解放本地资源,将计算密集型任务卸载到远程服务器
优化带宽利用率,仅传输增量源码而非全量静态资源
简化部署流程,实现“一次配置,一键发布”的自动化体验
本地与服务器职责分离,本地专注于内容创作,服务器负责构建与托管
核心原理
利用 Git 的“裸仓库”与“钩子”功能,将一次普通的 git push 指令转化为在远程服务器上自动触发静态站点生成的信号。
1.信号触发器 Git Hook
Git Hook 是 Git 在特定操作(如提交、推送等)前后自动执行的脚本。本方案使用的是 post-receive Hook,它仅在推送操作完全成功后被触发一次。这确保了部署操作的稳定性和可靠性。
2.执行载体:Git 裸仓库 (Bare Repository)
普通 Git 仓库包含工作目录(您的文件)和版本历史。而裸仓库则仅包含版本历史,它不包含文件的工作副本,因此无法在其内部直接进行文件修改或生成操作。它作为一个纯净的“中转站”或“调度中心”,专门用于接收推送并触发 Hook 脚本。
执行流程:

准备工作
本地环境 windows11
1.Node.js
2.Hexo
全局安装
npm install -g hexo-cli
项目安装(package.json)
{
"name": "hexo-site",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "hexo generate",
"clean": "hexo clean",
"deploy": "hexo deploy",
"server": "hexo server"
},
"hexo": {
"version": "7.3.0"
},
"dependencies": {
"hexo": "^7.3.0",
"hexo-filter-github-emojis": "^3.0.5",
"hexo-generator-archive": "^2.0.0",
"hexo-generator-category": "^2.0.0",
"hexo-generator-index": "^4.0.0",
"hexo-generator-search": "^2.4.3",
"hexo-generator-tag": "^2.0.0",
"hexo-image-link": "^0.0.6",
"hexo-permalink-pinyin": "^1.1.0",
"hexo-renderer-ejs": "^2.0.0",
"hexo-renderer-marked": "^7.0.1",
"hexo-renderer-stylus": "^3.0.1",
"hexo-server": "^3.0.0",
"hexo-theme-landscape": "^1.0.0",
"hexo-wordcount": "^6.0.1"
}
}
3.Git
远程环境 Ubuntu2404
1.Node.js
sudo apt update
sudo apt install nodejs npm
2.Hexo
全局安装
npm install -g hexo-cli
局部安装
使用package.json (项目目录下)
npm install --production
3.Git
sudo apt update
sudo apt install git
4.Nginx
sudo apt-get update
sudo apt-get install nginx -y # -y表示自动对所有交互选项回答yes
详细步骤
1. 服务器端 - 初始化 Git 裸仓库
登录服务器
ssh keyenzhou@ip # 已经配置免密登录
创建并初始化裸仓库
# 1. 创建存放仓库的目录
mkdir -p /home/keyenzhou/wiki
cd /home/keyenzhou/wiki
# 2. 初始化一个裸仓库(注意 --bare 参数)
git init --bare wiki.git
2. 服务器端 - 创建工作目录并设置权限
创建hexo工作目录
# 1. 创建目录(用于存放源码和生成静态文件)
sudo mkdir -p /home/keyenzhou/wiki/wiki
# 2. 将此目录的所有权赋予自己,避免权限问题
sudo chown -R keyenzhou:keyenzhou /home/keyenzhou/wiki/wiki
验证权限
ls -ld /home/keyenzhou/wiki/wiki
# 应显示所有者是 keyenzhou
3. 服务器端 - 创建并配置 Git Hook
自动化的核心
创建post-receive钩子文件
# 进入裸仓库的 hooks 目录
cd /home/keyenzhou/repo/wiki/wiki.git/hooks
# 使用 vim 创建 post-receive 文件(无后缀)
vim post-receive
编辑脚本内容(post-receive)
#!/bin/bash
# 当有推送时,这个脚本会被触发
# 设置目录
TARGET="/home/keyenzhou/wiki/wiki"
GIT_DIR="/home/keyenzhou/wiki/wiki.git"
# 循环读取所有被更新的引用(虽然你可能只推了一个分支)
while read oldrev newrev refname
do
# 只监听 master 分支的推送
if [[ $refname = "refs/heads/master" ]] ; then
echo "🎉 收到对 master 分支的推送,开始部署..."
# 强制将仓库内容检出到目标目录
git --work-tree="$TARGET" --git-dir="$GIT_DIR" checkout -f master
# 进入目录,执行部署命令
cd "$TARGET"
npm install --production
npx hexo clean && npx hexo generate
echo "✅ 部署完成!"
fi
done
# 循环结束
赋予脚本执行权限(关键步骤!)
chmod +x post-receive
4. 本地 - 配置 Git 远程仓库并推送
在本地Hexo博客根目录下,添加远程仓库地址
cd /d/wiki/ # 切换到你的本地Hexo项目目录
# 添加远程仓库的别名
git remote add origin keyenzhou@ip:/home/keyenzhou/wiki/wiki.git
将代码首次推动到服务器
# 添加所有文件到暂存区
git add .
# 提交更改
git commit --signoff
# 推送到服务器的远程仓库
# -u 等同于 git branch --set-upstream-to=origin/<branch> <mybranch>
# 将本地分支与远程分支相关联,同时推送
git push -u deploy master
建议:
设置git的默认commit message
设置全局提交模板
# 在用户根目录创建模板文件
mkdir -p ~/.git-templates
vim ~/.git-templates/git-commit-template.txt
# 设置全局提交模板
git config --global commit.template ~/.git-templates/git-commit-template.txt
设置项目提交模板 (建议)
cd /d/wiki/
vim .git-commit-template.txt
git config commit.template .git-commit-template.txt
笔者设置的模板为:
# 博客项目提交信息模板
类型:
post: new_post.md
design:
feature:
fix:
optimize:
config:
# 文章相关请注明文章标题或slug
# 示例:
# post: 添加《Git使用技巧》文章
# fix: 修复首页样式错位问题
# 详细描述:
#
# Breaking changes:
#
# 关联Issue: #
# 类型:
# post: 新文章
# design: 设计更新
# feature: 新功能
# fix: 修复问题
# optimize: 性能优化
# config: 配置变更
5. 验证与测试
观察推送过程
执行git push之后,观察命令行输出。如果配置成功能看到来自服务器的Hook脚本的输出信息。

登录服务器检查
# 检查工作目录是否有文件
ls -la /home/keyenzhou/wiki/wiki
# 检查静态文件是否已生成
ls -la /home/keyenzhou/wiki/wiki/public
后续日常使用
git add .
git commit --signoff
git push # 一键触发自动化部署
6. 服务器端 - 配置 Nginx Web 服务
完成环境准备后,使用sudo systemctl status nginx检查nginx状态
编辑配置文件
sudo vim /etc/nginx/sites-available/default
server {
listen 80 default_server;
listen [::]:80 default_server;
root /home/keyenzhou/wiki/wiki/public;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location ~* \.(css|js)$ {
add_header Cache-Control no-cache;
}
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
}
重启nginx服务
sudo service nginx restart
7. 访问博客
浏览器地址栏输入 http://ip 即可尝试访问,如果一切配置正确,应该能看到 Hexo 博客页面了!
常见问题
1. 403 forbidden
可能是nginx权限不足,可以将nginx与用户放在一个用户组里。
笔者是直接修改了用户目录的权限,使得nginx能够读取文件
sudo chmod -r 755 /home/keyenzhou
2. 为什么用post-reveive而不是post-update钩子
1. post-receive 钩子 (推荐)
时机:当整个
git push操作已经成功完成,所有引用(分支、标签)都已被更新之后。行为:它只运行一次。它是“推送后”的最终通知。
数据:它通过标准输入(stdin) 接收数据,每行包含三个参数:
<old-value> <new-value> <ref-name>。
2. post-update 钩子 (不推荐用于部署)
时机:在推送过程中,每成功更新一个引用(比如一个分支或一个标签)之后,就会立即触发。
行为:一次推送如果更新了 3 个分支,它就会被调用3 次。
数据:它通过命令行参数接收被更新的引用名称列表。
潜在问题:
如果你在 post-update 里写部署脚本,一次推送多个分支或标签会导致部署脚本被重复执行多次,这通常不是你想要的行为。
为什么必须用 post-receive?
效率:我们只关心代码是否推送成功,然后执行一次部署任务。
post-receive完美匹配。避免重复执行:如果不小心同时推送了分支和标签(
git push --all --tags),post-update会触发两次并尝试部署两次,可能造成冲突。而post-receive仍然只触发一次。信息更丰富:
post-receive可以通过标准输入知道每个引用的旧版本和新版本哈希值,这在做精细化的部署或通知时非常有用。
3.钩子脚本中为什么不使用git pull而是git checkout
git维护的目录不是一个开发环境,而是一个纯粹的部署目标。它不应该有自己的提交历史或需要合并的更改。使用 git pull 在这里是错误且危险的,我们很难处理自动化脚本执行过程中git pull产生的问题。
4. 关于LF的警告
warning: in the working copy of '.git-commit-template.txt', LF will be replaced by CRLF the next time Git to
LF (Line Feed):Linux/macOS 系统的换行符
CRLF (Carriage Return + Line Feed):Windows 系统的换行符
Git 检测到文件中的换行符与您的系统设置不匹配
git config --global core.autocrlf false