git原理分析介绍
003_git
1. 概述
关于分布式和集中式版本控制系统?
git是一种分布式版本系统,这里的”分布式“是相较于”集中式“而言的。
把数据集中保存在服务器节点,所有的客户节点都从服务器节点获取数据的版本控制系统叫做集中式版本控制系统,例如SVN。众所周知,subversion是基于中心版本仓库进行版本管理协作的版本管理工具。就像上图中那样,所有开发人员开始生产代码的前提是必须先从中心仓库checkout一份代码拷贝到自己本地的工作目录;而进行版本管理操作或者与他人进行协作的前提也是:中心版本仓库必须始终可用。这有点像以太网的“半双工的集线器(hub)模式”:svn中心仓库就像集线器本身,每个程序员节点就像连接到集线器上的主机;当一个程序员提交(commit)代码到中心仓库时,其他程序员不能提交,否则会出现冲突;如果中心仓库挂掉了,那么整个版本管理过程也将停止,程序员节点间无法进行协作,这就像集线器(hub)挂掉后,所有连接到hub上的主机节点间的网络也就断开无法相互通信一样。
git的数据不止保存在服务器上,同时也完整的保存在本机PC上,可以完全离线的情况下使用git,每个开发节点都保存完整的项目文件镜像。
如上图所示,git号称分布式版本管理系统,本质上是没有像subversion中那个所谓的“中心仓库”的。每个程序员都拥有一个本地git仓库,而不仅仅是一份代码拷贝,这个仓库就是一个独立的版本管理节点,它拥有程序员进行代码生产、版本管理、与其他程序员协作的全部信息。即便在一台没有网络连接的机器上,程序员也能利用该仓库完成代码生产和版本管理工作。在网络ready的情况下,任意两个git仓库之间可以进行点对点的协作,这种协作无需中间协调者(中心仓库)参与。
git与github?
git实现了分布式版本管理系统,每个git仓库节点都是自治的。诸多git仓库节点一起形成了一个分布式git版本管理网络。这样的一个分布式网络存在着与普通分布式系统的类似的问题:如何发现对端节点的git仓库、如何管理和控制仓库间的访问权限等。如果说linus的git本身是这个分布式网络的数据平面工具(实现client/server间的双向数据通信),那么这个分布式网络还缺少一个“控制平面”。
而github恰恰给出了一份git分布式网络控制平面的实现:托管、发现、控制…。其名称中含有的“hub”字样让我们想起了上面的“hub模式”:
我们看到在github的git协作模式实践中,引入了“中心仓库”的概念,各个程序员的节点git仓库源于(clone于)中心仓库。但是它和subversion的“中心仓库”有着本质的不同,这个仓库只是一个“upstream”库、是一个权威库。它并不是“集线器”,也没有按照“集线器”的那种工作模式进行协作。所有程序员节点的代码生产和版本管理操作完全可以脱离该所谓“中心库”而独立实施。
git本质原理?
git最核心的设计是如何解决文件变更记录的问题,并在此基础上实现版本切换、差异比较、分支管理以及分布式协作等高阶高能。
2. 基本原理
2.1. 数据对象
2.1.1. 初始状态
执行git init
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# tree .git/
.git/
|-- HEAD
|-- branches
|-- config
|-- description
|-- hooks
| |-- applypatch-msg.sample
| |-- commit-msg.sample
| |-- fsmonitor-watchman.sample
| |-- post-update.sample
| |-- pre-applypatch.sample
| |-- pre-commit.sample
| |-- pre-merge-commit.sample
| |-- pre-push.sample
| |-- pre-rebase.sample
| |-- pre-receive.sample
| |-- prepare-commit-msg.sample
| |-- push-to-checkout.sample
| `-- update.sample
|-- info
| `-- exclude
|-- objects
| |-- info
| `-- pack
`-- refs
|-- heads
`-- tags
|
2.1.2. 写入操作
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# echo "version 1" | git hash-object -w --stdin
83baae61804e65cc73a7201a7252750c76066a30
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/.git/objects/
|-- 83
| `-- baae61804e65cc73a7201a7252750c76066a30
|-- info
`-- pack
|
写入命令执行后,会返回个长度为40位的hash值,这个hash值是将待存储的数据外加一个头部信息一起做SHA-1校验运算而得的校验和。在git数据库中,它有一个名字,叫做“键值(key)”。相应的,git数据库其实是一个简单的“键值对(key-value)”数据库。事实上,你向该数据库中插入任意类型的内容,它都会返回一个键值。通过返回的键值可以在任意时刻再次检索该内容。
此时,我们再次执行find .git/objects/ -type f命令查看objects目录,会发现目录中多出了一个文件,这个文件存储在以新存入数据对应hash值的前2位命名的文件夹内,文件名为hash值的后38位。这就是git数据库的存储方式,一个文件对应一条内容,就是这么简单直接。
2.1.3. 查询操作
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# git cat-file -t 83ba
blob
root@c60e0f2aa8ca:/home/learning/learngit# git cat-file -p 83ba
version 1
|
2.1.4. 文件变更
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# echo "version 1" > file.txt
root@c60e0f2aa8ca:/home/learning/learngit# git hash-object -w file.txt
83baae61804e65cc73a7201a7252750c76066a30
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/
.git/objects/
|-- 83
| `-- baae61804e65cc73a7201a7252750c76066a30
|-- info
`-- pack
|
上面file.txt文件中有相同的内容, 将file.txt文件内容保存到库中后,发现返回的hash值与之前一样,且查询objects目录后,仍只有一个对象文件。可见git存储文件时,只关心文件内容,与文件名称无关。
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# echo "version 2" > file.txt
root@c60e0f2aa8ca:/home/learning/learngit# git hash-object -w file.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/
.git/objects/
|-- 1f
| `-- 7a7a472abf3dd9643fd615f6da379c4acb3e3a
|-- 83
| `-- baae61804e65cc73a7201a7252750c76066a30
|-- info
`-- pack
|
更改file.txt内容,同样的方式写入git数据库后,此时库中有2个数据对象了。
2.1.5. 变更回滚
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# echo "version 3" > file.txt
root@c60e0f2aa8ca:/home/learning/learngit# git cat-file -p 83ba
version 1
root@c60e0f2aa8ca:/home/learning/learngit# git cat-file -p 1f7a
version 2
root@c60e0f2aa8ca:/home/learning/learngit# cat file.txt
version 3
|
修改file.txt的内容,会发现.git的数据库中存储了file.txt两个版本的内容,工作区的file.txt文件则是独立的文件,与前两者无关。
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# echo "hello, world" > f1.txt
root@c60e0f2aa8ca:/home/learning/learngit# echo "hello, world" > f2.txt
root@c60e0f2aa8ca:/home/learning/learngit# echo "hello, world" > f3.txt
root@c60e0f2aa8ca:/home/learning/learngit# ls
f1.txt f2.txt f3.txt file.txt
root@c60e0f2aa8ca:/home/learning/learngit# git hash-object -w f1.txt
4b5fa63702dd96796042e92787f464e28f09f17d
root@c60e0f2aa8ca:/home/learning/learngit# git hash-object -w f2.txt
4b5fa63702dd96796042e92787f464e28f09f17d
root@c60e0f2aa8ca:/home/learning/learngit# git hash-object -w f3.txt
4b5fa63702dd96796042e92787f464e28f09f17d
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/
.git/objects/
|-- 1f
| `-- 7a7a472abf3dd9643fd615f6da379c4acb3e3a
|-- 4b
| `-- 5fa63702dd96796042e92787f464e28f09f17d
|-- 83
| `-- baae61804e65cc73a7201a7252750c76066a30
|-- info
`-- pack
5 directories, 3 files
root@c60e0f2aa8ca:/home/learning/learngit# git cat-file -p 4b5f
hello, world
|
如上,将内容完全相同的三个文件f1.txt,f2.txt,f3.txt
都写入.git
数据库,结果上看最终在库中只保存了一份数据对象。
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# git cat-file -p 83ba > file.txt
root@c60e0f2aa8ca:/home/learning/learngit# cat file.txt
version 1
|
如上将83ba对象中内容写入file.txt中,file.txt的内容就恢复到了第一个版本的信息,这就是版本回顾的本质。
2.1.6. 小结
.git/objects
目录即是.git
的数据库,文件的历史修改都以文件粒度完整的记录在数据库中,数据库中的数据对象与工作区中的被编辑文件是互相独立的,被编辑文件可以通过数据库中的这些数据对象实现历史版本回滚。
.git
数据库是按文件内容计算hash值作为数据对象的key,因此只要文件内容不变,工作区中不同的文件名在数据库中只有一条数据对象。
到此为止,已经分析了
- .git/objects
目录中存放了数据对象
- 工作区文件与数据库中数据对象是互相独立的,库中存储了工作区文件的历史版本,方便回滚;
- 工作区中相同内容的不同名文件在库中只有一个存储对象;
这些是git上层功能的基础核心,但距离一个基本的版本控制系统,还差如下基本问题需要解决:
- 如何记录文件名的变更
- 如何记录文件夹的变更
- 如何记录文件变更的时序关系
- 缺少对每次变更的说明记录,即变更日志
- 用户使用时,无需关注变更版本的hash值
2.2. 树对象
Git利用树对象(tree object)解决文件名保存的问题,树对象也能够将多个文件组织在一起。
Git通过树(tree)对象将数据(blob)对象组织起来,这很类似于一种文件系统——blob对象对应文件内容,tree对象对应文件的目录和节点。一个树(tree)对象包含一条或多条记录,每条记录含有一个指向blob对象或tree对象的SHA-1指针,以及相应的模式、类型、文件名。
blob对象只存储文件的内容,文件名和权限信息存储在tree对象中。tree对象既可以引用blob,也可以引用tree。如此一来,使用 tree 和 blob 两类对象,我们就可以存储一个目录下所有的文件内容和子目录结构。
我们可以通过项目根目录对应的 tree 对象来表示项目代码的版本。每一次内容改动都会产生一个新 tree 对象,也就是一个新版本。如果给定一个 tree 对象 以及它引用的所有 tree 对象 和 blob 对象,我们就能恢复对应的目录结构和文件内容。
当前git状态如下,
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
f1.txt
f2.txt
f3.txt
file.txt
nothing added to commit but untracked files present (use "git add" to track)
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/
.git/objects/
|-- 1f
| `-- 7a7a472abf3dd9643fd615f6da379c4acb3e3a
|-- 4b
| `-- 5fa63702dd96796042e92787f464e28f09f17d
|-- 83
| `-- baae61804e65cc73a7201a7252750c76066a30
|-- info
`-- pack
|
2.2.1. 创建暂存区
暂存区,也叫stage或者index,是一个文件,路径为.git/index
暂存区是working directory和object database的纽带。
创建暂存区,并将file.txt放入暂存区,此时objects下仍然只有3个对象。
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# git update-index --add file.txt
root@c60e0f2aa8ca:/home/learning/learngit# cat .git/index
DIRCf?�P�f?�P��"_
��
���a�Ne�s� rRu
vjfile.txt�-/�bQ�q���C��I
root@c60e0f2aa8ca:/home/learning/learngit# git ls-files --stage
100644 83baae61804e65cc73a7201a7252750c76066a30 0 file.txt
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/
.git/objects/
|-- 1f
| `-- 7a7a472abf3dd9643fd615f6da379c4acb3e3a
|-- 4b
| `-- 5fa63702dd96796042e92787f464e28f09f17d
|-- 83
| `-- baae61804e65cc73a7201a7252750c76066a30
|-- info
`-- pack
|
2.2.2. 提交暂存区
objects目录下多了一个391a4e90ba882dbc9ea93855103f6b1fa6791cf6
对象,类型为tree
。
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# git write-tree
391a4e90ba882dbc9ea93855103f6b1fa6791cf6
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/
.git/objects/
|-- 1f
| `-- 7a7a472abf3dd9643fd615f6da379c4acb3e3a
|-- 39
| `-- 1a4e90ba882dbc9ea93855103f6b1fa6791cf6
|-- 4b
| `-- 5fa63702dd96796042e92787f464e28f09f17d
|-- 83
| `-- baae61804e65cc73a7201a7252750c76066a30
|-- info
`-- pack
6 directories, 4 files
root@c60e0f2aa8ca:/home/learning/learngit# git cat-file -t 391a
tree
root@c60e0f2aa8ca:/home/learning/learngit# git cat-file -p 391a
100644 blob 83baae61804e65cc73a7201a7252750c76066a30 file.txt
|
如下以一个新文件做提交测试:
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# echo "add tree feature" > tree_test.txt
root@c60e0f2aa8ca:/home/learning/learngit# git update-index --add tree_test.txt
root@c60e0f2aa8ca:/home/learning/learngit# git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: file.txt
new file: tree_test.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
f1.txt
f2.txt
f3.txt
root@c60e0f2aa8ca:/home/learning/learngit# git ls-files --stage
100644 83baae61804e65cc73a7201a7252750c76066a30 0 file.txt
100644 a9428157240a2f9a359855f9fa20a2d2c6640fea 0 tree_test.txt
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/
.git/objects/
|-- 1f
| `-- 7a7a472abf3dd9643fd615f6da379c4acb3e3a
|-- 39
| `-- 1a4e90ba882dbc9ea93855103f6b1fa6791cf6
|-- 4b
| `-- 5fa63702dd96796042e92787f464e28f09f17d
|-- 83
| `-- baae61804e65cc73a7201a7252750c76066a30
|-- a9
| `-- 428157240a2f9a359855f9fa20a2d2c6640fea
|-- info
`-- pack
7 directories, 5 files
root@c60e0f2aa8ca:/home/learning/learngit# git cat-file -p a942
add tree feature
root@c60e0f2aa8ca:/home/learning/learngit# git write-tree
2778f9fbc519e7dded50e93a0a8a0312f5f35036
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/
.git/objects/
|-- 1f
| `-- 7a7a472abf3dd9643fd615f6da379c4acb3e3a
|-- 27
| `-- 78f9fbc519e7dded50e93a0a8a0312f5f35036
|-- 39
| `-- 1a4e90ba882dbc9ea93855103f6b1fa6791cf6
|-- 4b
| `-- 5fa63702dd96796042e92787f464e28f09f17d
|-- 83
| `-- baae61804e65cc73a7201a7252750c76066a30
|-- a9
| `-- 428157240a2f9a359855f9fa20a2d2c6640fea
|-- info
`-- pack
8 directories, 6 files
root@c60e0f2aa8ca:/home/learning/learngit# git ls-files --stage
100644 83baae61804e65cc73a7201a7252750c76066a30 0 file.txt
100644 a9428157240a2f9a359855f9fa20a2d2c6640fea 0 tree_test.txt
|
这说明两个问题:
如果添加git数据库中尚未存储的数据到暂存区,则在执行update-index的时候,会同时把该数据保存到git数据库。
添加文件进入暂存区的操作是追加操作,之前已经加入暂存区的文件依然存在——很多人会有误区,认为变更提交之后,暂存区就清空了。
2.3. 提交对象
至此,在git数据库中,我们可以完整的记录文件的状态、文件夹的状态;并且可以把多个文件或文件夹组织在一起,记录他们的变更过程。我们离一个完善的版本控制系统似乎已经不远了,而这一切实现起来又是如此简单——我们只是通过几个命令操作git数据库就完成了这些功能。
接下来,我们只要把数据库中各个版本的时序关系记录下来,再把对每一个版本更新的注释记录下来,不就完成了一个逻辑简单、功能强大、操作灵活的版本控制系统吗?
那么,如何记录版本的时序关系,如何记录版本的更新注释呢?这就要引入另一个git数据对象——提交对象(commit object)。
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# git commit-tree 2778f -m 'first commit'
3369eb2d17368f8c91bfb9bebeac0cae0c4b07a0
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/
.git/objects/
|-- 1f
| `-- 7a7a472abf3dd9643fd615f6da379c4acb3e3a
|-- 27
| `-- 78f9fbc519e7dded50e93a0a8a0312f5f35036
|-- 33
| `-- 69eb2d17368f8c91bfb9bebeac0cae0c4b07a0
|-- 39
| `-- 1a4e90ba882dbc9ea93855103f6b1fa6791cf6
|-- 4b
| `-- 5fa63702dd96796042e92787f464e28f09f17d
|-- 83
| `-- baae61804e65cc73a7201a7252750c76066a30
|-- a9
| `-- 428157240a2f9a359855f9fa20a2d2c6640fea
|-- info
`-- pack
9 directories, 7 files
root@c60e0f2aa8ca:/home/learning/learngit# git cat-file -t 3369
commit
root@c60e0f2aa8ca:/home/learning/learngit# git cat-file -p 3369
tree 2778f9fbc519e7dded50e93a0a8a0312f5f35036
author ethan <ethan@qq.com> 1712408138 +0000
committer ethan <ethan@qq.com> 1712408138 +0000
first commit
root@c60e0f2aa8ca:/home/learning/learngit# git log 3369
commit 3369eb2d17368f8c91bfb9bebeac0cae0c4b07a0
Author: ethan <ethan@qq.com>
Date: Sat Apr 6 12:55:38 2024 +0000
first commit
|
接触过git的小伙伴会发现,以上我们用到的这些指令在使用git过程中是用不到的。这是为什么呢?因为git对以上这些指令进行了封装,给用户提供了更便捷的操作命令,如add,commit等。
每次我们运行 git add 和 git commit 命令时, Git 所做的实质工作是将被改写的文件保存为数据对象,更新暂存区,记录树对象,最后创建一个指明了顶层树对象和父提交的提交对象。 这三种主要的 Git 对象——数据对象、树对象、提交对象——最初均以单独文件的形式保存在 .git/objects 目录下。
然而,小问题依然存在,截止目前为止,我们对版本和数据对象的操作都是基于hash键值的,这些毫无直观含义的字符串让人很头疼,不会有人愿意一直急着最新提交对应的hash键值的。git不会允许这样的问题存在的,它通过引入“引用(references)”来解决这一问题。
3. 基础用法
3.1. 底层命令
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# git init
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint: git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint: git branch -m <name>
Initialized empty Git repository in /home/learning/learngit/.git/
root@c60e0f2aa8ca:/home/learning/learngit# cat .git/HEAD
ref: refs/heads/master
root@c60e0f2aa8ca:/home/learning/learngit# cat .git/refs/heads/master
cat: .git/refs/heads/master: No such file or directory
root@c60e0f2aa8ca:/home/learning/learngit# echo "hello, world" > file
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/
.git/objects/
|-- info
`-- pack
2 directories, 0 files
root@c60e0f2aa8ca:/home/learning/learngit# git ls-files --stage
root@c60e0f2aa8ca:/home/learning/learngit# git update-index --add file
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/
.git/objects/
|-- 4b
| `-- 5fa63702dd96796042e92787f464e28f09f17d
|-- info
`-- pack
3 directories, 1 file
root@c60e0f2aa8ca:/home/learning/learngit# git ls-files --stage
100644 4b5fa63702dd96796042e92787f464e28f09f17d 0 file
root@c60e0f2aa8ca:/home/learning/learngit# git write-tree
084e43b5a2c5d69d3852486970e338048d66a7dc
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/
.git/objects/
|-- 08
| `-- 4e43b5a2c5d69d3852486970e338048d66a7dc
|-- 4b
| `-- 5fa63702dd96796042e92787f464e28f09f17d
|-- info
`-- pack
4 directories, 2 files
root@c60e0f2aa8ca:/home/learning/learngit# git commit-tree 084e -m "first commit"
21461a30f2b08fc22282ac08d79767d94de00ccf
root@c60e0f2aa8ca:/home/learning/learngit# git log
fatal: your current branch 'master' does not have any commits yet
root@c60e0f2aa8ca:/home/learning/learngit# git log 2146
commit 21461a30f2b08fc22282ac08d79767d94de00ccf
Author: ethan <ethan@qq.com>
Date: Sat Apr 6 13:57:59 2024 +0000
first commit
root@c60e0f2aa8ca:/home/learning/learngit# git update-ref refs/heads/master 21461
root@c60e0f2aa8ca:/home/learning/learngit# git log
commit 21461a30f2b08fc22282ac08d79767d94de00ccf (HEAD -> master)
Author: ethan <ethan@qq.com>
Date: Sat Apr 6 13:57:59 2024 +0000
first commit
root@c60e0f2aa8ca:/home/learning/learngit# git status
On branch master
nothing to commit, working tree clean
|
3.2. 普通命令
Bash |
---|
| root@c60e0f2aa8ca:/home/learning/learngit# git add file
root@c60e0f2aa8ca:/home/learning/learngit# git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: file
root@c60e0f2aa8ca:/home/learning/learngit# git ls-files --stage
100644 4b5fa63702dd96796042e92787f464e28f09f17d 0 file
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/
.git/objects/
|-- 4b
| `-- 5fa63702dd96796042e92787f464e28f09f17d
|-- info
`-- pack
3 directories, 1 file
root@c60e0f2aa8ca:/home/learning/learngit# git commit -m 'first commit'
[master (root-commit) 7288642] first commit
1 file changed, 1 insertion(+)
create mode 100644 file
root@c60e0f2aa8ca:/home/learning/learngit# git status
On branch master
nothing to commit, working tree clean
root@c60e0f2aa8ca:/home/learning/learngit# git ls-files --stage
100644 4b5fa63702dd96796042e92787f464e28f09f17d 0 file
root@c60e0f2aa8ca:/home/learning/learngit# tree .git/objects/
.git/objects/
|-- 08
| `-- 4e43b5a2c5d69d3852486970e338048d66a7dc
|-- 4b
| `-- 5fa63702dd96796042e92787f464e28f09f17d
|-- 72
| `-- 88642e6729ec3a50a2691bd83248a85cb217e2
|-- info
`-- pack
5 directories, 3 files
root@c60e0f2aa8ca:/home/learning/learngit# cat .git/refs/heads/master
7288642e6729ec3a50a2691bd83248a85cb217e2
root@c60e0f2aa8ca:/home/learning/learngit# git log
commit 7288642e6729ec3a50a2691bd83248a85cb217e2 (HEAD -> master)
Author: ethan <ethan@qq.com>
Date: Sat Apr 6 14:05:02 2024 +0000
first commit
|
每次我们运行 git add 和 git commit 命令时, Git 所做的实质工作是将被改写的文件保存为数据对象,更新暂存区,记录树对象,最后创建一个指明了顶层树对象和父提交的提交对象。 这三种主要的 Git 对象——数据对象、树对象、提交对象——最初均以单独文件的形式保存在 .git/objects 目录下。
从上面运行结果看:
- git add file
:
- git update-index --add file
,将数据对象保存到数据库
- git commit -m 'first commit'
- git write-tree
,新增tree对象并保存到数据库
- git commit-tree
,新增commit对象并保存到数据库
- git update-ref
,为commit对象的hash值添加ref引用,方便实用
4. 高阶用法
4.1. 版本回退
4.2. 分支管理
4.3. 冲突解决
4.4. cherry-pick
4.5. .gitignore
Bash |
---|
| git rm -r --cached .
git add .
git commit -m 'update .gitignore'
|
5. 参考资料
最后更新:
2024-04-14