夜owl

困到睡不着
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Git使用笔记2-基本工作原理和常用命令

Posted on 2025-11-20 11:29  夜owl  阅读(10)  评论(0)    收藏  举报

基本工作原理

基本概念

我们先来理解下 Git 工作区、暂存区和版本库概念:

工作区:就是你在电脑里能看到的目录。

暂存区:英文叫 stage 或 index。一般存放在 .git 目录下的 index 文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。

版本库:工作区有一个隐藏目录 .git,这个不算工作区,而是 Git 的版本库。

三棵树

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

工作区域(Working Directory)就是你平时存放项目代码的地方。

暂存区域(Stage)用于临时存放你的改动,事实上它只是一个文件,保存即将提交的目录树。

HEAD 是当前分支引用的指针,它总是指向该分支上的最后一次提交。 这表示 HEAD 将是下一次提交的父结点。 通常,可以把 HEAD 看做你的上一次提交的快照。head通常都是指向master分支。

Git 的工作流程一般是:

  1. 在工作目录中添加、修改文件;
  2. 将需要进行版本管理的文件放入暂存区域;
  3. 将暂存区域的文件提交到 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文件内容后

回到节点: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