首页 > 代码库 > Git 对象 和checkout 和stash的笔记

Git 对象 和checkout 和stash的笔记

 

6.1 Git 对象库探秘

通过查看日志的详尽输出 ,我们会惊讶的看到 许多魔幻的数字,这些魔幻数字 其实 就是SHA1 哈希值 

技术分享

 一个提交包括 三个哈希值表示的对象ID:

commit  e696bbb2f834d1c49efb0272315cbfb717c89207 :表示这是本次提交的唯一标识。

tree    56c886493b939f2d2b3500aab92ef372f824db0a:表示本次提交所对应的目录树 

parent   59e62a3c485508b3381f80144998df7bb2a5f9fb  & cccc1a1f787bb05b75ee9eed3f05575afcdddbcb :本次提交的父提交 //有两个父提交 后面会提到的 

研究 git  对象 的一个重量级武器 git cat-file 命令 。用下面的命令 可以查看下面三个ID的类型

$ git  cat-file   -t  e696bbbcommit$ git  cat-file   -t   56c8tree$ git cat-file    -t   59e6commit 

 

在应用 ID 的时候 ,没有必要把整个的40位的ID写全,只要从头开始的几位不冲突即可。下面再用 git cat-file 命令查看一下 这几个对象的内容 。

技术分享

在上面的目录树(tree)对象中看到一个新类型对象:blob对象,这个对象保存着 txt 的内容。

这些对象保存在GIT 库中的objects 目录下了(ID 前两位作为目录名,后38位 作为文件名),通过提交对象之间的相互关联,可以很容易地识别出一条跟踪链  ,下面这条语句显示每个提交对对象的parent属性。

git log --pretty=raw --graph 56c8864 

HEAD 和 MASTER 的奥秘

//执行下面的命令会看到工作区和暂存区中没有改动 $ git status -s -b ##master //上面的-s 参数以显示精简的输出外,还使用-b参数以便能够同时显示出当前工作分支的名称的名称。//下面的git branch 是分支管理的主要命令 ,也可以显示当前的工作分支$ git branch * master //* 表示 这个分支 是当前的分支 //现在连执行下面的三个命令会看到相同的输出$ git log -1 HEAD $ git log -1 master$ git log -1 refs/heads/master

 

 也就是说在当前版本库中,HEAD ,master,refs/heads/master 具有相同的指向 

$ find .git -name HEAD  -o -name master.git/HEAD.git/logs/HEAD.git/logs/refs/heads/master.git/refs/heads/master//显示一下.git /HEAD的内容$ cat .git/HEADref: refs/heads/master//把HEAD内容翻译过来就是:指向一个引用 :refs/heads/master 这个引用在文件 .git/refs/heads/master ,//看看文件.git/refs/heads/master的内容$ cat .git/refs/heads/master e642974239472492742934...//上面所显示的哈希值使用git cat-file 查看$ git cat-file -t e6429commit //显示该提交内容:$ git cat-file -p ..........哈希值内容显示//所以说分支master 指向的是一个提交ID(最想提交)。

 

 这样的分支多么的美丽和奇妙,既然可以从任何提交开始建立一条历史跟踪链,用一个文件指向这个链条的最新提交,那么这个文件就可以追踪整个提交历史。这个文件就是.git/refs/heads/master

目录.git/refs是保存引用的命名空间,其中.git/refs/heads目录下的引用又称为分支 ,git有一个底层命令git rev-parse 可以用于显示引用对应的提交ID 

$ git rev-parse masterSHA1 $ git rev-parse refs/heads/masterSHA1 $ git rev-parse HEADSHA1 //可以看出他们都指向同一个对象 

 

 Git  重置

//使用以下命令可以显示更短的ID$ git log --graph  --oneline 

 

分支游标master探秘

首先在工作区创建一个新文件,叫做new--commit.txt,然后提交到版本库中

$ touch new--commit.txt //添加 到工作区$ git  add new--commit.txt //添加 到暂存区 $ git  commit -m "this is commit  repository" //提交到版本库 

 

 此时工作目录下有两个文件 其中一个文件 就是new--commit.txt

$ ls 可以查询//查询master 的游标是否改变了 $ cat .git/refs/heads/master //在用git log 查看一下提交日志,可以看看刚才完成的提交 $ git log --graph --oneline 

 

应用refs/heads/master 就好像是一个游标,在有新的提交发生的时候指向了新的提交。游标可上可下,Git 提供了git reset 命令 ,可以将 "游标”指向任意一个存在的id

