ljzsdut
GitHubToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

01 Object

git基本3个空间

image-20220409154130778

git init发生了什么? 

[ Mac-mini:/Users/lijuzhang/test ]  ➜ mkdir git-demo
[ Mac-mini:/Users/lijuzhang/test ]cd git-demo
[ locolhost:/Users/lijuzhang/test/git-demo ]  ➜ git init .
Initialized empty Git repository in /Users/lijuzhang/test/git-demo/.git/

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ ls -a
.    ..   .git
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ ls -la .git
total 24
drwxr-xr-x   9 lijuzhang  staff  288  4  9 11:19 .
drwxr-xr-x   3 lijuzhang  staff   96  4  9 11:19 ..
-rw-r--r--   1 lijuzhang  staff   23  4  9 11:19 HEAD
-rw-r--r--   1 lijuzhang  staff  137  4  9 11:19 config
-rw-r--r--   1 lijuzhang  staff   73  4  9 11:19 description
drwxr-xr-x  13 lijuzhang  staff  416  4  9 11:19 hooks
drwxr-xr-x   3 lijuzhang  staff   96  4  9 11:19 info
drwxr-xr-x   4 lijuzhang  staff  128  4  9 11:19 objects
drwxr-xr-x   4 lijuzhang  staff  128  4  9 11:19 refs

我们发现,init后,会在当前目录下生成一个.git目录。该目录下有很多的文件。

