基本工作原理
基本概念
我们先来理解下 Git 工作区、暂存区和版本库概念:
工作区:就是你在电脑里能看到的目录。
暂存区:英文叫 stage 或 index。一般存放在 .git 目录下的 index 文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。
版本库:工作区有一个隐藏目录 .git,这个不算工作区,而是 Git 的版本库。

三棵树
你的本地仓库有 Git 维护的三棵"树"组成,这是 Git 的核心框架。这三棵树分别是:工作区域、暂存区域和 head

工作区域(Working Directory)就是你平时存放项目代码的地方。
暂存区域(Stage)用于临时存放你的改动,事实上它只是一个文件,保存即将提交的目录树。
HEAD 是当前分支引用的指针,它总是指向该分支上的最后一次提交。 这表示 HEAD 将是下一次提交的父结点。 通常,可以把 HEAD 看做你的上一次提交的快照。head通常都是指向master分支。
Git 的工作流程一般是:
- 在工作目录中添加、修改文件;
- 将需要进行版本管理的文件放入暂存区域;
- 将暂存区域的文件提交到 Git 仓库。
摘抄自第二章https://www.jianshu.com/p/e57a4a2cf077
https://blog.csdn.net/hcrw01/article/details/107449438
git三棵树
确定了原理之后,按照最简单的方式入手,按照git的三棵树的核心框架,我们首先需要的就是"种树",
先讲下Workiing Directory,也就是工作区域,先在本地建一个目录。
跟其他ide新建工程一样,工程要放在自己自定义的目录,可以用win自带的文件管理器建立好目录(全英文),也可以用在终端敲命令,这里建立的目录是
G:\future\GitProject\work
放入代码,这里新建一个hello.txt文件,使用编辑器(不要自带的记事本,使用UTF-8编码)打开随便写点内容,比如
hello git!
这不就是普通的文件夹放个文件嘛,对,这就是第一棵树,跟普通文件夹可以说没有区别额,但是差第一棵树还有最后一步。
初始化目录(创建仓库)-git init
打开终端,使用pwd,ls和cd等命令一顿操作,然后进入文件目录。

使用当前目录作为Git仓库,使用以下命令初始化。
git init
也可以这样~ ,在GitProject目录下,使用以下命令
git init work
初始化后有什么变化呢
最明显的,初始化后目录下多了⼀个隐藏的 .git的目录,这个目录是Git来跟踪管理版本库的,也就是版本库(Repository)。
实际有什么变化呢
这里需要用下面的指令了
查看状态变化- git status
先来一顿操作体验下命令的使用,分别在初始化完成后 输入初始化和查看状态命令,
git status
然后删除.git目录,再查看状态,如图

通过状态变化命令就可以知道初始化完成的变化,也就是创建了work的 git仓库,仓库根据.git目录来判定的。
对于小白入门学习来说,后面的探索会一直用到状态变化指令,如果感觉太长,可以试试这个。
git status -s
ps:回头看这个简单指令,显示可能会有人看不懂,建议查看状态每次都用两种查看对比,就不用特地去查用法了 。
而第一个树也一样,跟普通文件夹的区别也就是同一个目录下有没有.git目录。初始化完成也就种好了,Workiing Directory可以看成是指除了.git目录的工作区域,通过查看状态变化命令可以看出,在这个区域下的文件才能被识别。
接下来是种第二课树,也就是stage(index),第一次打开.git目录,是这样的

用一个框图来理解

第二课树呢?空白的,别急,
添加文件到暂存区-git add
输入命令,
git add hello.txt

查看文件夹,就多了一个index,这个就是第二个树,文件暂存区,并且add命令实现了第一个树到第二个树的操作,git add 更新了index,index存放的并不是实际文件,工作区域的实际文件内容(目录树)是被写入对象库中的新对象中,新对象的ID就是索引记录的内容。
同时状态变化,在 "Changes not staged for commit" 下面提示 有新文件。

题外话:学习内容很多是参考廖雪峰老师的教程,教程前面的add命令说是添加仓库,有点不准确,应该是暂存区,也就是stage(index),不过后面也解释了,开始被混淆了。
添加到仓库,个人理解是复制文件到工作区域(working directory)。
接下来是第三棵树,head指针
先说下master分支,关于名字,master是安装软件默认下的配置,一直next的都是这个名字的分支。
查看当前工作分支-- git branch
git branch
这个命令和状态变化命令加-b参数相近
git status -s -b
结果是

