场景
一天,小明从远程仓库上pull了一段公司代码,他开开心心的写完一个模块,并且提交到远程仓库,然后。。。惊恐的发现居然push到main上了,这时候他该怎么做?
- 直接提桶跑路
- 跪下来给leader认错
- 钝角
- 听卓卓怎么讲,说不定还有得救
前置知识
先来听听正常情况下main如何推进:
-
分支开发:开发者从
main分支拉取一个新的功能分支(feature/xxx)或修复分支(bugfix/xxx)。 -
开发和本地测试:在新分支上进行开发和测试,确保代码在独立环境中运行正常。
-
代码提交(Pull Request 或 Merge Request):
- 提交代码后,通过 Pull Request (PR) 或 Merge Request (MR) 的形式将分支代码合并到
main。 - 这个过程通常包括代码审查(Code Review)、自动化测试、CI/CD 检查等环节。
- 提交代码后,通过 Pull Request (PR) 或 Merge Request (MR) 的形式将分支代码合并到
-
合并到
main:- 确保分支代码通过所有检查后,由管理员或拥有权限的开发者通过合并工具(如 GitHub/GitLab 的 UI 或命令行工具)合并到
main。
- 确保分支代码通过所有检查后,由管理员或拥有权限的开发者通过合并工具(如 GitHub/GitLab 的 UI 或命令行工具)合并到
-
自动部署(如果设置了 CI/CD 流程):合并后的代码可能会触发自动化脚本,将代码部署到测试或生产环境。
而我们要是直接push到main上,无疑是少了很多测试与验证的环节,这时候代码就非常容易出错,个人开发还好,要是企业项目,有一点bug就可能要承受巨大的损失,所以,怎么避免,以及真push上去了该怎么补救,听我娓娓道来
预防措施实施
先来讲讲怎么预防错误
1. 启用保护分支
通过 Git 平台(如 GitHub、GitLab、Gitee 等)为 main 分支设置保护规则,确保只能通过 Pull Request 合并代码。
-
操作步骤(以 GitHub 为例):
- 打开仓库的 Settings(设置)页面。
- 在左侧导航栏选择 Branches。
- 找到 Branch Protection Rules(分支保护规则),点击 Add Rule。
- 设置规则:
- 填写分支名称(如
main)。 - 勾选以下选项:
- Require a pull request before merging:强制使用 Pull Request 合并。
- Require approvals:需要至少一个或多个审查批准。
- Require status checks to pass before merging:强制 PR 必须通过 CI 测试。
- Include administrators:将管理员也纳入限制范围。
- 填写分支名称(如
- 保存规则。
2.限制操作权限
通过权限管理工具,限制谁可以操作
main分支。- 操作步骤:
- 在 Git 平台中,设置仓库成员权限:
- Write 权限:只允许核心成员(如项目管理员)对
main分支进行操作。 - Read 权限:普通成员只能查看
main分支,但无法直接推送代码。
- Write 权限:只允许核心成员(如项目管理员)对
- 对 main分支启用合并限制:
- 需要指定的审查人批准后,才允许合并。
- 开启强制保护:
- 禁止强制推送(Force Push)。
- 禁止直接推送(Push to
main)。
- 在 Git 平台中,设置仓库成员权限:
已经push上去了如何补救
- 查看误操作的提交历史 检查最近的提交 运行以下命令,确认误操作的具体提交:
git log --oneline
找到误推的提交记录(比如 abc123 是误操作的提交哈希)。
- 回滚误推的提交 根据具体情况选择合适的回滚方式:
情况 1:误推了自己的提交
最好解决的情况,如果误推的代码对其他开发者没有影响,可以在本地回滚并强制推送覆盖。
步骤: 回滚到正确的状态:
git reset --hard HEAD~1
HEAD~1 表示回退 1 个提交,根据需要修改数字。 强制推送到远程:
git push origin main --force ⚠️ 注意:强制推送会覆盖远程代码,需确保没有其他开发者依赖当前分支的提交。
情况 2:误推覆盖了别人的代码
如果误推影响了他人代码,不要轻易使用 --force,应通过 Revert 恢复原状。
步骤: 生成 Revert 提交:
git revert <commit-hash>
使用误操作提交的哈希值(如 abc123),生成一个 Revert 提交。 推送 Revert 提交: git push origin main 远程仓库将回到误推之前的状态。 revert 原理: 找到指定提交(由 标识)。 生成一个新提交,用来抵消指定提交的改动。 例如,如果 添加了某些文件或代码,git revert 会生成一个删除这些改动的新提交。
情况 3:误推了未审核的代码
如果误推的代码未经审核,需暂时撤回分支,并重新发起 Pull Request。
步骤: 1.创建新分支保存误推内容:
git checkout -b temp-fix main
2.将误推的代码转移到 temp-fix 分支。 回滚 main 分支到正确的状态:
git reset --hard <last-correct-commit-hash>
git push origin main --force
重新提交误推内容:
在 temp-fix 分支中重新整理代码,并通过 Pull Request 合并。
为什么要创建新分支reset而不是用main直接reset? 假设 main 原本的提交历史如下:
A -> B -> C -> D -> E
你执行 git reset --hard C,然后强制推送:
A -> B -> C
其他开发者的分支基于 main 的旧历史创建,例如:
main: A -> B -> C -> D -> E
feature: A -> B -> C -> D -> E -> F -> G
当开发者试图将 feature 分支与新的 main 合并时,提交 D 和 E 在新的 main 中已经不存在:
新 main: A -> B -> C
feature: A -> B -> C -> D -> E -> F -> G
Git 无法自动解决这种分支分歧,开发者必须手动调整分支的基点。
创建新分支reset: 假设 main 的原始提交历史为:
A -> B -> C -> D -> E
如果发现 main 的历史有问题(例如提交 D 和 E 是错误的),你可以通过创建一个新分支(如 fix-main),进行必要的修正:
新分支 fix-main: A -> B -> C
此时,main 的历史依然保留为:
A -> B -> C -> D -> E
开发者的 feature 分支依然可以正常工作,因为它基于旧的 main(包含 D 和 E)。
- 与直接 reset 的区别 直接对 main 执行 git reset --hard 并强制推送后:
main 分支的历史被重写为:
A -> B -> C
所有依赖 D 和 E 的分支(如 feature)都会失效,因为它们的基点提交不再存在于新的 main 历史中。
当开发者尝试合并或拉取时,Git 会提示冲突,要求手动解决,极大增加了复杂性。
相反,如果使用新分支(如 fix-main)来修正问题,则:
开发者的分支依然可以基于旧 main 正常工作,历史不变。 你可以通过 Pull Request 或通知团队成员有新分支(fix-main)需要切换到新的开发基点。