.git目录下文件详解

  • config:当前仓库的本地配置文件。除此之外,git还有一个全局的配置文件:~/.gitconfig,此外全局的配置文件可以通过git config --global --list查看。

    # 查看全局配置文件
    [ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ cat ~/.gitconfig
    [core]
    	quotepath = false
    	excludefile = /Users/lijuzhang/.gitignore_global
    [user]
    	name = lijuzhang
    	email = lijuzhang@inspur.com
    
    # 查看全局配置 
    [ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git config --global --list
    core.quotepath=false
    core.excludefile=/Users/lijuzhang/.gitignore_global
    user.name=lijuzhang
    user.email=lijuzhang@inspur.com
    (END)
    
    # 修改/查看配置项: git config [--global]
    [ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git config --list
    
    [ locolhost:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git config user.name ljzsdut
    [ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ cat .git/config
    [core]
    	repositoryformatversion = 0
    	filemode = true
    	bare = false
    	logallrefupdates = true
    	ignorecase = true
    	precomposeunicode = true
    [user]
    	name = ljzsdut
    
    [ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git config --get user.name
    ljzsdut
    

git add发生了什么?

git add后文件变化

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ tree .git
.git
├── HEAD
├── config
├── description
├── hooks
│   ├── 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
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags

8 directories, 15 files
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)echo 'hello git' >git.txt
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git add git.txt
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ tree .git
.git
├── HEAD
├── config
├── description
├── hooks
│   ├── 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
├── index  #新生成的文件1
├── info
│   └── exclude
├── objects #(命名使用sha-1类型的hash值,对文件内容、对象类型、内容长度等信息的组合进行hash计算)
│   ├── 8d  #新生成的目录(文件名hash值的前2位)
│   │   └── 0e41234f24b6da002d962a26c2495ea16a425f #新生成的文件2(文件名hash值的第3位开始)
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags

9 directories, 17 files  #多了1个目录和2个文件。

git add命令执行后,会多了1个目录和2个文件。

如何计算object的hash值?

我们前面提到,object的hash值是对文件内容、对象类型、内容长度等信息的组合进行sha1 hash计算。具体进行hash的内容为对象type 文本长度\0文件内容,即对象的类型(取值blob, tree, commit, tag)、空格、文本的长度(单位Byte)、文件内容。

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ echo "blob $(wc -c git.txt |awk '{print $1}')\0$(cat git.txt)"
blob 10hello git

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ echo "blob $(wc -c git.txt |awk '{print $1}')\0$(cat git.txt)" |sha1sum
8d0e41234f24b6da002d962a26c2495ea16a425f  -

说明:sha1 hash可能会发生hash碰撞,一旦发生hash碰撞,后面添加的文件内容会被第1个文件的内容覆盖。官方计划将sha1升级为sha256。

查看对象文件?

我们已经知道,一个对象包含了:对象类型、对象大小、对象内容。我们可以通过cat-file子命令查询对象的以上信息。

# 查看对象的类型type
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git cat-file -t 8d0e41
blob

# 查看object的内容content
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git cat-file -p 8d0e41
hello git

# 查看对象大小
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git cat-file -s 8d0e41
10

object里存储了哪些内容:

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ cat .git/objects/8d/0e41234f24b6da002d962a26c2495ea16a425f
xK��OR04`�H���WH�,�6A�%

上面的乱码,是原始内容压缩后的二进制格式的内容,我们可以解压缩:

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ python3
Python 3.9.7 (default, Sep  3 2021, 12:45:31)
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import zlib
>>> c = open('.git/objects/8d/0e41234f24b6da002d962a26c2495ea16a425f','rb').read()
>>> zlib.decompress(c)
b'blob 10\x00hello git\n'

可见,object只存储文件的内容、大小、对象类型,而不存储文件的名字。可以使用如下方式验证:

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ echo 'hello git' >tmp.txt
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git add tmp.txt
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ tree .git
.git
├── HEAD
├── config
├── description
├── hooks
│   ├── 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
├── index
├── info
│   └── exclude
├── objects
│   ├── 8d
│   │   └── 0e41234f24b6da002d962a26c2495ea16a425f
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags

9 directories, 17 files  #发现文件数量没有发生任何的变化

其实文件名是存储在index文件中的,该文件中记录了所有位于index区域的文件。

如何查看index文件?

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ cat .git/index
DIRCbP��d$obP��d$ob����
�A#O$��-�*&�I^�jB_git.txtbQ�|bQ�|g����
�A#O$��-�*&�I^�jB_tmp.txtV諞�_��l����^%

看见,index里存储的也是二进制格式,可以使用ls-files子命令进行查看:

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git ls-files
git.txt
tmp.txt

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git ls-files -s
100644 8d0e41234f24b6da002d962a26c2495ea16a425f 0	git.txt
100644 8d0e41234f24b6da002d962a26c2495ea16a425f 0	tmp.txt
# 权限  blob对象                                    文件名  

修改文件后add发生了什么?

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ cat git.txt
hello git
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ echo 1 >>git.txt
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git add git.txt
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ tree .git
.git
├── HEAD
├── config
├── description
├── hooks
│   ├── 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
├── index
├── info
│   └── exclude
├── objects
│   ├── 50
│   │   └── 0403283f5ed39ff656d0acfaf7ce4ec22494dd  #修改后,新生成的
│   ├── 8d
│   │   └── 0e41234f24b6da002d962a26c2495ea16a425f  #修改前
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags

10 directories, 18 files


[ locolhost:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git cat-file -p 8d0e
hello git
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git cat-file -p 5004
hello git
1

可以发现,修改文件并执行git add后,修改之前的对象依旧存在,没有被删除。这是因为对象是可以被复用的,可能多个文件引用一个对象。即便是没有文件的对象,也不会立即被删除,这些对象称之为是垃圾对象。

git commit发生了什么?

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ tree .git
.git
├── HEAD
├── config
├── description
├── hooks
│   ├── 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
├── index
├── info
│   └── exclude
├── objects
│   ├── 50
│   │   └── 0403283f5ed39ff656d0acfaf7ce4ec22494dd
│   ├── 8d
│   │   └── 0e41234f24b6da002d962a26c2495ea16a425f
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags

10 directories, 18 files

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git commit -m "1st commit"
[master (root-commit) 3cb18e8] 1st commit
 2 files changed, 3 insertions(+)
 create mode 100644 git.txt
 create mode 100644 tmp.txt
 
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ tree .git
.git
├── COMMIT_EDITMSG #file
├── HEAD
├── config
├── description
├── hooks
│   ├── 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
├── index
├── info
│   └── exclude
├── logs #
│   ├── HEAD  #f
│   └── refs  #
│       └── heads #
│           └── master #f
├── objects
│   ├── 3c
│   │   └── b18e8893083b917ab79b34afa7bca9e3cfd426 #commit_id
│   ├── 50
│   │   └── 0403283f5ed39ff656d0acfaf7ce4ec22494dd
│   ├── 5c
│   │   └── 6781b18b7c61773120fca8b5006fb9cba71e49 #tree类型
│   ├── 8d
│   │   └── 0e41234f24b6da002d962a26c2495ea16a425f
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master  #f
    └── tags

15 directories, 24 files

多了2个object:

# 第一个object
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git cat-file -t 3cb18
commit  # type为commit,表示该object为commit类型

[ locolhost:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git cat-file -p 3cb18
tree 5c6781b18b7c61773120fca8b5006fb9cba71e49  #type为tree,类似目录
author ljzsdut <lijuzhang@inspur.com> 1649487010 +0800
committer ljzsdut <lijuzhang@inspur.com> 1649487010 +0800

1st commit


# 第2个object
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git cat-file -t 5c67
tree

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git cat-file -p 5c67
100644 blob 500403283f5ed39ff656d0acfaf7ce4ec22494dd	git.txt
100644 blob 8d0e41234f24b6da002d962a26c2495ea16a425f	tmp.txt

其他文件:

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ cat .git/HEAD
ref: refs/heads/master  #指向的分支

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ cat .git/refs/heads/master
3cb18e8893083b917ab79b34afa7bca9e3cfd426  #指向commit

此时的结构图:

image-20220409150725808

再进行一次commit:

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)echo 1 >>tmp.txt
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git add tmp.txt
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git commit -m "2nd commit"
[master 61b3faa] 2nd commit
 1 file changed, 1 insertion(+)

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ tree .git
.git
├── COMMIT_EDITMSG
├── HEAD
├── config
├── description
├── hooks
│   ├── 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
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── 0f
│   │   └── b63a8ddc80b787a97949e5cf276089790eb81d #tree
│   ├── 3c
│   │   └── b18e8893083b917ab79b34afa7bca9e3cfd426
│   ├── 50
│   │   └── 0403283f5ed39ff656d0acfaf7ce4ec22494dd
│   ├── 5c
│   │   └── 6781b18b7c61773120fca8b5006fb9cba71e49
│   ├── 61
│   │   └── b3faa3de3bf8d11d6d4d811833cbf92151c1e6 #commit
│   ├── 8d
│   │   └── 0e41234f24b6da002d962a26c2495ea16a425f
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master
    └── tags

17 directories, 26 files

发现又多了2个object:

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git cat-file -p 61b3faa
tree 0fb63a8ddc80b787a97949e5cf276089790eb81d
parent 3cb18e8893083b917ab79b34afa7bca9e3cfd426
author ljzsdut <lijuzhang@inspur.com> 1649488386 +0800
committer ljzsdut <lijuzhang@inspur.com> 1649488386 +0800

2nd commit

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git cat-file -p 0fb63a8
100644 blob 500403283f5ed39ff656d0acfaf7ce4ec22494dd	git.txt
100644 blob 500403283f5ed39ff656d0acfaf7ce4ec22494dd	tmp.txt

此时结构图如下:

image-20220409151208314

当有目录时:

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ mkdir folder1
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)echo file3 >folder1/file3.txt
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git add .
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master) ✗  ➜ git commit -m "3rd commit"
[master 916cedf] 3rd commit
 1 file changed, 1 insertion(+)
 create mode 100644 folder1/file3.txt
 
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ tree .git
.git
├── COMMIT_EDITMSG
├── HEAD
├── config
├── description
├── hooks
│   ├── 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
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── 0f
│   │   └── b63a8ddc80b787a97949e5cf276089790eb81d
│   ├── 3c
│   │   └── b18e8893083b917ab79b34afa7bca9e3cfd426
│   ├── 50
│   │   └── 0403283f5ed39ff656d0acfaf7ce4ec22494dd
│   ├── 5c
│   │   └── 6781b18b7c61773120fca8b5006fb9cba71e49
│   ├── 61
│   │   └── b3faa3de3bf8d11d6d4d811833cbf92151c1e6
│   ├── 6f
│   │   └── 1c48e7934b61a9eaecea3fe3c8832073ea0a7a
│   ├── 7c
│   │   └── 8ac2f8d82a1eb5f6aaece6629ff11015f91eb4
│   ├── 8d
│   │   └── 0e41234f24b6da002d962a26c2495ea16a425f
│   ├── 91
│   │   └── 6cedf33208cc0031d838fc942481ac11fd432b
│   ├── b4
│   │   └── 540ce0bad63a0f40de1619b97a4589a9259496
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master
    └── tags

21 directories, 30 files

发现多了3个object:

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git cat-file -p 916cedf
tree 6f1c48e7934b61a9eaecea3fe3c8832073ea0a7a  #object_1
parent 61b3faa3de3bf8d11d6d4d811833cbf92151c1e6
author ljzsdut <lijuzhang@inspur.com> 1649489508 +0800
committer ljzsdut <lijuzhang@inspur.com> 1649489508 +0800

3rd commit
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git cat-file -p 6f1c48
040000 tree b4540ce0bad63a0f40de1619b97a4589a9259496	folder1 #object_2 
100644 blob 500403283f5ed39ff656d0acfaf7ce4ec22494dd	git.txt
100644 blob 500403283f5ed39ff656d0acfaf7ce4ec22494dd	tmp.txt

[ locolhost:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git cat-file -p b4540c
100644 blob 7c8ac2f8d82a1eb5f6aaece6629ff11015f91eb4	file3.txt #object_3

此时的结构图:

image-20220409153519583

上图中,中间一层的那些tree,被称之为“root tree”。

object压缩与解压缩

git add产生的object,默认会自动使用zlib进行单个object的压缩。

对于一个文件,如果存在多次修改并git add操作,每次操作都会产生一个blob类型的object,即便该文件只是修改了一个字符,新产生的object也是一个全量的object,这就导致同一个文件的object的多个版本有大量的冗余部分,浪费存储空间。此时我们可以使用git gc命令进行delta压缩操作。

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git gc
Enumerating objects: 10, done.
Counting objects: 100% (10/10), done.
Delta compression using up to 8 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (10/10), done.
Total 10 (delta 0), reused 0 (delta 0)

压缩后,会在object目录下生成1个pack目录。每次压缩会产生一个idx和pack文件。

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ tree .git/objects
.git/objects
├── info
│   └── packs
└── pack
    ├── pack-3e76639f57843d61a0bc09b7f9c9d33a69afd4c5.idx
    └── pack-3e76639f57843d61a0bc09b7f9c9d33a69afd4c5.pack

2 directories, 3 files

可以使用命令git verify-pack -v <path_to_idx_or_pack_file>进行解析pack文件:

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git verify-pack -v .git/objects/pack/pack-3e76639f57843d61a0bc09b7f9c9d33a69afd4c5.idx
916cedf33208cc0031d838fc942481ac11fd432b commit 219 149 12
61b3faa3de3bf8d11d6d4d811833cbf92151c1e6 commit 219 150 161
3cb18e8893083b917ab79b34afa7bca9e3cfd426 commit 171 120 311
7c8ac2f8d82a1eb5f6aaece6629ff11015f91eb4 blob   6 15 431
500403283f5ed39ff656d0acfaf7ce4ec22494dd blob   12 21 446
6f1c48e7934b61a9eaecea3fe3c8832073ea0a7a tree   104 87 467
b4540ce0bad63a0f40de1619b97a4589a9259496 tree   37 48 554
0fb63a8ddc80b787a97949e5cf276089790eb81d tree   70 54 602
5c6781b18b7c61773120fca8b5006fb9cba71e49 tree   70 75 656
8d0e41234f24b6da002d962a26c2495ea16a425f blob   10 19 731
non delta: 10 objects
.git/objects/pack/pack-3e76639f57843d61a0bc09b7f9c9d33a69afd4c5.pack: ok

对pack文件进行解压缩:

git unpack-objects命令在解压缩时,如果对象已经存在object目录下(无论是object per file形式还是pack包形式),则不会解压缩出该对象。所以一般操作的时候,需要先把pack文件从object目录下移走,然后再解压:

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ mv .git/objects/pack/pack-3e76639f57843d61a0bc09b7f9c9d33a69afd4c5.pack .git/objects

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ tree .git/objects
.git/objects
├── info
│   └── packs
├── pack
│   └── pack-3e76639f57843d61a0bc09b7f9c9d33a69afd4c5.idx
└── pack-3e76639f57843d61a0bc09b7f9c9d33a69afd4c5.pack

2 directories, 3 files

# 解压缩
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git unpack-objects < .git/objects/pack-3e76639f57843d61a0bc09b7f9c9d33a69afd4c5.pack
Unpacking objects: 100% (10/10), done.


[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ tree .git/objects
.git/objects
├── 0f
│   └── b63a8ddc80b787a97949e5cf276089790eb81d
├── 3c
│   └── b18e8893083b917ab79b34afa7bca9e3cfd426
├── 50
│   └── 0403283f5ed39ff656d0acfaf7ce4ec22494dd
├── 5c
│   └── 6781b18b7c61773120fca8b5006fb9cba71e49
├── 61
│   └── b3faa3de3bf8d11d6d4d811833cbf92151c1e6
├── 6f
│   └── 1c48e7934b61a9eaecea3fe3c8832073ea0a7a
├── 7c
│   └── 8ac2f8d82a1eb5f6aaece6629ff11015f91eb4
├── 8d
│   └── 0e41234f24b6da002d962a26c2495ea16a425f
├── 91
│   └── 6cedf33208cc0031d838fc942481ac11fd432b
├── b4
│   └── 540ce0bad63a0f40de1619b97a4589a9259496
├── info
│   └── packs
├── pack
│   └── pack-3e76639f57843d61a0bc09b7f9c9d33a69afd4c5.idx
└── pack-3e76639f57843d61a0bc09b7f9c9d33a69afd4c5.pack

12 directories, 13 files

# 删除冗余的pack文件
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ rm -rf .git/objects/pack-3e76639f57843d61a0bc09b7f9c9d33a69afd4c5.pack

pack机制一般用于本地仓库和远程仓库通信时使用,可以节省网络带宽,提高工作效率。缺点是操作pack文件,需要实时计算解压,操作比较慢。

清理垃圾object

步骤:1、git gc 2、git prune

[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git gc
Enumerating objects: 13, done.
Counting objects: 100% (13/13), done.
Delta compression using up to 8 threads
Compressing objects: 100% (8/8), done.
Writing objects: 100% (13/13), done.
Total 13 (delta 1), reused 9 (delta 0)
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ tree .git/objects
.git/objects
├── 47
│   └── eda11bc3f7e5ae255b4acb266df938f70bc27a
├── fb
│   └── a977ffe3bb65cb9d6c9dba3106b498f6d0167e
├── info
│   └── packs
└── pack
    ├── pack-2d0f79f64bd327645172e062b2488f268a443770.idx
    └── pack-2d0f79f64bd327645172e062b2488f268a443770.pack

4 directories, 5 files
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git prune --dry-run  # 查看可以清理的垃圾对象。或使用git fsck查看dangling的对象
47eda11bc3f7e5ae255b4acb266df938f70bc27a blob
fba977ffe3bb65cb9d6c9dba3106b498f6d0167e blob
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ git prune
[ Mac-mini:/Users/lijuzhang/test/git-demo ] git:(master)  ➜ tree .git/objects
.git/objects
├── info
│   └── packs
└── pack
    ├── pack-2d0f79f64bd327645172e062b2488f268a443770.idx
    └── pack-2d0f79f64bd327645172e062b2488f268a443770.pack

2 directories, 3 files

总结

git add会产生object:每个文件会产生一个blob类型的object;

目录不会产生对象,其信息会记录在index文件中

当有目录时,即file1,git commit 会产生2个object:一个commit类型的object和一个tree类型的object(不产生blob类型的对象);

当没有目录时,即dir1/file1,git commit 会产生3个object,一个commit类型、1个被commit引用的tree类型、一个被tree引用的子tree,该子tree对象用于表示目录;