HN 382分热文里的终端小技巧,我实测了一遍,git pickaxe 最值

今天早上刷 HN,看到一篇 380 多分的热文,标题就叫 Small Programming Tricks,作者 Will Keleher。核心论点一句话就能说完:日常工程效率的大头,不是什么大架构决策,而是一个个小的知识点------知道某个语言特性存在、知道一条 git 冷命令、知道一个 sed 的写法。 说实话,"小技巧合集"这个题材我平时是直接划走的,它和水文之间只隔一层纸。但这篇有个不一样的地方:给的每个技巧都不需要前置知识,属于"知道就能用",而且作者提了一嘴自己的做法------之前在公司每天往 Slack 发一个技巧,发了一年,反响意外地好。我来了兴致,把终端打开,挑了几个挨个跑。有俩确实好使,还有一处原文的说法有歧义,实测之后才搞清楚坑在哪。 先说最值的。

git log -S,找一段代码的生死记录

原文管这个叫 "git pickaxe"(镐子),命令是 git log -S"字符串"。作用:列出所有让这个字符串在代码库里"出现次数发生变化"的提交。说白了就是找一段代码的生与死------它是什么时候加进来的,又是什么时候被删掉的。

我正好有个现成的案例。去年我们网关里有个限流逻辑,方法叫 checkQuota,后来一次大重构之后整个没了。当时想查它是被哪个提交动掉的,用的是 git blame,但重构把代码挪来挪去,blame 追到的行早就不是原来那行了,查了个寂寞。 这次直接:

lua 复制代码
git log -S"checkQuota" --oneline

输出大概长这样:

yaml 复制代码
b7e2d41 perf: 令牌桶替代计数器,网关层限流重写
8a3f19c fix: checkQuota 并发场景下的计数越界
c01d9e2 feat: 网关层接入限流

三个提交,把这段代码的一生讲完了:c01d9e2 把它加进来,8a3f19c 修过一次 bug,b7e2d41 把它删了。注意中间那个 fix 提交-------S 能看到它,是因为那次修复改变了 checkQuota 这个字符串的出现次数(函数体里多了一次调用)。

这和 git blame 的区别值得单独说一句。blame 回答的是"这行代码现在是谁写的",一旦代码被移动或重构,blame 的锚点就断了;-S 回答的是"这个字符串的生与死",它不关心代码在哪一行,只关心出现次数。所以越老的、被反复重构的代码库,-S 越好用。 原文还提了一句 git log -G"正则" 和它类似,但行被移动时也会显示。这个区别我正好构造了个场景试了:把调用 checkQuota 的那几行从方法头部挪到尾部,不增不减。

shell 复制代码
$ git log -S"checkQuota" --oneline   # 无输出:出现次数没变,镐子不看它
$ git log -G"checkQuota" --oneline
e4c1b99 refactor: 调整校验调用位置

结论很清楚:-S 数出现次数,纯移动它不管;-G 看 diff 里有没有匹配正则的增删行,挪动位置也算。想要"生死记录"用 -S,想要"所有碰过它的提交"用 -G

原文一个有歧义的说法:git checkout -

原文里有句 git checkout -,说它类似 cd -,能"check out your previous HEAD"。我第一反应是这玩意我经常用啊,不就是回到上一个分支吗。但再一琢磨,"previous HEAD"这个说法容易被理解成"回到上一个提交",然后有人在自己分支上敲一下试试------

css 复制代码
$ git branch
* feat-search
  main
$ git checkout -
Switched to branch 'main'

看,在 feat-search 上敲 git checkout -,它把你切回 main 了。"上一个 HEAD"指的是上一个所在的分支位置(等价于 git checkout @{-1}),不是上一个提交。如果你刚从 main 切到 feat-search 想安安静静干活,这一敲直接给你送回 main,工作区的东西还在,但分支换了,接下来的提交就跑到 main 上去了。 它真正好使的场景是来回横跳:改 feature 分支,切 main 看个东西,git checkout - 一步回来,再敲一下又过去,和 cd - 一个手感。至于"回到上一个提交",那是 git reset --hard HEAD~1 的事,别混。原文这句不算错,但确实容易让人想歪。

历史搜索:fzf 好使,atuin 惊艳

