📘 2026-07-08 部署问题总结:子模块、版本分歧与同步

前情提要: 子模块远程地址不是自己的仓库

本次修改是为了在网站底部悬挂ICP备案号,修改的文件是“…\my-blog\themes\butterfly\layout\includes\footer.pug”。

1
2
3
4
5
6
7
8
9
10
11
12
.footer-other
.footer-copyright
if owner.enable
- const currentYear = new Date().getFullYear()
- const sinceYear = owner.since
span.copyright
if sinceYear && sinceYear != currentYear
!= `© ${sinceYear} - ${currentYear} By ${config.author}`
else
!= `© ${currentYear} By ${config.author}`
<br>
a(href="http://www.beian.miit.gov.cn/" target="_blank") 本网站的备案号

修改之后进行提交,发现这个文件属于文件夹themes\butterfly,这是一个仓库中的子模块,无法直接提交到主仓库。

1. 问题描述

在修改 Butterfly 主题并尝试提交时,发现:

  1. 主仓库提示子模块有修改

    1
    modified:   themes/butterfly (modified content)
  2. 在子模块内执行 git push 时,提示权限错误:(幸好这次没有权限)

    1
    2
    ERROR: Permission to xxxx/hexo-theme-butterfly.git denied to xxx(username).
    fatal: Could not read from remote repository.
  3. 或者成功推送到了开发者的原仓库(如果正好有权限),导致本地修改进入了官方仓库。

本质原因:子模块 themes/butterfly 的远程地址默认指向了开发者的原仓库(jerryc127/hexo-theme-butterfly),而不是自己的 Fork。


2. 错误做法及其后果

错误做法 1:直接修改子模块并提交

1
2
3
4
cd themes/butterfly
git add .
git commit -m "feat: 自定义主题"
git push

后果

  • 如果远程地址是开发者的仓库,且没有权限,推送会被拒绝。
  • 如果恰好有权限,此次修改会直接进入官方仓库,影响所有使用该主题的人。

错误做法 2:强制推送到开发者仓库

1
git push --force origin master

后果

  • 覆盖开发者仓库的提交历史,极其危险
  • 可能导致开发者和所有使用该主题的人丢失提交记录。

3. 正确做法:使用 Fork 工作流

步骤 1:在 GitHub 上 Fork 主题仓库

  1. 访问 https://github.com/jerryc127/hexo-theme-butterfly
  2. 点击右上角的 Fork 按钮
  3. 建议勾选 **”Copy the dev branch only”**,只复制你需要的分支

步骤 2:修改本地子模块的远程地址

1
2
cd themes/butterfly
git remote set-url origin git@github.com:你的用户名/hexo-theme-butterfly.git

步骤 3:提交并推送到你的 Fork

1
2
3
git add .
git commit -m "feat: 自定义主题样式"
git push -u origin dev # 推送到你的 Fork

步骤 4:在主仓库中更新子模块引用

1
2
3
4
cd ../..
git add themes/butterfly
git commit -m "chore: 更新子模块指向自定义 Fork"
git push origin main

4.如何防止再犯

4.1 每次推送前检查远程地址

1
git remote -v

确保输出指向你的 Fork,而不是开发者的原仓库。

4.2 在子模块中设置一个保护

在子模块中,可以添加一个别名来提醒自己:

1
git config --local alias.push-origin '!echo "⚠️ 确认远程地址是 Fork 仓库!"; git push'

4.3 使用 .gitmodules 控制子模块地址

确保 .gitmodules 中的 URL 指向你自己的 Fork:

1
2
3
[submodule "themes/butterfly"]
path = themes/butterfly
url = git@github.com:你的用户名/hexo-theme-butterfly.git

4.4 养成好习惯

