Git基本原理

1、文件结构


Git基本原理_第1张图片


1.1  config文件

  该文件主要记录针对该项目的一些配置信息,例如是否以bare[远端仓库常用]方式初始化、remote的信息等,通过git remote add命令增加的远程分支的信息就保存在这里.   

Git基本原理_第2张图片

1.2  objects文件夹.   

该文件夹主要包含git对象。Git中的文件和一些操作都会以git对象来保存,git对象分为BLOB、tree和commit三种类型,例如git commit便是git中的commit对象,而各个版本之间是通过版本树来组织的,比如当前的HEAD会指向某个commit对象,而该commit对象又会指向几个BLOB对象或者tree对象。objects文件夹中会包含很多的子文件夹,除此以外,Git为了节省存储对象所占用的磁盘空间,会定期对Git对象进行压缩和打包,其中pack文件夹用于存储打包压缩的对象,而info文件夹用于从打包的文件中查找git对象;

1.3  HEAD文件. 

HEAD是一个引用,引用的是当前的分支,该文件指明了git branch(即当前分支)的结果,比如当前分支是master,则该文件就会指向master,但是并不是存储一个master字符串,而是分支在refs中的表示,例如ref: refs/heads/master。


Git基本原理_第3张图片


1.4  index文件.   

  该文件保存了暂存区域的信息。该文件某种程度就是缓冲区(staging area),内容包括它指向的文件的时间戳、文件名、sha1值等;

1.5  Refs文件夹 

  heads文件夹存储本地每一个分支最近一次commit对象的sha-1值,每个分支一个文件; 

  remotes文件夹则记录你最后一次和每一个远程仓库的通信,Git会把你最后一次推送到这个remote的每个分支的值都记录在这个文件夹中; 

  tag文件夹则是分支的别名,用处不大; 

  1.6. Log文件夹 

  logs则记录了本地仓库和远程仓库的每一个分支的提交记录,即所有的commit对象(包括时间、作者等信息)都会被记录在这个文件夹中,因此这个文件夹中的内容是我们查看最频繁的。


---   


  四个重要的文件或目录:HEAD及 index文件, objects及 refs 目录。这些是 Git 的核心部分。objects 目录存储所有数据内容,refs 目录存储指向数据 (分支) 的提交对象的指针,HEAD 文件指向当前分支,index 文件保存了暂存区域信息

2、工作区和状态

  2.1 工作区和暂存区   


Git基本原理_第4张图片



git 就是版本库,工作区是除了版本库之外的其他内容.


- git add 指令时,就是将对应修改的文件添加到暂存区中. 


- git commit指令,便会将暂存区中做出的修改提交到版本库. 


- git reset HEAD 指令时,暂存区的内容会被版本库的内容覆盖. 


- git rm --cached file时,直接从暂存区进行文件的删除,不会影响工作区的内容. 



- git diff 查看工作区和暂存区的区别

- git diff --cached 查看暂存区和版本库之间的区别

- git diff HEAD 查看工作区和版本库之间的区别


  2.2 git四种文件状态转换 


Git基本原理_第5张图片


首先了解一个文件:index 索引档案

当我们在工作区中进行了任何的操作,git都会向这个文件输入信息,当我们使用git status进行查询,就会用index来和版本库的最新状态进行比较,以此确定文件是什么状态。


3、git实现原理

  3.1  原理


  Git是一套内容寻址(content-addressable)文件系统,那么Git是怎么进行寻址呢?其实,寻址无非就是查找,而Git采用HashTable的方式进行查找,也就是说,Git只是通过简单的存储键值对(key-value pair)的方式来实现内容寻址的,而key就是文件的哈希值,value就是经过压缩后的文件内容。 


  3.2  git对象 


  Git对象的类型包括:BLOB、tree对象、commit对象。 

  BLOB对象可以存储几乎所有的文件类型; 

  tree对象是用来组织BLOB对象的一种数据类型,你完全可以把它想象成二叉树中的树节点,只不过Git中的树不是二叉树,而是"多叉树"; 

  commit对象表示每一次的提交操作,由tree对象衍生,每一个commit对象表示一次提交,在创建的过程中可以指定该commit对象的父节点,这样所有的commit操作便可以连接在一起,而这些commit对象便组成了提交树,branch只不过是这个树中的某一个子树罢了。


  3.3 演示git init过程.

  3.3.1

  新建空白仓库,查看.git中的objects文件夹,只有info和pack两个文件夹.

  echo a > a.txt 创建文件.

  git add .  提交到stage,再看objects文件夹.

echo b > a.txt 修改a.txt中内容,发现objects文件中多了一个组件。 

【每次把文件设置为staged状态的时候,就会在objects文件夹中创建一个blob组件,Git中的每个组件都是以hash的二进制方式来进行存储的【文件夹名称+文件名称 2 + 38】】hash码就是组件的唯一表示。 

【blob组件并不会对文件信息进行存储,而是对文件内容进行记录。  】

echo b > b.txt 新建一个和a内容一样的文件,发现objects文件并无变化

git已经发现这个blob已经存在了,就不会在增加新的组件。

