--- id: devops.git.scenario.hotfix-and-leaked-secret title: 线上紧急修复和密钥误提交:热修复分支、cherry-pick、清理历史和轮换密钥 type: scenario domain: devops stack: [git, cicd] tags: [热修复, cherry-pick, 密钥泄露, 历史清理, 安全] level: intermediate prerequisites: [devops.git.merge-rebase-recovery] est_hours: 1.5 related: [devops.git.interview, devops.cicd.pipeline] week: [8, 15] --- # 线上紧急修复和密钥误提交:热修复分支、cherry-pick、清理历史和轮换密钥 ## 业务背景与问题 团队按“release 分支发版”的方式工作:`main` 上已经合入了下个版本的多个功能(未测试完),线上运行的是 `release/1.4`。周五下午出现两件事: 1. 线上 1.4 版本有一个金额计算的 bug,需要马上修复,但不能把 main 上未测试的功能带上线。 2. 有同事把含有大模型 API Key 的 `.env` 文件提交并推送到了远程仓库。 ## 约束 - 热修复要在一小时内上线,而且下个版本也必须包含这个修复。 - 仓库在公司内部平台上,有十几个人已经拉取过包含密钥的提交。 ## 方案设计 **热修复** 1. 从线上版本的标签 / release 分支创建 `hotfix/amount-rounding`; 2. 修复 + 补测试,走合并请求和 CI,合入 `release/1.4`,打标签 `v1.4.1` 发布; 3. 把修复提交 `cherry-pick` 到 main(或从 release 合并回 main),保证下个版本不会“复活”这个 bug。 **密钥泄露**——顺序很重要: 1. **第一时间轮换密钥**:在模型服务商控制台作废旧 Key、生成新 Key,并更新部署环境的密钥配置。只要密钥进过远程仓库,就必须当作已经泄露,**清理历史不能代替轮换**。 2. 检查旧 Key 的调用记录,确认是否有异常使用。 3. 删除文件并加入 `.gitignore`;提供 `.env.example`。 4. 评估是否需要清理历史:用 `git filter-repo` 从所有历史中移除文件,强制推送,通知所有人重新克隆;托管平台上的缓存、合并请求、fork 中可能仍有副本,需要联系平台管理员。 5. 预防:提交前钩子扫描密钥(本仓库的 `tools/check_sensitive.py`),CI 中也执行密钥扫描。 ## 关键代码 ```bash # 热修复 git switch -c hotfix/amount-rounding v1.4.0 # ……修改代码、补测试…… git commit -am "fix(order): 金额四舍五入改为银行家舍入前先转换为分" # 合并请求合入 release/1.4 后打标签 git tag -a v1.4.1 -m "热修复:金额计算" git push origin v1.4.1 # 带回 main git switch main && git cherry-pick <修复提交哈希> # 密钥:先轮换!然后再处理仓库 git rm --cached .env echo ".env" >> .gitignore git commit -m "chore: 移除误提交的 .env,并忽略该文件" # 需要清理历史时(会改写所有提交哈希,需要团队协调): git filter-repo --path .env --invert-paths git push --force --all && git push --force --tags ``` `git filter-repo` 需要单独安装,用法以官方说明为准:https://github.com/newren/git-filter-repo 。在临时仓库里练习 cherry-pick、revert、reflog 的脚本见 [examples/git-lab](../examples/git-lab/)。 ## 取舍 | 做法 | 优点 | 缺点 | | --- | --- | --- | | **只轮换密钥 + 删除文件** | 快、不影响团队 | 旧密钥仍留在历史里(已作废,风险可控) | | 轮换 + 清理历史 | 历史中不再有密钥 | 所有提交哈希改变,所有人要重新克隆;平台副本仍需单独处理 | | 只清理历史不轮换 | —— | **错误做法**:密钥可能已被复制 | 热修复用 cherry-pick 还是“release 合并回 main”:cherry-pick 精确,只带修复;合并回 main 能保证 release 上所有改动都回到 main,但可能带来冲突。团队约定一种即可。 ## 易错点 - 热修复只改了 release 分支,忘了带回 main:下个版本 bug 又出现。 - 从 main 拉热修复分支:把未测试的功能带上线。 - 以为“删除文件再提交一次”密钥就安全了:历史里仍然有。 - 先花半天清理历史再轮换密钥:轮换应该是第一步。 - 强制推送清理后的历史却没通知团队:有人基于旧历史推送,又把文件带回来。 ## 面试怎么讲 > “线上版本出 bug 时,我从线上的标签拉热修复分支,修复并补测试后合入 release 分支打补丁版本发布,再 cherry-pick 回 main,避免把 main 上未测试的功能带上线。还处理过密钥误提交:第一步是作废并轮换密钥,检查调用记录;然后删除文件、加入 .gitignore,加上提交前的密钥扫描。是否清理历史要评估成本,清理时用 filter-repo 并通知团队重新克隆。”