#Git原理的简单理解
来源:互联网 发布:linux有什么认证 编辑:程序博客网 时间:2024/06/05 23:49
综述:从根本上来讲 Git 是一套内容寻址 (content-addressable) 文件系统,在此之上提供了一个 VCS 用户界面。
-
根据操作来一步步理解
1. 安装tree
工具,用于查看文件目录结构,可以有助于对于Git的学习理解。
brew install tree
安装好之后,可以在任意目录下输入tree
命令,就可以看到目录以树形结构进行展示,层级关系一目了然。
2. 新建一个干净的文件夹,执行ls -a
命令来显示所有文件(包括隐藏文件),可以看到两个文件:
. ..
不难联想到当我们想返回当前文件的上级目录,通常都会用cd ..
命令,而这里就有..
这个隐藏文件,所以可以理解为..
其实是对上级目录的一个标识文件。
3. 在当前目录执行git init
命令,然后再ls -a
查看文件情况。
. .. .git
多出来一个隐藏文件.git
,所有 Git 存储和操作的内容都位于该目录下。如果你要备份或复制一个库,基本上将这一目录拷贝至其他地方就可以了。
4. cd到.git
文件里,然后用tree
命令显示当前目录结构。一些文件的大概作用我做了简要注释。接下来重点展开的文件,我标注了*号标记。
$ tree.├── HEAD //* 文件指向当前的分支,这个文件后面解释├── config //项目特有的配置选项├── description //仅供 GitWeb 程序使用,不作深究├── hooks //客户端或服务端钩子脚本│ ├── applypatch-msg.sample│ ├── commit-msg.sample│ ├── post-update.sample│ ├── pre-applypatch.sample│ ├── pre-commit.sample│ ├── pre-push.sample│ ├── pre-rebase.sample│ ├── pre-receive.sample│ ├── prepare-commit-msg.sample│ └── update.sample├── info│ └── exclude├── objects //* 存储所有数据内容│ ├── info│ └── pack└── refs //* 存储指向数据 (分支) 的提交对象的指针,这个文件后面解释 ├── heads └── tags8 directories, 14 files
5. 了解git add
操作:模拟开发流程,在你的项目目录下创建一个新的文件,我这里以README.md
为例子。创建好之后在文件中随便写一点内容,然后执行git add .
操作,此时再进入.git
目录下执行tree
命令
$ tree.├── HEAD├── config├── description├── hooks│ ├── applypatch-msg.sample│ ├── commit-msg.sample│ ├── post-update.sample│ ├── pre-applypatch.sample│ ├── pre-commit.sample│ ├── pre-push.sample│ ├── pre-rebase.sample│ ├── pre-receive.sample│ ├── prepare-commit-msg.sample│ └── update.sample├── index├── info│ └── exclude├── objects│ ├── d0│ │ └── d37ed92ba0227dc7c3b9a5cb2a8bfa6a04a6f1│ ├── info│ └── pack└── refs ├── heads └── tags9 directories, 16 files
会发现在objects
目录下多出来了一些东西,这就是由我们刚刚的git add .
操作所生成的。那么这些文件代表什么呢。其实这个文件是Git为每份内容生成的一个文件,取得该内容与头信息的 SHA-1 校验和,创建以该校验和前两个字符为名称的子目录,并以 (校验和) 剩下 38 个字符为文件命名 (保存至子目录下)。
简单来说,就是git通过一种算法将我们提交的内容进行了计算,得到了一串数字,然后数字的前两位作为一个目录名,剩余的字符串作为文件名。
查看一下该文件的内容,可以通过 cat-file 命令。该命令是查看 Git 对象的瑞士军刀。传入 -p 参数可以让该命令输出数据内容的类型,注意完整的文件名是目录名+文件名:
$ git cat-file -p d0d37ed92ba0227dc7c3b9a5cb2a8bfa6a04a6f1#GitTest
我们添加的内容就是存放在这个文件中了。
6. 了解git commit
: 在刚刚操作的基础上,执行git commit -m
操作,同样观察.git
目录结构, 我提交的描述是“。
$ tree.├── COMMIT_EDITMSG├── HEAD├── config├── description├── hooks│ ├── applypatch-msg.sample│ ├── commit-msg.sample│ ├── post-update.sample│ ├── pre-applypatch.sample│ ├── pre-commit.sample│ ├── pre-push.sample│ ├── pre-rebase.sample│ ├── pre-receive.sample│ ├── prepare-commit-msg.sample│ └── update.sample├── index├── info│ └── exclude├── logs│ ├── HEAD│ └── refs│ └── heads│ └── master├── objects│ ├── 5f│ │ └── 3a2c5612b3687a8963fe76e46305b0c7ba5724│ ├── cf│ │ └── 0359c6428293e0b717f02292ef191ccc590592│ ├── d0│ │ └── d37ed92ba0227dc7c3b9a5cb2a8bfa6a04a6f1│ ├── info│ └── pack└── refs ├── heads │ └── master └── tags14 directories, 22 files
objects
目录下多了两个文件,我们一个个的来看:
$ git cat-file -p 5f3a2c5612b3687a8963fe76e46305b0c7ba5724tree cf0359c6428293e0b717f02292ef191ccc590592author liuyiyi <liuyiyi@lyydeMacBook-Pro.local> 1503989030 +0800committer liuyiyi <liuyiyi@lyydeMacBook-Pro.local> 1503989030 +0800first commit
这个文件的内容,就是我们git commit -m
中的描述信息,注意他有一个tree cf0359c6428293e0b717f02292ef191ccc590592
,这个是指向了这次提交时的项目根目录,什么意思看看就知道了:
$ git cat-file -p cf0359c6428293e0b717f02292ef191ccc590592100644 blob d0d37ed92ba0227dc7c3b9a5cb2a8bfa6a04a6f1 GitTestREADME.md
我们进行commit时项目目录的文件存在状况就是只有一个文件GitTestREADME.md
,所以如果我们进行指定版本的代码回滚时,就会根据这个值来置回当时根目录中文件的存在状态。
剩下一个文件是什么的呢:
$ git cat-file -p cf0359c6428293e0b717f02292ef191ccc590592100644 blob d0d37ed92ba0227dc7c3b9a5cb2a8bfa6a04a6f1 GitTestREADME.md
原来这就是上面我们在有关commit描述文件中发现的指向根目录的那个文件。
7. 继续添加新文件再执行一次git add.
和git commit -m
操作,一个命令指向完就观察一次.git
文件的变化状况, 我这里列出最终的结果状态, 过程就省略了:
$ tree.├── COMMIT_EDITMSG├── HEAD├── config├── description├── hooks│ ├── applypatch-msg.sample│ ├── commit-msg.sample│ ├── post-update.sample│ ├── pre-applypatch.sample│ ├── pre-commit.sample│ ├── pre-push.sample│ ├── pre-rebase.sample│ ├── pre-receive.sample│ ├── prepare-commit-msg.sample│ └── update.sample├── index├── info│ └── exclude├── logs│ ├── HEAD│ └── refs│ └── heads│ └── master├── objects│ ├── 58│ │ └── 7a55b8f052d0a74bd592ffe7c8c5ff8ccc06c5│ ├── 5f│ │ └── 3a2c5612b3687a8963fe76e46305b0c7ba5724│ ├── 92│ │ └── a36d18afcce6bdda094c8360551051695fc842│ ├── c9│ │ └── c50f0b74a03f5be47d97770a2d11e23983d425│ ├── cf│ │ └── 0359c6428293e0b717f02292ef191ccc590592│ ├── d0│ │ └── d37ed92ba0227dc7c3b9a5cb2a8bfa6a04a6f1│ ├── info│ └── pack└── refs ├── heads │ └── master └── tags17 directories, 25 files
多出来三个文件,拿到文件内容,可以看到和之前第一次文件里存储的东西都是同样的,一个是git add
提交的内容,一个是git commit -m
的内容,一个是当前根目录。需要注意的是这个保存了git commit -m
内容的目录,它与第一次commit有一些差别:
$ git cat-file -p 92a36d18afcce6bdda094c8360551051695fc842tree 587a55b8f052d0a74bd592ffe7c8c5ff8ccc06c5parent 5f3a2c5612b3687a8963fe76e46305b0c7ba5724author liuyiyi <liuyiyi@lyydeMacBook-Pro.local> 1503991626 +0800committer liuyiyi <liuyiyi@lyydeMacBook-Pro.local> 1503991626 +0800second commit
多了一个parent 5f3a2c5612b3687a8963fe76e46305b0c7ba5724
, 从字面就能理解是啥意思,指向它前一个commit,也可以理解为前一个节点, 可以求证一下:
$ git cat-file -p 5f3a2c5612b3687a8963fe76e46305b0c7ba5724tree cf0359c6428293e0b717f02292ef191ccc590592author liuyiyi <liuyiyi@lyydeMacBook-Pro.local> 1503989030 +0800committer liuyiyi <liuyiyi@lyydeMacBook-Pro.local> 1503989030 +0800first commit
由于第一次commit它是没有父节点的,所以没有parent, 而之后的commit都是有父节点的, 所以都会有parent这一行。
8. 回过头来讲讲HEAD, refs两个文件。HEAD众所周知是指向当前分支,但是它是怎么指向的呢,看看他的内容就知道了,要切记,HEAD本质上是一个文件!以上所有的东西都是文件!!
vim HEADref: refs/heads/master
HEAD的内容是一个文件路径,所以其实不是说HEAD指向了当前分支,而是它指向了一个文件的路径.根据这个文件路径看看文件内容:
vim refs/heads/master92a36d18afcce6bdda094c8360551051695fc842
这个路径下的文件内容,就是第二次commit的文件名称, 所以准确来说,这个文件指向了当前分支上的当前commit, 而HEAD文件内容是这个文件。如果文件进行回滚操作,refs/heads/master
文件中的值会随之改变,变为你回滚到的那个commit.
9. 创建一个新的分支develop
,观察.git
文件目录的变化:
└── refs ├── heads │ ├── develop │ └── master └── tags
会发现refs/headers下面多了一个文件,名为develop,获取develop文件的内容,指向了master第二次commit内容的文件。
vim refs/heads/develop92a36d18afcce6bdda094c8360551051695fc842
所以基于当前分支创建一个新的分支,其实本质上只是多了一个文件,而这个文件内容就是指向创建分之时最新的那个commit而已。创建分支的代价非常小。
10. 此时将分支切换到了develop
上,此时观察HEAD的变化,估计已经可以猜测到HEAD内容发生了怎么样的变化,文件内容将变refs/heads/develop
这个路径,也就是指向了当前分支develop
。
- #Git原理的简单理解
- mDNS 原理的简单理解
- mDNS原理的简单理解
- mDNS原理的简单理解
- jspatch 的简单原理理解
- Socket原理的简单理解
- mDNS原理的简单理解
- 理解git常用命令原理
- Hibernate内部原理的简单理解
- ios签名原理的简单理解
- 简单理解IOC和AOP的原理
- LVS工作原理的简单理解
- 简单理解DNS的工作原理
- 简单理解volatile变量的原理
- 简单理解smarty原理
- shiro的原理理解和简单demo的实现
- 一个简单的Servlet框架(帮助理解Servlet原理)
- iOS 简单理解协议代理的执行原理
- 3、flask第三站-模板
- 枚举enum(Swift)
- 线程锁
- 【NOIP2013提高组】花匠
- asp.net中web.config配置错误页
- #Git原理的简单理解
- centos7安装rabbitmq
- python的另外一个数据可视化包 seaborn
- 用n种颜色给正方体着色共用多少种不同的着色方案
- 图像处理;C++求已知两直线方程交点
- opencv 膨胀和腐蚀
- android dagger2使用全解
- 浅谈mysql的锁和索引之间莫大的联系
- 服务器IMM2实战介绍