Git仓库、缓冲区、工作副本相关概念

项目中多人合作,常混淆一些概念,感觉下面讲解不错:

仓库、缓冲区、工作副本:

同样以系列博文GIT科普系列3:底层存储机制Internal Objects中的整体示意图为例,介绍这三个概念。 

这里写图片描述


从上图可以看出工作副本(之所以叫做工作副本是相对于版本仓库而言的,其实就是你本地的工作目录)、缓冲区(Stage,或者Index)、仓库(Commit之后)是三个完全独立的存储空间(当然对于GIT管理系统自身而言,这三个区间的内容都以Content-based方式存储在.git目录下,详情参见博文GIT科普系列3:底层存储机制Internal Objects)。 
下面我们就以仓库、缓冲区、工作副本三个存储空间为基础,详细看一下GIT版本管理的流程,以及常见git指令完成的具体操作。 
【备注】:非特殊说明,下文截图均来自于Visual git guide项目

 

1. git add

 

这里写图片描述


git add指令是日常使用最多的基础指令(当然如果使用图形化GIT工具的话,可能会很少用到,都会集成到commit指令中。所以我一再强调最好使用Git Bash来完成相关操作)。以仓库、缓冲区、工作副本三个存储空间为出发点来看,git add指令实现的功能就是将工作副本中的非.gitignore排除的files “添加”(备注:这里虽然常常用添加来描述,但是可能最符合语义的应该是“拷贝”,因为git add指令后会在缓冲区存储空间新增一个二进制BLOB文件,也就是通常认为的快照snapshot)到缓冲区。该指令中支持Bash中常见的通配符,诸如*.jpg、*.txt等,可以快速筛选要添加或过滤不要添加的文件。

 

2. git commit

 

这里写图片描述


git commit是几乎所有图形化GIT工具最常用的指令。同样以三种存储空间角度出发,git commit就是将缓冲区的内容“添加”到仓库中,这里的“添加”同样具有“拷贝”的含义。

 

上面介绍的git add和git commit指令都是“添加”类指令,也就是如何向GIT的基于内容检索的文件系统(Content-Basd Filesystem)写入数据。有写入就还要有读取,下面看几个读取的指令:

3. git checkout

由于仓库、缓冲区、工作副本三个存储空间都是以链表的形式来存储记录每一次的改动,因此在读取(上文提到的写入数据也会跟三个存储空间具体所在的不同节点有关,但是总体来说数据写入只要记住一点,都是写入到当前空间对应的当前节点,所以情况比较简单)上需要根据三个存储空间所在的具体节点来进行判别。这也就是git checkout指令复杂的地方,下面分别看几种checkout指令结果图: 
指令1:

git checkout HEAD~ files #HEAD~代表HEAD的父节点,即~符号表示指针前向移动
  • 1

 

这里写图片描述


上图所示此刻实现的功能是将当前HEAD(当前分支处于master,分支名是永远指向一个分支的最后一个节点)指向的父节点中仓库的files文件读取并覆盖到缓冲区和工作副本,此刻这两个存储空间中与仓库中相同的文件内容保持一致,而往往缓冲区和工作副本两个存储空间的内容会比仓库多,因为很多还未完成的任务没有提交。 
指令2:

 

git checkout maint #maint代表另一个分支的最后一次提交
  • 1

 

这里写图片描述


此刻实现的功能是将maint分支最后节点对应的仓库内的所有文件读取并覆盖到缓冲区和工作副本。 
指令3:

 

git checkout master~3#~代表前向移动,3代表移动的节点数
  • 1

 

这里写图片描述


同样,此刻实现的功能是将master分支最后节点向前跳跃3个节点对应的仓库内的所有文件读取并覆盖到缓冲区和工作副本。

 

