使用强制推送,Luca——Jenkins 的开发者抹掉了 Github 上 1 个月的提交

  • Alex Blewitt
  • 唐巧

2013 年 12 月 22 日

话题:GitHubDevOpsGit语言 & 开发文化 & 方法

近日,Jenkins 项目的一位开发者在推送更改时,意外地使用了强制推送,造成该项目在Github 上的代码库中 1 个月的提交被抹掉。该项目的社区成员很快反应过来,并且将该问题修复,但是这突显出当此类问题发生时,Github 的开放性(以及Jenkins 项目的开放性:允许任意用户提交代码到代码仓库)可能将问题放大。

Git 的强制推送命令:git push --force 告诉服务器用自己当前提交的分支引用替换服务器代码仓库中的指定分支引用。正常情况下,Git 仓库只允许 fast forward 的推送,它的意思是,当前代码仓库的引用是推送引用的祖先。但是强制推送却没有这个限制,它允许将引用直接修改到以前的版本。

通过修改 Git 仓库的参数,将 git config 命令的 receive.denyNonFastForwards 设置成 true,我们可以禁止强制推送。

在某些场景下,开启强制推送是有用的。例如,当我们使用 git filter-branch 命令进行重构或过滤时,会造成新的引用不再是当前分支的祖先,所以正常的推送操作无法有效。另一种场景是:当镜像功能启用时,为了同步两个代码仓库中的内容,你会希望更改能够直接传递而不报错。

这正是这次出问题的场景,Luca 正在测试 Gerrit 的镜像插件,他将 Jenkins 仓库中的内容检出到本地。但是 Gerrit 的镜像插件被设置成了从本地仓库中更新,所以造成了所有远程的代码仓库都是他本地版本的镜像。不幸的是,Luca 的本地仓库并没有更新到最新的版本,所以随着网络同步,远程的代码仓库也被同步成了以前的版本。

幸运的是,所有受影响的代码仓库都因为提供以下功能而被修复 - 这也是 Git 版本控制系统(或者是任何分布式版本控制系统)的优点:你可以将代码仓库恢复到它任何一个历史版本,并且你可以很方便地做到。Github 提供了一个非常有用的服务端的 reflogs(用来记录每次对分支的修改)来重新获取历史版本。但如何在未来缓解这种事件,此事让我们想到以下 2 个问题:

  • 用户向多个代码仓库提交,或者更改来有代码审核渠道是否有意义?
  • Github 提供选择来设置 denyNonFastForwards 是否有意义?

Github 的主要竞争者,BitBucket就提供了选项来禁止非 fast forwards 的提交。BitBucket 由 Atlassian 运营管理,并且曾经只提供对分布式版本管理系统 Mercurial 的支持。但是,BitBucket 的增长来自对于 Git 仓库的支持,并且他们的解决方案Atlassian Stash仅仅提供对 Git 仓库的支持。

讽刺的是,Luca 有一家名为GerritForge公司,提供基于 Gerrit 的代码托管,最近他撰写了一本关于学习 Gerrit 代码审查的书,InfoQ 也对此进行了报道。或许,如果 Jenkins 仓库采用例如 Gerrit 的代码审查工具,这种事情就不会发生了。

除非 GitHub 提供禁止强制推送的配置,否则 Jenkins 的开发人员将编写一个工具来跟踪更新到 GitHub 的推送,并且记录提交的 SHA 值的变化。讽刺的是,他们计划使用 rsync 来备份这些记录到多个位置。

巨大的力量,伴随着巨大的责任。GitHub 的用户当然有权利使用强制推送。但是,当不确定 GitHub 是否会提供办法,以防止这种情况在未来再次发生之前,如果你有一个企业级的代码仓库并且没有备份,你需要了解相应的风险。

查看英文原文:Use the Force, Luca - Jenkins Developer Wipes out a Month of Commits on GitHub


感谢张龙对本文的审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ)或者腾讯微博(@InfoQ)关注我们,并与我们的编辑和其他读者朋友交流。

GitHubDevOpsGit语言 & 开发文化 & 方法