首页 > 其他分享 >Git底层原理与分析模型

Git底层原理与分析模型

时间:2024-06-08 23:00:28浏览次数:14  
标签:文件 git cat Git 版本 commit 分析模型 txt 底层

https://www.cnblogs.com/liqinglucky/p/git.html

一、git版本管理

1.1 背景--从写毕业论文讲文档备份

让我们从写毕业论文的经历讲起。通常开始写论文之前,先在一个开阔的空间创建了一个文件夹用于保存将来的论文稿。然后就开始了我们的 “毕业论文版本管理”。

这样管理存在的问题:

  1. 看不出每一个版本都更改了什么东西
  2. 合并多个文档版本的不同段落需要逐个打开手动复制。
  3. 文档副本很多的时候,和容易忘记那个才是自己的最终版本。
  4. 文档手滑删除了,只能再写一遍。

当然毕业论文至多就多复制几个文件备份就好了,但如果是上万个代码文件的项目工程如何管理呢?

1.2 版本控制系统

备份策略通常包括版本控制,或者叫“对变更进行追踪管理”。不管是集中式的 CVS、SVN 还是分布式的 Git 工具,实际上都是一种版本控制系统,我们可以通过他们很方便的管理我们的文件、代码等。

1.2.1 集中式与分布式

集中式版本控制系统[1]。版本库是集中存放在中央服务器的,开发者都是在自己本地电脑,先从中央服务器下载最新的版本,然后开始干活。干完活了,再把自己的活推送给中央服务器。集中式版本控制系统最大的毛病就是必须联网才能工作,中央服务器要是出了问题,所有人都没法干活了。

布式版本控制系统。分布式版本控制系统根本没有 “中央服务器”,每个人的电脑上都是一个完整的版本库。 开发者之间可以把各自的修改直接推送给对方。分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。

在实际使用分布式版本控制系统的时候,其实很少在两人之间的电脑上推送版本库的修改。因此,分布式版本控制系统通常也有一台充当 “中央服务器” 的电脑,但这个服务器的作用仅仅是用来方便 “交换” 大家的修改,没有它大家也一样干活,只是交换修改不方便而已。

1.3 git历史

Git 是当前流行的分布式版本控制管理工具,最初由 Linus Torvalds (Linux 创始人) 创造,于 2005 年发布[2]
[3]

[!quote] Git版本控制管理, Jon Loeliger, Mattbew McCullougb
Git诞生之前,Linux内核开发过程中使用BitKeeper来作为版本控制系统(VCS),然而在2005年春天,BitKeeper对免费版加入额外限制时,Linux社区意识到BitKeeper不再是长期可行的方案。
Linux 创始人Linus Torvalds也开始寻找替代方案。首先他回避再次使用商业解决方案。但当时现有的开源方案中的一些限制和缺陷也使得他放弃。

1.4 git特性

  1. 分布式开发。允许开发人员在自己本地离线并行开发,不需要与中心版本库时刻同步。
  2. 能够胜任上千开发人员协同。
  3. 性能优异。网络传输操作,需要使用“压缩”和“差异比较”技术。
  4. 保持完整性和可靠性。一个分布式版本控制系统,要能绝对保证数据的完整性和不被篡改。通过安全散列函数(SHA1)命名和识别数据库中的对象。
  5. 强化责任。能够定位谁改动了文件与改动说明。
  6. 原子事务。相关操作要么全部执行要么都不执行。
  7. 支持并鼓励基于分支的开发。
  8. 完整的版本库。每个人的版本库中都有一份完整历史修订记录。
  9. 清晰的内部设计。
  10. 免费自由。

二、git底层原理--数据结构

其实只要我们掌握几个基本的git命令其实就够了,但还是很有必要理解git的实现逻辑。[4]

[!tip]
工科很多东西都有一套底层逻辑,得出结论更多靠的是“推导”而非“记忆”。

2.1 仓库-- git初始化一个仓库

如果我们打算对该项目进行版本管理,第一件事就是使用 git init 命令,进行初始化

$ git init

git init 命令只会做一件事,就是在项目的根目录下创建一个 .git 的子目录,用来保存当前项目的一些版本信息。

2.2 三大分区--追踪文件管理状态

  • 工作区(Working Directory):能直接编辑文件。这个区位置最简单,就是我们的所有源代码目录。新增的文件和修改的文件修改状态为红色
  • 暂存区(stage,index):暂时存放的文件数据。可以理解为数据进入本地代码仓库之前存放的区域。git add操作将文件副本加入暂存区。文件修改状态为绿色。
  • 仓库区(commit History):纳入版本管理的文件数据。可以理解为一个本地的代码仓库。暂存区git commit的文件会被放入仓库区。文件修改状态清除。[5][6]