//注意下面的--hard 参数,会破环工作区未提交的改动  慎用 HEAD^代表HEAD的父提交 ,这条命令语句相当于将master重置到上一个老提交$ git reset --hard HEAD^//这个时候master分支的引用文件的指向更改为前一次的提交的ID ,$ cat .git/refs/heads/master //使用下面命令可以看出上一次的提交的文件 不存在了$ ls//重置命令不仅可以重置到前一次的提交,而且还可以直接使用提交ID重置到任何一次提交。//通过git log 查询最早的提交ID$ git log --graph --preety=oneline //然后重置到最早的一次提交$ git reset --hard 9e8a761//重置后 会发现文件 会返回到最原始的状态 $ cat welcome.txt 

 

 使用重置命令很危险,会彻底丢弃历史,那么,还能够通过浏览器提交历史的办法找到丢弃的提交ID,在使用重置命令恢复历史吗,不可能!因为重置让提交历史也改变了,提交日志可查看

$ git log

 

救世主:使用reflog挽救错误的重置

遇到错误的重置 也不要害怕 Git提供了一个挽救机制,通过.git/logs目录下日志文件记录了分支的变更。默认非裸版本库,(带有工作区)都提供分支日志功能,这是因为工作区的版本库都有如下设置。

$ git config core.logallrefupdatestreu

 

查看一下master分支的日志文件,.git/logs/refs/heads/master

$ tail -5 .git/logs/refs/heads/master

 

可以看出这个文件记录了master分支指向的变迁,最新的改变追加到文件的末尾,因为此最后出现,最后一行可以看出指向的id改变了 

技术分享

通过以上历史 就可以找回重置的命令 

git reset 是git中常用的命令之一 也是最危险最容易误用的命令。
用法一:git reset [-q] [<commit>] [--] <paths>
用法二:git reset [--soft |--mixed |--hard |--merge | --keep|] [-q] [<commit>]
以上两个用法 其中[<commit>] 都是可选项 ,可以 使用提交ID或者引用 如果省略<commit> 则相当于使用HEAD的指向作为提价ID。

Git 检出

HEAD 的重置即检出

HEAD 可以理解我头指针,是当前工作区的基础版本,当执行提交时,HEAD指向的提交作为新提交的父提交,看看当前的HEAD的指向。

//执行git branch 会看到但前处于master的分支$ git branch -v* master 59e62a3 does master follow this new commit//使用git checkout 命令检出该ID的父提交git checkout 59e62a3^ Note: checking out 59e62a3^.You are in detached HEAD state. You can look around, make experimentalchanges and commit them, and you can discard any commits you make in thisstate without impacting any branches by performing another checkout.If you want to create a new branch to retain commits you create, you maydo so (now or later) by using -b with the checkout command again. Example:  git checkout -b <new-branch-name>HEAD is now at 734cdc9... who does commit?//上面这一串代码就是git又在提示我们 现在HEAD处于分离头指针的状态 //什么叫头指针的状态 就是 HEAD具体指向了一个具体的提价ID,而不是个引用 //查看最新的提交reflog 也可以看出HEAD 指向一个具体的id$ git reflog -1734cdc9 HEAD@{0}: checkout: moving from master to 59e62a3^//注意上面的reflog 是HEAD头指针的变迁记录 而非master分支,查看一下HEAD 和master对应的提交ID ,发现他们的指向不一样。$ git rev-parse HEAD master734cdc93323e4d3fe1ea529e4a40378d3429d8c959e62a3c485508b3381f80144998df7bb2a5f9fb//如果 在分离头指针的模式修改了操作 ,可以使用合并的方式来挽救git merge "填写修改的ID"//此时查看日志会发现有两个父提交,这就是合并的奥秘$ git log --graph --pretty=oneline*   e696bbb2f834d1c49efb0272315cbfb717c89207 Merge commit cccc1a|| * cccc1a1f787bb05b75ee9eed3f05575afcdddbcb commit in detached HEAD mode* | 59e62a3c485508b3381f80144998df7bb2a5f9fb does master follow this new commit|/* 734cdc93323e4d3fe1ea529e4a40378d3429d8c9 who does commit?* 45cb1b380d3579f96e124d2b518df6e62eb05897 initialized. 

 

挽救分离头指针

深入了解git checkout命令
git checkout 这条命令也是一个危险的命令 ,因为这条命令会会重写工作区。
用法一:git checkout [-q] [<commit>] [--] <paths>...
用法二: git checkout [<branch>]
用法三:git checkout [-m] [[-b |--orphan] <new_branch> [<start_point>]

上面所用方法的区在于:
第一种用法 和第二种用法的区别在于,第一种用法的名命令中包含路径paths

 

Git 对象 和checkout 和stash的笔记