作为一名软件开发者,Git 是我离不开的版本管理工具,它可以记录并追溯开发进程,功能强大,用法灵活多样。围绕着它诞生的协作平台 GitHub、Gitee 等也深入人心。
Git 本身就是一门学问,其重要性不亚于软件开发。在长期的开源软件开发维护的过程中,我长期与 Git 打交道,对 Git 有了更多的感悟,也探索了一些非常实用的技巧。这些技巧,充分发挥了 Git 本身的特性,极大改善了我使用 Git 管理项目的体验。
下面,我就来分享让我受益最深的几点技巧,希望能给读者朋友们一些启发。
「原子化」提交,一个提交只做一件事
小时候,老师和长辈或许都告诫我们,在做事情的时候要「一心一意」,不要分散注意力到其他的事情上,这样做事才会高效、有成果。
一旦没有遵循「一心一意」的原则,那么干正事的时候碰壁就再寻常不过。就拿我本人来说,做正事的时候「摸鱼」,狂欢一阵子后再回来,面对还没有完成的正式任务,我是容易一脸懵的:我做了啥?进度怎么样了?我刚刚是怎么攻关难题的来着?——显然,这导致了时间和精力的浪费,更不利于事后复盘和提升。
不单单是做事,在使用 Git 的过程中,做每一个提交时也需要「一心一意」,也就是「原子化」(atomic)——确保每个提交只完成一项任务。
1)引例
可能一些开发者在组织 Git 提交时,习惯让一个提交做多件事情,把多项各自独立且不相关联的功能修改都放在同一个提交里。比如:
- 一个项目有 A、B、C 三个功能模块,作者对这三个模块都做了修改,还顺带在 2025 年来临之际修改了所有源码开头注释里版权说明的年份1。
- 一个算法函数有多个各自独立的部分,作者在一个提交里就对这几个部分做了修改。
- 作者原本在写 A 函数,但是发现 B、C、D 这几个函数的注释需要补充,于是统统做了修改,并放在同一个提交里。
这种提交方式看似省事儿,但无疑会给后续的开发工作「埋雷」。
2)为什么会「埋雷」?
根据笔者自己的经验体会,如果一个提交做了不止一件事情,那么有可能会在你日后开发的时候,给你带来一些「麻烦」:
0x00:难以排错
也许在开发过程中,某个提交给项目引入了 Bug。当你好不容易找到了存在问题的提交,但却发现这个提交做了不止一件事,你还要花费额外的精力来定位造成 Bug 的地方。
而如果你有不止一个提交都没有做到「一心一意」,那么你就很难直接浏览提交记录来找到问题所在,你就不得不翻阅一个个提交,浪费成倍的精力。
0x01:不便复盘
很多时候,开发者需要复盘自己的提交记录,看看自己做了哪些工作。然而,假如一个提交做了不止一件事,那么就不便于复盘开发过程。
例如,当你想要回顾某个功能的实现过程,找出实现该功能的提交时,却发现关键的代码都被混在几个大的、综合的提交里,难以找出。好比是考前复习的时候,你总结的关键知识点混在了一堆厚厚的复习资料里,自然会影响复习效率。
或许有些读者会选择详尽编写提交说明,通过检索提交说明来找出自己想要的提交。不过这带来的好处有限——你后续还是离不开使用 git show
,在一长串「包罗万象」的 diff 记录中,找到真正想找的修改记录。
2)「原子化」提交
践行「原子化」提交的理念,以上的麻烦将不复存在。
C++ 从 C++11标准开始,引入了「原子操作」(atomic)特性,可以确保一个变量在同一时间只有一个操作者在读写,从而防止多线程抢占造成冲突。我借鉴了「原子操作」的理念,提出了「原子化」提交——也就是确保每个提交只完成一个任务,从而改进提交记录的组织方式,防止潜在的杂乱和冲突。
原子化提交最大的优势,在于提交记录简洁、可追溯、易于查错:
- 使用
git log
浏览提交记录时,你的开发进程一目了然,可以精确到每个提交实现的功能、修复的 Bug,等等,十分利于复盘。 - 一旦某个提交出现问题,你可以快速浏览提交记录,短时间内找到可能存在问题的提交。然后,你就可以通过
git checkout <疑似有问题提交的前一个提交>
,暂时回到应用该提交前的状态,来排查故障。 - 在实现一个新功能、修复一个 Bug 的时候,你可以先浏览提交记录,快速判定之前是否有做了相关工作,从而避免「无用功」。
3)实践「原子化」提交
下面,我就以几个在实际开发场景中的例子,来演示「原子化」提交的操作。
实例一:修复 Bug 的「原子化」提交
假设你正在开发一个电商平台的项目,最近有用户反馈在使用优惠券时,无法正确抵扣金额。经过排查,你发现是优惠券计算逻辑中的一个 Bug。你修复了这个 Bug,并且为了提高代码的可读性,对相关代码进行了重构,将一些冗长的函数拆分成了更小的函数。
如果你遵循「原子化」提交的原则,你可以将这个过程分为两个提交:
提交序号 | 提交信息(commit message) | 提交内容 |
---|---|---|
1:专门修复 Bug | Fix discount calculation bug in coupon system | 在这个提交中,你只修改了计算优惠券金额的代码,确保 Bug 被修复。 |
2:进行代码重构 | Refactor coupon calculation code for better readability | 在这个提交中,你对代码进行了拆分和重构,但不涉及任何 Bug 修复的逻辑。 |
这样做的好处是,如果后续发现修复 Bug 的代码引入了新的问题,你可以很容易地通过 <strong>git log</strong>
找到第一个提交,然后使用 <strong>git checkout</strong>
检出到修复 Bug 之前的版本,快速定位问题。同时,代码重构的提交也不会干扰到 Bug 修复的提交,使得提交记录清晰明了。
实例二:新功能开发的「原子化」提交
假设你正在开发一个在线教育平台,需要添加一个新的功能:允许用户上传视频作业。这个功能涉及到多个方面的开发,包括前端页面的设计、后端接口的编写以及数据库表结构的调整。
如果你遵循「原子化」提交的原则,你可以将这个功能的开发过程分为多个提交:
提交序号 | 提交信息(commit message) | 提交内容 |
---|---|---|
1:专注于前端页面设计 | Design video assignment upload page | 在这个提交中,你创建了新的 HTML 文件和 CSS 样式,实现了页面的基本布局和样式。 |
2:编写后端接口 | Implement backend API for video assignment upload | 在这个提交中,你创建了新的 API,实现了视频文件的接收和存储逻辑。 |
3:调整数据库表结构 | Add database table for video assignments | 在这个提交中,你创建了新的数据库表,用于存储视频作业的相关信息,如用户 ID、作业 ID、视频文件路径等。 |
通过这样的「原子化」提交,你可以清晰地记录每个功能模块的开发进度,便于后续的复盘和维护。如果某个模块出现问题,你可以快速定位到相应的提交,进行针对性的排查和修复,而不会被其他模块的代码干扰。
实例三:性能优化的原子提交
假设你正在开发一款合成器软音源插件,最近发现插件占用处理器资源过高,影响用户体验。经过分析,你发现是由于合成器的振荡器、滤波器算法优化不当,且编译参数存在问题,拖累性能。为了优化性能,你需要调整合成器算法,并修改编译系统的编译参数。
如果你遵循「原子化」提交的原则,你可以将这个优化过程分为多个提交:
提交序号 | 提交信息(commit message) | 提交内容 |
---|---|---|
1:优化振荡器算法 | Optimize oscillator with SIMD | 在这个提交中,你使用 SIMD 指令来改善振荡器的性能。 |
2:优化滤波器算法 | Optimize filter with SIMD | 在这个提交中,你同样使用了 SIMD 指令,来提升滤波器的工作效率。 |
3:优化编译系统的编译参数设置 | Enable compiler optimization | 在这个提交中,你为编译系统添加了编译器优化参数,使编译器充分优化代码性能。 |
通过这样的原子化提交,你可以清晰地记录每个优化措施的实施过程,便于后续的评估和维护。如果某个优化措施带来了新的问题,你可以快速定位到相应的提交,进行针对性的排查和修复,而不会被其他优化措施的代码干扰。
4)原子化提交的潜在优势
值得一提的是,原子化提交除了有利于你自己的代码管理,还能起到「利他」的作用。
在 GitHub 等开源社区中,原子化提交可以方便其他开发者浏览你的提交记录和开发进程,使你的开源成果更好惠及他人。井井有条的提交记录,还可以给他人留下好印象,彰显你的专业水准。
另一方面,像 Linux 这样的大型项目,每个提交也都是原子化的,并且更加规范,这大大方便了二次开发时对新特性的反向移植工作。
我自己也在做 Android 内核的适配,曾经将新版本内核的特性反向移植到华为 P6 的 3.0.6 内核中。开发过程中,我会从新版本内核的提交日志中找到与某个特性有关的补丁,然后将其应用到当前内核中,几乎是一找一个准,节省了在海量代码中「找重点」的精力。
可见,原子化提交利己利人,具有非常显著的潜在优势,受益不只一点点。
善用 git rebase
,合并细碎提交
记得大概是 2018 年,我在刷知乎的时候见到一个提问:《Git commits 历史是如何做到如此清爽的?》,提问者非常惊讶于知名前端框架 Vue.js 源码库提交记录的干净、清爽。

