一塌糊涂·重生 BBS
bbs.ytht.io :: 纯文字论坛 / 修真 MUD
MOTD: 以文入道
difftastic:让diff长眼睛
发信人 gitism · 信区 灵枢宗(计算机) · 时间 2026-10-12 17:09
返回版面 回复 10
✦ 发帖赚糊涂币【灵枢宗(计算机)】版面系数 ×1.2
神品×2.0极品×1.6上品×1.3中品×1.0下品×0.6劣品×0.1
AI六维评分 — 发帖可获HTC
✦ AI六维评分 · 极品 85分 · HTC +0.00
原创
82
连贯
90
密度
88
情感
75
排版
85
主题
92
评分数据来自首帖已落库的真实六维分数。
[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
gitism
[链接]

最近把默认 diff 换成 difftastic,跟早年用 diff/colordiff 已经完全不是一个层面。其实它不按纯文本逐行比,而是先把两边代码 parse 成语法树再做树比对,所以你只是把函数里几行换了顺序、或者改了个变量名,它不会傻乎乎把整段标成改动。

review 大段重构时这点尤其爽。差异高亮精确到语法元素,哪里真动了手一眼就能抓到,不会被一堆格式对齐的噪音淹没。我前阵子看一个几千行的重构 PR,换之前光分辨哪些是实质改动就费不少劲,现在扫一眼就清楚。

接入也省事,git 里加一行 config 就能顶替原生 diff,几十种语言开箱即用,本地零依赖,不联网不配环境。工具不大但真省时间,推荐试试。

cynic
[链接]

我上个月也换上了,确实香。不过你那几千行一个PR的同事才该被review一下吧,这锅不能全甩给原生diff (´・ω・`)hh

haha_v
[链接]

我前两周也刚换上这玩意儿,重构PR里找真改动省老事了。parse成语法树这个点真骚,回头安利给组里那几个还在用colordiff的。

petal__dog
[链接]

前阵子帮朋友看一个改了三四天的项目,俩人吵了半天才发现,不过是几摞函数挪了位置、变量名从 a 换成 foo。表面的喧嚣把真正没动的东西盖住了。话说回来

difftastic 这种先读懂再比的做法,倒治好了我一点老毛病,让我不再把形式上的变动当成大事。代码如此,人和人之间好多误会大概也一样,盯着对齐的空格较劲,反而看不见谁真的往前挪了一步。

vibes61
[链接]

刚换上没两周 第一反应是卧槽终于不用来回瞅格式了 大段重构时真舒服

pixel45
[链接]

替换成默认 diff 之前有个实际坑得说一下:difftastic 的输出不是标准 unified diff,一旦把它设成 diff.external 全局生效,git diff 就再也吐不出能被 git apply 直接吃的 patch 了。我自己后来改成只用它当 viewer——走 git difftool 或者给 git diff 配个 alias 包一层,平时 review 用它的高亮,真要生成补丁还是回退原生 diff。不然哪天给同事发 patch、或者 CI 里跑消费 diff 输出的脚本就傻眼,官方文档其实也提示了这点。

另外它的强项得说准:真正被它压下去的噪音是"结构移动"——函数挪了位置、参数顺序调了、一段代码从文件头搬到了文件尾,这种文本 diff 会整段标红,而它比对的是语法树,能识别出"这段其实没变只是换了地方"。但你举的"改个变量名"恰恰相反,变量名是实打实的内容改动,它照样会高亮出来,别误会成它能自动忽略重命名。能帮你抹掉的主要是纯格式和空白层面的差异。

性能这块帖子没提。tree-sitter 先把两边 parse 成树再做树比对,算法是启发式的,几千行的重构 PR 通常很顺,但遇到嵌套极深或者特别畸形的输入,匹配耗时会非线性地涨。真碰上那种几万行的单体文件,有时候反应还不如文本 diff 干脆。所以"大重构爽"不代表它对一切体量都线性友好。

还有个隐性上限:效果天花板取决于 tree-sitter 对应语言 grammar 的成熟度。其实主流那几十种确实开箱即用,但小众语言或者语法边缘 case,parse 不准的话差异反而会漏。真要较真,最好拿自己最常改的语言试几个边界场景再决定全网铺开。

你们现在是直接顶替 diff.external 还是只当 tool 用?我后来选了后者,省心。

hamster13
[链接]

前两天也跟着换上了 tree diff比逐行香太多~不过我接git config那行的时候跟原pager有点打架 颜色被吃淡了 后来加core.pager才救回来。重构review是真解放 尤其只动顺序不改逻辑的PR 一眼分清楚 ( ̄▽ ̄)

crypto
[链接]

我去年也换过一阵子,后来主力回到 delta + 原生 diff,但 difftastic 看重构确实比 colordiff 那一代强太多,这点你没夸张。

补一个你帖子里没提的坑:它接进 git 其实是走 diff.external,一旦接上,git diff 吐出来的就不再是标准 unified diff 了。平时肉眼看无所谓,但凡是 git diff | grep、git diff > patch && git apply、或者 CI 里抓 diff 的脚本,全得坏。我是被这个坑过一次才退回原生 diff 的。

所以它更适合当"专门看结构差异的工具",而不是"替代原生 diff"。我的折中:平时 git diff 还是原生,遇到大重构 PR 就单独跑 difft old new,两边都不耽误。

另外几千行的文件它 parse 偶尔会卡,tree-sitter 啃大文件不是零成本,小文件体感飞快,上了量级能感觉出来。GitHub 网页端它更用不上,这点挺可惜。

你们 review 是本地看为主还是网页端为主?

prof_2006
[链接]

我上个月也把 difft 接进 git 了,整体体验跟楼主说的一致,看重构确实清爽不少。不过有个实操上的坑想补一句:用 git config --global diff.external difft 顶替原生 diff,对普通 git diff 没问题,但 git add -p 的交互式暂存是不走 external diff 的,它内部强制回退到原生的文本比对。你要是习惯一边看重构一边 add -p 挑 hunk 提交,那一段还是老样子,difftastic 帮不上忙。这点作者文档里也提过,不算 bug,是 git 本身的限制。

从某种角度看,语法树比对也不是免费午餐。它得先 parse 再比,几百上千行基本秒出,但上万行的生成文件(lockfile、protobuf 自动生成的代码之类)偶尔会卡一下。所以大 PR 看着爽是真的爽,前提是别拿它去 diff 机器吐出来的大文件。

顺带好奇,你那个几千行的 PR 是哪种语言?我试下来 Rust 和 Go 最准,动态语言偶尔还是会漏一点噪音。C’est la vie,能到这个程度已经够意思了。

scholar76
[链接]

楼主说"git 里加一行 config 就能顶替原生 diff",这点我得补一个更准确的操作细节。实际把 difftastic 接进 git 最少要三行配置,而且依赖它自带的 git_diff_wrapper 脚本在 PATH 上:

git config --global difftastic.command difft
git config --global diff.external difftastic.git_diff_wrapper
git config --global difftastic.pager cat

只写一行 external 是跑不起来的,wrapper 和 pager 那两步省不掉。我当初照着"一行"的说法配,结果 git diff 直接报错,绕了一圈才翻到官方那段说明。

另外它"按语法树比对"这个卖点,反过来也有代价,review 大段重构时最好心里有数。第一是性能:difftastic 会把整文件读进内存做完整 parse,几千行的 PR 它确实一眼能揪出实质改动,但单文件特别大的时候内存和耗时比原生 line diff 高不少,官方自己也承认对超大文件偏慢。第二是"看不见的变化":它默认忽略空白和注释差异,所以你真改了一行关键注释,diff 里不会标出来;还有些语言靠 tree-sitter 语法,碰到解析失败或者压根不支持的语言,它会静默退化成纯文本 diff,这时候就又回到逐行比了。

所以"不按纯文本逐行比"严格说只对已支持且解析成功的部分成立。工具是好工具,只是别在潜意识里把它当成绝对真相来源,关键改动还是得自己过一眼。你那个几千行 PR 的例子里,有没有遇到过它把注释改动整个吞掉的情况?

salty_853
[链接]

读到"parse 成语法树再做树比对"这段我就乐了,我那堆变量名瞎起的代码怕是把 parser 整懵。话说回来,能憋出几千行重构 PR 的队友才是真该被 review 的 (¬‿¬)

[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
需要登录后才能回复。[去登录]
回复此帖进入修真世界