git学习笔记

前言:最近读了《pro git》对git内部原理的阐释,我感觉收益匪浅。正好最近老师让写一遍git的博客,就写一篇读书笔记来和大家分享一下,顺便安利一下《pro git》。

平常开发中所使用的 git push 、 git pull 等命令,都是为了便于用户使用,对操作进行相应集成和封装。通过学习git的内部原理,有利于我们更好地了解 git。
接下来我们一步步从底层一点一点学习,逐步来构建一棵版本控制树。

.git目录

当我们通过git init 对项目初始化后,项目目录下会出现一个 .git 目录。 .git 目录里面存放着进行代码控制所需的所有数据和信息:
git.png
其中, objects目录、HEAd文件、index文件(可能刚初始化时不存在)和refs目录是Git的核心组成部分。objects目录存储所有数据内容;ref目录存储指向数据提交对象的指针;HEAD文件指向目前被检出的分支;index文件保存文件暂存区信息。

存取文件

我们在Git仓库中保存文件数据时,它会通过SHA-1 的方式对文件加密和存储,然后返回一个 SHA-1值 。之后我们需要通过这个SHA-1值 来获取文件。 我们来具体操作一下:

  1. 首先,创建一个 a.txt  文件, 内容为:

a.png

hello world
  1. 通过 git hash-objet -w 文件名 命令来将文件保存到 .git/objects 目录下,并返回一个 key:
$ git hash-object -w a.txt
8f10d293354542a5babf0e66021e462a7ddb588d

可以看到生成的SHA-1值为8f10d293354542a5babf0e66021e462a7ddb588d。

  1. 查看.git/objects目录

8f.png
8f2.png
会发现.git/objects 目录中出现了一个 8f目录 ,8f目录内部有一个10d293354542a5babf0e66021e462a7ddb588d文件,目录名+文件名恰好就是 SHA-1值。而这个文件就是我们的 a.txt 文件

  1. 通过 git cat-file -t SHA-1值 > 文件 获取Git仓库中的文件内容:
git cat-file -t 8f10d293354542a5babf0e66021e462a7ddb588d > b.txt

在我们的项目目录下就出现了b.txt文件,查看b.txt内容:

hello world

文件存取如图所示:
git.png

blob对象和tree对象

类似文件系统中的文件和目录,.git/object 中的对象格式有** Blob对象 **和 tree对象

我们存放的文件的格式为Blob对象,可以通过 git cat-file -t 命令来查看:

$ git cat-file -t 8f10d293354542a5babf0e66021e462a7ddb588d
blob

类似目录是用来管理文件的,tree对象是用来管理我们存入Git仓库的Blob对象的。一个tree对象可以管理多个blob对象tree对象。通常, Git根据某一时刻暂存区(即index区域)所表示的状态创建并记录一个对应的tree对象。具体操作如下:

  1. 通过 git update-index 为一个a.txt 创立暂存区(其中 10064表示为普通文件):
$ git update-index --add --cacheinfo 10064 8f10d293354542a5babf0e66021e462a7ddb588d a.txt

.git目录下出现 index文件,用来保存文件暂存区信息:
index.png

**

  1. 通过 git write-tree 命令将暂存区内容写入一个树对象:
$ git write-tree
6d110a67b08a546bbd5b28e59b3298ee1eacc22c

产生了一个SHA-1值为 6d110a67b08a546bbd5b28e59b3298ee1eacc22c 的文件,查看 .git/object 目录:
tree1.png
tree2.png

  1. 可以通过 git cat-file -t 命令来查看格式, 会发现为tree格式:
$ git cat-file -t 6d110a67b08a546bbd5b28e59b3298ee1eacc22c
tree
  1. 通过 git status 查看git仓库状态:
 $ git status
On branch master
No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   a.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        b.txt

然后我们就会发现,我们刚才的操作就类似 :

git add a.txt

修改文件和添加文件

修改文件

步骤如下:

  1. 修改 a.txt 内容:
goodbye world
  1. 提交 a.txt到git仓库:
$ git hash-object -w a.txt
c69ee2e20ca04f510508baa2f9b9ed3bc6032300

** .git/object **目录下有两个版本的a.txt :
version.png

  1. 更新暂存区,并且生产tree对象:
$ git update-index --add --cacheinfo 100644 c69ee2e20ca04f510508baa2f9b9ed3bc6032300 a.txt
$ git write-tree
f821a3927801b54d904f18dfd5eb0bdc96532abc

添加文件

把b文件当做新文件.

$ git hash-object -w b.txt
8f10d293354542a5babf0e66021e462a7ddb588d
$ git update-index --add --cacheinfo 100644 8f10d293354542a5babf0e66021e462a7ddb588d b.txt
$ git write-tree
1a6ba0f66390189a2c5c759bff5b6c77c4711818

查看当前状态

$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   a.txt
        new file:   b.txt

Commit tree

现在我们有6d110a67b08a546bbd5b28e59b3298ee1eacc22cf821a3927801b54d904f18dfd5eb0bdc96532abc1a6ba0f66390189a2c5c759bff5b6c77c4711818 三个tree对象,我们分别查看包含的blob对象:

$ git cat-file -p 6d110a67b08a546bbd5b28e59b3298ee1eacc22c
100644 blob 8f10d293354542a5babf0e66021e462a7ddb588d    a.txt
$ git cat-file -p f821a3927801b54d904f18dfd5eb0bdc96532abc
 100644 blob c69ee2e20ca04f510508baa2f9b9ed3bc6032300    a.txt
