- 第5章 Git暂存区
- 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:welcome.txt
- 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:welcome.txt
- 位于您当前工作的分支master上
- 下列修改还没有加入到提交任务(提交暂存区,stage)中,不会被提交:
- (使用"git add<file>…"命令后,改动就会加入到提交任务中,
- 要在下一次提交操作时才被提交)
- (使用"git checkout—<file>…"命令,工作区中当前您不打算
- 提交的修改会被彻底清除!)
- 已修改:welcome.txt
- On branch master
- Changes to be committed:
- (use "git reset HEAD<file>…"to unstage)
- modified:welcome.txt
- 位于分支master上
- 下列修改将被提交:
- (如果你后悔了,可以使用"git reset HEAD<file>…"命令
- 将下列改动撤出提交任务(提交暂存区,stage),否则执行提交命令
- 可真的要提交喽)
- 已修改:welcome.txt
- On branch master
- Changes to be committed:
- (use "git reset HEAD<file>…"to unstage)
- modified:welcome.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:welcome.txt
第5章 Git暂存区
上一章主要学习了三个命令:git init、git add和git commit,这三个命令可以说是版本库创建的三部曲。同时还通过对几个问题的思考了解了Git版本库在工作区中的布局,Git三个等级的配置文件及Git的别名命令等内容。
在上一章的实践中,DEMO版本库经历了两次提交,可以用git log查看提交日志(附加的—stat参数可以看到每次提交的文件变更统计)。
$cd/path/to/my/workspace/demo
$git log—stat
commit a0c641e92b10d8bcca1ed1bf84ca80340fdefee6 Author:Jiang Xin<jiangxin@ossxp.com>
Date:Mon Nov 29 11:00:06 2010+0800
who does commit?
commit 9e8a761ff9dd343a1380032884f488a2422c495a
Author:Jiang Xin<jiangxin@ossxp.com>
Date:Sun Nov 28 12:48:26 2010+0800
initialized.
welcome.txt|1+
1 files changed,1 insertions(+),0 deletions(-)
可以看到第一次(最早的)提交对文件welcome.txt有一行变更,而第二次(最新的)提交因为是使用—allow-empty参数进行的一次空提交,所以提交说明中看不到任何对实质内容的修改。
下面我们将仍在这个工作区继续新的实践和学习,以掌握Git的一个最重要概念:暂存区。
5.1 修改不能直接提交吗
首先更改welcome.txt文件,在这个文件后面追加一行。可以使用下面的命令实现内容的追加:
$echo "Nice to meet you.">>welcome.txt
这时可以通过执行git diff命令看到修改后的文件与版本库中的文件的差异。(实际上这句话有问题,与本地比较的不是版本库中的文件,而是一个中间状态的文件。)
$git diff
diff—git a/welcome.txt b/welcome.txt
index 18832d3..fd3c069 100644
—-a/welcome.txt
+++b/welcome.txt
@@-1+1,2@@
Hello.
+Nice to meet you.
对差异输出是不是很熟悉?在之前介绍版本库的“黑暗的史前时代”时,曾经展示了diff命令的输出,两者的格式是一样的。
既然文件修改了,那么就提交吧。提交能够成功吗?
$git commit-m "Append a nice line."
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:welcome.txt
#
no changes added to commit(use "git add" and/or "git commit-a")
提交成功了吗?好像没有!
提交没有成功的证据:
(1)先来看看提交日志,如果提交成功,应该有新的提交记录出现。
下面使用了精简输出来显示日志,以便更简洁和清晰地看到提交的历史。从其中能够看出版本库中只有两个提交,都是在上一章的实践中完成的。也就是说,刚才针对修改文件的提交没有成功!
$git log—pretty=oneline
a0c641e92b10d8bcca1ed1bf84ca80340fdefee6 who does commit?
9e8a761ff9dd343a1380032884f488a2422c495a initialized.
(2)执行git diff可以看到与之前相同的差异输出,这也说明了之前的提交没有成功。
(3)执行git status查看文件状态,可以看到文件处于修改状态,而且git status命令的输出和git commit提交失败的输出信息完全一样!
(4)对于习惯了像CVS和Subversion那样精简的状态输出的用户,可以在执行git status时附加上-s参数,显示精简格式的状态输出。
$git status-s M welcome.txt
提交为什么会失败呢?再回过头来仔细看看刚才git commit命令提交失败后的输出:
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:welcome.txt
#
no changes added to commit(use "git add" and/or "git commit-a")
把它翻译成中文则是:
位于您当前工作的分支master上
下列修改还没有加入到提交任务(提交暂存区,stage)中,不会被提交:
(使用"git add<file>…"命令后,改动就会加入到提交任务中,
要在下一次提交操作时才被提交)
(使用"git checkout—<file>…"命令,工作区中当前您不打算
提交的修改会被彻底清除!)
#
已修改:welcome.txt
#
警告:提交任务是空的噻,您不要再搔扰我啦(除非使用
"git add"和/或"git commit-a"命令)
也就是说,需要对修改的welcome.txt文件执行git add命令,将修改的文件添加到“提交任务”中,然后才能提交!
这个行为真的很奇怪,对于其他版本控制系统来说执行add操作是向版本库中添加新文件用的,修改的文件(已被版本控制跟踪的文件)在下次提交时会直接被提交。Git的这个古怪的行为会在下面的介绍中得到解释,大家会逐渐习惯并喜欢Git的这个设计。
好了,现在就将修改的文件“添加”到提交任务中吧:
$git add welcome.txt
现在再执行一些Git命令,看看当执行完“添加”操作后,Git库发生了什么变化:
执行git diff没有输出,难道是被提交了?可是只是执行了“添加”到提交任务的操作,相当于一个“登记”的命令,并没有执行提交哇?
$git diff
这时如果与HEAD(当前版本库的头指针)或master分支(当前工作分支)进行比较,就会发现有差异。这个差异才是正常的,因为尚未真正提交嘛。
$git diff HEAD
diff—git a/welcome.txt b/welcome.txt
index 18832d3..fd3c069 100644
—-a/welcome.txt
+++b/welcome.txt
@@-1+1,2@@
Hello.
+Nice to meet you.
执行git status命令,状态输出和之前的不一样了。
$git status
On branch master
Changes to be committed:
(use "git reset HEAD<file>…"to unstage)
#
modified:welcome.txt
#
再对新的Git状态输出进行翻译:
$git status
位于分支master上
下列修改将被提交:
(如果你后悔了,可以使用"git reset HEAD<file>…"命令
将下列改动撤出提交任务(提交暂存区,stage),否则执行提交命令
可真的要提交喽)
#
已修改:welcome.txt
#
不得不说,Git太人性化了,它把各种情况下可能使用到的命令都告诉给用户了,虽然这显得有点啰嗦。如果不要这么啰嗦,可以像下面这样用简洁的方式显示状态:
$git status-s
M welcome.txt
上面的精简状态输出与执行git add之前的精简状态输出相比,有细微的差别,发现了吗?
虽然都是M(Modified)标识,但是位置不一样。在执行git add命令之前,这个M位于第二列(第一列是一个空格),在执行完git add之后,字符M位于第一列(第二列是空白)。
位于第一列的字符M的含义是:版本库中的文件与处于中间状态——提交任务(提交暂存区,stage)中的文件相比有改动。
位于第二列的字符M的含义是:工作区当前的文件与处于中间状态——提交任务(提交暂存区,stage)中的文件相比有改动。
是不是还有一些不明白?为什么Git的状态输出中提示了那么多让人不解的命令?为什么存在一个提交任务的概念而又总是把它叫作暂存区(stage)?不要紧,马上就会专题讲述“暂存区”的概念。当了解了Git版本库的设计原理之后,理解相关Git命令就易如反掌了。
这时如果直接提交(git commit),加入提交任务的welcome.txt文件的更改就会被提交入库了。但是先不忙着执行提交,再执行一些操作,看看是否会被彻底地搞糊涂。
(1)继续修改一下welcome.txt文件(在文件后面再追加一行)。
$echo "Bye-Bye.">>welcome.txt
(2)然后执行git status,查看一下状态:
$git status
On branch master
Changes to be committed:
(use "git reset HEAD<file>…"to unstage)
#
modified:welcome.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:welcome.txt
#
状态输出居然是之前出现的两种不同状态输出的杂合体。
(3)如果显示精简的状态输出,也会看到前面两种精简输出的杂合体。
$git status-s
MM welcome.txt
上面的更为复杂的Git状态输出可以这么理解:不但版本库中最新提交的文件与处于中间状态——提交任务(提交暂存区,stage)中的文件相比有改动,而且工作区当前的文件与处于中间状态——提交任务(提交暂存区,stage)中的文件相比也有改动。
即现在welcome.txt有三个不同的版本,一个在工作区,一个在等待提交的暂存区,还有一个是版本库中最新版本的welcome.txt。通过不同的参数调用git diff命令可以看到不同状态下welcome.txt文件的差异。
(1)不带任何选项和参数调用git diff显示工作区的最新改动,即工作区与提交任务(提交暂存区,stage)中相比的差异。
$git diff
diff—git a/welcome.txt b/welcome.txt
index fd3c069..51dbfd2 100644
—-a/welcome.txt
+++b/welcome.txt
@@-1,2+1,3@@
Hello.
Nice to meet you.
+Bye-Bye.
(2)将工作区和HEAD(当前工作分支)相比,会看到更多的差异。
$git diff HEAD
diff—git a/welcome.txt b/welcome.txt
index 18832d3..51dbfd2 100644
—-a/welcome.txt
+++b/welcome.txt
@@-1+1,3@@
Hello.
+Nice to meet you.
+Bye-Bye.
(3)通过参数—cached或—staged调用git diff命令,看到的是提交暂存区(提交任务,stage)和版本库中文件的差异。
$git diff—cached
diff—git a/welcome.txt b/welcome.txt
index 18832d3..fd3c069 100644
—-a/welcome.txt
+++b/welcome.txt
@@-1+1,2@@
Hello.
+Nice to meet you.
好了,现在是时候提交了。执行git commit命令进行提交:
$git commit-m "which version checked in?"
[master e695606]which version checked in?
1 files changed,1 insertions(+),0 deletions(-)
这次提交终于成功了。如何证明提交成功了呢?
(1)通过查看提交日志,看到了新的提交:
$git log—pretty=oneline
e695606fc5e31b2ff9038a48a3d363f4c21a3d86 which version checked in?
a0c641e92b10d8bcca1ed1bf84ca80340fdefee6 who does commit?
9e8a761ff9dd343a1380032884f488a2422c495a initialized.
(2)查看精简的状态输出。
状态输出中,文件名的前面少了一个字母M,即只剩下第二列的字母M。那么第一列的M哪里去了?被提交了呗。即提交任务(提交暂存区,stage)中的内容被提交到版本库中。所以,第一列会因为提交暂存区(提交任务,stage)与版本库中的状态一致而显示一个空白。
$git status-s
M welcome.txt
提交的welcome.txt是哪个版本呢?可以通过执行git diff或git diff HEAD命令查看差异。虽然命令git diff和git diff HEAD的比较过程并不相同(可以通过strace命令跟踪命令执行过程中的文件访问),但是会看到如下面所示的相同的差异输出结果。
$git diff
diff—git a/welcome.txt b/welcome.txt
index fd3c069..51dbfd2 100644
—-a/welcome.txt
+++b/welcome.txt
@@-1,2+1,3@@
Hello.
Nice to meet you.
+Bye-Bye.