文档章节

《Git权威指南》读书笔记 第五章 Git暂存区

一万
 一万
发布于 2016/07/16 19:18
字数 3376
阅读 50
收藏 4

5.1 修改不能直接提交

首先修改welcome.txt文件,在这个文件后面追加一行:

echo "Nice to meet you." >> welcome.txt

使用git diff命令查看修改后的文件与暂存区(并不是版本库,后面会有相关讨论)中的文件的差异:

diff --git a/welcome.txt b/welcome.txt
index a8f9fd8..b0e5c6e 100644
--- a/welcome.txt
+++ b/welcome.txt
@@ -1 +1,2 @@
 Hello
+Nice to meet you.

差异的输出格式:

  • 以---开头的文件表示源文件;
  • 以+++开头的文件表示目标文件;
  • @@ 中间的数字表示行号和行数,-1表示源文件第一行开始共一行,+1,2表示目标文件从第一行开始共2行;
  • 文件内容中以空格开头的表示源文件和目标文件共同拥有的行,-开头的行表示只在源文件中出现的行,+开头的行表示只在目标文件出现的行。

此时使用git commit命令并不会提交修改,反而会报错:

git commit -m "Append a nice line."

On branch master
Changes not staged for commit:
        modified:   welcome.txt

no changes added to commit

使用git status查看文件状态,加上-s参数显示精简格式的状态输出:

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:   welcome.txt

