导语:程序员的血腥复仇——论如何偷偷修改代码而不被别人发现...

背景介绍

上周笔者在工作中发现git仓库出现了一个奇怪的问题,master分支中某文件的一次commit丢失掉了,但diff中没有任何记录,这让笔者一度怀疑是git或者code平台自己出了问题。

在code平台一条条比对后发现变动发生在feature分支merge master分支之后。

原本SHA为8950d的edit.vue 文件最近一次修改是在一周前。

在merge之后该文件回滚到了两周前。

通过查询该文件的commit记录,可以看到最近的一次SHA为49c1a的commit确实丢掉了。

先明确前提,这是在一次merge中丢失的,而非经历了rebase或者reset操作,并没有对历史记录进行修改。

这里回顾下整个过程中的git 操作流,先从master checkout一个feature分支,在该分支提交了几次commit,merge master 到 feature,然后在master再次merge feature。

应该说这里虽然有不规范之处(没有提交merge request而是本地直接在master上merge然后push),但整体还算常规操作,即使是在merge中发生了冲突,不小心操作失误,按道理也不会没有diff记录。

merge的parent-1和parent-2

google一下找到了一篇相似的文章https://blog.laisky.com/p/git-merge/

该文章是在master分支上git pull,由于pull 的默认行为是 pull —merge,所以其实也是在merge中丢失的。

原文作者给出了一个比较清晰的解释:

众所周知,merge 是将两个 branch 合并为一个,所以每一个 merge commit 拥有两个 parents。当我们在 gitlab 或者 source tree 查看一个提交的具体修改时,其实就是将本次提交和其 parent 做 diff。而由于 merge commit 有两个 parent,并会将其排序为 1 和 2,当你试图查看一个 merge commit 的修改时,其实显示的是相对于 parent-1 的 diff。这样的一个问题是,如果 remote 不幸成为了 parent-2,那么你就可以通过巧妙的构造 parent-1 来实现一次“隐身”的代码修改。

我们提取原文核心,重点在于merge时的diff记录是相对于当前分支,假如当前分支是两周前的版本,而外来分支是一周前的版本,当merge时放弃掉一周前的版本,对原分支来说这次merge之后与之前并未发生改变,所以diff中自然也没有记录。

merge request 的不同之处

这个解释似乎也说的过去,不过在合并到master分支之前必然要本地merge一下master才可以快速合并,这个操作是逃避不了的,如果在本地merge时错误解决冲突会被隐藏下来,这岂不是git一个很大的缺陷吗?那code平台的merge request后的code review还有意义吗?

笔者自己搭建了一个测试仓库发现如果提交merge request,在code review的diff界面是看得到这次修改的,在提交之后也能在history中看到diff。难道gitlab(code平台应该是基于gitlab开发的)平台自己的diff算法更高级,所以才能发现这次错误?

笔者到这里产生了一个猜测,在本地操作的时候git 的diff算法有缺陷,它简单地把每一次commit的diff patch在一起,而code平台是老老实实做了两个文件夹的diff。

git diff的差异

在google之后,果然发现了不同(其实并不然…)!

在几个stackoverflow的问答和github的issue中笔者发现 github平台的pull request(虽然gitlab是merge request,实际上差不多)是使用了git diff的三点操作,而直接diff是两点操作,区别如下:

笔者一度以为突破口就在这里,但是仔细分析了git log —graph之后发现在merge request之前本地feature分支就已经merge了一次master,在这个情形下git diff的两点操作和三点操作根本没什么不同。

链接:What are the differences between double-dot “..” and triple-dot “…” in Git diff commit ranges? - Stack Overflow

https://github.community/t5/How-to-use-Git-and-GitHub/GitHub-pull-requests-showing-invalid-diff-for-already-merged/td-p/3000

merge的原理和fast-forward

Git merge采取三路合并策略,三路分别是基准分支(分叉的节点)、mine、theirs。

如果mine和theirs相对基准都发生了改变 那git 就报冲突,然后让你人工决断。否则,git将取相对于base变化的那个为最终结果。

一次普通的merge会新建一个commit节点(7号节点)。

而如果在feature分支从master checkout之后,master并未出现新的commit,就会出现三种策略。

默认git merge会采取第三种策略,直接将master指针移到feature的头上即可,这里不会出现一个message为“merge xxx into xxx”的commit。

回到问题发生的场景上,在feature分支上执行git merge master的时候发生了一次普通的合并,生成一个“merge xxx into xxx”的commit,由于上文说到的原因,这个commit节点没有记录diff。当checkout回master再从master merge feature分支的时候,满足了fast-forward的条件,所以没有再次进行diff操作,没有对上次失误进行再次检查。

而code平台merge request默认的操作是—no-ff(这里补充一下,github是有squash选项的,但是code平台不支持),所以会强制再次进行一次diff,这时候上次merge中隐藏的错误得到了一个再次暴露出来的机会,在code review中就可以发现了。

解决方案

这个问题出现的根本原因有两个:
  • 浅层原因:merge时错误处理了冲突

  • 深层原因:没有走code平台merge request,没有禁止master分支直接pull
笔者回顾这个问题时想到,假如别有用心的人利用这种机制上的漏洞,在merge中故意修改代码,这些修改将不会出现在git的任何一次commit diff中,除非对master分支上一个挨一个commit排查。
甚至于在merge时采取squash或者rebase等方法,把这次commit 与其他commit混淆起来,是否就可以彻底把自己隐匿起来呢?
为了避免重现此次错误,强烈建议提高master分支敏感性,设置为protected分支禁止直接操作,所有对master分支的merge统一走merge request!
额外提一句,还应该避免在公用开发机上设置code平台 ssh 密钥,防止被盗用身份提交commit。
是否真的发生过利用这种方案恶意报复公司的案例呢?笔者也是很好奇。