Ctrl+R 反向搜索历史,这个估计大多数人都知道。原文推荐的两个进阶,我都装了。 fzf 装好 shell 集成之后,Ctrl+R 变成模糊搜索,敲几个片段就能把那条命令捞出来,体验比原生好一截。这个属于"装了就回不去"系列,没什么好说的。 真正让我意外的是 atuin。它把 shell 历史整个搬进一个 SQLite 数据库:

arduino 复制代码
curl --proto '=https' --tlsv1.2 -LsSf https://setup.atuin.sh | sh
atuin import auto    # 把现有 shell 历史导进去

然后在 .zshrc 里加一行 eval "$(atuin init zsh)",重开终端,Ctrl+R 就变成它的全屏搜索界面了。按目录过滤、按退出码过滤、按时间范围过滤,都能做。命令行里也能直接查:

shell 复制代码
$ atuin search "docker compose" --limit 3

我库里攒了小半年的历史,import 完两万多条,搜两个月前那条带一长串挂载参数的 docker 命令,两秒的事。以前这种时候我的操作是 history | grep,翻半天。 要说顾虑,就是它默认还想让你注册账号做跨机同步------同步是可选的,不注册纯本地用也没问题,但我能理解有人对"历史命令上云"过敏,我自己是只开本地。

其他几个,顺手验了下

剩下的就不挨个展开了,挑几个说印象:

SELECT 不带 FROM 这条,原文举的例子是 SELECT TRUE <> NULL,让我先猜返回什么。我猜 true,拿 sqlite3 一跑,返回的是空(NULL)------NULL 和任何值比较结果都是 UNKNOWN,SQL 三值逻辑这个坑,光看书真不如自己跑一遍记得牢。

EXPLAIN ANALYZE 是常识级的,PostgreSQL 和 MySQL 8.0.18+ 都支持,真执行一遍查询再给性能数据,比纯 EXPLAIN 靠谱,做慢查询优化的应该都在用,不细说。

globstar 那条我之前真没用过:bash 里 ** 递归匹配默认是关的,shopt -s globstar 打开后 ls **/*.md 能替代大部分 find,zsh 原生就支持。就顺势把 rg 的事也说了------rg 替代 grep 没什么可争的,没换的今天换。 JS 那几条里 Promise.withResolvers() 我之前真不知道,Node 22 起可用:

arduino 复制代码
const { promise, resolve, reject } = Promise.withResolvers();

以前要自己在外面声明两个变量才能在 Promise 外面控制 resolve,现在一行。写任务队列的时候挺干净。

写在后面

一上午跑下来,我的结论和原文重合度挺高:这堆技巧单拎出来哪个都撑不起一篇文章,但它们是纯赚的------学的时候两分钟,用的时候每次都省一点,攒起来就是复利。

我自己的做法是在笔记里开了一个"终端小技巧"的页面,每次从别人文章或者 HN 评论里捡到一条就记一条。这次实测完又添了三条进去。至于原文作者那种每天往群里发一条的做法,我们组倒是可以试试,就是得做好被同事嫌烦的心理准备。 工具全集齐了,活还是得自己干,这个道理小技巧救不了。

相关推荐
横木沉10 小时前
IntelliJ IDEA 无法识别 Git:Git is not installed 问题解决
java·git·github·intellij-idea
为你学会写情书12 小时前
Git 学习指南:从日常命令到多人协作与提交规范
git
东小黑13 小时前
Git 仓库操作与 VSCode 使用指南
git·vscode·gitee
拼图2091 天前
Git常用语法
分布式·git·学习
ShineWinsu1 天前
对于Git:基础操作的超详细保姆级解析
linux·c++·git·面试·备份·管理·版本控制器
沉迷...1 天前
Git skip-worktree 使用笔记(本地忽略配置文件)
javascript·git
解道Jdon1 天前
JDK 27发布:紧凑对象头、G1默认、量子安全TLS
ide·windows·git·svn·eclipse·github·visual studio
天天喝旺仔2 天前
Git 内部原理深度解析:从 blob/tree/commit 对象到 packfile 与垃圾回收
数据结构·数据库·git·算法·哈希
艾莉丝努力练剑2 天前
【Git:综合复盘】Git 原理与使用
大数据·人工智能·git·elasticsearch·面试