本地仓库结构
git项目有3个主要的部分组成:工作区(working directory / working tree),暂存区(staging area),版本库(git directory / repository)
git directory / repository:git版本库,其实就是git用于存储自己的元数据,以及文档数据库的地方,默认就是在项目的.git隐藏目录中
working directory / working tree:工作区,保存的是一个项目当前的一个版本对应的所有文件,这些文件是从git版本库中的压缩后的数据库中提取出来,然后放到我们的磁盘上去。
staging area:暂存区,就是一个文件,包含在git版本库中,主要是保存了下一次要提交到的那些文件信息。在git中,对暂存区有另外一个名称,叫做index,也就是索引。
上面三个区域的协作关系大致如下:
(1)首先会在工作区修改某个版本的文件
(2)将某些修改后的文件放入git暂存区中,准备下一次提交到git版本库中去
(3)执行一个提交操作,将暂存区中的文件保作为一个快照保存到git版本库中去
如果一个文件,已经有一个版本被保存到了版本库,那么就是committed状态;如果这个文件被修改了,同时被加入了暂存区,那么就是staged状态;如果这个文件修改了,还没有加入暂存区,那么就是modified状态。
工作区,working directory
所谓的工作区,指的就是当前你的git管理的项目,在本地的那个目录,也就是你能直接看到,编辑的那个目录,这就是工作区。
未提交到暂存区文件为红色. 提交到暂存区为绿色,
git的重要机制
Git有很多特有的机制,都是跟普通的版本控制系统不一样的
(1)快照机制
普通的版本控制系统,比如说CVS,SVN等,是通过一开始提交一个原始文件,然后后面每次对文件进行修改之后再次提交,都维护这次提交对应的一个差异,通过维护每个版本的差异,就可以通过应用差异,或者回退差异,来前进或者后退文件的版本。
Git用的不是这种维护每次提交的差异,而是用的快照。每次提交文件,都是保存一份这个文件当前这个状态的一个完整快照,同时对这次提交维护一个指针,指向这个文件快照。
(2)本地化操作
大多数的git版本控制操作,只要在本地执行即可,所有的版本文件都在本地,因此操作是非常快速的。相比较于那些依赖网络的集中式版本控制系统来说,他们的大多数操作要依赖网络,速度是很慢的。
比如说通过git查看提交历史,比较历史文件的差异,都可以在本地完成,不需要通过服务器做任何事情。
如果我们在飞机或者或者上,没有网;或者在家里,没有vpn。都没有问题,随便做开发、写代码,提交代码,在本地就可以了,等有网络的时候,再把提交的版本推送到远程服务器上去。但是SVN之类的,就不可以提交了,因为没有网,没法连接到服务器。
(3)完整性保证
git在存储任何文件之前,都会对其执行一个校验和,然后用校验和指向那个文件。这是git内核保证的,这样我们是不可以手工修改git版本库中的任何文件的,因为修改了文件之后,会导致计算出来的校验和与之前保存的校验和不匹配,文件会破损。
git用的是SHA-1 hash算法来计算校验和,这是一个40位的字符串,基于文件的内容计算出来的,看起来大概是这样的:
24b9da6552252987aa493b52f8696cd6d3b00373
如果手动破坏.git中存储的文件的内容,git会不承认,因为内容变化之后,会导致内容计算出来的SHA-1 40位的hash值变化,跟之前存储的hash值不同,就认为文件破损
(4)仅仅添加数据
git通常来说,仅仅会在自己的数据库中添加数据,因此提交文件到git之后,很少会丢失,而且如果我们定期提交文件到远程服务器,就更少丢失。
git status查看代码文件的状态
代码文件分成两种,一种是tracked,一种是untracked。tracked文件就是已经提交到git版本库中的文件,后面可以处于modified或者staged状态;untracked文件,就是从来没有提交到git版本库的代码文件(也从来没有放入暂存区)。
在oa-auth的server包下,可以新建一个TestServiceImpl.java,然后执行git status命令,看一下当前的状态,红色文件还在工作区没有提交到暂存区
此时git status会告诉你,有文件没有被提交到git仓库中或者是暂存区中过,就是untracked,就是从来没有被git追踪过它的版本,甚至第一个版本都没有被git追踪。
接下来执行命令:
git add --all .,也可以给大家开始来解释一下这些命令的含义,git add .,就是将当前新增或者是修改过的文件,加入暂存区,但是加了--all之后,如果有文件被删除,也会将文件被删除的状态加入暂存区中
一般工作中,都是用git add --all .
但是也可以直接git add 某个文件,把指定的文件加入到暂存区中
从暂存区提交到仓库
总结一下
(1)新文件刚创建:untracked,此时仅仅停留在工作区中
(2)git add 新文件:new file,此时已经被追踪了,放入了暂存区中
(3)git commit 新文件:committed,已经被追踪了,放入了git仓库中
(4)修改那个文件:modified,changes not staged to be committed,没有加入暂存区,被修改的内容仅仅停留在工作区中
(5)git add 修改文件:modified,changes to be committed,修改的文件版本被已经加入暂存区
(6)git commit 修改文件:committed,修改后的新版本提交到了git仓库中
一般自己创建的版本库,刚开始文件都是untracked,然后git add和git commit命令执行之后,就被提交了第一个版本到git版本库,此时就全部都是tracked了。如果是从远程版本库克隆下来的,那么刚开始就是tracked。
接着对tracked的文件修改之后,就是modified;然后对modified文件再执行git add命令之后,就是staged,进入了暂存区;接着执行git commit命令之后,就将暂存区中的文件都提交到了版本库中,此时就是unmodified,and tracked。
6种状态映射到3种状态的补充说明
git管理的文件有三种状态:提交状态(committed),修改状态(modified),暂存状态(staged)。
提交状态:我们的文件已经安全的保存在git的本地数据库中了。
修改状态:我们修改了文件,但是还没有提交到git的数据库中去。
暂存状态:将修改后的文件标记为即将通过下一次提交,保存到git数据库中去。
(1)新文件刚创建:untracked,此时仅仅停留在工作区中
(2)git add 新文件:new file,此时已经被追踪了,放入了暂存区中 => staged
(3)git commit 新文件:committed,已经被追踪了,放入了git仓库中 => committed
(4)修改那个文件:modified,changes not staged to be committed,没有加入暂存区,被修改的内容仅仅停留在工作区中 => modified
(5)git add 修改文件:modified,changes to be committed,修改的文件版本被已经加入暂存区 => staged
(6)git commit 修改文件:committed,修改后的新版本提交到了git仓库中 => committed
git log
在我们提交了很多次之后,可以查看提交历史,使用git log命令即可,可以看到每次commit的信息,包括了commit的SHA-1、作者、日期、提交说明。
包含了一个commit的SHA-1 hash值,40位的字符;作者,从之前安装好git之后做的设置来的;date;提交备注,inital commit,git commit -m ''
head 指针
(HEAD -> master)是什么意思?
版本控制系统中,都有一个概念,叫做分支,就是同样的一份儿代码,可以拉取多个分支,每个分支的代码刚开始都是一样的,然后不同的人可以基于不同的分支去开发,然后开发完之后,再将多个分支的代码合并在一起
分支,就是一份代码拷贝,基于这份独立的代码拷贝,去写自己的代码
最后将多个分支合并在一起,就是将不同的人写的代码合并到一起
git里面,每次git init之后,默认初始就创建一个分支,叫做master分支
我们每次写代码,然后git commit提交,都会形成一次新的commit,然后默认就是在master分支对应的代码拷贝上,进行代码修改和提交的
每次提交之后,master分支就会对应着最新的一次提交
HEAD,指针,指向了我们当前所处的分支,因为当前我们默认就处于master分支上,所以HEAD指针就是指向master的
日志其他子命令
git log --patch -2,--patch可以显示每次提交之间的diff,同时-n可以指定显示最近几个commit。这个是很有用的,可以看最近两次commit之间的代码差异,进行code review是比较方便的。
用git log --stat,可以显示每次commit的统计信息,包括修改了几个文件,有多少行插入,多少行删除。
用git log --pretty=oneline,可以每个commit显示一行,就是一个commit SHA-1和一个提交说明。用git log --pretty=format:"%h - %an, %ar : %s",可以显示短hash、作者、多长时间以前、提交说明。
用git log --oneline --abbrev-commit --graph,这是最有用的,可以看到整个commit树结构,包括如何合并的,就显示每个commit的SHA-1和提交说明,同时SHA-1显示短值。
- -oneline:显示一行,不要显示多行那么多东西,一行里,就显示commit的标识符,SHA-1 hash值,40位的;提交备注;显示分支和HEAD指向哪个commit
- -abbrev-commit:commit的标识符,每一次commit,都有一个唯一的标识符,就是一个SHA-1 hash值,40位,显示一个短值,默认显示前7位,就是说前7位就可以唯一定位这个commit了,不需要完整的40位
- -graph:显示图形化的commit历史,这个大家后面学习到分支那里就知道了,如果有分支的话,commit历史会形成一棵树的形状,这个时候用--graph可以看清楚这颗commit树长什么样子,很有的
告诉大家,通过git log命令,就可以看到你多次往git仓库中提交不同代码版本的整个历史
图解git日志原理
就是你每次git add之后,git commit之后,会在git仓库里存储什么东西,存储的那些东西跟分支还有HEAD之间的关系是什么???
用一张图画清楚,工作区、版本库、暂存区、master分支以及HEAD指针的关系
工作区和版本库是两个分离的区域
在工作区修改代码之后,执行git add命令,会将代码放入版本库中的暂存区;接着执行git commit命令之后,会将暂存区中的代码提交到版本库中的master分支上,而HEAD指针就指向master分支的最新一次提交。
所以现在我们就很清楚了,git add,其实就是可以多次修改代码,多次git add,然后将每次修改的代码,都放入暂存区中
;而git commit,就是一次性将暂存区中的代码,全部提交到master分支上,master分支会出现一个最新的commit,也就是一个最新的代码版本;
而HEAD作为一个指针,永远指向master分支的最新一次commit的代码版本。
git reset
git reset --hard HEAD^,就可以回退到上一个版本
HEAD^,代表了什么?
HEAD -> master -> commit(add methods for classes)
HEAD^,代表的是commit(add methods for classes)的上一个commit(add two files)
git reset --hard HEAD^,就是将仓库、暂存区、工作区,全部恢复到上一个commit(add two files)对应的状态
git reset --hard HEAD~5,退回到HEAD之前的倒数第5个commit的状态
git reset --hard d324644,指定一个commit的hash值,回退到很老的版本
git reset这个命令,可以任意穿梭到历史的任何一个版本上去
- 软, master 分支reset到modify这个提交对象, 本次新增/修改的文件保留在暂存区
- 混合 , master 分支reset到modify这个提交对象, 本次新增/修改的文件保留在工作区,需要自己add到暂存区
- 硬, , master 分支reset到modify这个提交对象, 本次新增/修改的文件丢失
- 保留 master 分支reset到modify这个提交对象, 本次新增/修改的文件丢失
git reflog
如果我要回来呢?重新回到之前add methods for classes那个commit对应的版本
找回之前的分支
实现了我们第一讲所说的,版本管理的困难,被git轻易的解决了
(1)记录下来了每一个版本
(2)可以让我们通过简单的命令,在任何一个版本中任意穿梭,实现了版本控制功能
git最核心的原理
(1)三个区域:工作区、暂存区、仓库
(2)提交历史:在仓库中,blob->tree->commit->master-HEAD,这套东西形成了git的数据结构