git commit -a 的误区

Author: Charles Zhu
Summary: git 错误理解

SVN下的项目管理

在使用SVN进行版本管理的时候,我喜欢完成一个功能后进行提交。因为SVN设计的项目大多是在Linux系统,由于缺少较好的IDE,通常会先使用svn status 查看修改的文件,然后逐一确认,最后使用 svn ci -a -m \"commit content\" 进行提交。

git下的误区

大多数情况下,我一直按照SVN的方式处理git项目。我也一直感觉使用git commit -a很爽,直到有一天发现git的log显示...
接下来我们来做一些操作:

$ git init
$ echo "this is a init text" >> hello.txt
$ git status
On branch master

Initial commit

Untracked files:
(use "git add <file>..." to include in what will be committed)

    hello.txt

nothing added to commit but untracked files present (use "git add" to track)

很显然,我们需要先“添加” 文件到版本库(注意这个add在git中不是添加到版本库),然后进行提交:

$ git add .
$ git commit -m "init commit"

接下来修改文件:

$ echo "add a line" >> hello.txt 
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

    modified:   hello.txt

no changes added to commit (use "git add" and/or "git commit -a")

仔细看log,它提示我们先进行add操作再进行commit(如果确定要提交的情形下),但是等一下,add操作不是已经做了吗?难道add的含义不是添加文件到版本库以便进行追踪?

git add到底是什么意思

我们先接着上面继续操作:

$ git add hello.txt 
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

    modified:   hello.txt

这时的log和之前相比,没有了add的建议操作,为了更明显显示这种区别:

$ echo "difference" >>hello.txt
$git status 
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

    modified:   hello.txt

Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

    modified:   hello.txt

或者用-s参数:

$git status -s
MM hello.txt //在shell中2个M的颜色显示不一样

这里可以明白,git中的add操作和svn中的add操作是完全不一样的意思。
在git中, 执行add操作后是把文件加入版本库的暂存区,而commit针对的内容就只是暂存区。
git commit -a 其实会先执行add操作再提交暂存区的内容,所以这样做是一个非常不好的实践,因为add到暂存区的操作实际是需要我们严格控制的。至于git上为什么要有这样的设计,那么需要知道 reset 命令。这个下次再讲。

反思

很早就知道“暂存区”的概念,可是一直没有深入。git 的 addcommit 一直使用,可是却没有想到背后的涵义。所谓 “知其然不知其所以然”,实是大忌!