)
文章目录1. 创建git本地仓库2. 配置git3. 认识工作区、暂存区、版本库重要结论4. 对象库5. add和commit命令6. git log命令与commit id7. 查看.git目录8. 底层逻辑链路总结1. 创建git本地仓库想用 Git 管理文件版本第一步通常是先创建一个本地仓库在项目目录里执行 git initGit 就会把这个目录纳入版本控制之后用 git add 和 git commit 把文件变更记录进仓库就能查看历史、回滚版本、开分支了。后面的我们讲到再详谈第一步我们先来创建git本地仓库首先我们来创建一个项目目录比如我们就叫做git创建一个 Git 本地仓库对应的命令为 git init 注意命令要在项目目录下执行这样就创建好了我们发现当前目录下多了一个 .git 的隐藏文件这个.git 目录就是 Git 本地仓库的核心仓库本体/元数据目录。Git 通过它来保存版本历史、分支、配置和索引等信息从而实现对项目文件的版本控制。不要手动修改这个目录里面的文件不然改乱了就把 Git 仓库给破坏了。2. 配置git那创建完git本地仓库后首先要做的事情是设置你的 用户名称 和 e-mail 地址这是非常重要的。配置命令为git config --global user.name 你的用户名称git config --global user.email 你的邮箱地址其中 --global 是一个可选项。如果使用了该选项表示这台机器上所有的 Git 仓库都会使用这个配置。如果你希望在不同仓库中使用不同的 name 或 e-mail 可以不要 --global 选项但要注意的是执行命令时必须要在仓库里。是否配置好了呢我们可以使用git config -l命令它会列出当前生效的所有 Git 配置项删除对应的配置命令为git config [--global] --unset user.namegit config [--global] --unset user.email3. 认识工作区、暂存区、版本库那现在我们已经创建好了git本地仓库也进行了配置那下面是不是就可以写一些文件让它进行管理了比如我们可以先来创建一个readme文件如果大家之前用过Gitee/GitHub相信肯定见过readme文件README 文件是项目的说明文档通常放在仓库根目录用 Markdown 编写。它告诉别人这个项目是什么、怎么安装、怎么运行、如何配置和参与贡献。Gitee/GitHub平台会自动把 README 渲染到仓库首页所以它相当于项目的“门面”和“快速上手手册”。README 本身也受 Git 版本控制修改后同样需要提交。那问一个问题现在这个Readme文件是否可以被git管理了呢答案是还不行因为当前所在的这个git目录并不是本地的git仓库上面我们也提到.git 目录才是 Git 本地仓库的核心仓库本体/元数据目录也可以叫做版本库那需要把文件放到.git目录里面嘛也不是的并且我们上面强调了不要手动对.git目录进行任何修改这需要我们再来了解几个概念先来了解第二个概念叫做工作区.git所在的目录我们把它叫做工作区当然.git 目录本身保存版本控制数据不属于工作区即我们写代码或文件的地方所以当前Readme文件所在的这个git目录就是工作区也就是说我们写的代码或者其它各种文件要想后面能被git成功管理放在这里就是对的那想真正用git管理工作区里面的文件还需要怎么做呢来看这张图首先工作区我们已经了解了在这里我们的工作区就是git目录不是.git目录图中左侧为工作区右侧为版本库。Git 的版本库里存了很多东西我们先来了解暂存区。暂存区Staging Area英文通常叫 stage 或 index。它物理上一般存放在 .git 目录下的 index 文件中即 .git/index。因为叫 index所以我们在平时交流或阅读官方文档时也会把暂存区叫作索引Index。可以把暂存区想象成“购物车”或者“快递打包箱”你在工作区超市货架挑好了要买的商品修改的文件这里的“修改”包括新增修改删除不能直接带走得先放进购物车git add 到暂存区等你确认没问题了再去收银台结账执行提交操作 git commit 对应分支会做相应的更新可以简单理解为暂存区的目录树才会被真正写到版本库中。这些命令我们后面还会介绍和演示。为什么要有暂存区很多新手会问为什么改了文件不直接 commit非要先 add 一下暂存区的存在是 Git 区别于其他版本控制工具如 SVN的一个重要优势它有三大作用先简单了解有些概念需要在后续的学习中才能深入理解。继续看上面那张图再往右边我们还看到什么head和master又是什么masterGit 为你自动创建的第一个分支也是默认分支。关于分支我们后面会详细介绍)HEAD图中的指针。它指向当前所在的版本或分支图中指向了 master 分支。它告诉 Git 你当前处于哪个分支或提交节点。重要结论虽然上面还有些概念我们不能很好的理解但至少目前可以得到一个结论新建或粘贴进项目目录的文件并不能称之为向仓库中新增文件而只是在工作区新增了文件。必须要通过使用 git add 和 git commit 命令才能将文件添加到git仓库中进行管理4. 对象库objects 为 Git 的对象库当执行 git add 命令时暂存区的目录树被更新同时工作区修改的文件被写入到对象库中的一个新的对象中就位于 “.git/objects” 目录下。图中的 stage暂存区和 master分支它们里面画的文件夹和文件其实并不是真正的文件实体而是对象的索引/指针。真正的内容去哪了全部被压缩存放在底部的 objects对象库 中。5. add和commit命令上面我们得出结论新建或粘贴进项目目录的文件并不能称之为向仓库中新增文件而只是在工作区新增了文件。必须要通过使用 git add 和 git commit 命令才能将文件添加到git仓库中进行管理那下面我们就使用我们一开始创建的那个readme文件进行一个演示来熟悉一下这两个命令首先可以给Readme文件里面随便写点内容第一步addgit add 文件名可以跟多个文件空格隔开也可以是目录名将文件添加到暂存区staging area当然直接git add .就可以把当前目录中所有没添加到暂存区里面的修改文件添加进去那么此时readme这个文件就已经从工作区被放到暂存区里面了。第二步commitgit commit -m 提交的内容信息或修改信息不要随便写注意 git commit 后面的 -m 选项要跟上描述本次提交的 message由用户自己完成这部分内容绝对不能省略并要好好描述是用来记录你的提交细节是给我们人看的。git commit会将暂存区的文件更改提交到本地版本库。成功执行并且我们看到git给我们打印了对应的日志信息一个文件被改变增加了一行内容我们还可以多次 add 不同的文件而只 commit 一次便可以提交所有文件是因为需要提交的文件是通通被 add 到暂存区中然后一次性 commit 暂存区的所有修改。6. git log命令与commit id截至目前为止我们已经能够将文件直接提交至本地仓库了。我们可以使用 git log 命令来查看下历史提交记录日志信息我们看到就列出来我们历史提交的所有信息因为目前这个仓库只提交了一次然后commit 后面有一长串内容这个是这次提交对应的commit id。这是一串由 40 个十六进制字符组成的字符串。它是 Git 根据你提交的内容、作者信息、提交时间、提交说明以及父节点等信息通过密码学算法SHA-1计算出来的。这个 ID 有什么用在后续的学习中这个 ID 会非常有用。比如如果嫌git log输出信息太多看得眼花缭乱的可以试试加上 --prettyoneline 参数7. 查看.git目录我们查看.git目录的目录结构其实上面图中的部分都可以找到然后我们说master是Git 为你自动创建的第一个分支也是默认分支。那它里面存的是啥呢能够看到master里面存的其实是最近一次提交的commit id这意味着Git 里的分支极其轻量。它本质上就是一个包含提交 ID 的文本文件。每次你产生新的提交commitGit 就会把新的 ID 覆盖写入到这个文件里分支指针也就自然而然地“向前移动”了一步。HEAD 则是一个指向当前分支的指针它里面存着 ref: refs/heads/master。所以链条是HEAD 指向 mastermaster 指向最新的 Commit ID。前面我们还提到图中的 stage暂存区和 master分支它们里面画的文件夹和文件其实并不是真正的文件实体而是对象的索引/指针。真正的内容去哪了全部被压缩存放在底部的 objects对象库 中。现在我们已经看到了master分支里面的内容是一个commit id所以commit id 就是commit对象的索引那如何通过commit id这个索引找到其对应的提交对象呢查找 object 时要将 commit id 分成2部分其前2位是文件夹名称后38位是文件名称。那这里所说的这个Commit 对象提交对象又是什么呢找到这个文件之后一般不能直接看到里面是什么该类文件是经过 sha 安全哈希算法加密过的文件我们可以使用 git cat-file 命令来查看版本库提交对象的内容如果你想查看这个 Commit 对象的真实内容包括作者、时间、对应哪棵树等请使用 -ppretty-print参数git cat-file -p 40位哈希值如果你想看这个对象到底是个啥类型是 commit 还是 tree 还是 blob用 -t 参数之前我们只提交了一次如果多提交几次然后再查看分支里面存的最后一次提交对应的commit 对象的内容那这些内容分别是什么意思第一行tree 7724b3e84d6476cc2e31f3a5603bfc7c6a02038b它说明当前这个 Commit 包含一棵目录树Tree 对象记录本次提交后的目录结构同样也存在objects中。这个 Tree 的哈希值是7724…那tree对象又是什么呢我们也可以来看一下它的内容可以看到这里面能找到我们之前提交的所有文件以及它们对应的文件内容对象索引objects中也能找到对应的对象这里又出来一个文件内容对象是什么呢我们也可以来看一下这里面就包含了文件的内容。如果修改了这里面的内容也会随之改变前面说暂存区存的也是对象的索引其实就是文件内容对象的索引第二行parent 80a6a4d8dd4b10f7c8b9cb6db5ae839e5aeb7b42这是父节点指针。它指向了你上一次的提交为什么第一次提交没有这行 因为第一次提交是“始祖”它没有前任。而从第二次提交开始每次 Commit 都会用 parent 记录上一次是谁这就形成了一条单向链表也就是 Git 的提交历史线后三行8. 底层逻辑链路总结现在我们已经在 Git 的底层建立了一个完美的时间线闭环第一步git add 时发生了什么当我们执行 git add 时Git 会把工作区里的文件内容压缩打包生成一个 Blob 对象 存放在 objects 库中。同时暂存区index更新记录下文件的名称、权限以及指向这个 Blob 内容对象的索引哈希值。第二步git commit 时又发生了什么当我们敲下 git commitGit 会基于暂存区的结构生成一棵 Tree 对象目录树并生成一个 Commit 对象这些对象都存放进objects库中。对应分支中存储最后一次提交的commit对象的commit id索引。完整的底层链路你敲 git commit生成了 Commit 对象358f44a…。Commit 对象里存了一个指向 Tree 对象 的指针7724b3e…。Tree 对象就像一张清单列出了当时项目里有 Readme 和 test.txt。顺着清单里的哈希值Git 就能在 objects 库中精准找到这两个 Blob 对象也就是真正的代码文本内容。最后master 分支的指针指向最新的 Commit IDHEAD 指向 master。所以依靠这套完美的底层数据结构Git 就可以轻松实现版本的追踪与回溯并找到不同版本对应的内容对象如何追踪因为每个 Commit 对象都记录了 parent父节点指针这就形成了一条单向链表。顺着这条链表Git 可以毫无遗漏地追溯从第一次提交到现在的每一次修改。如何回溯因为所有的历史快照Commit、Tree、Blob都完好无损地存放在 .git/objects 对象库里。当我们需要回退到某个版本时Git 根本不需要去修改或覆盖旧的代码它只需要把 master 分支的指针重新指向历史中的某一个 Commit ID 即可。如何找到内容一旦指针指向了那个过去的 CommitGit 就能顺藤摸瓜通过 Commit 找到 Tree再通过 Tree 找到一个个具体的 Blob瞬间把当时的“文件目录结构”和“代码文本内容”完完整整地还原到你的工作区。