操作 检查点
进入子模块 git remote -v 确认地址
修改子模块 确认分支正确(如 dev
提交子模块 确认推送目标是你自己的 Fork
提交主仓库 确认子模块引用已被 git add

5、快速诊断:你的子模块指向谁?

1
2
cd themes/butterfly
git remote -v
输出 指向 状态
git@github.com:jerryc127/hexo-theme-butterfly.git 开发者原仓库 ⚠️ 危险,请改为你的 Fork
git@github.com:你的用户名/hexo-theme-butterfly.git 你的 Fork ✅ 安全
https://github.com/你的用户名/hexo-theme-butterfly.git 你的 Fork(HTTPS) ✅ 安全

6. 总结

错误做法 正确做法
直接向开发者仓库推送 Fork 后推送到自己的仓库
在子模块中不做 remote -v 检查 每次推送前确认远程地址
修改后只提交子模块,不更新主仓库引用 修改后同时更新主仓库的子模块引用
在服务器上修改子模块 所有子模块修改都在本地完成

一句话总结永远不要直接推送到你不拥有的仓库。先 Fork,再推送。 🎯

一、核心问题概览

问题 现象 根本原因
子模块版本落后 本地主题修改(备案号)不显示,服务器版本为旧提交 主仓库中的子模块引用未更新,服务器 git submodule update 拉取了旧版本
主仓库版本分歧 本地与服务器 main 分支提交哈希不一致,出现额外合并提交 在服务器上执行了 git pullgit push,产生了本地没有的合并提交
子模块注册信息丢失 fatal: No url found for submodule path .gitmodules.git/config 中的子模块配置被删除或损坏
服务器上执行 Git 操作权限不足 chown: Operation not permitted 钩子脚本以 git 用户执行 chown,只有 root 有权更改文件所有者

二、解决方案速查表

2.1 子模块版本落后(最常见)

问题:本地修改了主题并推送到了 GitHub,但服务器上的主题没有更新。

原因:主仓库中没有更新子模块的引用。

解决流程

1
2
3
4
5
6
7
8
9
10
11
# 1. 在子模块中提交并推送到 GitHub
cd themes/butterfly
git add .
git commit -m "feat: 更新主题"
git push origin dev

# 2. 回到主仓库,更新子模块引用(关键步骤!)
cd ../..
git add themes/butterfly
git commit -m "chore: 更新子模块引用"
git push origin main

验证:在服务器上执行 git submodule update --init --recursive,然后 hexo generate


2.2 主仓库版本分歧(本地和服务器提交哈希不同)

问题:本地 git log -1 和服务器网站目录的 git log -1 显示的提交哈希不同。

原因:在服务器上执行了 git pullgit push,导致产生了本地没有的合并提交。

解决流程

1
2
3
4
5
6
# 在本地拉取服务器上的提交,合并历史
cd /d/Blog/my-blog
git pull origin main

# 如果有冲突,解决后提交
git push origin main

验证:再次对比本地和服务器的提交哈希,应该一致。


2.3 子模块注册信息丢失(.gitmodules 中找不到子模块)

问题fatal: No url found for submodule path 'themes/butterfly' in .gitmodules

原因:子模块的 .gitmodules.git/config 配置被删除或损坏。

解决流程(在服务器网站目录执行):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cd /www/wwwroot/www.xjelly.space

# 1. 清除所有子模块残留
git rm --cached themes/butterfly 2>/dev/null
rm -f .gitmodules
git config --local --remove-section submodule.themes/butterfly 2>/dev/null
rm -rf themes/butterfly

# 2. 重新克隆主题(从你的 Fork)
git clone git@github.com:xxxx/hexo-theme-butterfly.git themes/butterfly

# 3. 注册子模块
git config -f .gitmodules --add submodule.themes/butterfly.path themes/butterfly
git config -f .gitmodules --add submodule.themes/butterfly.url git@github.com:xxxxx/hexo-theme-butterfly.git
git config --local --add submodule.themes/butterfly.path themes/butterfly
git config --local --add submodule.themes/butterfly.url git@github.com:xxxxx/hexo-theme-butterfly.git

# 4. 提交并推送
git add .gitmodules themes/butterfly
git commit -m "chore: 重新注册子模块"
git push origin main

2.4 服务器钩子中 chown 报错

问题:钩子脚本执行 chown 时提示 Operation not permitted

原因:钩子以 git 用户执行,而 chown 需要 root 权限。

解决:在钩子中移除 chown 命令,只使用 chmod 修复权限。

1
2
3
# 只修改权限,不更改所有者
find $WEB_DIR -type d -exec chmod 755 {} \;
find $WEB_DIR -type f -exec chmod 644 {} \;

三、经验教训

3.1 修改子模块的正确流程(每次都要执行两次提交)

步骤 操作 推送目标
1 cd themes/butterfly
2 git add . && git commit -m "..." && git push origin dev GitHub(你的 Fork)
3 cd ../..
4 git add themes/butterfly
5 git commit -m "chore: 更新子模块引用" && git push origin main 服务器裸仓库

关键点:第 5 步是让服务器知道子模块有新版本的关键,不能省略


3.2 服务器网站目录的角色

  • 网站目录接收更新的地方,不是生产更新的地方。
  • 在服务器上尽量避免执行 git commitgit push,除非你明确知道自己在做什么。
  • 如果必须在服务器上提交,**一定要先 git pull,再 git push**。

3.3 版本一致性检查命令

1
2
3
4
5
6
7
# 检查主仓库版本
cd /d/Blog/my-blog && git log --oneline -1 # 本地
cd /www/wwwroot/www.xxxx.space && git log --oneline -1 # 服务器

# 检查子模块版本
cd themes/butterfly && git log --oneline -1 # 本地
cd /www/wwwroot/www.xxxx.space/themes/butterfly && git log --oneline -1 # 服务器

如果主仓库版本不一致,在本地执行 git pull origin main 同步。
如果子模块版本不一致,在服务器执行 git submodule update --init --recursive


3.4 核心原则:所有修改都在本地完成

操作 推荐位置 不推荐位置
修改代码 本地 服务器网站目录
提交代码 本地 服务器网站目录
推送代码 本地 服务器网站目录
拉取代码 服务器钩子(自动) 手动在服务器执行
生成静态文件 服务器钩子(自动) 手动在服务器执行

四、快速诊断流程图

1
2
3
4
5
6
7
8
9
graph TD
A[网站显示异常] --> B{本地 hexo s 是否正常?}
B -->|否| C[检查本地代码/配置]
B -->|是| D{服务器子模块版本与本地一致?}
D -->|否| E[在服务器执行 git submodule update]
D -->|是| F{主仓库版本与本地一致?}
F -->|否| G[在本地执行 git pull origin main]
F -->|是| H[在服务器执行 hexo clean && hexo generate]
H --> I[强制刷新浏览器]