这才是真正的 git

1. Git是怎么存储信息的

前置知识: 相关Linux命令介绍

  • echo : 输出文本
  • echo 'text' > filename : 将字符串输出重定向,当前目录没有对应文件,则创建,并将字符串输出到文件中
  • tree : 主要功能是创建文件列表,将所有文件以树的形式列出来
  • cat filename: 一次性显示整个文件
$ git init
$ echo '111' > a.txt
$ echo '222' > b.txt
$ git add *.txt

以上命令行的意思是:

  • 初始化一个git项目
  • 将 a.txt 中的内容重写为 ‘111’, 如果没有 a.txt, 新建
  • 同上
  • 将所有的 .txt 文件添加到暂存区
$ tree .git/objects
.git/objects
├── 58
│   └── c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c
├── c2
│   └── 00906efd24ec5e783bee7f23b5d7c941b0c12c
├── info
└── pack
$ cat .git/objects/58/c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c
xKOR0a044K%

这意味着当我们把文件添加到暂存区的时候,实际上每个文件都在 .git/objects 下生成了一个 blob 对象

这里我们可以借助 git 一个很高级的命令 git cat-file [-t] [-p] 来验证一下

git cat-file Provide content or type and size information for repository objects.
git cat-file 命令显示版本库对象的内容、类型及大小信息。
-t
Instead of the content, show the object type identified by object.
显示对象的类型。
-p
Pretty-print the contents of object based on its type.
根据对象的类型,以优雅的方式显式对象内容。

$ git cat-file -t 58c9
blob
$ git cat-file -p 58c9
111

在这里插入图片描述
在这里插入图片描述
那么也就是在 git object 中生成了两个键为文件 hash值, 值为对应文件内容的键值对的 blob 对象

$ git commit -m '[+] init'
$ tree .git/objects
.git/objects
├── 0c
│   └── 96bfc59d0f02317d002ebbf8318f46c7e47ab2
├── 4c
│   └── aaa1a9ae0b274fba9e3675f9ef071616e5b209
...
$ git cat-file -t 4caaa1
tree
$ git cat-file -p 4caaa1
100644 blob 58c9bdf9d017fcd178dc8c0...	a.txt
100644 blob c200906efd24ec5e783bee7...	b.txt

当我们将暂存区的代码提交到本地仓库的时候, git 在 objects 下生成了两个新的对象, 其中一个是 tree 类型的对象, 对象中包含着此次提交的版本号, 文件名等快照, 相当于一个映射

在这里插入图片描述

$ git cat-file -t 0c96bf
commit
$ git cat-file -p 0c96bf
tree 4caaa1a9ae0b274fba9e3675f9ef071616e5b209
author lzane 李泽帆  1573302343 +0800
committer lzane 李泽帆  1573302343 +0800
[+] init

另一个对象是 commit 对象, 里面包含了 提交的版本号,作者,提交者的信息映射
在这里插入图片描述

那分支和 tag 在哪里呢?

$ cat .git/HEAD
ref: refs/heads/master

$ cat .git/refs/heads/master
0c96bfc59d0f02317d002ebbf8318f46c7e47ab2

HEAD, 分支,普通的 Tag 可以简单的理解成是一个指针, 指向对应 commits 的 hash 值
在这里插入图片描述

在这里插入图片描述

Q1: 为什么要把文件的权限和文件名储存在 Tree object 里面而不是 Blob object 呢?

如果我们的这个文件有成千上万行代码, 当我们更改文件名的时候, git 只需要生成一个新的 提交映射就 ok, 而如果是在 blob object 对象中储存, 就要生成一个新的 blob对象 , 体积庞大.

2. Git 的三个分区及变更历史的形成

在这里插入图片描述

  • 工作目录 ( working directory ):操作系统上的文件,所有代码开发编辑都在这上面完成。
  • 索引( index or staging area ):一个暂存区域,会在下一次commit被提交到Git仓库。
  • Git仓库( git repository ):由Git object记录着每一次提交的快照,以及链式结构记录的提交变更历史
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 当我们修改 a.txt 文件并 add 之后
  • git 会将新的 a.txt 添加到 git 仓库生成一个 blob 节点
  • 并更新当前的索引
  • 执行 commit 之后
  • 将当前索引生成一个 tree 节点
  • 新建一个 commit 节点
  • 更新分支指向

在这里插入图片描述

思考一下git的各种命令,尝试在上图将它们“可视化”出来

Q2:每次commit,Git储存的是全新的文件快照还是储存文件的变更部分?

是全新的, 复杂度上来说更友好

Q3:Git怎么保证历史记录不可篡改?

hash值, 文件一旦发生变化对应的hash值就会改变

3. Git 实用技巧

  1. 误操作导致分支不见了,如何恢复?不小心删了分支
    git reset --hard: 回退到指定版本
    git rebase: 把本地未push的分叉提交历史整理成直线
    当我们本地有多次commit,想要推送到远端而远端有新的提交的时候,我们拉取了新的提交之后就会有本地的提交历史就会分叉,可以使用 git rebase 命令将提交历史整理成一条直线
    git reflog: 查看命令历史,以便确定要回到未来的哪个版本
  2. 如何获得一个干净的工作空间?
    git stash push [-u | --include-untracked]: 将工作区和暂存区的文件贮藏起来,比如你改到一半的bug

默认情况下, git stash 会缓存下列文件:

  1. 添加到暂存区的修改;
  2. Git跟踪的但并未添加到暂存区的修改

但不会缓存以下文件:

  1. 在工作目录中新的文件;
  2. 被忽略的文件

使用 -u 或者 -- include-untracked 参数可以贮藏未跟踪或忽略的文件
使用 -a 或者 --all 参数可以贮藏当前目录下的所有修改

  1. 从Git历史中删除一个文件
    git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
  2. 其他
    git commit --amend: 重新操作上一次的提交说明,在日志是没有操作记录的
    git rebase -i origin/master: 交互式变基
    git show-branch
    git blame [filename]: 追溯一个指定文件的历史修改记录
    git bisect

FCC 这才是真正的 git -李泽帆

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值