2.3 对象类型(objects)--commit,tree,blob

Git 可以通过一种算法可以得到任意文件的 “指纹”(40 位 16 进制数字),然后通过文件指纹存取数据,存取的数据都位于 objects 目录。

块(blob: binary large object):文件内容本身,不包含文件名。[7]
目录树(tree):记录blob标识、路径名和目录里的所有文件。
提交(commit): 保存版本库中提交时刻的快照。

一个commit表示了什么?
A: 每个commit索引出此刻完整工作区源文件
B: 每个commit索引出此次所有工作区新增文件内容

[!quote] Git版本控制管理 4.1.5节, Jon Loeliger, Mattbew McCullougb
git内部数据库存储文件的每个版本,而不是差异
git用blob之间的区别计算历史,
git需要创建一个工作目录时,对文件系统说“我有这样大的一个blob数据,应该放在路径tree下,你能重新构造?”
直接存储每个版本的完整内容是否效率太低了?如果只添加一行到文件,git是否要存两个版本的全部内容?不是,不完全是。git的打包文件存储机制,定位内容相似的全部文件,然后只存储一份全部内容。之后计算文件间差异并只存差异。如果你只更改一行内容,git会存储新版本的全部内容,然后记录一行的差异,存储在包里。
打包文件跟对象中其他对象存储在一起。也用于网络版本库中的高效数据传输。

梳理 git管理文件版本的原理:
1 每个源文件内容本身映射成blob对象,而源文件名与文件路径映射成tree。指纹(SHA1)算法保证了对象的全局唯一性。
2 用commit索引tree, blob来追踪还原工作区的所有文件。一个commit就是一个完整的版本副本。
3 commit会自动追加成commit链还原了版本的全部历史。

三、git分析模型--有向无环图

理解了commit的实质基本原理后,建立commit的基本分析模型[8][9]:有向无环图(DAG)。有向无环图是不会回到起点的图。一个commit表示一个节点,一个节点表示了所有文件的一次版本。节点有父链接(历史改动生成的指纹)指向上一个commit,只要根据git的抽象模型分析git的操作就够了。只需关注commit id的变化分析。

3.1 commit与reset操作

一次commit就是整个仓库所有代码的改动。

git add a.txt
git commit -m "add a.txt"

git log看commit链。

# git log
commit dea03e51887ee93dbe862d8c6a4e5f64ca586d60 (HEAD -> master)
Author: Your Name <[email protected]>
Date:   Tue Jun 4 03:17:26 2024 +0000

    add b.txt

commit 966737211d560207bc6d7e01be267707adc22ca6
Author: Your Name <[email protected]>
Date:   Tue Jun 4 03:12:04 2024 +0000

    add a.txt

通过git diff可以看到改动了哪些内容。
比较两次commit id的diff

git diff commit_id1 commit_id2

3.2 分支与merge操作

分支就是在某个commit发生分叉的commit链。
merge就是将两个commit链合并。

查看该 commit object 后可以看到,执行 merge 操作之后,会将原本版本链基础上衍生出一个新的 commit,并且该 commit 拥有两个 parent 父指针:
1 两个分支合并,不冲突的时候,git计算合并结果,并创建一个新提交
2 有冲突是,通常出现在同一个文件,同一行两个分支都各自进行了改动。git自己不解决冲突,而是留给开发人员处理,然后开发人员做一次commit。

如何避免 git 冲突?
“各位同仁们,接下来的四五个小时内我即将提交一笔代码,恳请大家在这段时间不要merge任何代码,求求大家了,晚上给你们买奶茶!”
只要是多人协作开发,文件的冲突是不可避免的,剩下的问题只是根据git提示手动解决冲突。

3.3 push与pull操作

对于处在远端的中央仓库,我们每次尝试通过 push 向远端推送一个 commit 时,远端仓库都会对提交版本的正确性进行校验,校验方式是沿拟提交 commit object 的 parent 指针向前遍历,倘若能找到某个 parent commit object 和远端分支上最后一个 commit object 的 key 值相同,才可能允许这次 push 行为,以此保证版本链的连贯性.

追踪远端分支

git checkout -b dev --track origin/dev

3.4 子模块(submodule)

子模块是一个链接,就像文件夹的快捷方式。
git submodule可以看到记录子模块的文件.gitmodule,文件的内容就是记录的子模块的那个仓库的最新commit id。

四、操作演示

4.1 分区与对象