no changes added to commit (use "git add" and/or "git commit -a"
git status -s
 M welcome.txt

welcome.txt文件处于修改状态。

根据git status输出的提示,需要对修改的文件执行git add命令,将修改的文件添加到“提交任务”中,然后才能提交。此处与svn差别很大,在svn中执行add操作是向版本库中添加新文件,修改的文件在下次提交时会直接提交。

按照提示将修改的文件添加到提交任务中:

git add welcome.txt

在执行完添加操作,而没有执行提交操作时,查看一下Git工作区发生了什么变化:

1、执行git diff没有输出,说明是本地文件与暂存区的比较;

2、执行git diff HEAD(当前版本库的头指针),会有差异输出,说明此时比较的是本地文件和版本库的最新版本;

3、执行git status,输出与添加前不一样了:

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

        modified:   welcome.txt

4、执行git status -s,以简洁方式显示状态:

git status -s
M  welcome.txt

乍一看貌似与添加前的输出一样,其实它们有细微的差别:

  • 虽然都是M(Modified)标识,但是位置不一样。在执行git add命令之前,M位于第二列(第一列是一个空格),在执行git add命令之后,M位于第一列(第二列是空白)。
  • 位于第一列的M的含义是:版本库中的文件与暂存区的文件相比有改动;
  • 位于第二列的M的含义是:工作区当前的文件与处于赞春去的文件相比有改动。

为了加深理解,暂不提交之前的添加任务,而继续修改文件:

echo "Bye-Bye." >> welcome.txt

查看一下状态:

 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

这里同时出现了前文讨论的两种状态。再查看精简的状态:

git status -s
MM welcome.txt

同时出现了两个M:不但版本库中最新提交的文件与处于暂存区中的文件相比有改动,而且工作区与暂存区中的文件相比也有改动。

即现在有三个不同版本的welcome.txt:一个在工作区,一个在暂存区,一个是版本库中最新版本的welcome.txt。

使用不同参数调用git diff命令可以看到不同状态下welcome.txt文件的差异:

  • 不带任何参数的git diff比较工作区和暂存区的差异:
  • git diff
    diff --git a/welcome.txt b/welcome.txt
    index b0e5c6e..45cf837 100644
    --- a/welcome.txt
    +++ b/welcome.txt
    @@ -1,2 +1,3 @@
     Hello
     Nice to meet you.
    +Bye-Bye.
    
    
  • git diff HEAD比较工作区和版本库最新版本的差异:
  • git diff HEAD
    diff --git a/welcome.txt b/welcome.txt
    index a8f9fd8..45cf837 100644
    --- a/welcome.txt
    +++ b/welcome.txt
    @@ -1 +1,3 @@
     Hello
    +Nice to meet you.
    +Bye-Bye.
    
  • 带--cached 或 --staged参数查看暂存区和版本库之间的差异:
  •  git diff --staged
    diff --git a/welcome.txt b/welcome.txt
    index a8f9fd8..b0e5c6e 100644
    --- a/welcome.txt
    +++ b/welcome.txt
    @@ -1 +1,2 @@
     Hello
    +Nice to meet you.
    

执行提交操作:

git commit -m "which version checked in?"
[master 326f237] which version checked in?
 1 file changed, 1 insertion(+)

提交成功,使用git status -s查看状态:

git status -s
 M welcome.txt

文件名前少了第一个M,只剩下第二个M,说明暂存区的任务被提交到了版本库,现在只剩下工作区和暂存区的差异。

5.2 理解Git暂存区

在版本库.git目录下有一个index文件,看一下这个文件:

1、撤销工作区中welcome.txt文件尚未提交的修改(5.1中添加的Bye-Bye那一行)并查看工作区状态:

git checkout -- welcome.txt
git status -s

git status -s无输出,说明未提交的修改已经被撤销了。

2、查看.git/index的时间戳:

ls --full-time .git/index
-rw-r--r-- 1 x250 197121 145 2016-07-16 07:52:20.640437700 +0800 .git/index

时间戳是刚刚运行git status -s的时间。

3、再次查看工作区状态并查看.git/index的时间戳:

git status -s
ls --full-time .git/index
-rw-r--r-- 1 x250 197121 145 2016-07-16 07:52:20.640437700 +0800 .git/index

时间戳没有变化。

4、更改welcome.txt文件的时间戳,但并不改变它的内容(可以使用touch命令,它用来修改文件时间戳,或者新建一个不存在的文件)。然后查看工作区状态并查看.git/index的时间戳:

touch welcome.txt
git status -s
ls --full-time .git/index
-rw-r--r-- 1 x250 197121 145 2016-07-16 07:58:10.512449200 +0800 .git/index

发现git status -s依然无输出,说明工作区没有新的修改;但是.git/index时间戳改变了。

以上的实验说明当执行git status命令(或者git diff命令)扫描工作区改动的时候,先依据.git/index文件中记录的时间戳、长度等信息判断工作区文件是否改变。如果工作区文件的时间戳改变了,说明文件的内容可能被改变了,需要打开文件、读取文件内容,与更改前的原始文件相比较,判断文件内容是否被更改。如果文件内容没有改变,则将该文件新的时间戳记录到.git/index文件中。因为如果要判断文件是否更改,使用时间戳、文件长度等信息进行比较要比通过文件北荣比较快得多,所以Git这样的实现方式可以让工作区状态扫描更快速的执行。

文件.git/index是包含文件索引的目录树,记录了文件名和文件的状态信息,文件的内容没有存储在其中,而是保存在Git对象库.git/objects目录中,文件索引建立了文件和对象库的对应关系。

下图展示了工作区、版本库中的暂存区和版本库之间的关系:

  • 图中左侧为工作区,右侧为版本。在版本库中标记为index的区域是暂存区,标记为master的是master分支所代表的目录树;
  • HEAD指向master分支,所以图中所示命令中出现HEAD地方可以用master来替换;
  • 图中的objects表示的区域为Git的对象库,位于.gti/objects目录下;
  • 当对工作区修改(或新增)的文件执行git add命令时,暂存区的目录树将被更新,同时工作区修改(或新增)的文件内容会被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中;
  • 当执行提交操作时,暂存区的目录树会写到版本库中,master指向新写入的目录树(内容与提交的暂存区的目录树一致);
  • 当执行git reset HEAD时,暂存区的目录树会被重写,会被master分支指向的目录树的内容所替换,但是工作区不受影响;
  • 当执行git rm --cahced <file> 命令时,会直接从暂存区删除文件,工作区不会改变;
  • 当执行git checkout . 或 git checkout -- <file>命令时,会用暂存区全部的文件或指定的文件替换工作区的文件,即这个命令会清除工作区中未添加到暂存区的改动;
  • 当执行git checkout HEAD . 或 git checkout HEAD <file> 命令时,会用HEAD指向的master分支中的全部或部分文件替换暂存区和工作区的文件,即它会清除工作区中未添加的改动还会清除暂存区中未提交的改动。

5.3 浏览暂存区和版本库的目录树、git diff

1、浏览暂存区和版本库的目录树

对于HEAD指向的目录树,可以使用git ls-tree来查看,-l参数可现实文件的大小:

git ls-tree -l HEAD
100644 blob b0e5c6e24bc84d489773b2fda5accf005bc912f1      25    welcome.txt

输出中的信息从左到右:

  • 第一个字段是文件的读写属性(rw-r--r--);
  • 第二个字段说明是Git对象库中的一个blob对象(文件);
  • 第三个字段是该文件在对象库中的ID(一个40位的SHA1哈希值);
  • 第四个字段是文件大小(单位是字节);
  • 第五个字段是文件名。

下面的命令清除当前工作区中没有加入版本库的文件和目录:

git clean -fd

下面的命令用暂存区内容刷新工作区:

git checkout .

对工作区做一些修改:

$ echo "Bye-Bye." >> welcome.txt

$ mkdir -p a/b/c

$ echo "Hello." > a/b/c/hello.txt

$ git add .

x250@x250-PC MINGW64 ~/demo (master)
$ echo "Bye-Bye." >> a/b/c/hello.txt

$ git status -s
AM a/b/c/hello.txt
M  welcome.txt

查看工作区文件的大小:

$ find . -path ./.git -prune -o -type f -printf "%-20p\t%s\n"
./a/b/c/hello.txt       16
./welcome.txt           36

显示暂存区的目录树:

 git ls-files -s
100644 18832d35117ef2f013c4009f5b2128dfaeff354f 0       a/b/c/hello.txt
100644 45cf8376b221e28cb9c5afb382cfe15ceb3dc520 0       welcome.txt

-s命令要去显示暂存区中Object的对象名。输出中的0不是文件大小而是暂存区编号。如果想针对暂存区的目录树使用git ls-tree命令,需要先将暂存区的目录树写入Git对象库(git write-tree):

$ git write-tree
1343285bb5205fe9eed661efebcc9d65bf0cea7e

$ git ls-tree -l 1343285
040000 tree 53583ee687fbb2e913d18d508aefd512465b2092       -    a
100644 blob 45cf8376b221e28cb9c5afb382cfe15ceb3dc520      34    welcome.txt

git write-tree的输出是写入Git对象库中的TreeID,这个ID将作为下一条命令的参数。

在git ls-tree命令中,没有把40位的ID写全,而是使用了前几位,实际上只要不与其他对象的ID冲突,就可以使用缩写ID。

git ls-tree输出的第一条显示目录a是一个tree对象。

如果想要递归显示目录内容,则使用-r参数。使用参数-t可以把递归中遇到的每棵树都显示出来,而不是只显示最终文件。

2、git diff

通过调用git diff并添加不同参数,可以对工作区、暂存区和HEAD中的内容进行两两比较。如下图所致(1)工作区和暂存区的比较:

$ git diff
diff --git a/a/b/c/hello.txt b/a/b/c/hello.txt
index 18832d3..e8577ea 100644
--- a/a/b/c/hello.txt
+++ b/a/b/c/hello.txt
@@ -1 +1,2 @@
 Hello.
+Bye-Bye.

(2)暂存区和HEAD比较

git diff --cached
diff --git a/a/b/c/hello.txt b/a/b/c/hello.txt
new file mode 100644
index 0000000..18832d3
--- /dev/null
+++ b/a/b/c/hello.txt
@@ -0,0 +1 @@
+Hello.
diff --git a/welcome.txt b/welcome.txt
index b0e5c6e..45cf837 100644
--- a/welcome.txt
+++ b/welcome.txt
@@ -1,2 +1,3 @@
 Hello
 Nice to meet you.
+Bye-Bye.

(3)工作区和HEAD比较

git diff HEAD
diff --git a/a/b/c/hello.txt b/a/b/c/hello.txt
new file mode 100644
index 0000000..e8577ea
--- /dev/null
+++ b/a/b/c/hello.txt
@@ -0,0 +1,2 @@
+Hello.
+Bye-Bye.
diff --git a/welcome.txt b/welcome.txt
index b0e5c6e..45cf837 100644
--- a/welcome.txt
+++ b/welcome.txt
@@ -1,2 +1,3 @@
 Hello
 Nice to meet you.
+Bye-Bye.

5.4 不要使用git commit -a

Git的提交命令(git commit)可以带上-a参数,对本地所有变更的文件执行提交操作,包括对本地修改的文件和删除的文件,但不包括未被版本库跟踪的文件。

这个命令的确可以简化一些操作,减少用git add命令表示变更文件的步骤,但是如果习惯了使用这种偷懒的提交命令,机会丢掉Git暂存区最大的好处:对提交内容进行控制的能力。作者推荐不要使用git commit -a。

5.5 保存当前的工作进度

 git stash
Saved working directory and index state WIP on master: 326f237 which version checked in?
HEAD is now at 326f237 which version checked in?

$ git status
On branch master
nothing to commit, working directory clean

git stash命令备份当前的工作区的内容,从最近的一次提交中读取相关内容,让工作区保证和上次提交的内容一致。同时,将当前的工作区内容保存到Git栈中。

运行完git stash后使用git status命令,发现工作区中尚未提交的改动都不见了。

© 著作权归作者所有

一万
粉丝 30
博文 102
码字总数 173386
作品 0
朝阳
程序员
私信 提问
加载中

评论(2)

一万
一万 博主

引用来自“wo545”的评论

注意的好细。值得一看
13
wo545
wo545
注意的好细。值得一看
《Git权威指南》读书笔记 第九章 恢复进度

9.1 恢复保存的进度 查看第五章保存的进度: 恢复进度: 查看工作区状态,进度已经找回了: 对找回进度的工作区做以下处理: (1)对当前的暂存区进行提交: (2)撤销之前的提交,工作区和暂...

一万
2016/07/18
59
0
《Git权威指南》读书笔记 第七章 Git重置

提交可以通过对父提交的关联实现对提交历史的追溯。可以使用下面的命令对提交进程追溯: 7.1 分支游标master 当有新的提交发生时,文件.git/refs/head/master的内容如何改变? 首先创建一个新...

一万
2016/07/18
30
0
《Git权威指南》读书笔记 第八章 Git检出(checkout)

重置命令(git reset)主要用于修改master引用指向的提交ID,修改过程中HEAD的指向并没有改变(一致指向/refs/heads/master,而这个master被git reset命令改变)。 相对的Git检出命令(git c...

一万
2016/07/18
56
0
Git 的暂存区(staging area)理解

通常在很多传统集中式版本控制系统中,只有两个空间用来管理你的数据,一个是你的working copy(工作区),另一个便是 datastore(版本库),然而在Git中,引入了staging area(index)这一概...

麦壳原野
2014/04/02
714
0
《Git权威指南》读书笔记 第四章 git初始化

学习和记笔记从第四章开始,前面有点冗长,暂时略过,有时间再看。 4.1 创建版本库及第一次提交 查看git版本: 首先设置Git的配置变量。这些设置会保存在用户主目录下的.gitconfig文件或etc...

一万
2016/07/15
33
0

没有更多内容

加载失败,请刷新页面

加载更多

米联客(MSXBO)USB3.0 UVC摄像头实现基于FT602Q芯片方案

USB3.0 UVC摄像头实现基于FT602Q芯片方案 USB3.0接口芯片FT602Q支持UVC协议,可以很方便的实现一个USB相机。这里我们采集HDMI输入的视频信号,实现了一个USB3.0的HDMI采集卡。 逻辑结构如下图...

msxbo
26分钟前
4
0
未初始化指针问题

《C和指针》书上说 int *a ... *a = 12 这样写声明一个变量,但未对指针初始化 如果指针是函数的形参,比如 void func(int *a) { (* a) = 12;//这样操作有无问题? } ======================...

天王盖地虎626
42分钟前
7
0
Python的一些细节 II

1. isinstance() 与 type() 区别 class type(name, bases, dict) name -- 类的名称。 bases -- 基类的元组。 dict -- 字典,类内定义的命名空间变量。 返回值:一个参数,返回对象的类型;三...

Eappo_Geng
53分钟前
4
0
笔试题-武汉珞珈德毅笔试题

1.写出Java语言的基本数据类型。 2.简述cookie和session区别。 1、cookie数据存放在客户的浏览器上,session数据放在服务器上。 2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行...

QuasimodoALei
今天
7
0
IDEA Maven project: 'xxx/pom.xml' already exists in VFS

Failed to create a Maven project: ‘xxx/pom.xml‘ already exists in VFS idea创建项目后,发现项目有问题,删除后重新创建,提示错误如下。 解决办法 1.通过idea打开任意一个项目 2.File...

国产大熊猫
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部