【blob组件是在代码提交到Stage区域的时候生成的, 而且是以内容来生成一个字节码文件】

git hash-object a.txt

这个hash码就是blob组件的名称。

git commit -m "init"

发现新增了两个子文件,这个就是commit组件。hash就是组件的id。

git cat-file -p ****** 

cat-file可以获取组件的信息,可以看到tree组件,依然是hash作为名称。

之后再cat-file一下tree组件,可以看到最开始提交的两个blob组件已经被记录在了tree中

流程: 

当我们添加或者修改了文件,并且add到stage area之后,首先会根据文件创建不同的blob。 

当我们进行commit之后,会马上创建一个tree组件把需要的blob组件添加进去,之后再封装到一个commit组件中完成本次提交。 

在将来进行reset的时候可以直接使用git reset --hard ******可以恢复到某个特定的版本,在reset之后,git会根据commit组件的id快速找到tree组件,然后根据tree组件找到blob组件,之后对仓库进行还原,整个过程都是以hash和二进制的方式进行操作,效率非常之高。

4、git补充

4.1、gitignore文件无法忽略某些文件.   

在管理一个版本库时,有时候不想要管理某些文件,这个时候我就把这个问件写到.gitignore文件中,这样应该就可以将这个文件忽略,不再进行版本管理了,但是经常出现的情况是:将这些文件名写到其中了,使用git status查看发现这些文件并没有被忽略掉。 

想要.gitignore起作用,必须要在这些文件不在暂存区中才可以,.gitignore文件只是忽略没有被staged(cached)文件,对于已经被staged文件,加入ignore文件时一定要先从staged移除,才可以忽略。

如果想要添加

git add -f App.class

4.2、开发途中,解决bug 

git stash  储存当前工作现场

  git checkout master.

git checkout -b feature_bug_1.

git add / commit.

git checkout master.

git merge --no-ff -m "fix bug".

git checkout dev.

git status.

git stash list.

git stash apply 恢复 git stash drop 丢弃。

git stash pop [等同于以上两步]。


多次stash后,使用git stash apply stash@{0}

4.3、git Rebase 

  rebase操作可以把本地未push的分叉提交历史整理成直线;

  rebase的目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比。


  ![avatar](/Users/qitmac000584/Desktop/Git分享/rebase.jpg

)

4.4、git blame.


git blame [filename].


4.5、git cherry-pick commitid.

git cherry-pick可以理解为”挑拣”提交,它会获取某一个分支的单笔提交,并作为一个新的提交引入到你当前分支上。 当我们需要在本地合入其他分支的提交时,如果我们不想对整个分支进行合并,而是只想将某一次提交合入到本地当前分支上,那么就要使用git cherry-pick了 

4.6、git clean.

git clean -n

是一次clean的演习, 告诉你哪些文件会被删除. 记住他不会真正的删除文件, 只是一个提醒

git clean -f

删除当前目录下所有没有track过的文件. 他不会删除.gitignore文件里面指定的文件夹和文件, 不管这些文件有没有被track过

git clean -f path

删除指定路径下的没有被track过的文件

git reset --hard和git clean -f结合使用他们能让你的工作目录完全回退到最近一次commit的时候.

4.7、git grep.

检索指定关键字: git grep www.

检索关键字出现在哪一行: git grep -n www.

检索指定关键字出现在哪些文件中: git grep --name-only www.

在指定commit中检索:git grep com 3a4d5c

4.8、本地代码回滚

【本地代码库回滚】: 

git reset --hard commit-id :回滚到commit-id,将commit-id之后提交的commit都去除.

git reset --hard HEAD~3:将最近3次的提交回滚

4.9、远程代码回滚

【远程代码库回滚】:

这个是重点要说的内容,过程比本地回滚要复杂 

原理:先将本地分支退回到某个commit,删除远程分支,再重新push本地分支 

操作步骤: 

1、git checkout the_branch 

2、git branch the_branchbackup.  //. 备份一下这个分支当前的情况 

3、git reset --hard commitid.  //. 把the_branch本地回滚到the_commit_id 

4、git push origin : the_branch  //. 删除远程 the_branch 

5、git push origin the_branch  //. 用回滚后的本地分支重新建立远程分支 

6、git push origin :the_branchbackup  //. 如果前面都成功了,删除这个备份分支 

4.10、git revert 和 git reset的区别 

1. git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit。 

2. 在回滚这一操作上看,效果差不多。但是在日后继续merge以前的老版本时有区别。因为git revert是用一次逆向的commit“中和”之前的提交,因此日后合并老的branch时,导致这部分改变不会再次出现,但是git reset是之间把某些commit在某个branch上删除,因而和老的branch再次merge时,这些被回滚的commit应该还会被引入。 

3. git reset 是把HEAD向后移动了一下,而git revert是HEAD继续前进,只是新的commit的内容和要revert的内容正好相反,能够抵消要被revert的内容。



Git基本原理_第6张图片
基本命令


推荐一款配色插件: agnoster: https://github.com/agnoster/agnoster-zsh-theme

你可能感兴趣的:(Git基本原理)