近日,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的主要竞争者,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)关注我们,并与我们的编辑和其他读者朋友交流。