关于Git暂存区的理解

     暂存区可以说是Git的三大重要的区域之一,另外两个分别是工作目录和Git仓库,所以说对暂存区的深入理解可以帮助我们理解很多Git命令背后隐藏的工作原理。今天,本文将以一名初学者的角度详细讲解Git暂存区,涉及到的Git命令也会作简单的介绍但不会深入。

    在这里,我们先抛出Git暂存区的概念和相关理解,后面会一一说明。
    (1)所谓的暂存区只是一个简单的索引文件而已。
    (2)暂存区这个索引文件里面包含的是文件的目录树,像一个虚拟的工作区,在这个虚拟工作区的目录树中,记录了文件名、文件的时间戳、文件长度、文件类型以及最重要的SHA-1值,文件的内容并没有存储在其中,所以说  它像一个虚拟的工作区。
    (3)索引指向的是.Git/objects下的文件。 
    (4)暂存区的作用:除非是绕过暂存区直接提交,否则Git想把修改提交上去,就必须将修改存入暂存区最后才能commit。每次提交的是暂存区所对应的文件快照。
    好了结论说到这,上面的结论开始接触时你不一定都能理解,下面开始进行详细说明。
    当我们在某个目录下运行git init命令后,在该目录下便会生成一个.git的子目录,这个目录是隐藏的。它是用来保存元数据以及对象数据库的地方,这个目录可以说是Git的核心,每次克隆镜像仓库时,实际上拷贝的这个目录里的内容而已,然后再根据这个目录恢复工作目录(工作目录:你所git init的目录,但不包含.git这个隐藏的目录),即在工作目录下生成相应的项目文件和目录。而我们的暂存区也位于这个.git目录之下,名为index,它一般很小,一般不超过1KB左右,这和它存储的内容是有关系的。
    在这个目录下,除了index这个暂存区索引文件,还有其他重要的文件和目录,其中有存放数据对象的objects目录(这可以说是我们Git的一个仓库了)、存放引用文件的refs目录以及存放指向当前分支的HEAD文件。
    和暂存区极其相关的一条命令是
  1. git add <文件名>//将一个文件添加进暂存区
    git add可以分两条底层命令实现:
 git hash-object <文件名>
git update-index <文件名>
    运行第一条命令,Git将会根据新生成的文件产生一个长度为40的SHA-1哈希字符串,并在.git/objects目录下生成一个以该SHA-1的前两个字符命名的子目录,然后在该子目录下,存储刚刚生成的一个新文件,新文件名称是SHA-1的剩下的38个字符。
    第二条命令将会更新.git/index索引,使它指向新生成的objects目录下的文件。
    在objects目录下新生成的文件是什么文件呢?
    在这个目录下,Git将其下文件分为四种类型,其中有两种是tree类型和blob类型,blob类型文件存储的就是我们运行命令增加的文件加上一个特定的文件头,而tree类型文件就相当于我们系统下的目录了,里面存储的是多条记录,每一条记录含有一个指向 blob 或子 tree 对象的 SHA-1 指针,并附有该对象的权限模式 (mode)、类型和文件名信息,它和blob类型对象不一样,存储的并非文件的内容。
    如果生成一个新的文件存入Git仓库中而没有第二步的更新.git/index索引,那么将会发生两种情况:
    (1)如果该文件从未添加进暂存区,也就是将该文件从未更新到index索引中,那么Git是会一直显示未跟踪的文件。
    模拟一下该情况,可以选择一个无用的目录进行git init,然后在运行git status,在目录下新建一个test.txt文件并增加内容,运行git status,运行git hash-object  d.txt,再运行git status。
     #初始化仓库
$git init
Initialized empty Git repository in E:/t/test/.git/
#查看Git当前状态
$git status
On branch master
Initial commit
nothing to commit (create/copy files and use "git add" to track)
#在当前目录下新建一个内容为"this is a test file"的test.txt
$echo 'this is a test file ,version 1'> test.txt
#再次查看Git的状态,Git会提示工作目录下有一个未跟踪的文件
$git status
On branch master
Initial commit
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
nothing added to commit but untracked files present (use "git add" to track)
#此时查看.git/objects目录,只有两个文件夹info和pack
$git hash-object -w test.txt
4617a36485976a90eb72e7020911dea0c892956b
#此时.git/objects目录下有了46子目录以及该子目录下17a36485976a90eb72e7020911dea0c892956b文件
#Git仍然显示test.txt为未跟踪文件
$git status
On branch master
Initial commit
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
nothing added to commit but untracked files present (use "git add" to track)
    (2)如果文件曾经添加进暂存区,那么结果会是怎样?那么结果显示的是修改结果未放入暂存区。
    模拟下情况:git add,git status ,git commit -m "add test.txt file ,version 1",git status,修改文件,git status
     #将我们上次的test.txt添加进暂存区