小白先不管分支的具体含义,初始化的git仓库master分支都是空白的,也就是HEAD指向的内容是空白的。那么先提交,继续探索。
提交暂存区内容到版本库(repository)--git commit
命令用法:
git commit -m 'version_1'

此时的框架是这样的
提交完成后
状态变化,提示没有需要提交内容。

很多教程都是说提交命令是提交到库,到底提交了什么,提交到哪里去了。
带着这两个问题继续探索。
除了status命令,我们需要更加有用的工具,这就用到查看提交记录命令了。
查看历史提交记录-- git log
我们探索就需要详细的记录,使用一下格式查看提交记录
git log -1 --pretty=raw

如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline参数:
提交一次的结果是看不出历史记录的真正的内容和head的作用,需要再来一次
我们修改下文件内容
echo "Nice to meet you." >> hello.txt
修改了想看下具体差异,可以使用下面的命令
查看差异—git diff
git diff hello.txt

Ps:前面没有截图,后面补的是echo "new difference" >> hello.txt
。哈哈,我学会了时光机,我重新回去,再跑一遍截了图

查看差异是一个很强大的工具,通过不同参数可以比较三棵树之前的的内容的差异,如下图。

修改后状态变化,在 "Changes not staged for commit" 下面提示 文件修改。
再次添加文件
git add hello.txt
状态变化 在 "Changes to be committed:" 下面提示 文件修改。


提交文件
git commit -m 'version_2'
状态变化 提示没有需要提交内容。
提交完成再来看日志
git log -1 --pretty=raw

日志显示提交是三个对象,commit,tree,parent
分别是
本次提交的ID,目录树的ID,父提交ID(上一次提交的ID)
每个ID用特定的算法生成的40位哈希值表示,对于不知道哈希值的人来说,简直就是魔法数字,那么如何知道它代表着什么,一个重要的命令就可以拨开迷雾。
查看文件—git cat-file
输入以下命令,-t查看类型,-p查看内容,哈希值不需要输入完全,4位即可。
git cat-file -t 30e8
git cat-file -t 008
git cat-file -t 008b
git cat-file -p 191c
查看commit的内容和tree的内容如下
所以我们更加详细看看tree blob对象的内容
git cat-file -p 8ddd
发现就是hello.txt文件的内容。经过整理思路就比较清晰了。整个关系是这样的

到现在我们已经知道commit了什么内容了吧。
接下来看提交到了哪里,我们就要查看HEAD和master了。
cat .git/HEAD
cat .git/refs/heads/master

发现HEAD是一个指向master分支的指针
而master分支内容就是最新的commit的ID。
整个结构是这样的

到现在我们也知道commit的对象是master了,每次commit都会更新master到最新,同时目录树也是最新的。
到这里我们也就知道第三课树就是一个存放指向master的指针,但都是最新的。
三棵树的必要性
我们现在知道三课树是什么了,进一步理解三棵树才能更好的使用git
首先我们思考为什么要有暂存区
有时候你可能感觉add命令有点多余,暂存区有点多余,操作有点麻烦,直接将文件存起来不久可以了吗。
Git强大的设计之一就是暂存区,强大之处必有道理。
我们使用git就是文件保存恢复等。想要知道比较文件内容有没有变化修改,从而获得自己想要的版本,是需要通过不同版本的文件对比的。
如果是
知道暂存区通过跟踪工作区的文件来检索差异,而存放的内容只是一个索引,不是实际的文件。
索引包含了跟踪文件的名字,修改时间,大小等。
Git时光机
git强大之一功能就是回溯,就像哆啦A梦的时光机,能够回到任意的时间点,让你重生。在使用强大的时光机之前,我们先思考一下:
我们把三棵树跟FC游戏对应起来
工作区对应正在游戏中
暂存区对应每过一关给你自动的存档,这不是真正的存档,关机之后还是会第一关玩起
Head对应手抄的存档代码,就是关机之后也能继续接着玩
可能比喻不是很恰当,但是我觉得可以加深我的理解,你也可以找个更合适的比喻对象。
我们知道三棵树的变化都会是git仓库的status发生变化,对应的我们玩冒险游戏也是。
那么就引出下面一个问题?
你玩在游戏的过程中,你会选择在哪个时间节点使用时光机,回到哪一个时间节点。
其实这个问题就是关于使用什么命令使用什么参数的问题。
带着这个问题我们来玩一场冒险游戏。(请自动代入fc冒险岛4)
首先我们先看下我们之前提交的记录