// 实时观察.git目录的变化
$ watch -n .5 tree .git

// .git目录
.git
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD     <<< 指向当前commit
├── index    <<< 暂存区文件
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── 96
│   │   └── 6737211d560207bc6d7e01be267707adc22ca6
│   ├── bc
│   │   └── 9a8c7d02d20e99c0481003176a906d4c6e0cf3
│   ├── c9
│   │   └── b2240e42509686a034104179629ad74f72d3f4
│   ├── cc
│   │   └── 147aadf9175a075ea6f2c455692d074b45c329
│   ├── de
│   │   └── a03e51887ee93dbe862d8c6a4e5f64ca586d60
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master   <<< 指向当前分支commit
    └── tags

objects

// 对象类型:blob
# git cat-file -t c9b224
blob
// 对象内容: 文件内容
# git cat-file -p c9b224
1 hello
2 git

// 对象类型:tree
# git cat-file -t cc147a
tree
// 对象内容: 文件内容
# git cat-file -p cc147a
100644 blob c9b2240e42509686a034104179629ad74f72d3f4    a.txt
100644 blob c9b2240e42509686a034104179629ad74f72d3f4    b.txt

// 对象类型:commit
# git cat-file -t dea03e
commit
// 对象内容: 文件内容
# git cat-file -p dea03e
tree cc147aadf9175a075ea6f2c455692d074b45c329
parent 966737211d560207bc6d7e01be267707adc22ca6
author Your Name <[email protected]> 1717471046 +0000
committer Your Name <[email protected]> 1717471046 +0000

add b.txt

index

# git ls-files --stage
100644 c9b2240e42509686a034104179629ad74f72d3f4 0       a.txt
100644 c9b2240e42509686a034104179629ad74f72d3f4 0       b.txt
// 当前分支
.git# cat HEAD
ref: refs/heads/master

# git cat-file -t HEAD
commit
root@ubuntu:/home/git# git cat-file -p HEAD
tree cc147aadf9175a075ea6f2c455692d074b45c329
parent 966737211d560207bc6d7e01be267707adc22ca6
author Your Name <[email protected]> 1717471046 +0000
committer Your Name <[email protected]> 1717471046 +0000

add b.txt

# git cat-file -t refs/heads/master
commit
# git cat-file -p refs/heads/master
tree cc147aadf9175a075ea6f2c455692d074b45c329
parent 966737211d560207bc6d7e01be267707adc22ca6
author Your Name <[email protected]> 1717471046 +0000
committer Your Name <[email protected]> 1717471046 +0000

add b.txt

logs

# cat .git/logs/HEAD
0000000000000000000000000000000000000000 966737211d560207bc6d7e01be267707adc22ca6 Your Name <[email protected]> 1717470724 +0000  commit (initial): add a.txt

966737211d560207bc6d7e01be267707adc22ca6 dea03e51887ee93dbe862d8c6a4e5f64ca586d60 Your Name <[email protected]> 1717471046 +0000  commit: add b.txt

4.2 远端仓库

git clone ssh://root@server/home/git

遇到的问题:push到远端失败

Writing objects: 100% (3/3), 308 bytes | 154.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0

remote: You can set the 'receive.denyCurrentBranch' configuration variable
remote: to 'ignore' or 'warn' in the remote repository to allow pushing into
remote: its current branch; however, this is not recommended unless you
remote: arranged to update its work tree to match what you pushed in some
remote: other way.

解决:
root@ubuntu:/home/git/.git# cat config
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[receive]                        <<< 增加策略
        denyCurrentBranch = ignore

4.3 子模块

git submodule add ssh://root@server/home/plugin

子模块

git/plugin# git log
commit cbb1dd676b4d269e6cf02a4eadb4946391adbfa1 (HEAD -> master, origin/master, origin/HEAD)
Author: Your Name <[email protected]>
Date:   Sat Jun 1 13:03:18 2024 +0000

    c.txt


git/plugin# cd ../
git# git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   .gitmodules
        new file:   plugin

git# git submodule
 cbb1dd676b4d269e6cf02a4eadb4946391adbfa1 plugin (heads/master)

git# cat .gitmodules
[submodule "plugin"]
        path = plugin
        url = ssh://root@server/home/plugin

