Web目录与Git仓库不一致已出问题,需用git archive/rsync比对文件级差异,排除非源码目录和敏感配置,结合cron巡检与日志告警实现故障前拦截。

生产 Web 目录和 Git 仓库不一致,不是“可能出问题”,而是“已经出问题”——常见表现是页面样式错乱、JS 报 404、新功能没生效,但 git log 显示最新提交已推送。根本原因往往是:手动改了线上文件、部署脚本漏同步、或 hook 没触发。
用 git ls-tree + rsync --dry-run 快速发现文件级偏差
比对不能只看“有没有 commit”,得验证“线上目录是否真等于某次 commit 的展开结果”。git ls-tree -r HEAD 输出的是 Git 仓库当前 HEAD 所有文件的路径+SHA1,而 rsync --dry-run -avc --delete 可以模拟同步并报告差异(-c 强制校验内容而非修改时间)。
- 先在 Web 根目录(如
/var/www/html)执行:rsync -avn --delete /dev/null/ . --exclude='.git'确保 rsync 能遍历目录且无权限错误 - 再运行完整比对命令(假设仓库在
/opt/myapp.git,Web 目录为/var/www/html):cd /opt/myapp.git && git archive HEAD | tar -t | sort > /tmp/git-files.txt && find /var/www/html -type f -printf '%P\n' | sort > /tmp/web-files.txt && diff -u /tmp/git-files.txt /tmp/web-files.txt
- 注意跳过
.git、node_modules、vendor等非源码目录,否则误报率极高
避免因软链接、权限、.env 导致的“假不一致”
Web 目录里常存在 current → releases/20260928 这类软链接,或 .env 配置文件(Git 不跟踪),直接比对会失败。必须区分“声明内容”和“运行时内容”。
-
git archive只导出 Git 跟踪的文件,不包含软链接目标内容;若需比对真实文件内容,改用git checkout-index -a -f --prefix=/tmp/git-checkout/生成快照再 diff -
.env、config.php等敏感配置必须从比对范围排除,并单独用sha256sum校验其是否存在、权限是否为600 - Web 服务器用户(如
www-data)对某些文件只有读权限,rsync -c会因无法读取而跳过——改用diff -q或cmp逐文件比对更可靠
用 cron + git rev-parse 实现每小时自动巡检
定期比对不是为了“知道不一致”,而是为了“在故障前拦截”。把比对逻辑封装成脚本,配合 cron 和简单告警,就能形成闭环。
- 脚本开头加:
WEB_ROOT="/var/www/html"; REPO_DIR="/opt/myapp.git"; EXPECTED_COMMIT=$(cd "$REPO_DIR" && git rev-parse HEAD) - 关键检查项:
cd "$WEB_ROOT" && git status --porcelain(确认没人在 Web 目录里直接改);cd "$REPO_DIR" && git fsck --no-dangling(防止仓库损坏) - cron 示例(每小时执行):
0 * * * * /usr/local/bin/check-web-consistency.sh >> /var/log/web-consistency.log 2>&1 - 发现不一致时,用
logger -t web-consistency "MISMATCH: $(hostname) $WEB_ROOT ≠ $EXPECTED_COMMIT"写入系统日志,再由 rsyslog 转发到 Slack
最易被忽略的一点:Git 仓库本身可能不是 bare 仓库,或者 Web 目录是通过 git pull 更新的——这种模式下,git status 在 Web 目录里永远显示 “clean”,但实际可能卡在 merge 冲突状态。真正可靠的比对,永远基于 git archive 或 git checkout-index 生成的纯净快照,而不是依赖工作区状态。