我们知道log记录的是commit的ID,后面的version是自己添加的描述目前是这这样的
提交的有点少,我们提交多点内容,。
先跟git 说bye,在hello.txt 添加内容bye(跟老婆挥手的画面有了吗?)
然后创建一个新文件travel,内容为Game start! 开始冒险,!
再加入内容Find a monster,Fight!中途遭遇野怪,开始战斗!
最后加入内容Defeat!Died! 打不赢,太菜了,死掉了!
记录如下面所示

辛苦画了整个框图
就拿最后一次提交按照游戏经历来描述是这样的,被打败之后,死亡-》系统存档-》手抄了一份存档,死掉了怎么办,读档呗,使用时光机。
首先是git reset指令。
重置HEAD指向—git reset
在冒险过程中,每次提交完使用git log可以发现我们每次提交都是跟时间一样逐渐递进的,HEAD一直都是指向鱼尾最后一次提交。而git reset最主要的功能就是重置HEAD的指向。因为重置命令危险性较高,可能导致你的冒险经历销毁。重置命令有不同的参数,
来看玩家们不同的模式的操作
模式1:--hard
上面说的时光机的操作要记得的两个节点
作为一个小白,在git的第一次冒险就失败了很正常,那么经历过失败之后,
正常断电了,读档就是拿着上一份的手抄存档读取,也就是下面的指令实现的。
启动时光机
git reset --hard HEAD^
说明,HEAD^是上一次提交的福父提交标志,还可以使用HEAD^^回到上上次的travel,使用HEAD~3回到上三次的v3。
从记录来看,使用最危险的--hard参数,把head重置指向父提交,可以将三棵树重置到上一次提交的节点,工作区和暂存区也指向了父提交的内容,
重置过程
Hard指针重置点:父提交
使用节点:HEAD指向defeat 的commit。工作区与暂存区没改动。
回到节点: HEAD 指向上一次提交了的fight的commit。工作区与暂存区没改动。
--hard模式的重置甚至是git log也会被重置,如果你没有记录下来commit的ID,是没有git reset --hard HEAD* 这样的命令回来的,只能用ID,git log没有记录,但是git reflog还有。
查看分支日志git reflog
日志文件是在.git/log保存的,跟git log 不同就是log是最新的,regflog是全部更新记录。
git reflog show master
show master 是指定分支,在只有一个分支的情况下可以省略
记录内容组成是,commit的省略的哈希值ID,(HEAD->master)表示HEAD指向了这个commit,HEAD@{2}是编号,后面的是操作。
有了编号的表达式,可以使用命令跳转前n次的变更值,使用以下命令:
git reset --hard HEAD@{4}
就不需要敲commit的ID值了,使用一下命令可以来回跳转,方便测试
git reset --hard master@{1}
觉得记录太多了,只需要前面的几行,可以使用一下命令查看前5行:
git reflog show master | head -5
继续来说游戏
--hard模式是最危险的的,也有谨慎玩家使用--soft模式来逆转时光。
模式2:--soft
使用如下指令,回到死亡之后(提交commit为defead之后)的节点。
git reset --hard 7469f
HEAD为上一次的指向,启动时光机。
git reset --soft HEAD^
先看记录,HEAD被重置了,指向父提交,工作区内容没有变化,状态显示暂存区有修改,但是没有差异,
显而易见,soft模式只重置HEAD,其他没有变化。
重置过程
Hard指针重置点:父提交
使用节点:HEAD指向defeat 的commit。工作区与暂存区没改动。
回到节点:提交defeat 的commit之前,添加文件add travel之后。
要是试试下面的这个
重置过程
Hard指针重置指向:父父提交(HEAD^^)
使用节点:HEAD指向defeat 的commit。工作区与暂存区没改动。
回到节点: ?
这个节点就不是历史有的了,可以比喻时空交错了,这个节点相当于
回到提交了tarvel的commit,然后工作区加入fight和died,添加文件到了add。
这种时空交错的方式更多是针对特殊的场景,我们只需要时光机的功能就好。
继续探索,介绍下reset的mixed(普通)模式
模式3:--mixed(空参数 )
使用如下指令,回到死亡之后(HEAD指向defeat 的commit。工作区与暂存区没改动)的节点。
git reset --hard 7469f
Hard指针重置指向:父提交(HEAD^),启动时光机
git reset HEAD^
//or use git reset –mixed HEAD^


