前言
在上一篇教程中,我们详细讲解了 git add 命令,知道了如何把工作区的修改放入暂存区(Stage)。但暂存区只是一个"中转站",真正让代码进入版本库、被Git永久记录下来的,是 git commit 命令。
git commit 是Git中最核心、使用频率最高的命令之一。每一次commit,都相当于给项目拍了一张"快照",记录下当前所有文件的状态。日后你可以随时回到任何一个快照,查看历史、对比差异、撤销修改。
本篇将从基本用法、提交信息规范、常用选项、内部原理等多个维度,带你彻底搞懂 git commit。
一、git commit 的作用
1.1 从暂存区到版本库
Git有三个重要区域:
- 工作区(Working Directory):你正在编辑的文件
- 暂存区(Staging Area / Index) :
git add后的文件,准备提交 - 版本库(Repository) :
git commit后的文件,被永久记录
git commit 的作用就是把暂存区 的内容,写入版本库,生成一个新的提交对象(commit object)。
工作区 ──git add──▶ 暂存区 ──git commit──▶ 版本库
1.2 创建一个快照
每次commit,Git会:
- 根据暂存区的当前状态,生成一个tree对象(记录目录结构)
- 生成一个commit对象(包含tree的hash、父commit的hash、作者信息、提交信息、时间戳)
- 将当前分支指针(如
HEAD、master)移动到新的commit
这样,你就拥有了一个可以随时回溯的历史版本。
二、基本用法
2.1 git commit -m "消息"(最常用)
这是日常开发中使用最多的形式:
# 先把修改加入暂存区
git add README.md
# 提交到版本库,附带提交信息
git commit -m "添加项目说明文档"
终端输出:
[main 8f3a2d1] 添加项目说明文档
1 file changed, 10 insertions(+)
create mode 100644 README.md
输出解读:
[main 8f3a2d1]:当前分支是main,新提交的简短hash是8f3a2d1添加项目说明文档:提交信息1 file changed, 10 insertions(+):1个文件被修改,新增10行create mode 100644 README.md:新建了README.md文件
2.2 git commit -a -m "消息"(跳过add)
对于已经被Git跟踪 的文件,可以用 -a 选项跳过 git add,直接提交:
# 修改了一个已跟踪文件 src/app.js
git commit -a -m "修复登录bug"
终端输出:
[main 2b5c8e9] 修复登录bug
1 file changed, 3 insertions(+), 1 deletion(-)
这相当于 git add + git commit 两步合一。
注意 :-a 只对已跟踪文件有效。新建的文件(未跟踪)不会被包含进来,必须先 git add。
2.3 git commit(不带-m,打开编辑器)
如果不带 -m,Git会打开默认编辑器(通常是vim或nano),让你编写多行提交信息:
git commit
在vim编辑器中会看到:
# 请为您的提交输入说明。以 '#' 开头的行将被忽略,而空提交
# 将会被中止。
#
# 位于分支 main
# 要提交的变更:
# 新文件: src/utils.js
#
你在第一行写提交标题,空一行后写详细说明,保存退出即可完成提交。
设置默认编辑器:
# 设置为vim
git config --global core.editor vim
# 设置为VS Code
git config --global core.editor "code --wait"
# 设置为Sublime Text
git config --global core.editor "subl -w"
三、提交信息的规范
好的提交信息能让团队协作事半功倍。目前业界最流行的是 Conventional Commits(约定式提交) 规范。
3.1 Conventional Commits 规范
提交信息格式:
type(scope): subject
body
footer
type 类型(必填):
| type | 说明 |
|---|---|
| feat | 新功能(feature) |
| fix | 修复bug |
| docs | 文档变更 |
| style | 代码格式(不影响功能) |
| refactor | 重构(既不是新功能也不是修bug) |
| test | 测试相关 |
| chore | 构建/工具/依赖等杂务 |
| perf | 性能优化 |
| ci | CI配置相关 |
| build | 构建系统相关 |
| revert | 回退某次提交 |
scope (可选):影响的模块/范围,如 auth、api、ui。
subject(必填):简短描述,动词开头,小写,不加句号。
3.2 好的提交信息 vs 坏的提交信息
好的示例:
feat(auth): 添加微信扫码登录功能
- 新增 QrLogin 组件
- 对接 /api/auth/qr 接口
- 支持二维码过期自动刷新
fix(api): 修复分页参数page从0开始导致首页数据缺失的问题
接口要求page从1开始,前端误传0,导致第一页返回第二页数据
坏的示例:
update
修改了一些东西
fix bug
asdfasdf
坏信息的问题:无法从历史中看出这次提交到底做了什么,回溯问题时非常痛苦。
3.3 多行提交信息的写法
方式一:用多个 -m
git commit -m "feat(user): 添加用户头像上传功能" -m "支持jpg/png格式,限制2MB大小" -m "Closes #123"
每个 -m 会成为单独的段落。
方式二:在消息中用换行
git commit -m "feat(user): 添加用户头像上传功能
支持jpg/png格式,限制2MB大小
Closes #123"
方式三:编辑器中编写(推荐长信息使用)
git commit
# 在编辑器中:
# feat(user): 添加用户头像上传功能
#
# 支持jpg/png格式,限制2MB大小
#
# Closes #123
四、提交信息模板配置
团队可以配置统一的提交信息模板,执行 git commit 时自动加载。
4.1 创建模板文件
# 创建模板文件
cat > ~/.gitmessage << 'EOF'
# <type>(<scope>): <subject>
#
# <body>
#
# <footer>
#
# type: feat/fix/docs/style/refactor/test/chore/perf
# scope: 影响的模块
# subject: 简短描述,不超过50字
# body: 详细说明,每行不超过72字
# footer: 关联issue,如 Closes #123
EOF
4.2 配置Git使用模板
git config --global commit.template ~/.gitmessage
之后每次 git commit(不带-m),编辑器中都会显示这个模板作为提示。
4.3 配置提交信息校验
配合 commit-msg 钩子可以强制校验格式:
# .git/hooks/commit-msg
#!/bin/sh
msg=$(cat "$1")
if ! echo "$msg" | grep -qE '^(feat|fix|docs|style|refactor|test|chore|perf)(\(.+\))?: .+'; then
echo "提交信息不符合规范!"
echo "格式: type(scope): subject"
exit 1
fi
chmod +x .git/hooks/commit-msg
五、git commit 的常用选项
5.1 --amend:修改上一次提交
这是最常用的"补救"命令,下文会专门详解。
# 修改上一次提交的信息
git commit --amend -m "新的提交信息"
5.2 --allow-empty:空提交
通常如果暂存区没有变化,Git会拒绝提交。强制提交一个空提交:
git commit --allow-empty -m "标记:开始重构工作"
用途:有时需要做一个标记性提交,或者触发CI流水线。
5.3 --no-verify:跳过钩子
如果你配置了 pre-commit / commit-msg 钩子,但临时想跳过:
git commit --no-verify -m "临时提交,跳过校验"
警告:慎用!可能绕过代码格式检查、测试等保护措施。
5.4 -s:添加 Signed-off-by
在提交信息末尾添加 Signed-off-by 行,常用于开源项目(如Linux内核):
git commit -s -m "修复内存泄漏问题"
提交信息会变成:
修复内存泄漏问题
Signed-off-by: 张三 <zhangsan@example.com>
5.5 --author:指定作者
git commit --author="李四 <lisi@example.com>" -m "代李四提交"
注意:committer仍然是你自己,author是指定的那个人。
5.6 --date:指定日期
git commit --date="2024-01-15T10:00:00" -m "补记昨天的提交"
5.7 常用选项速查表
| 选项 | 说明 |
|---|---|
| -m "msg" | 指定提交信息 |
| -a | 自动add已跟踪文件 |
| --amend | 修改上一次提交 |
| --allow-empty | 允许空提交 |
| --no-verify | 跳过钩子 |
| -s | 添加Signed-off-by |
| --author | 指定作者 |
| --date | 指定日期 |
| -C | 复用指定提交的信息 |
| -F | 从文件读取提交信息 |
5.8 -C 和 -F 选项详解
-C 复用提交信息(不打开编辑器):
# 复用某次提交的信息,创建新提交
git commit -C 8f3a2d1
# -c 类似,但会打开编辑器让你修改
git commit -c 8f3a2d1
-F 从文件读取提交信息:
# 把提交信息写在文件里
cat > commit-msg.txt << 'EOF'
feat(api): 重构用户认证接口
- 统一使用JWT
- 移除session方案
- 添加token自动刷新
EOF
git commit -F commit-msg.txt
这在需要复杂多行提交信息时很方便,可以用编辑器先写好再提交。
六、git commit --amend 详解(重点)
--amend 是很多人用错的命令,这里详细讲解。
6.1 修改提交信息
# 刚才提交了,但发现信息写错了
git commit -m "fix: 修復登录bug" # "修復"是繁体,想改成"修复"
# 修改上一次提交的信息
git commit --amend -m "fix: 修复登录bug"
终端输出:
[main 9a1b3c4] fix: 修复登录bug
Date: Mon Nov 2 10:30:00 2026 +0800
注意hash变了(从原来的变成了 9a1b3c4)。
6.2 追加遗漏的文件
场景:提交后发现漏了一个文件。
# 刚才的提交
git commit -m "feat: 添加用户列表页面"
# 发现忘了提交 UserList.css
git add UserList.css
# 把这个文件追加到上一次提交(不改变提交信息)
git commit --amend --no-edit
--no-edit 表示沿用原来的提交信息,不打开编辑器。
6.3 同时修改信息和追加文件
git add UserList.css
git commit --amend -m "feat: 添加用户列表页面及样式"
6.4 重要警告:amend会改变commit hash
--amend 本质上是用新提交替换旧提交,生成一个全新的commit对象,hash完全不同。
# amend前的历史
git log --oneline
# 8f3a2d1 旧的提交
# 2b5c8e9 上上次提交
# amend后
git commit --amend -m "新的信息"
git log --oneline
# 9a1b3c4 新的提交 ← hash变了!
# 2b5c8e9 上上次提交
已推送到远程的提交,不要用amend! 否则别人拉取时会冲突,需要 git push --force,可能覆盖别人的工作。
七、提交的内部原理
7.1 commit对象的结构
每次commit,Git会创建一个commit对象,内容如下:
tree 4b825dc642cb6eb9a060e54bf8d69288fbb4f7d7
parent 2b5c8e9a1b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e
author 张三 <zhangsan@example.com> 1735510200 +0800
committer 张三 <zhangsan@example.com> 1735510200 +0800
feat: 添加用户列表页面
字段说明:
- tree:指向一个tree对象,记录当前提交时刻的完整目录结构
- parent:指向父提交(第一次提交没有parent)
- author:作者信息(姓名、邮箱、时间戳)
- committer:提交者信息(可能与author不同,比如代提交)
- 空行后的内容:提交信息
7.2 查看commit对象
# 查看最近一次commit的完整内容
git cat-file -p HEAD
输出:
tree a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0
parent 2b5c8e9a1b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e
author 张三 <zhangsan@example.com> 1735510200 +0800
committer 张三 <zhangsan@example.com> 1735510200 +0800
feat: 添加用户列表页面
7.3 提交链
每个commit通过parent指针指向上一个commit,形成一条链:
HEAD → commit3 → commit2 → commit1 → (null)
这就是Git历史的本质------一条由parent指针串起来的commit链。
八、跳过暂存区直接提交 git commit -a
8.1 基本用法
git commit -a -m "修改了app.js"
# 等价于
git add -u # 只add已跟踪文件
git commit -m "修改了app.js"
8.2 注意事项
新文件不会被提交:
# 新建了一个文件 new.js(未跟踪)
echo "console.log('new')" > new.js
# 修改了已跟踪文件 app.js
echo "console.log('updated')" >> app.js
git commit -a -m "更新"
终端输出:
[main 5d6e7f8] 更新
1 file changed, 1 insertion(+)
只有 app.js 被提交,new.js 因为是未跟踪文件,被忽略了。
删除的文件会被提交:
git rm old.js
git commit -a -m "删除old.js"
# 或者不用 -a,因为 git rm 已经暂存了删除操作
git commit -m "删除old.js"
8.3 何时用 -a
- 适合:快速提交,只改了已跟踪文件,且没有新建文件
- 不适合:有新建文件,或只想提交部分修改(需要选择性add)
九、实战:一个完整的提交工作流
下面演示一个真实开发中的提交流程。
9.1 场景:开发一个登录功能
# 1. 创建功能分支
git checkout -b feature/login
# 2. 编写代码(略)
# 创建了 src/pages/Login.vue
# 修改了 src/router/index.js
# 创建了 src/api/auth.js
# 3. 查看状态
git status
输出:
位于分支 feature/login
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
src/api/auth.js
src/pages/Login.vue
修改尚未加入提交:
(使用 "git add <文件>..." 更新要提交的内容)
src/router/index.js
9.2 分批提交(推荐)
# 第一次提交:API层
git add src/api/auth.js
git commit -m "feat(api): 添加登录认证接口
- 新增 login 接口
- 新增 logout 接口
- 新增 token 存储工具函数"
# 第二次提交:页面
git add src/pages/Login.vue
git commit -m "feat(ui): 实现登录页面
- 表单校验
- 登录失败提示
- 登录成功跳转首页"
# 第三次提交:路由配置
git add src/router/index.js
git commit -m "feat(router): 添加登录页面路由"
9.3 查看提交历史
git log --oneline
输出:
c3d4e5f feat(router): 添加登录页面路由
b2c3d4e feat(ui): 实现登录页面
a1b2c3d feat(api): 添加登录认证接口
9a1b3c4 上一次提交
...
9.4 发现遗漏,用amend补救
# 发现Login.vue里漏了一个loading状态
# 修改后
git add src/pages/Login.vue
git commit --amend --no-edit
# 现在loading修改被合并到了"实现登录页面"那次提交
十、常见问题 FAQ
Q1:提交信息写错了怎么办?
如果还没push:
git commit --amend -m "正确的信息"
如果已经push,且是个人分支:
git commit --amend -m "正确的信息"
git push --force-with-lease
如果是共享分支,建议用新的提交修正,而不是amend。
Q2:提交了不该提交的文件怎么办?
# 从最近一次提交中移除文件(但保留在工作区)
git reset HEAD^ --hard # 危险,会丢失修改
# 更安全的方式:
git reset --soft HEAD^ # 撤销提交,保留暂存
git rm --cached 误提交的文件
git commit -m "重新提交"
Q3:如何修改历史中某次提交的信息?
git rebase -i HEAD~3 # 交互式rebase最近3次提交
# 把要改的那行开头的 pick 改成 reword
# 保存后会让你编辑那条提交信息
Q4:commit后文件没变?
检查是否忘记 git add:
git status
# 如果显示"修改尚未加入提交",说明忘了add
Q5:如何查看某次提交改了什么?
git show <commit-hash>
Q6:如何撤销最近一次提交(保留修改)?
git reset --soft HEAD^
# 提交被撤销,修改还在暂存区
Q7:如何修改已经push的提交信息?
如果只是个人分支,可以:
git commit --amend -m "新的信息"
git push --force-with-lease
如果是共享分支,不要这样做。应该用新的提交来修正:
git commit -m "docs: 修正上一次提交描述的错误"
Q8:commit时提示"pre-commit hook failed"怎么办?
说明pre-commit钩子(代码检查)没通过。两个选择:
# 1. 修复问题后重新提交(推荐)
# 根据错误提示修复代码
git add .
git commit -m "..."
# 2. 强制跳过钩子(不推荐)
git commit --no-verify -m "..."
Q9:如何查看某次提交的详细信息?
# 查看提交的元信息和diff
git show 8f3a2d1
# 只看统计
git show --stat 8f3a2d1
# 只看提交信息
git log -1 --format="%B" 8f3a2d1
Q10:如何把多次提交合并成一次?
用交互式rebase的squash功能:
git rebase -i HEAD~3
# 把第2、3行的pick改成squash或s
# 保存后会合并这3次提交
十一、最佳实践
11.1 提交粒度
- 一次提交只做一件事:不要把"修复bug"和"添加功能"混在一次提交
- 小步提交:完成一个小功能就提交,而不是攒一大堆一起提交
- 可独立运行:每次提交后代码应该能正常编译/运行,不要提交"半成品"
11.2 提交信息规范
- 遵循 Conventional Commits 规范
- 标题用动词开头:
添加、修复、重构、删除 - 标题不超过50字符,body每行不超过72字符
- 说明为什么 做这个修改,而不仅仅是做了什么(代码本身已经说明了做了什么)
11.3 频繁提交
- 频繁提交 = 更细的历史记录 = 更容易回溯和撤销
- 不要等到一天结束才提交一次
- 每完成一个逻辑单元就提交
11.4 不要提交的内容
- 配置文件中的密码、密钥
- 临时文件、日志文件
- IDE配置(视团队约定)
- 编译产物(dist/、build/)
用 .gitignore 排除这些文件。
11.5 团队协作
- 提交前先
git pull,避免冲突 - 不要
--force推送到共享分支 - 使用PR/MR进行代码审查,而不是直接push到主分支
11.6 提交前自查清单
每次提交前,过一遍这个清单:
git status------ 确认要提交的文件范围正确git diff --staged------ 逐行检查将要提交的代码- 确认没有调试代码(console.log、print、debugger)
- 确认没有硬编码的密码、密钥、路径
- 确认代码能通过编译/测试
- 写有意义的提交信息
- 提交后
git log --oneline -1确认提交成功
11.7 提交信息写法进阶
好的body应该解释"为什么":
fix(auth): 修复token过期后用户无法重新登录的问题
之前的逻辑在token过期时会直接跳转登录页,但没有清除
本地存储的过期token。用户重新登录后,旧的token仍然
存在,导致请求带上过期token而失败。
改为:跳转登录页前先清除所有本地认证信息。
关联issue:
feat(user): 添加用户头像上传功能
支持jpg/png格式,限制2MB大小
Closes #123
Closes #123 会在GitHub/GitLab上自动关闭对应的issue。其他关键字:
Closes #123、Fixes #123、Resolves #123:关闭issueRefs #123、See #123:关联但不关闭Closes #123, #124:关闭多个issue
十二、小结
本篇详细讲解了 git commit 命令:
| 知识点 | 要点 |
|---|---|
| 基本用法 | -m 指定信息,-a 跳过add,不带-m用编辑器 |
| 提交信息规范 | Conventional Commits:type(scope): subject |
| --amend | 修改上次提交,会改变hash,已推送的不要用 |
| 常用选项 | --allow-empty、--no-verify、-s、--author |
| 内部原理 | commit对象包含tree、parent、author、message |
| -a 选项 | 只提交已跟踪文件,新文件不会被包含 |
| 最佳实践 | 小粒度、规范信息、频繁提交 |
核心记忆:
git add <file> # 加入暂存区
git commit -m "信息" # 提交到版本库
git commit -a -m "信息" # 跳过add直接提交已跟踪文件
git commit --amend # 修改上次提交(慎用于已推送的提交)
下一篇我们将学习 git status------如何查看仓库当前的状态,这是每次操作前后都应该看一眼的命令。
作者:全栈陈序员 出处:CSDN 声明:本文为原创文章,转载请注明出处