【备注1】:上述git checkout指令的三种格式都是实现从仓库“读取并覆盖”缓冲区和工作副本。这里要强调一下“读取并覆盖”。我们以当前态表示缓冲区和工作副本中的文件、以下一状态表示所获取的目标仓库的内容来详细讲解“读取并覆盖”的含义,主要分为几种: 
- 1)当前状态与下一状态相同的文件:“读取并覆盖”后没有任何变化 
- 2)当前状态存在但下一状态不存在的文件:“读取并覆盖”后删除该类文件 
- 3)当前状态不存在当下一状态存在的文件:“读取并覆盖”后最新增该类文件

对于第二种情况要额外小心,因为这会导致你本地未提交处于中间状态的文件丢失,工作白白忙活了。 
【备注2】:上述指令3会出现一种状态,叫做detached HEAD,言外之意HEAD指针没有指向任何一个分支(即没有指向任何一个链表的最后节点)。此刻如果提交的话会导致此次提交处于不稳定状态,如果不进行进一步操作,GIT管理系统会在下一次启动垃圾回收时刻将该节点删除。详情如下: 

这里写图片描述


上图在detached HEAD状态下,运行了git commit在git仓库中创建了一个新的编号为2eecb的节点,关联到之前checkout后指向的b325c节点,并以此作为父节点。由上图可以看出,除了HEAD指针指向2eecb节点以外,没有任何分支指向该节点。这就是所谓的detached HEAD状态。(为什么只有HEAD指向某个节点时,会被认为是detached HEAD状态呢?因为HEAD是用来实现多个分支之间跳转的,即多个链表跳转,而不能为某个链表所独有。只有分支才能属于某个链表,而且都制定分支所在链表的最后一个节点)。 
此时如果我们执行git checkout master,将HEAD重新移动到master分支时,如下图所示: 

这里写图片描述


此刻上面新建的2eecb节点就变成了GIT仓库链表的叶子节点,即该节点没有下一级子节点。对于这种没有被任何分支或者HEAD所指向的叶子节点,GIT垃圾回收会认为该节点无效,因为从理论上来看你是无法再跳转到2eecb节点的(当然直接输入git checkout 2eecb是可以实现这个功能的,谁天天没事记这个是吧?)。 
如果你还是希望保留2eecb这个叶子节点,那么要不你就把HEAD指针移过去(但是此刻你就不能再其它分支进行工作了,因为HEAD要永远指向当前工作分支),其实这里HEAD指向2eecb时,可以理解为一个“无名分支”或者“匿名分支”。显然依靠HEAD指针是不合理的,那么只有一种办法,给2eecb叶子节点建立一个分支,即执行git checkout -b new,如下图所示即可。 

这里写图片描述


此刻2eecb叶子节点有了new分支指向,就不会被GIT垃圾回收给清理掉了,当然你又多了一个分支。

 

4. git reset

除了git checkout是读取类指令以外,还有一个比较很常用的指令,git reset,同样可以实现“读取并覆盖”的功能。

git reset HEAD~3#这个与git checkout的类似,HEAD是当前分支最新节点,~3表示前向移动3个节点
  • 1

 

这里写图片描述


如上图所示:git reset相较于git checkout来说更灵活了一些,增加了–soft和–hard两个参数,可以分别控制“读取和覆盖”的工作区间,可同时覆盖缓冲区和工作副本或只覆盖缓冲区。 
【备注】:仔细观察git reset与git checkout的两个读取指令的示意图,你会发现git reset强大的地方不单单在于新增了–soft和–hard参数,而是git reset是同时操作分支和HEAD,而git checkout只操作HEAD。——这也就是我们之前提到了GIT实现回滚的底层依据,详情参见博文GIT科普系列1:git如何放弃本地working directory的修改,以及回滚

 

总结:

以仓库、缓冲区、工作副本三个独立存储空间为基础,就比较容易理解git的版本管理机制,以及各个指令所实现的功能。在此基础上,再熟背git指令,你就可以自由的、轻松的来管理的文档了。fighting!

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值