容易中枪的Git高危操作

649 阅读3分钟

问题

李四在develop分支提交了错误的代码(涉及到2commitcommit-acommit-b)并且已经push,这时发现提交代码有问题,想要回滚,于是执行了以下操作:

  1. git commit * 2:分别提交了commit-acommit-b
  2. git push:将commit-acommit-b提交推送到服务器端
  3. 发现commit-acommit-b提交是错误的,后悔了,于是继续下面的操作进行代码“回滚”
  4. git reset --hard HEAD^^:清理掉本地commit-acommit-b
  5. git push --force:强制远程服务器端清理掉commit-acommit-b

那么问题是,上述操作是否安全?

场景分析

上述操作的危险性在于git本身是分布式的,如果在第2步第4步之间,有人已经将commit-acommit-b提交fetchpull到了本地,那么此人本地已经有了commit-acommit-b,之后他重新在feature分支做提交时可能会带上commit-acommit-b,导致远程的服务器端重新出现了不想要的代码。

下面我们将分几种情况分别进行实验和分析,探究在git工具中进行上述操作的影响

develop分支fetch

假设张三在第2、4步之间于develop分支上执行了fetch操作,那么其过程和结果如下:

  1. 张三在develop分支上执行了fetch操作,命令行界面显示dc1e846..3f821e9 develop -> origin/develop,本地的origin/develop上会有commit-acommit-b提交 image.png

  2. 李四执行第4、5步,进行代码回滚

  3. 张三继续在develop分支上执行fetch操作,命令行界面显示+ 3f821e9...a113d93 develop -> origin/develop (forced update),此时本地的origin/developcommit-acommit-b提交消失 image.png

可见这种情况下达到了想要的回滚效果

feature分支fetch/pull

假设张三在第2、4步之间在另一个分支feature分支上执行了fetchpull操作,其结果如上,回滚效果生效

develop分支pull

假设张三在第2、4步之间于develop分支上执行了pull操作,那么其过程和结果如下:

  1. 张三在develop分支上执行了pull操作,则本地会有commit-acommit-b提交 image.png
  2. 李四执行第4、5步,进行代码回滚
  3. 张三继续在develop分支上执行pull操作,命令行界面显示 + 7a6e40c...a113d93 develop -> origin/develop (forced update),此时本地的origin/developcommit-acommit-b提交消失,但developcommit-acommit-b提交并未消失 image.png
  4. 张三继续在develop分支上执行git status操作,显示他的分支要领先于远程分支,可以执行git push image.png
  5. 张三继续在develop分支上执行git push操作,则远程重新出现commit-acommit-b提交)

假设一种更复杂的情况,替换掉第5步:

  1. 张三在develop分支修改一些内容,提交了commit-张三,然后再push

  2. 李四在develop分支修改一些内容,提交了commit-李四(与commit-张三不冲突)

  3. 李四在develop分支上执行git pull操作,本地重新出现了commit-acommit-b提交 image.png

结论

如果在push之后,回滚之前,其他人已经将该分支上进行了pull操作,则被回滚的内容将会出现在其他人的代码仓库里,并随着push被继续推送到远程仓库,此时回滚视为失败。如果他人只进行了fetch操作,则对回滚无影响,可视为回滚成功。

但关键在于,无法保证在push和回滚之间,其他人不在此分支上进行pull操作,所以对已经push的内容进行回滚是不安全的,应避免