$git add test.txt
#查看状态
$git status
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: test.txt
#提交
git commit -m "add test.txt file ,version 1"
#查看当前状态,当前工作目录是干净的,没有什么要提交的了
#此时查看.git/objects目录,多了两个子目录,一个是tree对象,一个是commit对象暂时忽略它
$git status
On branch master
nothing to commit, working directory clean
#继续修改
$echo 'this is a test file ,version 2'> test.txt
#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: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
$git hash-object -w test.txt
1ed62f7cc06e6a57cdbaf9a5a5023aa93bd1ffb1
$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: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
    前后两次,一次显示未跟踪,一次显示未放入暂存区,我们可以得出一个结论:即使在.git的仓库中生成了相应的数据对象文件,如果没有update index更新索引,索引还是指向.git/objects下旧版本的文件或者文件在index中没根本有索引,Git也无法commit。如果我们在第二种情况下,更新索引同时提交,那么提交的test.txt版本是最近的一次add的版本。另外,我们可以看到git add 后更新的暂存区只是一些文件索引,而文件的快照是保存在.git/objects目录下。
    如果你在工作目录下再新建一个文件,查看Git状态,Git只显示刚刚新建的文件为未跟踪文件,而Git对我们那个test.txt没有提示,这时我们add新建的文件然后提交,提交的文件包括了刚刚新建的文件和test.txt,我们每一次提交时,都无需对曾经git add 的文件再进行一次git add 操作,说明每次add后,Git都会在index下更新文件索引,而且该索引是一直存在的,除非使用git rm删除。
    
     #创建一个new.txt新文件
$echo 'this is a new.txt file'> new.txt
#添加到暂存区并提交,此时可以看到提示信息中有master b5e4b5c,该数值为commit对象的SHA-1值
$git add new.txt
$git commit -m "add new.txt file"
[master b5e4b5c] add new.txt file
file changed, file insertion(+)
create mode new.txt
#通过cat-file命令可以查看对象中存储的内容,可以看到master commit对象指向的是一棵树对象,即代表根目录
$git cat-file -p b5e4b5c
tree 019ab86ed8f73265378a25dc00bff6c085f35cb0
parent 8d50d0c061da5104551bdbaa060e0d4c916411c1
author cposture <cposture@.com>+
committer cposture <cposture@.com>+
add new.txt file
#查看树对象根目录的存储内容,可以发现tree对象存储的是指向两个我们创建的文件SHA-1值和其他相关信息,而不是数据本省内容
$git cat-file -p 019ab86
blob 3e1267f7b311e69a691cf2ff5b90b46afaa1a359 new.txt
blob d5210ccb24bd6e8bbc994ade3be3b48a9001f3b7 test.txt
    理解暂存区,还要弄清楚我们到底提交上去的是什么?这和暂存区有什么联系?
    回答这个问题前,我们先回想一下,上文说的.git/objects下文件有四种类型,其中第三种是commit对象。
    commit对象指明了该时间点项目快照的顶层树对象、作者/提交者信息(从 Git的 user.name 和user.email中获得)以及当前时间戳、一个空行,以及提交注释信息。
    简单地增添一个文件后,我们每次执行git commit操作,一般都会在.git/objects目录下生成另外两个文件(这在上文模拟的第二种情况的命令行的第13行注释中也提到过),一个是tree对象类型文件,另一个便是commit类型文件,为什么会生成两个文件而不是一个。
    这个commit对象中包含的tree对象SHA-1值指向了最新版本的根目录树对象。这个tree对象是根据你的暂存区而创建的,每个tree都是这样,所以创建tree前都需将文件或目录添加进暂存区。如果是添加的是目录则会再生成一个子tree对象,再根据目录下的文件分别生成相应的blob普通文件和子tree对象。这个根tree对象中包含的多条记录,简单的概括,分别是修改后添加进暂存区SHA-1改变了的文件或目录(上文说过的git add 分为的两步,其中最后一步是更新修改文件在暂存区的SHA-1值)或者是没修改但曾添加进暂存区,SHA-1值仍不变的文件或目录。Git正是拷贝暂存区的这些目录树和文件的SHA-1等信息到commit对象而生成Git的一个提交,即commit对象,commit对象保存的提交完整的记录了当前所有已跟踪文件的快照。所以你所提交的项目来自于暂存区!
    
    简而言之,文件索引即暂存区,建立了文件和.git/objects目录下的对象实体之间的映射关系,如下图:
 
 

