Git 分支与 rebase 日常工作流笔记
这篇是我自己用了一段时间后整理的工作流笔记。不追求标准答案,只记录在多人协作里行之有效的几个习惯:怎么开 feature 分支、提交搞乱了怎么用交互式 rebase 收拾干净、rebase 撞上冲突时按什么顺序想问题,以及几个用顺手的 alias。
为什么坚持用 feature 分支
主分支(我这里叫 main)我尽量保持随时可发布。任何一个新功能、一个修复,都先切出一条独立分支:
git switch -c feat/user-pagination
# 旧版本 Git 可以用 git checkout -b feat/user-pagination
分支名我习惯加个前缀,比如 feat/、fix/、chore/,一眼就能看出意图。这样做的好处很直接:main 上的历史是一条干净的主线,每个功能的探索过程都关在自己的分支里,没合并之前怎么折腾都不影响别人。
开发途中我会频繁提交,哪怕是半成品。提交信息此时随便写,反正后面要整理。先把进度存下来,比纠结提交粒度更重要。
用交互式 rebase 整理提交
功能写到一半,本地往往攒了一堆零碎提交:“先这样”、“改个拼写”、“再调一下”。这种历史合进 main 会很难读。提交前我会用交互式 rebase 把它们捏成几个有意义的提交。
git rebase -i main
Git 会打开一个待办列表,每一行一个提交,前面是动作。常用的几个:
pick:保留这个提交,原样不动。reword:保留改动,但重写提交信息。squash:把这个提交并进上一个,两条信息都留着让你编辑。fixup:和 squash 类似,但直接丢掉这个提交的信息,适合“改个拼写”这类没必要单独记录的小修补。drop:直接删掉这个提交。
一个典型的整理,把三个零碎提交压成一个完整功能提交:
pick a1b2c3d 加分页骨架
fixup d4e5f6a 改个拼写
fixup b7c8d9e 补默认页大小
存盘退出后,这三个就合成一条,提交信息以第一条为准。我还会顺手把它 reword 成一句说清楚做了什么的话,例如“为用户列表接口加入分页支持”。
这里有个我吃过亏的点:交互式 rebase 会改写提交历史。所以只对还没推送、或者只有自己在用的分支这么做。已经推上去、别人可能基于它工作的提交,不要随便重写。
解决 rebase 冲突的思路
把分支 rebase 到最新的 main 上,是我合并前的固定动作,这样能让我的提交叠在最新代码之上,历史是直的:
git switch feat/user-pagination
git fetch origin
git rebase origin/main
撞上冲突时,我不会慌着改。先搞清楚当前在重放哪一个提交,Git 会告诉你停在哪。心里有个顺序:
- 先看冲突文件里的标记。
<<<<<<<到=======之间是当前 main 上已有的内容,=======到>>>>>>>之间是我这个提交想引入的改动。 - 判断这两边是不是真冲突,还是只是改了相邻的行。很多时候两边都要保留,只是格式上挨在了一起。
- 改完删掉那三行标记,确认这一份是我真正想要的最终结果,再
git add标记为已解决。 - 接着
git rebase --continue,处理下一个有冲突的提交。一次只面对一个提交的冲突,问题就小很多。
如果中途发现方向不对,或者冲突多到想重来,随时可以 git rebase --abort 退回到 rebase 之前的状态,什么都不会丢。这个后路让我做 rebase 时心里很踏实。
还有个习惯能少踩坑:rebase 之前先确认工作区是干净的,没有未提交的改动。要么先提交,要么 git stash 存起来。
merge 还是 rebase
这两个不是对立的。我的分工大概是:本地整理自己的提交、把功能分支更到最新 main 上,用 rebase,图的是历史干净;真正把功能并回 main 时,用 merge,保留一个合并点,能看出这一组提交是一个完整功能进来的。
原则就一条前面提过:别 rebase 已经共享出去的历史。自己分支上随便整理,共享分支上只做 merge,基本不会出乱子。
几个用顺手的 alias
这些配在全局 ~/.gitconfig 的 [alias] 段里,日常省了不少键:
[alias]
co = checkout
sw = switch
st = status -sb
cm = commit -m
lg = log --oneline --graph --decorate -20
last = log -1 HEAD
unstage = reset HEAD --
amend = commit --amend --no-edit
几个我用得最多的:
git st:短格式状态,加上分支信息,比默认输出清爽,一眼看完。git lg:带图的单行历史,看分支怎么分叉合并特别直观,整理提交前先用它扫一眼现状。git unstage:把误加进暂存区的文件退回去,名字比 reset 好记。git amend:刚提交完发现漏了个文件,补git add后跑这个,直接并进上一个提交且不改信息。注意它同样改写历史,只用在没推送的提交上。
小结
核心就几句话:功能各走各的分支;本地用交互式 rebase 把提交整理成能读的样子;rebase 撞冲突时一次只想一个提交,留好 abort 后路;共享出去的历史不要重写。把这些变成肌肉记忆之后,日常协作里的提交历史会干净很多,回头查问题也轻松。