要了解git commit 和git commit -a 之间的区别,您需要了解git 中的索引,也称为暂存区。
索引本质上存储将进入下一次提交的每个文件的状态。 (这里的“状态”是指文件的确切内容,git 通过其哈希标识。)当您键入 git commit 而没有任何其他参数时,git 将进行一个新的提交,其中所有文件的状态与索引中的完全相同。这可能与您的工作树非常不同,这是 git 的一个有用功能,我将在下面解释。
git add 真正做的是“暂存”文件,或将其当前状态添加到索引中。不管它最初是否被跟踪,你说的是“在下一次提交中,我希望这个文件与这个内容完全相同”。重要的是要意识到这记录了您要添加的内容,而不是文件名 - 这意味着如果您在使用 git add 暂存文件后继续对文件进行更改,您将看到来自 @987654326 的输出@ 有时会让来自其他版本控制系统的人感到困惑:
$ git status
[...]
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# modified: foo.txt
#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: foo.txt
...但是如果您了解索引,那绝对有意义。您可以通过以下方式查看您已经暂存的更改(即索引中的文件状态与 HEAD 之间的差异,这通常是您正在处理的分支上的最后一次提交):
$ git diff --cached
...您可以通过以下方式查看所有尚未暂存的更改(即您的工作副本和索引之间的差异):
$ git diff
那么为什么这很有用呢?从本质上讲,这个想法是,当每个提交只包含一组逻辑分组的更改,这些更改对项目进行明确定义的改进时,项目的历史是最有用的。您可以使这些提交越小越好,因为稍后美妙的工具git bisect 可以快速帮助您追踪哪个更改引入了错误。除非您非常自律,否则在修复错误的过程中,您通常会编辑其他文件,而这些文件实际上并不需要更改以修复问题,或者可能最终修复两个逻辑上不同的问题决定提交您的更改。在这些情况下,索引可以帮助您分离出这些更改 - 只需 git add 每个包含您想要在第一次提交中更改的文件,运行 git commit,然后执行相同的操作来创建第二次提交。 [1]
如果您习惯这样做并喜欢工作流程,您有时会只想在文件中暂存一些更改而不是其他更改,在这种情况下,您会想要了解git add -p(然后是它的交互式s 和e 选项!)
但是,回到最初的问题 - git commit -a 有什么不同?从本质上讲,“在创建此提交之前,还要将每个已修改(或删除)的文件暂存为当前状态。”因此,如果您认为在提交之前在索引中仔细暂存文件没有用处,您可以一直使用“git commit -a”。但是,我认为使用 git 的好处之一是它鼓励您创建 漂亮 提交,并且积极使用索引对此有很大帮助。 :)
注意事项:
为了保持上面的解释简单,有些陈述有些近似(例如我没有提到索引还存储文件的(非)可执行状态,它可以在合并期间有多个版本,等等等等)
[1] 如果您想确保历史记录中的每个提交都经过正确测试,那么您应该小心执行此操作 - 如果这在您的项目中很重要,您应该在每次新提交之前测试树,然后再推送...