$ git cat-file -p 1a6ba0f66390189a2c5c759bff5b6c77c4711818
100644 blob c69ee2e20ca04f510508baa2f9b9ed3bc6032300    a.txt
100644 blob 8f10d293354542a5babf0e66021e462a7ddb588d    b.txt

即:
git.png
可以根据tree对象来访问不同版本的代码,但是这样我们要记住每一个tree对象的内容、何时创建、是谁保存等信息。如果能够给每一个tree对象附加说明信息,并且以链表的形式组织起来就好了。这时候我们就需要 commit tree

  1. 通过 echo '说明信息' | git commit-tree tree对象SHA-1值  对6d110a67b08a546bbd5b28e59b3298ee1eacc22c、来创建commit对象:
$ echo 'first commit' | git commit-tree 6d110a6
7b0860404899f161f92ba35fefff99a905389cc7

再对1a6ba0f66390189a2c5c759bff5b6c77c4711818 创建commit对象,并且通过参数-p 7b0860指向 上一个commit-tree:

$ echo 'second commit' | git commit-tree 1a6ba0 -p 7b0860
7b0860404899f161f92ba35fefff99a905389cc7
  1. 然后查看两个tree对象的内容:
$ git cat-file -p 7b0860404899f161f92ba35fefff99a905389cc7
tree 6d110a67b08a546bbd5b28e59b3298ee1eacc22c
author unknown <1179373651@qq.com> 1602423688 +0800
committer unknown <1179373651@qq.com> 1602423688 +0800

first commit

$ git cat-file -p 7ab9a76432ce2ffa03a0f5a5a390ea990afc483f
tree 6d110a67b08a546bbd5b28e59b3298ee1eacc22c
parent 7b0860404899f161f92ba35fefff99a905389cc7
author unknown <1179373651@qq.com> 1602424538 +0800
committer unknown <1179373651@qq.com> 1602424538 +0800

second commit

即:
git222.png

  1. 通过 git log 最后一个SHA-1值 查看一下提交记录:
$ git log --stat 7ab9a7
commit 7ab9a76432ce2ffa03a0f5a5a390ea990afc483f
Author: unknown <1179373651@qq.com>
Date:   Sun Oct 11 21:58:02 2020 +0800

    second commit

 a.txt | 4 ++--
 b.txt | 2 ++
 2 files changed, 4 insertions(+), 2 deletions(-)

commit 7b0860404899f161f92ba35fefff99a905389cc7
Author: unknown <1179373651@qq.com>
Date:   Sun Oct 11 21:41:28 2020 +0800

    first commit

 a.txt | 2 ++
 1 file changed, 2 insertions(+)

我们就可以根据 commit-tree 进行版本控制了。
(是不是很眼熟,这就是 commit的内部原理)。

Git场景4 模拟

  1. 我们在github创建一个项目 tryGit

git.png

  1. 通过 git clone 克隆远程仓库到本地
$ git clone git@github.com:loser-wang/tryGit.git

tryGit.png

  1. 创建一个新分支,并且切换
$ git checkout -b work_branch
Switched to a new branch 'work_branch'

$ git branch
  main
* work_branch

  1. 更改readme.txt
version1 !!
have a try
$ git add readme.md
$ git status
On branch work_branch
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   readme.md

$ git commit readme.md -m 'first version'
[work_branch c5bf091] first version
 1 file changed, 1 insertion(+)

  1. 切换回 main主分支,并且合并:
$ git checkout main
Switched to branch 'main'

$ git merge work_branch
Updating b0f73f2..c5bf091
Fast-forward
 readme.md | 1 +
 1 file changed, 1 insertion(+)

  1. 在github上修改readme.md:

version2.png

  1. 通过 git fetch 获取远程文件,并且通过git diff 比较不同
$ git fetch origin main
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 637 bytes | 21.00 KiB/s, done.
From github.com:loser-wang/tryGit
 * branch            main       -> FETCH_HEAD
   b0f73f2..916389e  main       -> origin/main

$ git diff main origin/main
diff --git a/readme.md b/readme.md
index 825b3ae..0210b0c 100644
--- a/readme.md
+++ b/readme.md
@@ -1,2 +1,2 @@
-version 1
+version 2
 have a try!
 
$ git merge origin/main
Auto-merging readme.md
CONFLICT (content): Merge conflict in readme.md
Automatic merge failed; fix conflicts and then commit the result.

  1. 对两个版本的不同,进行取舍修改,然后合并,并且提交远程仓库:
$ git add readme.txt
$ git commit -m 'version 3'
[main 2d43083] version 3
$ git push origin main
Enumerating objects: 10, done.
Counting objects: 100% (10/10), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 517 bytes | 258.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:loser-wang/tryGit.git
   916389e..2d43083  main -> main
  1. git rebase
$ git add .
$ git commit . -m 'version4'
[main ec1ccf6] version4
 1 file changed, 2 insertions(+), 1 deletion(-)

$ git add .
$ git commit . -m 'version5'
[main cb932de] version5
 1 file changed, 1 insertion(+), 1 deletion(-)
 
$ git rebase -i head^^
Successfully rebased and updated refs/heads/main.

参考: https://mp.weixin.qq.com/s/Km5KuXPETvG0wCGHrvj9Vg、《pro git》

posted @ 2020-10-13 13:56  loser_wang  阅读(200)  评论(0编辑  收藏  举报