Git历险记(四)——索引与提交的幕后故事
我想如果看过《Git历险记》的前面三篇文章的朋友可能已经知道怎么用git add,git commit这两个命令了;知道它们一个是把文件暂存到索引中为下一次提交做准备,一个创建新的提交(commit)。但是它们台前幕后的一些有趣的细节大家不一定知晓,请允许我一一道来。
Git 索引是一个在你的工作目录(working tree)和项目仓库间的暂存区域(staging area)。有了它, 你可以把许多内容的修改一起提交(commit)。 如果你创建了一个提交(commit),那么提交的一般是暂存区里的内容, 而不是工作目录中的内容。
一个Git项目中文件的状态大概分成下面的两大类,而第二大类又分为三小类:
- 未被跟踪的文件(untracked file)
- 已被跟踪的文件(tracked file)
- 被修改但未被暂存的文件(changed but not updated或modified)
- 已暂存可以被提交的文件(changes to be committed 或staged)
- 自上次提交以来,未修改的文件(clean 或 unmodified)
看到上面的这么多的规则,大家早就头大了吧。老办法,我们建一个Git测试项目来试验一下:
我们先来建一个空的项目:
$rm -rf stage_proj $mkdir stage_proj $cd stage_proj $git init Initialized empty Git repository in /home/test/work/test_stage_proj/.git/
我们还创建一个内容是“hello, world”的文件:
$echo "hello,world" > readme.txt
现在来看一下当前工作目录的状态,大家可以看到“readme.txt”处于未被跟踪的状态(untracked file):
$git status # On branch master # # Initial commit # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # readme.txt nothing added to commit but untracked files present (use "git add" to track)
把“readme.txt"加到暂存区: $git add readme.txt
现在再看一下当前工作目录的状态:
$git status # On branch master # # Initial commit # # Changes to be committed: # (use "git rm --cached <file>..." to unstage) # # new file: readme.txt #
可以看到现在"readme.txt"的状态变成了已暂存可以被提交(changes to be committed),这意味着我们下一步可以直接执行“git commit“把这个文件提交到本地的仓库里去了。
暂存区(staging area)一般存放在“git目录“下的index文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。索引是一个二进制格 式的文件,里面存放了与当前暂存内容相关的信息,包括暂存的文件名、文件内容的SHA1哈希串值和文件访问权限,整个索引文件的内容以暂存的文件名进行排 序保存的。
但是我不想马上就把文件提交,我想看一下暂存区(staging area)里的内容,我们执行git ls-files命令看一下:
$git ls-files --stage 100644 2d832d9044c698081e59c322d5a2a459da546469 0 readme.txt
我们如果有看过上一篇文章里 的"庖丁解牛", 你会发现“git目录“里多出了”.git/objects/2d/832d9044c698081e59c322d5a2a459da546469”这 么一个文件,再执行“git cat-file -p 2d832d” 的话,就可以看到里面的内容正是“hello,world"。Git在把一个文件添加暂存区时,不但把它在索引文件(.git/index)里挂了号,而 且把它的内容先保存到了“git目录“里面去了。
如果我们执行”git add“命令时不小心把不需要的文件也加入到暂存区中话,可以执行“git rm --cached filename" 来把误添加的文件从暂存区中移除。
现在我们先在"readme.txt"文件上做一些修改后:
$echo "hello,world2" >> readme.txt
再来看一下暂存区的变化:
$git status # On branch master # # Initial commit # # Changes to be committed: # (use "git rm --cached <file>..." to unstage) # # new file: readme.txt # # Changed but not updated: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: readme.txt #
大家可以看到命令输出里多了一块内容:“changed but not updated ...... modified: readme.txt”。大家可能会觉得很奇怪,我前面不是把"readme.txt"这个文件给添加到暂存区里去了吗,这里怎么又提示我未添加到暂存区 (changed but not updated)呢,是不是Git搞错了呀。
Git 没有错,每次执行“git add”添加文件到暂存区时,它都会把文件内容进行SHA1哈希运算,在索引文件中新加一项,再把文件内容存放到本地的“git目录“里。如果在上次执行 “git add”之后再对文件的内容进行了修改,那么在执行“git status”命令时,Git会对文件内容进行SHA1哈希运算就会发现文件又被修改了,这时“readme.txt“就同时呈现了两个状态:被修改但未 被暂存的文件(changed but not updated),已暂存可以被提交的文件(changes to be committed)。如果我们这时提交的话,就是只会提交第一次“git add"所以暂存的文件内容。
我现在对于“hello,world2"的这个修改不是很满意,想要撤消这个修改,可以执行git checkout这个命令:
$git checkout -- readme.txt
现在再来看一下仓库里工作目录的状态:
$git status # On branch master # # Initial commit # # Changes to be committed: # (use "git rm --cached <file>..." to unstage) # # new file: readme.txt #
好的,现在项目恢复到我想要的状态了,下面我就用git commit 命令把这个修改提交了吧:
$git commit -m "project init" [master (root-commit) 6cdae57] project init 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 readme.txt
现在我们再来看一下工作目录的状态:
$git status # On branch master nothing to commit (working directory clean)
大家可以看到“nothing to commit (working directory clean)”;如果一个工作树(working tree)中所有的修改都已提交到了当前分支里(current head),那么就说它是干净的(clean),反之它就是脏的(dirty)。
SHA1值内容寻址
正如Git is the next Unix 一文中所说的一样,Git是一种全新的使用数据的方式(Git is a totally new way to operate on data)。Git把它所管理的所有对象(blob,tree,commit,tag……),全部根据它们的内容生成SHA1哈希串值作为对象名;根据目 前的数学知识,如果两块数据的SHA1哈希串值相等,那么我们就可以认为这两块数据是相同 的。这样会带来的几个好处:
- Git只要比较对象名,就可以很快的判断两个对象的内容是否相同。
- 因为在每个仓库(repository)的“对象名”的计算方法都完全一样,如果同样的内容存在两个不同的仓库中,就会存在相同的“对象名”。
- Git还可以通过检查对象内容的SHA1的哈希值和“对象名”是否匹配,来判断对象内容是否正确。
我们通过下面的例子,来验证上面所说的是否属实。现在创建一个和“readme.txt“内容完全相同的文件”readme2.txt“,然后再把它提交到本地仓库中:
$echo "hello,world" > readme2.txt $git add readme2.txt $git commit -m "add new file: readme2.txt" [master 6200c2c] add new file: readme2.txt 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 readme2.txt
下面的这条很复杂的命令是查看当前的提交(HEAD)所包含的blob对象:
$git cat-file -p HEAD | head -n 1 | cut -b6-15 | xargs git cat-file -p 100644 blob 2d832d9044c698081e59c322d5a2a459da546469 readme.txt 100644 blob 2d832d9044c698081e59c322d5a2a459da546469 readme2.txt
我们再来看看上一次提交(HEAD^)所包含的blob对象:
$git cat-file -p HEAD^ | head -n 1 | cut -b6-15 | xargs git cat-file -p 100644 blob 2d832d9044c698081e59c322d5a2a459da546469 readme.txt
很明显大家看到尽管当前的提交比前一次多了一个文件,但是它们之间却是在共用同一个blob对象:“2d832d9”。
No delta, just snapshot
Git 与大部分你熟悉的版本控制系统,如Subversion、CVS、Perforce 之间的差别是很大的。传统系统使用的是: “增量文件系统” (Delta Storage systems),它们存储是每次提交之间的差异。而Git正好与之相反,它是保存的是每次提交的完整内容(snapshot);它会在提交前根据要提交 的内容求SHA1哈希串值作为对象名,看仓库内是否有相同的对象,如果没有就将在“.git/objects"目录创建对应的对象,如果有就会重用已有的 对象,以节约空间。
下面我们来试验一下Git是否真的是以“snapshot”方式保存提交的内容。
先修改一下"readme.txt",给里面加点内容,再把它暂存,最后提交到本地仓库中:
$echo "hello,world2" >> readme.txt $git add readme.txt $git commit -m "add new content for readme.txt" [master c26c2e7] add new content for readme.txt 1 files changed, 1 insertions(+), 0 deletions(-)
我们现在看看当前版本所包含的blob对象有哪些:
$git cat-file -p HEAD | head -n 1 | cut -b6-15 | xargs git cat-file -p 100644 blob 2e4e85a61968db0c9ac294f76de70575a62822e1 readme.txt 100644 blob 2d832d9044c698081e59c322d5a2a459da546469 readme2.txt
从上面的命令输出,我们可以看到"readme.txt"已经对应了一个新的blob对象:“2e4e85a”,而之前版本的"readme.txt“对应的blob对象是:“2d832d9”。下面我们再来看一看这两个”blob“里面的内容和我们的预期是否相同:
$git cat-file -p 2e4e85a hello,world hello,world2 $git cat-file -p 2d832d9 hello,world
大家可以看到,每一次提交的文件内容还是全部保存的(snapshot)。
小结
Git内在机制和其它传统的版本控制系统(VCS)间存在本质的差异,所以Git的里"add"操作的含义和其它VCS存在差别也不足为奇,“git add“不但能把未跟踪的文件(untracked file)添加到版本控制之下,也可以把修改了的文章暂存到索引中。
同时,由于采用“SHA1哈希串值内容寻值“和”快照存储(snapshot)“,让Git成为一个速度非常非常快的版本控制系统(VCS)。
转载于:https://www.cnblogs.com/zhoug2020/p/5080639.html
Git历险记(四)——索引与提交的幕后故事相关推荐
- Git 历险记(三)——创建一个自己的本地仓库
2019独角兽企业重金招聘Python工程师标准>>> 如果我们要把一个项目加入到Git的版本管理中,可以在项目所在的目录用git init命令建立一个空的本地仓库,然后再用git ...
- 如何撤消Git中的最新本地提交?
我不小心将错误的文件提交给Git ,但是我还没有将提交推送到服务器. 如何撤消本地存储库中的那些提交? #1楼 我想撤消我们共享存储库中的最新五次提交. 我查找了要回滚的修订版ID. 然后我输入以下内 ...
- 使用git向码云上提交代码
为什么写这篇文章呢?其实吧,以前我都是在github上存放项目的,但是有个很蛋疼的问题,就是github这个传输速度吧,真是不想说了,毕竟是国外的网站嘛,国内做了限制很正常嘛,所以,就开始搞码云了. ...
- IDEA中对Git的常规操作(合并,提交,新建分支,更新)
IDEA中对Git的常规操作 场景一:小张创建项目并提交到远程Git仓库 场景二:小袁从远程Git仓库上获取项目源码 场景三:小袁修改了部分源码,提交到远程仓库 场景四:小张从远程仓库获取小袁的提交 ...
- git拉取项目、提交代码简单教程
最近要用HBuilderX做一个uniapp项目,再写一篇git拉项目代码.交代码的文章,虽然和上一篇差不多啦,不过就是上一篇的交代码没有用指令,这一篇就用一下. 首先是要装好git和node,可以自 ...
- [Git] Git整理(四) git rebase 的使用
概述 在之前总结分支相关内容时说道,合并两个分支的提交可以使用git merge,然而除了这种方式之外,还有一种方式就是使用git rebase,这两种方式的最终结果都相同,但是合并历史却不同:git ...
- 详解如何使用git sqush合并多次未提交commit
转自:https://www.cnblogs.com/dsxniubility/p/4460834.html 1.git squash技术 在使用git作为源代码管理器时,需要时不时将自己所作出的改变 ...
- Git(四) Git冲突处理
Git(四) Git冲突处理 上篇文章主要说到Git的图形化管理工具 SourceTree的基本操作, 本篇文章接着上篇内容继续为大家介绍 Git的使用中冲突处理. 你好,[程序职场]专注于:Spri ...
- 如何在Git中更改多次提交的作者和提交者名称以及电子邮件?
我当时正在学校计算机上编写一个简单的脚本,然后将更改提交到Git(在笔驱动器中的一个回购中,该回购是从家里的计算机中克隆的). 经过几次提交后,我意识到我正在以root用户身份提交东西. 有什么办法可 ...
最新文章
- SimplifiedHibernate:简化了的Hibernate
- Windows Mysql添加用户
- mate30pro什么时候可以升级鸿蒙,mate 30pro什么时候能装鸿蒙系统?
- python 地理信息_GitHub - sujeek/geospatial-data-analysis-cn: Python地理信息数据教程中文版(GeoPandas、GIS)...
- Ajax — 聊天机器人演示
- 用 Docker Machine 创建 Azure 虚拟主机
- 1091 N-自守数 (15 分)
- 用 vue2 和 webpack 快速建构 NW.js 项目(1)
- Comparable接口的使用:(知识回顾)
- C++ 内建函数对象
- 性能测试基本功 - Centos5.5下安装LAMP
- C#创建单链表,翻转单链表
- Scala快速入门到精通 视频教程 百度云网盘下载地址
- 自己的旧手机做html服务器,旧手机改座机_怎么将手机改成为座机
- 速成KeePass全局自动填表登录QQ与迅雷(包括中文输入法状态时用中文用户名一键登录)...
- 填坑—c语言写单片机中断程序无法返回到中断点—解决办法
- tmooccn达内登录_达内在线tmooc登陆
- Modelsim仿真过程(完整版)
- 【LeetCode】279. 完全平方数 【动态规划】【四平方和定理】
- 用正则表达式抓取电话号码
热门文章
- 为啥led灯用一年后暗了很多_嫌原车的卤素“蜡烛灯”太暗,想换掉,该选氙气灯还是LED灯?...
- android 读取其他应用程序,android – 在另一个应用程序中请求我自己的ContentProvider的读取权限...
- 网络营销推广软件教你学会单页面SEO优化技巧,轻松赢流量!
- 网络推广——网络推广如何安排网站关键词的布局策略?
- 浅析企业网站页面设计如何才能更吸引用户注意!
- 服务器信号分析,服务器及其讯号解析装置 Server and its signal analysis apparatus
- lvs在linux系统下安装,Linux下安装lvs
- android p获取通话记录_[android] 取得最近通话记录的方法
- 怎么提升软件测试质量,【软件测试】涨姿势,测试总监亲授如何做测试质量管理...
- AdaBoostClassifier实战