就在这个问题下,作者尤雨溪(Evan You)现身说法:
多用 rebase。
就是大佬这短短三个字的经验谈,让我对 git rebase 这个功能产生了浓厚的兴趣。那时的我恐怕也不会想到,后来的我会如此频繁地使用 rebase,从而摆脱既往使用 Git 时提交记录琐碎、杂乱的问题,离正确使用 Git 更近一步。
1)琐碎的提交从何而来?
大家都知道,作为版本管理系统,Git 要保证提交记录可靠、可追溯,因此不能像文本编辑那样随意更改提交记录,最多只提供 git commit --amend
功能来允许你更改最新的提交。
但在开发过程中,或许你不太可能时时刻刻像 Vue.js 的提交记录那样,使自己每个提交都保持清爽、规范。回头使用 git log 浏览提交记录,你可能会发现你提交了太多琐碎的内容,「细致」到写一行代码、修一个错别字、修一个注释都有单独的提交。
很多时候这些提交是很琐碎的,没有必要单独保留,好比是你在文字处理软件中每写一句话就按“保存”,而每个修改都被单独保存成了一个文件。久而久之,提交记录就变得冗长琐碎,管理的时候就很难追溯到有用的、重要的提交。
这个时候, git rebase
就派上用场了。
Git 的设计师考虑到用户整理提交记录的需求,于是就设计了 git rebase
这一功能,允许你合并、编辑、重排已有的提交,使修改后的提交井然有序,就像 Vue.js 的提交记录那样清晰。
2)实战演示如何合并琐碎提交
以下面这个提交记录为例,记录了某项目从零开始写 main()
函数的过程,仅仅是添加文本输出与修改注释的提交就有好几个。
注意:假设这些提交彼此之间没有冲突,每个提交都是在原有提交之上的微调。
$ git log --pretty=oneline # 使用单行模式输出提交记录。最新的提交在前。
3a51f37493191451413b8dc7428d63351ce4b1e3 (HEAD -> main, origin/main) main: 修改注释中的错别字
6587eeb437c8b139965085ddf99bd72bae682f89 main: 添加注释
61b04c318c24434996587eeb437c8b13996587e6 main: 添加操作结束的文本输出
61500445ebae1eb855ab216c6bbcec6ee73bd270 main: 微调操作开始的文本
7dad6bec684949ab0188085ddf99bc724c7b7b59 main: 添加操作开始的文本输出
996587eeb437c8b13996451413b8dc7603452f32 main: 添加基本文本输出
554bb6603452f3bf5705ac200effbdfc0aa97465 main: 创建main函数
接下来,笔者就要把所有提交都合并到第一个提交「main: 创建main函数
」(以下简称「目标提交」)当中。
0x00:检查代码树是否有未提交的更改
为了防止 rebase 弄乱代码仓库,Git 强制要求你的工作区「干净」,也就是不存在已经修改但没有提交的文件。否则,你是没有办法 rebase 的。
根据你的开发进度,你可以先提交修改;或者是使用 git stash
将修改暂时保存起来,等到完成 rebase 后再运行 git stash pop
恢复你的修改。
0x01:进入 git rebase 模式
使用 git log
查看提交记录,记住「目标提交」的 ID。然后,运行以下命令,开始 rebase:
git rebase -i 554bb6603452f3bf5705ac200effbdfc0aa97465~
这个命令,允许你修改从「最新提交」到「目标提交」在内的所有提交。注意不要漏了提交 ID 后面的波浪线,否则会把「目标提交」给漏掉2。
稍等片刻,Git 会自动打开文本编辑器(通常是 Vim 或 Nano),列出一系列提交。其中:
- 较新的提交列在文档的后面3,顺序是与
<strong>git log</strong>
相反的; - 每一行前面的
pick
是操作指令,意为采用该提交。这是默认的行为。
pick 554bb660 main: 创建main函数
pick 996587ee main: 添加基本文本输出
pick 7dad6bec main: 添加操作开始的文本输出
pick 61500445 main: 微调操作开始的文本
pick 61b04c31 main: 添加操作结束的文本输出
pick 6587eeb4 main: 添加注释
pick 3a51f374 main: 修改注释中的错别字
0x02:合并提交
在本例中,从996587ee
到3a51f374
的这几个提交,都是要并入「目标提交」的提交。
我们把这些提交对应行行首的 pick
改为 fixup
(或单个字母“f
”)。fixup
指令的作用是合并提交,但是只保留前一个提交(相邻一个比它早的提交)的说明。如下所示:
pick 554bb660 main: 创建main函数
f 996587ee main: 添加基本文本输出
f 7dad6bec main: 添加操作开始的文本输出
f 61500445 main: 微调操作开始的文本
f 61b04c31 main: 添加操作结束的文本输出
f 6587eeb4 main: 添加注释
f 3a51f374 main: 修改注释中的错别字
随后,保存文件并关闭编辑器,Git 就会自动开始 rebase,一个个把新的提交并入相邻的前一个提交中,直到目标提交。这样,那些琐碎的提交都被并入我们的目标提交中,如此一来提交记录就清爽了不少:
$ git log --pretty=oneline # 使用单行模式输出提交记录
f5815166356e85a5fe244f6024c2e401f04b10fa (HEAD -> main, origin/main) main: 创建main函数
如果你希望保留相关提交的说明文本(以备参考等),那么你可以使用 <strong>squash</strong>
指令(或单个字母“<strong>s</strong>
”),保存文件并关闭编辑器后, Git 会打开一个新文档,在这里你可以检查、修改提交说明。
注意:
经过 rebase 之后,原本的「目标提交」ID 会发生变化,因为 Git 实际上生成了一个新的提交。
3)如果琐碎的提交是后来才做出的
随着项目开发的推进,你达成的目标越来越多,提交数量也随之增长。但你发现一个早期编写的功能里,注释、代码缩进这些细节存在问题,于是再做了几个提交来修改。
现在,你想把这些琐碎的修改合并到该功能的提交中。此时你依然可以运用 git rebase
,先调整提交顺序,然后再使用 fixup
(或 squash
)指令来合并提交。
0x00:准备工作
在继续操作之前,你需要确保仓库里没有未提交的更改。
另一方面,你还要保证那些琐碎的提交不与你既往的修改产生冲突。比如说,如果你的提交除了修改注释,还顺带修改了函数结构、变量定义等内容,那么很可能会与你的其他提交造成冲突,需要你手动干预,造成不必要的麻烦。
注意:
假设下文的提交彼此之间没有冲突,每个提交都是在原有提交之上的调整。
0x01:进入 rebase 模式,重排提交顺序
在运行 git rebase
之前,你需要通过 git log
检索提交日志,找到你的「目标提交」。
在下面的例子中,目标提交是554bb660
(「创建 main」函数),你的任务就是要把你的目标是把 3a51f374
与 6587eeb4
这两个琐碎的提交合并到 554bb660
这个提交中。4
$ git log --pretty=oneline # 使用单行模式输出提交记录
...
3a51f374 main: 修改注释中的错别字
6587eeb4 main: 修正代码缩进
996587ee process: 使用libfftw3,优化合成器算法逻辑
34e87ac3 process: 创建process函数
554bb660 main: 创建main函数
...
然后,运行 git rebase
命令:
git rebase -i 554bb660~
此时, Git 依然会打开一个文本编辑器,内容如下:
pick 554bb660 main: 创建main函数
pick 34e87ac3 process: 创建process函数
pick 996587ee process: 使用libfftw3,优化合成器算法逻辑
pick 6587eeb4 main: 修正代码缩进
pick 3a51f374 main: 修改注释中的错别字
我们在 git rebase
给我们打开的文本编辑器里,把3a51f374
与 6587eeb4
这两个提交对应的行,整体复制到 <strong>554bb660</strong>
的后面,并将原有的行注释掉。就像下面这样:
pick 554bb660 main: 创建main函数
# 将提交所对应的行复制到我们的目标提交后面
pick 6587eeb4 main: 修正代码缩进
pick 3a51f374 main: 修改注释中的错别字
pick 34e87ac3 process: 创建process函数
pick 996587ee process: 使用libfftw3,优化合成器算法逻辑
# 为保险起见,将原有的行注释掉,而不是直接移动
#pick 6587eeb4 main: 修正代码缩进
#pick 3a51f374 main: 修改注释中的错别字
警告:
不要修改pick
后面的内容,尤其是提交 ID,否则提交记录可能会发生混乱。
0x02:合并提交
确认提交顺序无误后,将待合并的提交对应行行首的 pick
指令改为 fixup
(或 squash
),如下所示:
pick 554bb660 main: 创建main函数
# 将提交所对应的行复制到我们的目标提交后面
fixup 6587eeb4 main: 修正代码缩进
fixup 3a51f374 main: 修改注释中的错别字
pick 34e87ac3 process: 创建process函数
pick 996587ee process: 使用libfftw3,优化合成器算法逻辑
# 为保险起见,将原有的行注释掉,而不是直接移动
#pick 6587eeb4 main: 修正代码缩进
#pick 3a51f374 main: 修改注释中的错别字
保存文件后,Git 随即开始 rebase 工作,这样我们就可以化琐碎为清爽,得到一个干净的提交记录了。
3)注意事项
在进行 rebase 前,务必要检查你要合并的提交与「目标提交」之间是否存在冲突。一旦存在冲突,那么 git rebase
就无法继续,会要求你手工修改你的仓库代码来处理冲突,这需要更多的时间和精力——因为你要保证代码正常无误。
另一方面,经过 rebase 合并所得的提交,本质上是一个全新的提交,并且也改变了原有的提交记录,因此如果别人 fork 了你的代码,在与你的仓库同步时必定会发生冲突。你或许需要告知你的团队成员,或者是通过 README 来告知代码共享平台5上的用户,告诉他们使用 git pull --rebase
来同步你的修改。
提交顺序不满意?也可以用 rebase 搞定
我自己在找到开发项目的新灵感后,会马上新建一个 Git 仓库,开始动手实践,并把我写的源代码提交到仓库里。然而当我想进一步把仓库上传到 GitHub 时,却发现:我忘了加上许可协议、README 和 .gitignore
!
考虑到这些文件都是在新建项目时就要添加的,如果我在完成了一部分程序功能后补上去,再回看 Git 提交记录,总是会觉得格外「别扭」。在这样的情况下,我依然可以运用 git rebase,单独调整提交顺序,还我自己一个科学有序的提交记录。
1)实践如何调整提交顺序
这里举一个高度简化的例子:假设笔者有一个项目,已经完成了程序的主体开发工作,后来才补上 README 与许可协议。提交日志如下(较新的提交在前面):
$ git log --pretty=oneline # 使用单行模式输出提交记录
0fb4a3b0 添加 README.md
acbc6080 添加许可协议(GPLv3)
a841dbc1 UI 的 bug 修复
16f688a6 DSP 性能优化
75ca23f6 完成 UI 开发
277aad5f 完成 DSP 开发
eb323b0e 初始提交(Initial commit)
我希望把与 README、许可协议相关的提交──也就是 0fb4a3b0
、acbc6080
──挪到时间顺序上的初始提交之后,也就是提交日志中初始提交的前一行。
0x01:打开 git rebase
在本例中,我已经确定好了「目标提交」,也就是初始提交,并且已经确保工作区没有未提交的代码。
然后,运行 git rebase,定位到目标提交:
git rebase -i eb323b0e
接下来 Git 会打开文本编辑器,显示以下内容(较新的提交在文档的后面):
pick 277aad5f 完成 DSP 开发
pick 75ca23f6 完成 UI 开发
pick 16f688a6 DSP 性能优化
pick a841dbc1 UI 的 bug 修复
pick acbc6080 添加许可协议(GPLv3)
pick 0fb4a3b0 添加 README.md
需要注意的是,git rebase 不能显示初始提交,所以我们能看到的最早的提交是紧邻初始提交之后(比初始提交新)的第一个提交。
0x02:开始挪动
先把0fb4a3b0
、acbc6080
这两个提交对应的行整体复制到 277aad5f
(也就是初始提交后的第一个提交)前面,然后将原有的那两行注释掉,如下所示:
# 将提交所对应的行复制到我们的目标提交后面。
# 本例中的目标提交是初始提交,所以我们实际上是把要挪动的提交放在 git rebase 文档的最前面
pick 0fb4a3b0 添加 README.md
pick acbc6080 添加许可协议(GPLv3)
pick 277aad5f 完成 DSP 开发
pick 75ca23f6 完成 UI 开发
pick 16f688a6 DSP 性能优化
pick a841dbc1 UI 的 bug 修复
# 为保险起见,将原有的行注释掉,而不是直接移动
#pick acbc6080 添加许可协议(GPLv3)
#pick 0fb4a3b0 添加 README.md
确认无误后,直接保存,此时 Git 就会自动开始 rebase 工作,稍等片刻再查看提交记录,你会发现提交顺序变了,README 与许可协议所对应的提交就出现在了初始提交的后面,大功告成。
2)注意事项
考虑到 rebase 时可能会面临的冲突,你需要三思而后行。
通常只建议重排那些对其他提交记录几乎没什么干扰的提交,例如你的提交只创建、修改了某一个特定的文件(README、许可协议这样的文件)。若重排不慎,你就不得不花费大量的精力来处理 rebase 的冲突,还有可能把提交记录弄乱。6
同样地,你还需要告知你的团队成员或用户,使用 git pull --rebase
来合并你的更改。
对于未成型项目,git commit --amend
也许更适合你
git commit 是 Git 用于提交修改的命令。它有一个参数 --amend
,允许你修改最新一次提交的内容。有时你发现刚刚提交的代码有错误,或者是提交说明有问题,但是你不想再新建一个提交来修正这些错误,那么你就可以使用 git commit --amend
这个命令。
然而,对我来说,git commit --amend
的用途远不止于此。在项目还未成型的阶段,我使用它来保持提交记录的清爽。
1)为什么我会常用 git commit --amend
?
我开发的项目,主要是将现有的开源音频插件移植到 DPF 这个跨平台框架7,这往往是「摸着石头过河」——无论是 DPF 还是被移植的插件,都缺乏文档,全靠我自己摸索。
在早期阶段,代码文件结构、模块和功能代码、编译系统等都还没有定稿,程序也只实现了部分功能:这就是未成型的状态。我个人习惯一边写功能一边调试,常常反复调整代码结构和算法,直到真正实现我预期的目标为止。
这,往往意味着我要持续修改源代码库。如此背景之下,如果每个修改都单独提交到仓库里,那么就意味着仓库里会有数十甚至上百条极其琐碎的提交记录,待到项目成型时还要用 git rebase
来整理提交。你可以想象一下,用 WPS 写文章,每写一句话就另存一个文件,是什么样的感觉。
2)如何妙用 git commit --amend
?
为了解决上述问题,git commit --amend
就成为了我最常用的操作之一。我会采用这样的思路:
- 第一步,用一个提交来存放项目成型前的所有开发工作。提交说明为「
Early development (WIP!)
」,其中「WIP(Work In Progress)
」表示该提交随时可能被覆盖。做出这个提交之后,就不要再做新的提交。 - 第二步,每次完成一部分功能,确认代码编写与程序运行无误时,就使用
git add
命令来将修改过的文件暂存(stage)起来,随后使用git commit --amend
更新这个提交的内容。 - 第三步,继续完成开发,直到项目成型。此时,再运行
git commit --amend
,重新修改提交说明,将「WIP
」等字样删除,代表我的早期开发工作已经定稿。
我个人偏向在项目成型之后,才开始转变为以一个个单独提交的方式来持续开发。这里的「成型」,大致可理解为代码结构稳定,程序预期功能已经实现,至少有可以跑起来的 Alpha 版本推出。
如此一来,提交记录就会变得清爽,不会让琐碎的早期开发记录「挤占」你的 git log,事后也不需要再单独花时间来 rebase。
3)注意事项
- 由于这种方法并不会为每个更改留下单独的提交,后续难以通过
git log
回溯,故只建议在项目未成型时使用。如果你不放心,依然可以每做一组工作就提交一次,对此我建议在提交说明里做标记,以备项目成形后再 rebase 提交记录。 - 在
git commit --amend
之前,也务必确保程序与代码无误,以免混乱。
除此之外,当你在为你的项目添加新功能时,你也可以在原型设计、测试的阶段使用git commit --amend
,因为这个过程常常就像写一篇新文章,你或许也不希望每写一句话就又来另存一个新文档。
写在最后
在日常与 Git 打交道的过程中,我一直致力于精进 Git 的使用。上面这几点技巧,就是我自己的实践成果,着力于让提交日志更清爽、规范,方便后续的维护。让我们再来回顾一下:
- 使用「原子化」提交,每个提交只做一件事,利于管理、维护和回顾你的开发进度;
- 灵活运用
git rebase
,合并细碎的提交,按需要重排提交,使提交记录清爽、规范; - 项目未成型时,活用
git commit --amend
,保持提交记录清爽。
当然,以上的技巧,更多体现出我个人的使用习惯,客观上也改善了我自己 Git 仓库的质量。相信我的分享能为感兴趣的读者朋友们提供参考,一同将 Git 用得更自在。
- 1例如,把「Copyright 2010-2024 Author.」修改为「Copyright 2010-2025 Author.」。
- 2波浪线的含义:选中你指定提交的前一个提交。只有加上波浪线,git rebase 才会包含你指定的那个提交。(其实就是开区间与闭区间的区别。)
- 3即:越靠下的行,对应的提交越新。
- 4这里是一个简化版的例子。实际的项目当中,你的「目标提交」与待合并的琐碎提交之间可能相隔十几个甚至几十个提交。
- 5例如 GitHub、Gitee。
- 6一个典型的例子是:提交001创建了一个新函数,提交002修改了该函数的内容。如果你重排时把002放在001的前面(即,时间更早的位置),那么必定会造成合并冲突。
- 7DPF 全称 DISTRHO Plugin Framework,是一款跨平台音频插件开发框架,让你用一套代码库就可以生成多种音频插件格式,包括 VST 2.4、VST3、LV2、CLAP,同时支持 Windows、macOS 与 Linux 平台。