【git重案组】如何逃避git blame的追踪?相关推荐

  1. Git详解之六 Git工具(转)

    Git 工具 现在,你已经学习了管理或者维护 Git 仓库,实现代码控制所需的大多数日常命令和工作流程.你已经完成了跟踪和提交文件的基本任务,并且发挥了暂存区和轻量级的特性分支及合并的威力. 接下来你 ...

  2. Git学习5:Git常用命令简明用法

    不要使用git commit -a 该命令可以对本地所有的变更文件(包括对本地修改和删除的文件)执行提交操作,但是不包括未被版本库跟踪的文件. git命令补充说明 显示.git目录所在的位置 git ...

  3. Git详解之六 Git工具

    Git详解之六 Git工具 Git工具 现在,你已经学习了管理或者维护 Git仓库,实现代码控制所需的大多数日常命令和工作流程.你已经完成了跟踪和提交文件的基本任务,并且发挥了暂存区和轻量级的特性分支 ...

  4. 你什么时候使用git rebase而不是git merge?

    什么时候建议使用git rebase与git merge ? 成功改造后我还需要合并吗? #1楼 在合并/ rebase之前: A <- B <- C [master] ^\D <- ...

  5. 【代码管理】GitHub超详细图文攻略 - Git客户端下载安装 GitHub提交修改源码工作流程 Git分支 标签 过滤 Git版本工作流

    GitHub操作总结 : 总结看不明白就看下面的详细讲解. . 作者 :万境绝尘  转载请注明出处 : http://blog.csdn.net/shulianghan/article/details ...

  6. 【git学习二】git基础之git管理本地项目

    1.背景 git基础打算分两部分来说,一部分是对于本地项目的管理,第二部分是对于远程代码仓库的操作.git执行本地项目管理包括对于相关文件的追踪,暂存区的比较分析,提交,撤销等功能. 2.本地项目管理 ...

  7. 一个 .git 目录,领悟 Git 的强大!

    Git 是一个强大的工具,但是使用起来却不是很友好.如果程序员们能够真正花时间去理解 Git 的构成,将会避免很多不必要的麻烦. 以下为译文: 初学 Git 就像一个不懂当地语言的人来到一个陌生的国家 ...

  8. Git知识总览(六) Git分支中的远程操作实践

    前几篇博客陆陆续续的讲了好多关于Git操作的内容, 其中在上篇博客聊了<Git中的merge.rebase.cherry-pick以及交互式rebase>,本篇博客仍然也不例外,不过本篇博 ...

  9. springboot去掉git版本控制_关于 Git 提交这些规范,你都遵守了吗?

    来源:人人贷大前端技术中心http://juejin.im/post/5d0b3f8c6fb9a07ec07fc5d0 git是现在市面上最流行的版本控制工具,书写良好的commit message能 ...

  10. 【git学习】统计git项目某user的代码量

    查看自己的代码量:(直接awk编程) git log --author="username" --pretty=tformat: --numstat | awk '{ add += ...

最新文章

  1. 动态规划:求最大公共子串
  2. 饿了么风神登录_海底捞首度登陆第三方外卖!联手饿了么全国配送“小火锅”...
  3. 8.Using Categorical Data with One Hot Encoding
  4. jsp网页实现自动刷新和自动跳转页面
  5. python 连接MYSQL数据库(入门帖)
  6. 遵义 计算机应用能力,遵义市人力资源和社会保障局
  7. java.sql.SQLException: Field 'id' doesn't have a default value解决方法
  8. XLSTransformer 导出Excel数据
  9. 计算机专业笔记本需要小键盘吗,笔记本小键盘数字键不能用 并点击解除锁定...
  10. weblogic部署war包
  11. OSChina 周四乱弹 —— 人类首张黑洞照片
  12. php正则表达式在线测试工具,在线测试正则表达式工具:适合asp.net vb.net cs.net等Web或者Windows程序,便于你快速编写正确正则表达式,提供正则表达式模板供参考。...
  13. Java ffmpeg视频压缩IOS播放没有声音问题解决(超级简单)
  14. 计算机科学的顶级会议期刊,一些计算机领域的顶级会议和期刊
  15. 【电气专业知识问答】问:什么叫组合电器?什么是GIS?
  16. 西南科技大学计算机考研好考吗,西南科技大学考研难吗?一般要什么水平才可以进入?...
  17. 百度图片推广广告位怎么做,图片广告位如何投放
  18. MindFusion教程:Charting for Java Swing中的FunctionSeries
  19. Linux定时运行Python脚本
  20. JDK源码阅读之路【不断更新】

热门文章

  1. Cool!15个创意的 CSS3 文本效果【下篇】
  2. 软引用、弱引用、 java
  3. [转]C#中多路IP摄像机的视频监控系统
  4. PHP 解析xml(包含非英文字符)
  5. 牛客多校2 - All with Pairs(字符串哈希+next数组)
  6. CH - 0502 七夕祭(思维+中位数优化+前缀和优化)
  7. 皇牌空战无限服务器,《皇牌空战:无限》正式停服 一个搏击长空的时代终结[多图]...
  8. edit plus 中文插件_iMindMap手绘思维导图软件中文版
  9. linux排序语言,Go语言排序sort的使用
  10. ESP8266开发笔记