状态显示,HEAD被重置了,指向父提交,工作区内容没有变化,状态显示暂存区被重置,有差异,差异说明了暂存区被重置为父提交的暂存区。
重置过程
Hard指针重置点:父提交
使用节点:HEAD指向defeat 的commit。工作区与暂存区没改动。
回到节点:HEAD指向travel的commit。添加文件add travel之前,改动工作去内容之后
其实常用是这种情况
Hard指针重置点:当前提交
回到节点:HEAD指向travel,添加文件add travel之前,改动工作去内容之后
这个功能相当于log一直提示的git restore -stage

git reset真的重置了HEAD指向
我们来看下重置后的HEAD的内容
cat .git/HEAD
cat .git/refs/heads/master
这个HEAD好像也没有变化吧。。。
我们再试试,reset当然能回去也能回来,使用commit的ID,可以任意跳转到那个commit的ID节点
git reset --hard 7469f
好像没有变化额。只是master的内容指向改变了。
关于这个HEAD指针,跟C语言指针对比起来,HEAD的指向根本没有变化,只不过指向的内容发生了变化,小白不懂,就当做重置了"指向"吧,先存疑
重置woking directoty—git retore
Resert是重置HEAD的指向,也就是commit改变,如果是不用改变commit的情况下,时光机使用更多的是restore,也就是状态提示的指令
Ps:在head指向当前commit的情况 commit不改变,很多操作跟restor的功能是一样的。
修改-添加文件会使两棵树的发生对应状态变化,使用不同参数指定重置目录树的区域
1重置工作区域
参数可以选择
-W, --worktree (default) restore the working tree (default)
修改文件或者删除文件之后,想撤销,使用一下命令
git restore .
与add指令一样需要指定文件路径。
2重置暂存区
参数可以选择
-S, --staged restore the index
添加文件之后,使用status
git restore -S .
需要同时重置工作区和暂存区可以带两个参数。
git restore -WS .
上面介绍了需要重置区域,那么重置的区域是根据那个目录重置的呢
Restore命令默认是
使--worktree参数时
重置工作区是用当前暂存区index指向的目录树重置所选择的区域
使--staged参数时
重置暂存区是用当前HEAD指向的目录树重置所选择的区域。
当然可以用-s, --source <tree-ish>指定特定的目录树。
使用一下命令,工作区变回死亡之后。
git restore -s 52e3 .
使用一下命令,工作区变回开始冒险之后
git restore -s d912 .
是不是很熟悉的时光操作,从某种意义来说,commit跳跃了,如果只针对工作区来说,跟reset的效果是一样的,不同的是head没有被重置。
git分支
我们一直都是随时间一条线在使用时光机,但是git还可以有两条线,这就是分支了
首先是管理分支
分支指令git branch
不带参数 功能是查看分支指令,输出结果的*表示当前分支
$ git branch
带一个名词是 新建分支
我们在travel新建train,paly分支任务,为了冒险先训练
git reset --hard d912
git branch train
git branch play
与新建相对就是删除,删除play分支,冒险怎么能去玩耍。
git branch -d play
我们之前一直介绍的是三棵树,其中HEAD一直是指向master的分支,操作时光机也是调整HEAD不断在master分支跳跃,因为我们没有走支线任务,。
分支跟三棵树一样同样重要,我们走支线需要告诉HEAD要切换,这就是需要
检出命令checkout
我们先恢复上面的reset重置HEAD指向的问题
使用checkout将分支切换到train
$ git branch train
查看状态,log和HEAD文件可以看到
HEAD的指向变成了train
现在再思考之前的reset重置HEAD的指向的问题。
是不是有点清晰了呢。
HEAD一直指向分支,就不能指向commt吗
试试就知道一个分离头指针。
既然要训练
我们新建一个train.txt每天做基础训练
提交
然后我发现头秃了,变强了
在travel添加提交
在train分支变强了之后可以去回去主线打怪了
$ git branch master
Merge
为了报仇我们要找到之前被打的那个怪兽
我们是从travel走的分支,当时还没有遇怪,时光机跳转到fight
然后我们用加入支线的成果
分支合并git merge
浙公网安备 33010602011771号