本文链接:http://www.cnblogs.com/cposture/p/4178003.html

【原创】关于Git暂存区的理解的更多相关文章

  1. 第二节《Git暂存区》

    在上一节中我们的demo版本库经历了一次提交,我们可以使用git og --stat查看一下提交日志. [root@git demo]# git log --statcommit 986a1bd458 ...

  2. Git学习之Git 暂存区

    ============================= 修改文件后是否可以直接提交 ============================ (1) 向文件中追加一行内容  $ echo &quo ...

  3. git 填坑之 git 暂存区忽略文件

    今天修改公司项目文件的时候,发现被修改的文件的始终不显示已经被修改. 本来是想要把 KWh 改成 kWh,然后执行 git status 后,没有文件修改记录: 开始分析究竟是因为什么导致的这样诡异的 ...

  4. idea 编辑器Git暂存区的使用

    平时在开发时候 一般线上环境和线下环境区别会很大,所以一下线下的自己测试环境的代码没有如果提交会影响线上环境,所以一般都会使用git的一个暂存区作为临时存放不需要提交的代码,这样每次提交代码都可以在不 ...

  5. Git学习笔记(2)——版本的回退,和暂存区的理解

    本文主要记录了版本的回退,以及工作区,暂存区概念的理解. //开始之前,先回顾上次的内容,修改文件如下,并提交到版本库. Git is a distributed version control sy ...

  6. git学习(四):理解git暂存区(stage)

    与一般的版本管理不同的是,git在提交之前要将更改通过git add 添加到暂存区才能提交(git commit).即使是已经交给了git来管理的文件也是如此.这里继续学习git的暂存区. 通过git ...

  7. git分布式版本控制系统权威指南学习笔记(二):git add暂存区的三个状态以及暂存区的理解

    文章目录 不经过git add(到暂存区),能直接进行commit吗? 举个

  8. Git权威指南学习笔记(二)Git暂存区

    例如以下图所看到的: 左側为工作区,是我们的工作文件夹. 右側为版本号库,当中: index标记的是暂存区(stage),所处文件夹为.git/index,记录了文件的状态和变更信息. master标 ...

  9. git暂存区

    在使用git开发时,有三个概念需要知道,工作区,暂存区和版本库.工作区就是直接进行操作的地方,版本库是要将修改提交的地方,那么暂存区是干什么的呢?下面将对暂存区深入研究. 一.修改后能直接提交吗? 在 ...

随机推荐

  1. 16.The Effect of Advertisement 广告的影响

    16.The Effect of Advertisement 广告的影响 (1) The appeal of advertising to buying motives can have both n ...

  2. Executors创建的4种线程池的使用

    Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.newFixe ...

  3. elasticsearch 修改磁盘比例限制

    命令为:下面也可以单独进行限制,官网说可以设置具体数值,但是无没成功,有成功的大神可以在下面告诉我一下哈 PUT _cluster/settings{ "persistent": ...

  4. 如何从本地远程访问虚拟机内的Mysql服务器?

    假设重装了操作系统,则本地的很多软件可能都需要重新安装,比如数据库.但是,假设我们把一些重要的软件安装在虚拟机当中,则在重装操作系统之前,数据库服务器可以和虚拟机一起进行备份.重装操作系统之后,原先的 ...

  5. Spring PropertyEditor Spring conversion框架分析

    PropertyEditor https://blog.csdn.net/pentiumchen/article/details/44026575 conversion https://blog.cs ...

  6. influence maximization

    Robust Influence Maximization 首先简要介绍一下这个问题:在一个社交网络图中寻找固定数量的节点,使得这些节点对所有节点的影响值尽可能的大.这个问题由于在病毒式营销,谣言监控 ...

  7. python 变量,if,while,运算符

    变量由变量名 赋值 值 a = 1 ###### 必须是一个等于号 一个等于号是赋值 变量的命名规则: 1.数字,字母,下划线组成 2.数字不能在前面 3.区分大小写 4.禁止使用python关键字 ...

  8. karaf 控制台 常用linux指令(1)

    0,切换命令当前域名 -bash代码 *:feature 命令是由域名+命令名组成的,切换当前域名,比如切换到*:feature,那么再输入list,则执行的是feature:list,切换到*:bu ...

  9. 闵可夫斯基和(Mincowsky sum)

    一.概述 官方定义:两个图形A,B的闵可夫斯基和C={a+b|a∈A,b∈B}通俗一点:从原点向图形A内部的每一个点做向量,将图形B沿每个向量移动,所有的最终位置的并便是闵可夫斯基和(具有交换律) 例 ...

  10. program--历史故事