Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【git重案组】如何逃避git blame的追踪?

【git重案组】如何逃避git blame的追踪?

作者头像
腾讯技术工程官方号
发布于 2019-09-10 04:10:10
发布于 2019-09-10 04:10:10
1.3K0
举报

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

背景介绍

上周笔者在工作中发现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。

是否真的发生过利用这种方案恶意报复公司的案例呢?笔者也是很好奇。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-09-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯技术工程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
day10 | Git的正确使用姿势与最佳实践 | 第三届字节跳动青训营笔记
同时这也是课表的第9天课程《Git的正确使用姿势与最佳实践》。PC端阅读效果更佳,点击文末:阅读原文即可。
千羽
2022/11/11
1.2K0
day10 | Git的正确使用姿势与最佳实践 | 第三届字节跳动青训营笔记
Git的正确使用姿势与最佳实践|青训营笔记
课程链接:https://live.juejin.cn/4354/yc_Git-posture
白泽z
2022/08/18
7010
Git的正确使用姿势与最佳实践|青训营笔记
Git工作流实战-超级干货(3)
本地的 master 和远程分支 origin/master 是关联起来的,origin/master 就对应着远程仓库的 master分支
全栈程序员站长
2022/06/29
4130
使用Git与GitHub协同开发并搭建私有GitLab代码托管服务器
Linus在1991年创建了开源的Linux,从此全世界的工程师参与了Linux的开发,期初Linus是通过手动diff的方式进行代码审核和合并的,后来BitKeeper的东家BitMover公司出于人道主义精神,授权Linux社区免费使用这个版本控制系统。安定团结的大好局面在2005年就被打破了,原因是Linux社区牛人聚集,不免沾染了一些梁山好汉的江湖习气。开发Samba的Andrew试图破解BitKeeper的协议(这么干的其实也不只他一个),被BitMover公司发现了(监控工作做得不错!),于是BitMover公司怒了,要收回Linux社区的免费使用权。Linus花了两周时间自己用C写了一个分布式版本控制系统,这就是Git!一个月之内,Linux系统的源码已经由Git管理了!如果当时BitMover公司专门为Linux发布一个社区版,也许这家公司也就名垂青史了,可是当时这家公司思想境界没那么高而且Linus也那么牛!
星哥玩云
2022/07/24
1.6K0
使用Git与GitHub协同开发并搭建私有GitLab代码托管服务器
Git实战(五)| 让工作更高效,搞定Git的分支管理
上一篇讲到Git的分支管理实操,在线合并和本地合并都进行了实操。毕竟:光说不练是假把式。而只练不整理,只能是傻把式了。分支管理到底如何进行管理呢? 先以GitLab上的一张经典的图打头,作为一个总体概览,也方便理解分支的管理和走向:
霍格沃兹测试开发
2022/04/26
2800
Git实战(五)| 让工作更高效,搞定Git的分支管理
上一篇讲到Git的分支管理实操,在线合并和本地合并都进行了实操。毕竟:光说不练是假把式。而只练不整理,只能是傻把式了。分支管理到底如何进行管理呢?
霍格沃兹测试学院
2020/01/08
6740
Git实战(五)| 让工作更高效,搞定Git的分支管理
Git 全功能介绍
作者:xqkuang,PCG 前端开发工程师 Git 历史和现状 Git 是 Linux 作者 Linus 的另一个作品。2002 年他还在使用 Bitkeeper 作为 Linux 内核的版本管理,但因为它是 Copyright 有版权的软件备受质疑,然后 Andrew Tridgell 对 Bitkeeper 进行逆向工程,导致 BitMover 要回收 Linux 开发者的 Bitkeeper 的免费使用权,Linus 一怒之下花了 10 天写出了 Git。 名字的意思是:egotistical ba
腾讯技术工程官方号
2022/04/07
1.1K0
Git 全功能介绍
Git 实战
完整的把远程库克隆到本地 克隆下来后不要在主分支里面做开发 clone进行一次,从无到有的过程,更新用pull
用户1212940
2020/03/18
6410
版本控制工具Git详解
这是一个学Git无法绕开的话题,也是面试的常见题,我猜很多人的回答都是百度上直接背的,有了解过SVN底层的实现原理吗?
Java架构师必看
2021/05/14
1K0
版本控制工具Git详解
gitflow 开发流程学习(第一部分)
gitflow 流程是非常专业而且标准的 git 处理流程,因为要学习其核心思想和应用,故有此文章系列,本文章系列会分为两部分,第一部分学习基本的内容和基础的流程,第二部分会学习其他流程和 hotfix,release 和 tag 之类的高级用法。 一、gitflow 的分支学习 项目中长期存在的两个分支: master:主分支,负责记录上线版本的迭代,该分支代码与线上代码是完全一致的。 develop:开发分支,该分支记录相对稳定的版本,所有的 feature 分支和 bugfix 分支都从该分支创建。
前端正义联盟
2018/06/20
1.2K0
GIT常用指令
注:并不是修改上一次commit,而是生成新的commit取代上一次commit。
Careteen
2022/02/14
4430
GIT常用指令
超大规模 Spark 集群灰度发布 CI CD
目前主流的代码管理工具有,Github、Gitlab等。本文所介绍的内容中,所有代码均托管于私有的 Gitlab 中。
Jason Guo
2018/10/11
1.6K0
超大规模 Spark 集群灰度发布 CI CD
Git基础知识(七)--分支开发工作流
大型项目中,通过类似的方式使分支具有不同级别的稳定性。当它们具有一定程度的稳定性后,再把它们合入更高级别的稳定性分支中。使用多个长期分支的方法并非必要,但是当你在一 个非常庞大或者复杂的项目中工作时,就会提供很大的帮助。
zx钟
2019/07/19
1.2K0
git的操作说明超详细
个人在学习Git工作流的过程中,从原有的 SVN 模式很难完全理解Git的协作模式,直到有一天我看到了下面的文章,好多遗留在心中的困惑迎刃而解:
双面人
2019/05/22
1.7K0
git的操作说明超详细
Git实战(四)| Git分支管理实操,搞定在线合并和本地合并
类似于SVN这种集中式版本管理,三年前刚来上海工作时候,在华为驻场上班,华为用的就是SVN,印象最深的就是那个小乌龟的图标;后来到外面工作,渐渐发现用Git的非常多,慢慢学习了解发现Git这种分布式的版本管理确实很好很强大,后面也就重点学习Git的分支管理策略了(其实SVN我现在压根就不会了,哈哈。。。)
霍格沃兹测试开发
2022/04/26
3500
git学习总结03 — 分支管理
merge 分支合并有 fast-forward 和 no-fast-forward 两种模式。下图 dev 合入 master,默认触发快进模式(fast-forward),因为只需要修改指针即可实现合并;而普通模式(no-fast-forward)需要生成一个新的commit,因此即使 dev 分支删除,也能从 master 分支历史上看出分支合并信息。
CS逍遥剑仙
2020/05/30
1.5K0
Git 分支管理策略汇总
最近,团队新入职了一些小伙伴,在开发过程中,他们问我 Git 分支是如何管理的,以及应该怎么提交代码?
AlwaysBeta
2022/11/11
1.3K0
Git 分支管理策略汇总
Git入门学习到进阶3
但是在GitHub上,利用Git极其强大的克隆和分支功能,广大人民群众真正可以第一次自由参与各种开源项目了。
全栈工程师修炼指南
2022/09/28
5540
Git入门学习到进阶3
珍藏多年的 Git 问题和操作清单
本文整理自工作多年以来遇到的所有 Git 问题汇总,之前都是遗忘的时候去看一遍操作,这次重新整理了一下,发出来方便大家收藏以及需要的时候查找答案。
猿天地
2019/09/03
1.4K0
珍藏多年的 Git 问题和操作清单
Git实战(四)| Git分支管理实操,搞定在线合并和本地合并
类似于SVN这种集中式版本管理,三年前刚来上海工作时候,在华为驻场上班,华为用的就是SVN,印象最深的就是那个小乌龟的图标;后来到外面工作,渐渐发现用Git的非常多,慢慢学习了解发现Git这种分布式的版本管理确实很好很强大,后面也就重点学习Git的分支管理策略了(其实SVN我现在压根就不会了,哈哈。。。)
霍格沃兹测试学院
2020/01/08
7100
Git实战(四)| Git分支管理实操,搞定在线合并和本地合并
相关推荐
day10 | Git的正确使用姿势与最佳实践 | 第三届字节跳动青训营笔记
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档