参考


  1. Git原理入门解析 (qq.com) ↩︎

  2. Git版本控制管理, Jon Loeliger, Mattbew McCullougb ↩︎

  3. Git-内部原理 pro-git在线文档 ↩︎

  4. [万字串讲git版本控制底层原理及实战分享]( https://www.bilibili.com/video/BV1Gu4y1u7ut/, https://zhuanlan.zhihu.com/p/670878449) ↩︎

  5. .git目录 ↩︎

  6. 手撕Git,告别盲目记忆 ↩︎

  7. objects ↩︎

  8. 动画图解Git的10大命令 ↩︎

  9. git图解 ↩︎

标签:文件,git,cat,Git,版本,commit,分析模型,txt,底层
From: https://www.cnblogs.com/liqinglucky/p/18239066/git

相关文章

  • python>tqdm实现git进度条效果
    注意1:这里是在python3环境下使用的git,安装要使用pipinstallGitpython来安装在python环境下的git注意2:这个方法可适用于windows环境和Linux环境importgitimporttqdmrepo_url='https://gitee.com/alichinese/oebuild-bin.git'local_path='F:\\test\\oebuild-b......
  • 靶机练习:Gitroot
    信息收集扫描全端口以发现服务访问80端口,有hint尝试绑定域名到/etc/vuln,同时提示中存在用户名jen,可以尝试爆破ssh绑定后能访问站点了用wpscan能扫出wordpress的用户名接下来没爆破出密码尝试使用wfuzz扫描子域名wfuzz-c-uhttp://gitroot.vuln-H"HOST:FUZZ.gitroo......
  • digit 手写数据库笔记 (机械学习)
    参考书籍第三章内容digit手写数据库#最初的分类器#digits手写数字库importnumpyasnpimportmatplotlib.pyplotaspltfromsklearnimportdatasetsfromsklearnimporttree#性能评价相关的库fromsklearnimportmetrics#digits数据加载digits=......
  • 大语言模型的底层原理,ChatGPT,文心一言等人工智能体是如何产生的?本文将详细讲解
    文章目录基础介绍一、预训练1.数据准备质量过滤敏感内容过滤数据去重数据预处理实践质量过滤去重隐私过滤2.词元化BPE分词WordPiece分词Unigram分词3.数据调度总结参考文献基础介绍大语言模型是指在海量无标注文本数据上进行预训练得到的大型预训练语言模型,例......
  • 【产品经理修炼之道】- 产品的底层逻辑
    什么是产品?或许有些人对于这个司空见惯的概念,并没有一个很清晰的答案,或者不知道如何去回答。我们常常会去了解其中的深层逻辑,却忘了记住产品的基础与底层逻辑。作者分享了他关于产品底层逻辑的理解,希望对你有所启发。各位,产品到底是什么?饭店服务员的微笑属于产品吗?你健身房......
  • 5、Git之版本号
    5.1、概述每一次提交,Git都会生成相关的版本号;每个版本号由40位16进制的数字组成。这40位16进制的数字,是根据提交的内容,通过SHA-1算法计算出来的。版本号具体还分为两部分,前2位是目录名,后38位是文件名。5.2、文件操作5.2.1、初始化本地库如上图所示,刚创建的......
  • 【Git】Git 的基本操作 -- 详解
    一、创建Git本地仓库要提前说的是,仓库是进行版本控制的一个文件目录。我们要想对文件进行版本控制,就必须先创建一个仓库出来。创建⼀个Git本地仓库对应的命令为gitinit,注意命令要在文件目录下执行,例如:我们发现,当前目录下多了一个.git的隐藏文件,.git目录是Git来......
  • 讲清楚 Elasticsearch scroll 的底层原理
    Elasticsearch的Scroll主要用于高效地分批检索大量数据记录,适用于那些数据量过大而不能一次性通过标准搜索请求获取所有结果的场景。Scroll机制的工作原理类似于数据库中的游标(cursor),它允许用户发起一次搜索请求后,通过维护一个持续的上下文(context)来分批次获取所有匹配的文档,......
  • GitHub飙升!京东认证的“Python编程入门三剑客”究竟好在哪?
    Python凭借着简单易学、功能强大,已经跃居TIOB编程语言榜首,并且已经开始了它的霸榜之旅。如何选择一套适合自己的Python学习教程,是每个Python爱好者面临的首要问题。今天给小伙伴们带来的是图灵&京东认证的“Python编程入门三剑客”,先看《Python编程从入门到实践》,打好Python入......
  • GitHub狂揽6700 Star,Python进阶必备的案例、技巧与工程实践
    当下是Python急剧发展的时代,越来越多的人开始学习和使用Pyhon,而大家也遇到了各种问题。这份手册清晰、细致地介绍了Python代码应该遵循的编程风格,并解释了背后的原理和机制。入门Python语言相对简单,但写出优雅的代码并非易事。这份手册深入讲解了Python进阶知识的方方......