1、文件结构
1.1 config文件
该文件主要记录针对该项目的一些配置信息,例如是否以bare[远端仓库常用]方式初始化、remote的信息等,通过git remote add命令增加的远程分支的信息就保存在这里.
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。
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 就是版本库,工作区是除了版本库之外的其他内容.
- git add 指令时,就是将对应修改的文件添加到暂存区中.
- git commit指令,便会将暂存区中做出的修改提交到版本库.
- git reset HEAD 指令时,暂存区的内容会被版本库的内容覆盖.
- git rm --cached file时,直接从暂存区进行文件的删除,不会影响工作区的内容.
- git diff 查看工作区和暂存区的区别
- git diff --cached 查看暂存区和版本库之间的区别
- git diff HEAD 查看工作区和版本库之间的区别
2.2 git四种文件状态转换
首先了解一个文件: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的内容。
推荐一款配色插件: agnoster: https://github.com/agnoster/agnoster-zsh-theme