git push到main上了?我直接提桶跑路!(才怪)

4,694 阅读6分钟

场景

一天,小明从远程仓库上pull了一段公司代码,他开开心心的写完一个模块,并且提交到远程仓库,然后。。。惊恐的发现居然push到main上了,这时候他该怎么做?

  1. 直接提桶跑路
  2. 跪下来给leader认错
  3. 钝角
  4. 听卓卓怎么讲,说不定还有得救

前置知识

先来听听正常情况下main如何推进:

  1. 分支开发:开发者从 main 分支拉取一个新的功能分支(feature/xxx)或修复分支(bugfix/xxx)。

  2. 开发和本地测试:在新分支上进行开发和测试,确保代码在独立环境中运行正常。

  3. 代码提交(Pull Request 或 Merge Request):

    • 提交代码后,通过 Pull Request (PR) 或 Merge Request (MR) 的形式将分支代码合并到 main
    • 这个过程通常包括代码审查(Code Review)、自动化测试、CI/CD 检查等环节。
  4. 合并到 main

    • 确保分支代码通过所有检查后,由管理员或拥有权限的开发者通过合并工具(如 GitHub/GitLab 的 UI 或命令行工具)合并到 main
  5. 自动部署(如果设置了 CI/CD 流程):合并后的代码可能会触发自动化脚本,将代码部署到测试或生产环境。

而我们要是直接push到main上,无疑是少了很多测试与验证的环节,这时候代码就非常容易出错,个人开发还好,要是企业项目,有一点bug就可能要承受巨大的损失,所以,怎么避免,以及真push上去了该怎么补救,听我娓娓道来

预防措施实施

先来讲讲怎么预防错误

1. 启用保护分支

通过 Git 平台(如 GitHub、GitLab、Gitee 等)为 main 分支设置保护规则,确保只能通过 Pull Request 合并代码。

  • 操作步骤(以 GitHub 为例):

    1. 打开仓库的 Settings(设置)页面。
    2. 在左侧导航栏选择 Branches
    3. 找到 Branch Protection Rules(分支保护规则),点击 Add Rule
    4. 设置规则:
      • 填写分支名称(如 main)。
      • 勾选以下选项:
        • Require a pull request before merging:强制使用 Pull Request 合并。
        • Require approvals:需要至少一个或多个审查批准。
        • Require status checks to pass before merging:强制 PR 必须通过 CI 测试。
        • Include administrators:将管理员也纳入限制范围。
    5. 保存规则。

    2.限制操作权限

    通过权限管理工具,限制谁可以操作 main 分支。

    • 操作步骤:
      • 在 Git 平台中,设置仓库成员权限:
        • Write 权限:只允许核心成员(如项目管理员)对 main 分支进行操作。
        • Read 权限:普通成员只能查看 main 分支,但无法直接推送代码。
      • 对 main分支启用合并限制:
        • 需要指定的审查人批准后,才允许合并。
      • 开启强制保护:
        • 禁止强制推送(Force Push)。
        • 禁止直接推送(Push to main)。

已经push上去了如何补救

  1. 查看误操作的提交历史 检查最近的提交 运行以下命令,确认误操作的具体提交:
git log --oneline

找到误推的提交记录(比如 abc123 是误操作的提交哈希)。

  1. 回滚误推的提交 根据具体情况选择合适的回滚方式:

情况 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)。

  1. 与直接 reset 的区别 直接对 main 执行 git reset --hard 并强制推送后:

main 分支的历史被重写为:

A -> B -> C

所有依赖 D 和 E 的分支(如 feature)都会失效,因为它们的基点提交不再存在于新的 main 历史中。

当开发者尝试合并或拉取时,Git 会提示冲突,要求手动解决,极大增加了复杂性。

相反,如果使用新分支(如 fix-main)来修正问题,则:

开发者的分支依然可以基于旧 main 正常工作,历史不变。 你可以通过 Pull Request 或通知团队成员有新分支(fix-main)需要切换到新的开发基点。