提交更改

本节部分内容参考自 Pro Gitgit-recipes,在原文基础上有一定修改。

git commit

git commit 命令将缓存的快照提交到项目历史。提交的快照可以认为是项目安全的版本,Git 永远不会改变它们,除非你这么要求。和 git add 一样,这是最重要的 Git 命令之一。

最常用的方式是 git commit -m "<message>",这会将暂存区的内容提交到历史,并且附加一个提交信息。

在文本编辑器中输入提交信息

如果不指定 -m 参数,Git 会打开你的文本编辑器,等待你输入提交信息。这在想要输入多行的提交信息时非常有用。

Git 会使用什么文本编辑器?在 Linux 上,默认会按照 $EDITOR 环境变量的指定,一般是 GNU nano 或者 vim;而在 Windows 上,是在安装 Git for Windows 时设置的,一般是 VSCode。如果想要更改 Git 使用的文本编辑器,可以使用 git config --global core.editor 设置。

编辑器会显示类似下面的文本信息(本例选用 Vim 的屏显方式展示):


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Your branch is up-to-date with 'origin/master'.
#
# Changes to be committed:
#	new file:   README
#	modified:   CONTRIBUTING.md
#
~
~
~
".git/COMMIT_EDITMSG" 9L, 283C

可以看到,默认的提交消息包含最后一次运行 git status 的输出,放在注释行里,另外开头还有一个空行,供你输入提交说明。你完全可以去掉这些注释行,不过留着也没关系,多少能帮你回想起这次更新的内容有哪些。

更详细的内容修改提示可以用 -v 选项查看,这会将你所作的更改的 diff 输出呈现在编辑器中,以便让你知道本次提交具体作出哪些修改。

退出编辑器时,Git 会丢弃注释行,用你输入的提交说明生成一次提交。

提交前暂存

默认情况下,git commit 只会提交暂存区中的文件,那些更改过但未暂存的文件不会被提交。

如果想要在提交之前自动暂存,可以指定 -a 参数,这会提交已暂存的文件和已更改的文件,并且文件的更改就算没有暂存也包括在内。注意,与 git add . 不同,未跟踪的文件不会被提交

重视每一次提交

Git 初学者最容易犯的一个问题就是随意地使用 git commit 提交,甚至把提交当成随时可用的存档点,把很多做到一半的工作提交进去。

好吧,对于初学者来说,也无可厚非。多用一点 Git 命令,熟悉一下也是好的。毕竟每个人都有起步的过程嘛。

但是,这是一篇「重学指南」,当你再一次学习 git commit 时,你就必须开始养成重视提交的习惯了

重视提交的内容

首先要重视提交的内容。最好的情况下,每一次提交都包含一次完整的功能变更,没有任何尚未完成的搁置的工作。你不应该把写到一半的功能提交进储存库。

当然,这只是理想的情况。实际肯定不会总是这么顺利。还有一些不那么好的情况,也是允许的。

  • 上上策:提交中不含未完成的工作,并且仅包含一个功能变动。
  • 上策:提交中不含未完成的工作。
  • 中策:本地的提交可以包含未完成的工作,但不能把这样的提交推送到远程。
  • 下策:不完整的提交被推送上去,现在所有人都看到你做了多么糟糕的事情。

这几种情况中,除去下策,其他都是在一个科学管理的项目中允许发生的。

如果不得不……

有的时候,出于某些特殊的目的,我们不得不提交一些不完整的代码。但是在这么做之前,请想一想,有没有别的更好的方式来让我完成这件事?

  • 我想临时切换到别的分支:请优先使用 git stash,它就是为这一需求设计的。如果你需要在另一个分支上长期工作,也可以考虑用 git worktree 建立一个新的工作树,这样两个分支的内容就不会相互干扰了。
  • 我需要在别的机器上继续这项工作:的确是棘手的情况。这种时候,允许你偷偷地把未完成的工作提交上去,在提交消息里警告别人不要在这个节点签出。工作完成后,用下面所说的修改提交或者 git rebase -i(将在后面的文章里介绍)覆盖掉之前未完成的提交。
  • 我不小心在提交里漏了一个文件:这种事情在所难免。下文的修改提交会对你有帮助。

重视提交消息

其次要重视提交消息。最好的情况下,提交消息要符合一定的格式规范,并且清晰描述了这次提交做出的变更

为什么格式规范那么重要?第一个原因是能够让别人快速理解你的项目历史,第二个原因是这有助于使用自动化工具。

提交的格式规范有很多种,没有哪个比哪个更好。常见的提交格式规范有「约定式提交」和「gitmoji」等等,关于它们的详细信息可以看附录中的内容。

修改提交

上面我们提到了一种情况:在本地的提交中可以包含未完成的工作,但这样的提交不能推送到远程。

你可能会疑惑:git push 的时候,所有的提交历史都会推送,那么怎么保证那些不完整的提交不被推送呢?

很简单。因为保存未完成工作的提交永远是最后一个提交,所以只要我们用新的提交替换掉它就可以了。

完成这一操作的命令,是 git commit --amend

“amend”是修正的意思。用这条命令产生的提交,会替换掉上一次的提交。

比如说,在提交之前,提交的历史记录是这样的:

* b9d4ede :construction: [WIP] Some work in progress
* 78e1527 :zap: Improve UI performance
* 34a1e52 :sparkles: Connect to the database
* f6a09fe :bug: Fix stats not updating

然后产生一条新的 amend 提交:

git commit --amend -m ":bug: Fix layout issues"

新的历史记录会是这样:

* d5b5ad3 :bug: Fix layout issues
* 78e1527 :zap: Improve UI performance
* 34a1e52 :sparkles: Connect to the database
* f6a09fe :bug: Fix stats not updating

可以看到,原本的包含未完成工作的提交,被新的提交替换掉了。现在所有的提交都是完整又可爱的。

需要注意的是,提交的哈希值在 git commit --amend 之后发生了变化。这意味着如果那条被替换的提交已经推送到远程,你需要用 git push --force 来强制覆盖它。