【Git】从入门到精通(六)—— git commit:提交到版本库

前言

在上一篇教程中,我们详细讲解了 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会:

  1. 根据暂存区的当前状态,生成一个tree对象(记录目录结构)
  2. 生成一个commit对象(包含tree的hash、父commit的hash、作者信息、提交信息、时间戳)
  3. 将当前分支指针(如 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 提交前自查清单

每次提交前,过一遍这个清单:

  1. git status ------ 确认要提交的文件范围正确
  2. git diff --staged ------ 逐行检查将要提交的代码
  3. 确认没有调试代码(console.log、print、debugger)
  4. 确认没有硬编码的密码、密钥、路径
  5. 确认代码能通过编译/测试
  6. 写有意义的提交信息
  7. 提交后 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:关闭issue
  • Refs #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 声明:本文为原创文章,转载请注明出处

相关推荐
真上帝的左手38 分钟前
30. 技术专题-‌应用框架组件-搜索引擎-ES
大数据·分布式·elasticsearch·搜索引擎·中间件
Elasticsearch42 分钟前
两条规则,一次故障,一条告警:变点检测如何减少告警噪声
elasticsearch
天河归来10 小时前
第三方接口不稳定,如何让核心任务继续完成
java·大数据
GlobalInfo11 小时前
2026年全球机器人用超平执行器市场研究报告:市场规模达7.51亿美元,人形机器人商业化与轻量化运动控制需求推动行业增长
大数据·人工智能·数码相机·microsoft·机器人
IT古董13 小时前
《Agent开发工程师成长指南》- 第4章 第2节:Prompt设计五要素——任务、角色、上下文、约束与输出格式
大数据·人工智能·prompt
Allen_LVyingbo15 小时前
NVIDIA CUDA Tile 在医疗计算中的算子形式化、算法重构与内核实现
大数据·python·算法·重构·知识图谱·健康医疗·数据库架构
全栈陈序员16 小时前
【Git】从入门到精通(一)—— Git简介:什么是版本控制系统
大数据·git·elasticsearch
中国搜索直付通16 小时前
分账系统有风险吗?资金安全怎么保障?
大数据·支付通道·分账系统·分账支付
阿里云大数据AI技术16 小时前
云栖实录|阿里云AI搜索系列产品全新发布 - 新引擎
人工智能·elasticsearch