git原理

简介

Git 和其它版本控制系统(包括 Subversion 和近似工具)的主要差别在于 Git 对待数据的方法。

  • 大部分版本控制系统,例如(CVS、Subversion、Perforce、Bazaar 等等),采用增量文件系统,存储每个文件版本的差异;

  • Git 更像是把数据看作是对小型文件系统的一组快照。 每次你提交更新,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件;如果文件发生修改,Git 将存储该文件全部内容。

    img

增量文件系统

img

快照文件系统

存储原理

目录结构

1
sudo apt-get install tree #安装tree命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
$ tree .git
.git
├── COMMIT_EDITMSG #本地最后一个提交的信息
├── ORIG_HEAD #当前远程库的HEAD
├── HEAD #本地库的当前HEAD
├── config #git配置信息,包括用户名,email,remote repository的地址,本地branch和remote branch 的follow关系
├── description #该git库的描述信息,文件仅供 GitWeb 程序使用。
├── index #文件存储着暂存区的内容信息
├── hooks #钩子程序,可以被用于在执行git命令时自动执行一些特定操作,例如加入changeid
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── fsmonitor-watchman.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 #info目录下的exclude文件包含项目全局忽略匹配模式,与.gitignore文件互补。
│ └── exclude
├── logs
│ ├── HEAD
│ └── refs
│ ├── heads #本地仓库对应分支所有操作
│ │ └── hexo
│ └── remotes #远程仓库对应分支所有操作
│ └── origin
│ ├── HEAD
│ ├── hexo
├── objects #目录存储着Git数据库的所有内容,包括三类对象commit,tree和blob
│ ├── e8
│ │ └── 4551234f24b6da002d962a26c2495ea16a425f
│ ├── info
│ └── pack
└── refs
├── heads #本地库各个分支对应的当前指针
│ └── master
├── remotes #远程库各个分支对应的当前指针
│ └── origin
│ ├── HEAD #远程仓库当前分支
│ └── master #远程库master分支对应的指针
└── tags #本地库tag的当前指针

object对象

object对象包括Blob对象、Tree对象与Commit对象,对象的所有索引全部采用哈希值进行索引。

  • Blob对象:二进制数据块存储文件的内容,如果两个文件在同一个版本仓库中,那么它们将会共享同一个blob对象,例如文件夹e8中的文件4551234f24b6da002d962a26c2495ea16a425f其中存储的便是文件内容。
  • Tree对象:存储目录结构,指向其他tree对象或blob对象。
  • Commit对象:用于存储版本信息,tree存储tree SHA1签名;parent存储上一个版本的commit SHA1签名;author,作者的信息;committer实际提交者的信息

img

git指针

主要介绍refs/headsrefs/tagsrefs/remotesHEADORIG_HEAD

  • HEAD:本地库的HEAD,保存refs/中的文件。例如refs/heads/master

  • ORIG_HEAD:当进行一些有风险的操作的时候,如resetmerge或者rebase,Git会将HEAD原来所指向commit对象的sha-1值存放于ORIG_HEAD文件中。也就是说ORIG_HEAD可以让我们找到进行最近一次危险操作之前的HEAD位置

    可以使用 git reset --hard ORIG_HEAD回退到上一次reset之前。

  • refs/heads:存储各个分支信息,保存有commit SHA1签名

  • refs/tags:存储各个tag信息,保存有commit SHA1签名

  • refs/remotes/repertoryName/HEAD:远程库的当前分支

  • refs/remotes/repertoryName/remoteBranchName:远程仓库对应分支最后一次commit

HEAD里面的内容是当前的ref,而当前ref的内容是commit SHA1, commit 对象内容是tree SHA1, tree对象的内容是文件夹/文件信息,而blob对象存储着文件的具体内容。

1567273416311

命令

git add

将文件添加进暂存区,将文件保存为blob对象存储在object目录下,但并不会创建tree对象与commit对象,而是更新index文件(即暂存区)。

git commit

生成tree对象与commit对象,存储版本信息。tree对象用于记录目录信息,将暂存区(index文件)转换为tree对象;commit对象用于记录版本信息,指向tree对象。移动指针,并更新refs\headsrefs\tags里的commit SHA1签名,与当前HEAD的信息。

本地分支

命令 注释 备注
branch 创建分支 \ref\heads创建分支
check out 切换分支 HEAD指针指向另一个本地分支。
merge 合并分支 将出现分叉提交的分支整合在一起时

img

分支合并

远程分支

命令 注释 备注
git clone 克隆 远程下载
git fetch 获取 将远程内容下拉到本地版本库
  1. 远程仓库,并且有一些提交记录 。拉取下来

    1567275853775

  2. 其他人提交了一些修改至远程仓库

    1567275878316

  3. 你在本地分支上继续开发

    1567275901406

  4. 你进行拉取你本地没有的数据

    1567275928382

变基合并

  1. 创建了一个特性分支 server,为服务端添加了一些功能,提交了 C3 和 C4。 然后从 C3 上创建了特性分支 client,为客户端添加了一些功能,提交了 C8 和 C9。 最后,你回到 server 分支,又提交了 C10。

    1567276114506

  2. 假设你希望将 client 中的修改合并到主分支并发布,但暂时并不想合并 server 中的修改

    1
    $ git rebase --onto master server client

    1567276173408

  3. 进行合并 client 分支

    1
    2
    $ git checkout master
    $ git merge client

    1567276241588

  4. 进行合并 server 分支

    1
    $ git rebase master server

    1567276291937