背景:

公司内部主要以Git作为版本管理工具,在日常工作中发现大家使用Git很不熟练,而且学习的积极性不高,似乎GIT给人以一种望而却步的感觉。究其根源(个人臆测)有几点:

一、以为GIT相较于SVN新颖很多,是很高端高大上的工具,上手很难;
二、习惯于传统的图形化GUI操作(这应该得利于Windows的全球普及),对于GIT的诸多bash指令没有好感,自动降低了学习和掌握的信心;
三、对GIT整体概念不清晰,或盲目背诵指令,或完全依赖于各种图形化Git工具。

鉴于此,博主希望尽可能的从底层机制来讲解GIT的原理,使得大家能够对GIT有一个宏观的认识,进而打消莫名的恐惧,达到熟练使用GIT进行代码版本管理的目的。本文之前GIT科普系列1:git如何放弃本地working directory的修改,以及回滚、 GIT科普系列2:git代码检出与日常维护、 GIT科普系列3:底层存储机制Internal Objects三篇已经对GIT的底层机制有了一个简单的介绍,此次作为一个总结篇,借助于图解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的父节点,即~符号表示指针前向移动


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

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


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

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


同样,此刻实现的功能是将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个节点


如上图所示: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!

作者:zssure@163.com
时间:2016-09-17

GIT科普系列4:仓库/缓冲区/工作副本,傻傻分不清楚?相关推荐

  1. GIT科普系列5:index in git

    背景: git的使用其实没有想象中的那么复杂,平日里真正使用的指令都是极少数.最简单格式的.之所以使用过程中感觉操作复杂.冲突不断,究其根源是对git的设计理念缺乏了解,没有搞清楚git实现版本控制的 ...

  2. 【Git 教程系列第 5 篇】Git 的工作流程

    这是[Git 教程系列第 5 篇],如果觉得有用的话,欢迎关注专栏. Git 工作流程 第一步:克隆 Git 资源(远端仓库)作为工作目录. 第二步:在克隆的资源上添加或修改文件(如果其他人修改了,你 ...

  3. ios git解决冲突_适用于iOS和Linux的远程同步解决方案:Git和工作副本

    ios git解决冲突 如何在iOS上使用Git设置跨平台的云同步解决方案以在任何地方工作. (How to set up a cross-platform cloud sync solution f ...

  4. Git学习系列之Git基本操作提交项目(图文详解)

    前面博客 Git学习系列之Git基本操作克隆项目(图文详解) 然后可以 cd 切换到 LispGentleIntro 目录, 新增或者修改某些文件.这里只是模拟一下操作, 实际情况可能是 使用 Ecl ...

  5. Git学习系列之Windows上安装Git之后的一些配置(图文详解)

    不多说,直接上干货! 前面博客 Git学习系列之Windows上安装Git详细步骤(图文详解) 第一次使用Git时,需要对Git进行一些配置,以方便使用Git. 不过,这种配置工作只需要进行一次便可, ...

  6. 《走进git时代系列一》 你该怎么玩?

    首先,这篇分享不是git命令操作大全,不是某代码托管服务的硬广, 只是希望激发仍然在使用中世纪时期版本管理系统的同学们,能够放弃你手里的SVN,转向更先进的思路. 所以,大家不会看到非常多的Comma ...

  7. Kafka科普系列 | 原来Kafka中的选举有这么多?

    欢迎跳转到本文的原文链接:https://honeypps.com/mq/kafka-basic-knowledge-of-selection/ 面试官在考查你Kafka知识的时候很可能会故弄玄虚的问 ...

  8. Kafka科普系列 | 什么是LSO?

    欢迎跳转到本文的原文链接:https://honeypps.com/mq/kafka-basic-knowledge-of-lso/ 很多同学对于Kafka的认知仅限于在LEO和HW之间,有可能认知还 ...

  9. 前端科普系列(2):Node.js 换个角度看世界,

    [前端科普系列]往期精彩内容: 前端科普系列(1):很有趣的一篇前端简史,作者有心了~主要介绍 web 前端发展的历史.大事件. 本文为系列文章(2),主要介绍 Node.js 的前世今生.核心科技以 ...

最新文章

  1. 继AutoML后,第四范式发布软硬一体化AI集成系统SageOne
  2. c#语言程序设计实例,C#语言程序设计用.ppt
  3. SAP屏幕设计器专题:树控件的使用(九)
  4. 『设计模式』适配器模式(Adapter)
  5. 消费者驱动的微服务契约测试套件Spring Cloud Contract
  6. iOS-各种报错,各种error
  7. 拓端tecdat|R语言在RCT中调整基线时对错误指定的稳健性
  8. 【虹膜识别】基于matlab GUI形态学虹膜检测【含Matlab源码 959期】
  9. 附pdf下载 | 《机器学习数学基础》电子书
  10. Spark安装与配置(单机版)(保姆级教程)
  11. 用plsql导出oracle,使用PLSQL进行Oracle数据导入导出[转]
  12. 网卡驱动程序igb和ixgbe
  13. 你知道PDF拆分合并怎么弄吗?两个方法帮你轻松搞定!
  14. ICTCLAS汉语词性标注集+中文字体对应的文件名+ 常用字体、颜色、线性、标记
  15. SSL证书错误怎么办?浏览器常见SSL证书报错解决办法
  16. JAVASCRIPT网页特效案例教程pdf
  17. 使用GeoGebra绘制三角形的外接圆和内切圆
  18. Windows slmgr.vbs 命令详解
  19. 提高pyautogui识图率的优化方案
  20. ESP32开发(1)----Espressif-IDE开发环境配置

热门文章

  1. J-link下载出现Verification of RAMCode failed @ address 0x20000000问题的解决
  2. 视频监控 | XDFS电信全球眼云存储扩容项目
  3. 面试必备系列JUC(6)--八锁详解
  4. nginx指令之——tcp_nodelay
  5. 斯佩里左右脑分工理论
  6. 种棉12载的他,为何最终选择千耘导航?
  7. 【涂鸦移动】 面试题记录
  8. 经济基础知识(初级)【2】
  9. 近期世界范围内地震频发,来个地震知识扫盲贴~
  10. 初识 OpenGL ES2.0