OctoBlog 以 GitHub 为蓝本的个人博客系统
全部文章

我的 Git 工作流:从一堆乱七八糟的 commit 到一个能读的历史

一条规矩:主分支上的每个 commit 都应该是可运行的

这意味着两件事:功能分支允许乱,合并前必须干净。

BASH
# 功能分支上的历史长这样
$ git log --oneline
a1b2c3d fix typo
9e8f7g6 WIP
5d4e3f2 还是不行
1c2b3a4 加个日志看看

# 合并前整理成一条,或者拆成有意义的几条
$ git rebase -i main

整理历史:fixup 是重点

我几乎不用 squash,因为它在交互式 rebase 里需要我重新写一遍 message。更好的做法是在提交时就标记:

BASH
$ git commit --fixup=1c2b3a4
$ git rebase -i --autosquash main

--autosquash 会自动把 fixup! 的提交排到目标提交后面并标记为 fixup,我只需要 :wq。

用 worktree 同时处理两件事

线上出 bug 时,我不想 stash 掉手头做到一半的功能。git worktree 就是为此而生:

BASH
$ git worktree add ../hotfix main
$ cd ../hotfix
$ git switch -c fix/urgent-null-check
# 修完、提交、推送,然后
$ git worktree remove ../hotfix

两个目录共享同一个 .git,不需要克隆第二份仓库。

找 bug:git bisect 比你想的好用

BASH
$ git bisect start
$ git bisect bad                 # 当前版本有问题
$ git bisect good v1.4.0         # 这个版本是好的
$ git bisect run npm test        # 全自动二分

20 个 commit 的范围,4 次构建就能定位。

三个救命命令

命令 什么时候用
git reflog 误删分支、rebase 搞砸了
git switch -c new main 想知道"这个文件以前长什么样"
git add -p 一个文件里混了多个改动,想分开提交

git reflog 是底线保障:只要 commit 过一次,通常在 90 天内都能找回来。

BASH
$ git reflog
5d4e3f2 HEAD@{0}: rebase (finish): returning to refs/heads/main
1c2b3a4 HEAD@{1}: commit: 加个日志看看
# 找回被 rebase 丢掉的提交
$ git switch -c rescue 1c2b3a4

关于 pull

我的默认配置:

BASH
$ git config --global pull.rebase true
$ git config --global rebase.autosquash true
$ git config --global merge.conflictStyle zdiff3

zdiff3 会在冲突里同时显示「原始版本 / 你的版本 / 对方版本」,比默认的 diff3 更容易判断。用过就回不去了。

一条经验:Git 的复杂度只在你想undo时才显现。 平时花十分钟学会的原理,能省下未来某个下午的慌乱。