7 个让你看起来像巫师的 Git 命令
Source: Dev.to
你知道到底是什么把那些看起来好像很懂的开发者和每天只会去 Google “how to undo git commit” 的人区分开来吗?
这并不是因为懂得花哨的算法或记住设计模式,而是因为你对 Git 的了解已经超越了 add、commit、push,以及慌乱中搜索合并冲突。
我说的就是那些能让你的结对编程伙伴停下来惊呼:“等等,你真的能做到吗?”的命令。那些深层技巧能把 30 分钟的灾难恢复变成 10 秒的炫技。
Source:
1. git reflog – 你不知道的撤销按钮
每个开发者都有这样的时刻:把代码强制推送到错误的分支,执行了 git reset --hard 后看着自己的工作消失,或者在 rebase 之后发现提交历史变成了抽象艺术。
你以为它已经没了。其实没有。
git reflog 是 Git 的飞行记录仪。它会记录 HEAD 曾经指向的每一次位置,即使这些位置已经不在任何分支上。被删除的提交、被放弃的 rebase、强推导致的“牺牲品”——它们全部都在里面。
场景:你刚刚执行了 git reset --hard HEAD~3,立刻意识到把错误的提交给删掉了。胃里一阵翻江倒海,三小时的工作瞬间消失。
# 查看 HEAD 曾经指向的所有位置
git reflog
输出类似:
a1b2c3d HEAD@{0}: reset: moving to HEAD~3
f4e5d6c HEAD@{1}: commit: implement payment webhook
7g8h9i0 HEAD@{2}: commit: add Stripe integration
j1k2l3m HEAD@{3}: commit: refactor checkout flow
只要……回到过去。
git reset --hard f4e5d6c
就这么简单。你的提交又回来了。毫不费力。当其他人惊慌失措、重新克隆仓库时,你已经像时间旅行者一样轻松恢复了历史。
专业提示:Reflog 条目默认在 90 天后过期,所以别等到三个月后才发现需要恢复某些内容。
2. git bisect – 让 Git 为你找出 bug
想象一下:你的 CI 通过,测试也通过,但在最近的 47 次提交中,登录页在 Firefox 上开始倒置渲染。没有人知道是哪一次提交导致的。
你可以像动物一样一个一个地检出每个提交。或者让 Git 自动在历史记录中进行二分查找。
# 开始查找
git bisect start
# 将当前(出错)状态标记为坏
git bisect bad
# 标记一个已知的好提交(比如上周的发布标签)
git bisect good v2.3.1
Git 会检出中间的提交。你进行测试。
# 如果它坏了:
git bisect bad
# 如果它正常:
git bisect good
重复上述操作。Git 会在 log₂(n) 步内缩小范围。
47 次提交? 只需要大约 6 次检查,而不是 47 次。
如果在好状态和坏状态之间有 1 000 次提交,你大约只需 10 步就能找到罪魁祸首。它实际上在你的提交历史上做了一次二分搜索。这才是真正有用的计算机科学™。
**进阶用法:**如果你有一个可以自动判断好坏的测试脚本:
git bisect start HEAD v2.3.1
git bisect run ./test-login-page.sh
去喝杯咖啡吧。等你回来时,Git 已经找到了导致问题的确切提交,你只需指向那个有罪的提交,就能像终端里的福尔摩斯一样。
3. git stash -p – 外科手术式的暂存
大家都知道 git stash。但大多数人把它当作大锤子来用:把所有改动都暂存,然后祈祷它能完好无损地恢复。
-p 参数(patch 的缩写)让你交互式地选择 哪些 块要暂存。适用于你同时在处理两件事(别装作不知道)的场景,需要只提交其中一部分而不包括另一部分。
场景: 你正进行一个功能的开发,但在途中发现并修复了一个 bug。你想现在就提交这个 bug‑fix,然后继续开发功能。
# 交互式挑选要暂存的内容
git stash -p
Git 会一次展示一个改动块:
Stash this hunk [y,n,q,a,d,s,e,?]?
y = stash this hunk
n = keep this hunk (don’t stash)
s = split into smaller hunks
q = quit (done selecting)
此时你的工作目录只剩下 bug‑fix。
git add -A && git commit -m "fix: null check on user session"
把功能的工作恢复回来:
git stash pop
s 选项是秘密武器:当 Git 把两个改动合并成一个块时,它可以把该块再拆分成更小的块。对工作树进行精确的外科手术。
额外组合:
git stash -p -m "WIP: auth refactor"
让你既可以选择性地暂存,又可以为暂存项命名,避免三天后玩“哪个暂存是哪一个?”的轮盘赌。
Source: …
4. git rebase -i – 像你本来想的那样重写历史
交互式 rebase 是唯一最强大的 Git 命令,也是大多数人最害怕的。别再害怕了。它不过是编辑一个 TODO 列表。
你可以把提交压缩(squash)在一起、重新排序、重新编写提交信息、完全丢弃提交,或者就地编辑它们。它就是在文本编辑器里操作你的提交历史,而你就是那个文件的“神”。
场景: 你在实现一个功能时做了 6 次提交,其中三次是 “fix typo”(修正拼写错误)、“WIP”(进行中)和 “actually fix the thing I said I fixed”(实际上修复了我说过要修的东西)。你不想让这些出现在永久记录里。
# 交互式地 rebase 最近的 6 次提交
git rebase -i HEAD~6
编辑器打开后显示:
pick a1b2c3d add user authentication
pick d4e5f6g WIP: trying stuff
pick h7i8j9k fix typo in auth middleware
pick l0m1n2o actually implement auth middleware
pick p3q4r5s add auth tests
pick t6u7v8w fix test assertion
将其改为:
pick a1b2c3d add user authentication
squash d4e5f6g WIP: trying stuff
squash h7i8j9k fix typo in auth middleware
squash l0m1n2o actually implement auth middleware
pick p3q4r5s add auth tests
fixup t6u7v8w fix test assertion
保存并关闭。现在你拥有了一段干净、专业的历史。
squash将提交合并到前一个提交,并允许你重新编写提交信息。fixup在不提示新信息的情况下合并提交。
5. git cherry-pick:从其他分支偷取
有时你只需要另一个分支上的单个提交。不是合并,也不是变基——只要同事在 develop 上推送的那个紧急修复,而你现在迫切需要在你的功能分支上使用它。
# 通过哈希值抓取特定提交
git cherry-pick a1b2c3d
# 需要多个提交?可以。
git cherry-pick a1b2c3d f4e5d6c
# 只挑选更改而不提交(仅将更改暂存)
git cherry-pick --no-commit a1b2c3d
实际会出现的场景: 生产环境出问题了。修复已经在 develop 分支中,但它被埋在 30 条其他尚未准备好上线的提交之下。你只需要这个修复,而不是那 30 条其他内容。
git checkout main
git cherry-pick d4e5f6a # 只挑选热修复的提交
git push origin main
注意: Cherry‑pick 会创建一个新提交,内容相同但哈希不同。如果以后合并源分支,Git 通常能智能处理,但有时会出现看似重复的冲突。这就是权衡所在——值得。
6. git log -S: 搜索整个历史
git log 本身很无聊。git log --oneline 稍微好一点。但 git log -S 就像带搜索栏的时光机。
-S 标志(称为 pickaxe)会在历史中每个 diff 的 内容 中搜索——而不是提交信息,而是实际的代码改动。它会找到添加或删除特定字符串的提交。
情景: 有人删除了 validatePayment() 函数。没人知道何时删除的,也没人知道为什么删除,git blame 也帮不上忙,因为自那以后文件已经重构了六次。
# 查找每个添加或删除 "validatePayment" 的提交
git log -S "validatePayment" --oneline
示例输出
f4e5d6c remove legacy payment validation
a1b2c3d refactor: extract payment validation
9z8y7x6 add payment validation to checkout
# 想看实际的 diff 吗?
git log -S "validatePayment" -p
# 使用正则表达式搜索
git log -G "validate.*Payment" --oneline
-S查找出现次数发生变化的提交。-G查找字符串出现在 diff 中的提交(即使出现次数没有变化)。
对于大多数调查工作,-S 是你想要的。
没人提及的强大组合
git log --all --oneline --graph --decorate
这会在终端直接显示分支拓扑的可视化图。把它设为别名 git lg,你几乎不再需要 GUI Git 客户端(好吧,有时会用,但频率大大降低)。
7. git worktree:一次在两个地方
这个功能真的被低估了。git worktree 让你能够在不同目录中 同时 检出多个分支,所有目录共享同一个 .git 数据。再也不需要 stash, 再也不需要把仓库克隆两遍, 再也不需要为了切分支而提交半成品代码。
场景: 你正埋头在一个特性分支上。项目经理发来信息:“main 上有严重 bug,必须立刻修复”。你到处都有未暂存的实验代码,真的不想把这些东西全部 stash。
# 为热修复创建一个新的 worktree,检出 main
git worktree add ../hotfix-login main
此时你的目录结构如下:
/projects/myapp → 你的特性分支(保持不变)
/projects/hotfix-login → main 分支,准备修复
cd ../hotfix-login
# 修复 bug,提交,推送。你的特性分支完全不受影响。
完成后,清理工作:
cd ../myapp
git worktree remove ../hotfix-login
这比 git stash 用于上下文切换要好得多。你的特性分支保持原样——脏文件、打开的编辑器标签页,全部不变。热修复则在一个完全独立的目录中进行。
我几乎每天在代码评审时也会用它。可以在单独的 worktree 中检出同事的分支,运行、调试,而自己的工作保持不受影响。彻底改变游戏规则。
实际的高手操作
这些命令都不是“高级”的。它们都写在文档里,已经存在多年。之所以让你看起来像个巫师,是因为大多数开发者从不去学习 add、commit、push、pull 之外的东西,遇到合并冲突就慌忙搜索。
你今天不必全部记住。挑一个就行。下次遇到 Git 烂摊子时,尝试使用它,而不是直接删库重新克隆。这就是升级的方式:一次一次的 “哎呀” 时刻。
有哪条 Git 小技巧帮过你吗? 在评论里留下吧。我一直在寻找新的法术。 🧙♂️