教程目录
一、Git三大分区
Git三大分区概念
Git 本地数据管理,大概可以分为三个区,工作区,暂存区和版本库。
我们先看一张图
工作区(Working Directory)
直接编辑的地方,肉眼可见,直接操作。
GitTest是我本地的一个仓库, 其中GitTest目录就是我们的工作区,但不包括.git这个目录
暂存区/缓存区(Stage 或 Index)
数据暂时存放的区域。
缓存区是一个看不见的区域,个人觉得这是Git的一个安全机制吧,就是说你提交代码的时候有可能会提交一些错误的代码,所以这个区域就是为了让你有机会反悔提交的代码。
在项目目录下使用指令“git add fileName”就可以把代码存入缓存区,这里的fileName是指的要往缓存区添加的文件名,当然也可以使用“.”代表当前的整个目录。
“git status”可以查看缓存区有什么文件。
版本库(commit History)
存放已经提交的数据,push 的时候,就是把这个区的数据 push 到远程git仓库了。
而.git这个目录就是本地仓库区,你commit的一切更改都会存储到本地仓库中
远程仓库(remote)
简单来说,就是我们工作过程中不能将主项目放到某一个人的本地电脑上,这时就需要有一个地方存储主项目,这个地方就是我们的git远程仓库。
Git本地库与远程库
-
git本地库,适合个人在本地使用版本管理工具来管理自己的资料
-
git远程库,适合团队的协作,成员之间共享资料
Git 远程库与远程库之间
-
fork: 看到别人优秀的项目,光看不过瘾,手痒也想写点东西,但是又没有机会加入核心开发团队,那么就可以fork下别人代码,到自己的远程库中。其实就是clone一份。自己就可以完全掌控代码库了。
-
pull request:当自己将别人fork过来的代码库,修改了一部分功能,又觉得自己的工作成果不想浪费,就想共享给原作者,那么就可以对原作者的代码库发起一个pull request。
Git 三种状态
-
已提交(committed):已提交表示数据已经安全的保存在本地数据库中。
-
已修改(modified):已修改表示修改了文件,但还没保存到数据库中。
-
已暂存(staged): 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
二、Git底层命令
基础的 linux 命令
目录命令
查询目录中的内容
ls [选项] [文件或目录]
选项:
-
-a:显示所有文件,包括隐藏文件
-
-l:显示详细信息
-
-d:查看目录属性
-
-i:显示inode
注:ls -l可以简写为ll
建立目录
mkdir [-p] [目录名]
选项:
-
-p:递归创建
切换所在目录
cd [目录]
# 返回上次的目录
cd -
# 进入上一级目录
cd ..
# 进入当前目录
cd .
显示当前所在目录
# 显示当前所在目录
pwd
删除空目录
# 删除空白目录,若包含文件则不能删除
rmdir [目录]
文件处理命令
创建空文件
touch [文件名]
删除文件或目录
rm [文件或目录]
选项:
-
-r:删除目录
-
-f:强制
注意:rm -rf / 强制删除根目录下的所有(相当于自杀,绝对不可以使用)
复制文件或目录
cp [选项] [原文件或目录] [目标目录]
选项:
-
-r:复制目录
-
-p:连带文件属性复制
-
-d:若原文件是链接文件,则复制链接属性
-
-a:相当于都复制 同-pdr
剪切或改名
mv [原文件或目录] [目标目录]
搜索文件
find path [-option]
选项:
-
-name:文件名称
-
-iname:不区分大小写
可以使用通配符:
-
* 匹配任意内容
-
? 匹配任意一个字符
-
[] 匹配任意一个中括号内的字符
# 将对应clea铺在控制台
find ./
# 将目前目录及其子目录下所有延伸档名是 c 的文件列出来。
find ./ -name "*.c"
# 将目前目录其其下子目录中所有一般文件列出
# find 目录名 -type f :将对应目录下的文件平铺在控制台
find ./ -type f
# 将目前目录及其子目录下所有最近 20 天内更新过的文件列出
find ./ -ctime -20
# 查找指定区域内名称为“good”的文件!
find ./demo -name "good.txt"
# 增加i是指不区分大小写
find ./demo -iname "good.txt"
文件查看和编辑
查看
# 从第一个字节开始正向查看
cat 文件名
# 从最后一行开始反向查看
tac 文件名
# 查看一个文件的前n行,n为指定行数
head -n 文件名
# 查看一个文件的后n行,n为指定行数
tail -n 文件名
编辑
vim 文件名
vi/vim 共分为三种模式,分别是命令模式,输入模式和底线命令模式。
1、命令模式
启动vi/vim,便进入了命令模式。此状态下敲击键盘动作会被vim识别为命令,而非输入字符。
常用命令:
命令 | 说明 |
---|---|
Ctrl + f | 屏幕向下移动一页,相当于Page Down键 |
Ctrl + b | 屏幕向上移动一页,相当于Page Up键 |
0或Home键 | 移动到这一行的最前面字符处 |
$或End键 | 移动到这一行的最后面字符处 |
gg | 移动到这个档案的第一行 |
G | 移动到这个档案的最后一行 |
数字 + Enter键 | 向下移动指定行数 |
x(小写) | 向后删除一个字符,相当于del键 |
X(大写) | 向前删除一个字符,相当于backspace键 |
dd | 删除光标所在的那一行 |
yy | 复制光标所在的那一行 |
p(小写) | 将已复制的数据,粘贴在光标下一行 |
P(大写) | 将已复制的数据,粘贴在光标上一行 |
u | 复原前一个动作,撤销 |
. | 重复前一个动作 |
3、输入模式
进入输入模式的命令:
命令 | 说明 |
---|---|
i | 在目前光标所在处输入 |
I | 在光标所在行的第一个非空格符处开始输入 |
a | 在目前光标所在的下一个字符处开始输入 |
A | 在光标所在行的最后一个字符处开始输入 |
o | 在光标所在的下一行输入新的一行 |
O | 在光标所在的上一行输入新的一行 |
Esc | 退出输入模式,回到命令模式 |
3、底线命令模式
在命令模式下按下:(英文冒号)就进入了底线命令模式。
命令 | 说明 |
---|---|
:w | 将编辑的数据写入硬盘档案中 |
:wq | 存档并离开 |
ZZ | 存档并离开 |
:q! | 不存档并离开 |
:set number/nu | 显示行号 |
其他命令
清除屏幕
clear :清除屏幕
控制台输出
# 往控制台输出信息
echo 'test content'
# 往文件中输出信息
echo 'test content' > test.txt
初始化新仓库
什么是版本库呢?
版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
所以,创建一个版本库非常简单,进入工作区,右键打开git base here命令行;
命令:git init
解析:要对现有的某个项目开始用 Git 管理,只需到此项目所在的目录,执行:git init
作用:初始化后,在当前目录下会出现一个名为 .git 的目录,所有 Git 需要的数据和资源都存放在这个目录中。不过目前,仅仅是按照既有的结构框架初始化好了里边所有的文件和目录,但我们还没有开始跟踪管理项目中的任何一个文件。
Git目录
hooks 目录
hooks 目录包含客户端或服务端的钩子脚本;
这是一个有趣的特性。Git 提供了一套脚本,可以在每个有意义的 Git 阶段自动运行。这些被称为钩子的脚本可以在提交 (commit)、变基 (rebase)、拉取 ( pull ) 操作的前后运行。脚本命名预示着它的执行时机。如我们可以编写 pre-push 的作为钩子,进行推送代码前的检查。
info 目录
info 包含一个全局性排除文件
你可以将不想被 git 管理的文件记录到 .gitignore 文件中。
排除文件的意思是不想共享这个文件。例如你不想共享你的 IDE 自定义配置,将其添加到 .gitignore 文件中即可。
logs 目录
logs 保存日志信息
保存所有更新的引用记录。打开logs文件夹可以看到其中有两个文件,refs文件夹和HEAD文件。
1、refs文件夹中有两个文件夹:
heads文件里面存储的是本地分支的对象,每个对象的文件名就是本地的一个分支名。我们使用git branch查看本地所有分支时,查询出的分支就是heads文件夹下所有文件的名称,这些分支文件中存储的是对应分支下的操作记录。
remotes文件夹里存储的是远程的所有分支对象,每个对象的文件名称就是远程的一个分支名称。这些分支文件中保存了远程仓库对应分支所有操作
2、logs下的HEAD文件保存的是所有的操作记录,使用git reflog查询的结果就是从这个文件来的。
objects 目录
objects 目录存储所有数据内容;
存放所有的 git 对象,哈希值一共40位,前 2 位作为文件夹名称,后 38 位作为对象文件名。
refs目录
refs目录存储指向数据的提交对象的指针(分支)
refs下有三个文件夹。
heads:里面包含所有的本地分支,每个分支都是文件,文件中存储了分支当前指向的commit;打开refs\heads\master文件查看,可以看到其中保存的确实是最近一次提交的commit id。
tags 叫做里程碑,或者版本发布用或记录重要版本。
remotes,远程仓库信息
其中\refs\remotes\origin\HEAD:指向远程仓库当前分支。
config 文件
config 文件包含项目特有的配置选项
description 文件
description 用来显示对仓库的描述信息
HEAD 文件
HEAD 文件指示目前被检出的分支
index 文件
index 文件保存暂存区信息,一个二进制文件
git对象
git对内容版本控制
Git的核心部分是一个简单的键值对数据库。你可以向该数据库插入任意类型的内容,它会返回一个键值,通过该键值可以在任意时刻再次检索该内容。
向数据库写入内容并返回对应键值
命令:
echo 'test content' | git hash-object -w --stdin
说明:
-w选项指示 hash-object 命令存储数据对象;若不指定此选项,则该命令仅返回对应的键值
--stdin(standard input)选项则指示该命令从标准输入读取内容;若不指定此选项,则须在命令尾部给出待存储文件的路径
返回:
该命令输出一个长度为 40个字符的校验和。这是一个 SHA-1哈希值
示例:从标准输入流读取内容,生成对应的哈希值;但是没有真正存储
示例:从标准输入流读取内容存储在本地库,并生成对应的哈希值;
查看Git是如何存储数据的
命令:
find .git/objects -type f
返回:
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
这就是开始是Git存储内容的方式:一个哈希对应一条内容。前两个字符用于命名子目录,余下的 38个字符则用作文件名。
根据键值拉取数据
命令:
git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
-p选项可指示该命令自动判断内容的类型,并为我们显示格式友好的内容
返回:对应文件的内容
注:cat查看内容是文本存储压缩后的内容;
git对文件版本控制
创建一个新文件并将其内容存入数据库
命令:
# 存文件
git hash-object -w文件路径
# 返回对应文件的键值
git hash-object文件路径
示例:
# 命令:
# 把内容写入文件中
echo 'version 1' > test.txt
# 存储文件
git hash-object -w test.txt
# 返回:
83baae61804e65cc73a7201a7252750c76066a30
git在执行git add命令时出现以下警告:
warning: LF will be replaced by CRLF in test.txt. The file will have its original line endings in working directory
因为Git的换行符检查功能。LF是linux下的换行符,而CRLF是enter + 换行。
向文件里写入新内容,并再次将其存入数据库
# 命令:
echo 'version 2' > test.txt
git hash-object -w test.txt
# 返回:
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
查看数据库内容
# 命令:
find .git/objects -type f
git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30
git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
# 利用 cat-file -t命令,可以让 Git告诉我们其内部存储的任何对象类型
git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
# 返回:
blob
问题总结
1.记住文件的每一个版本所对应的 SHA-1值并不现实 (希望记录的是项目版本/快照)
2.在Git中,文件名并没有被保存——我们仅保存了文件的内容
解决方案:树对象
注意:当前的操作都是在对本地数据库进行操作不涉及暂存区。
树对象
树对象(tree object),它能解决文件名保存的问题,也允许我们将多个文件组织到一起。Git以一种类似于 UNIX文件系统的方式存储内容。
所有内容均以树对象和数据对象(git对象)的形式存储,其中树对象对应了 UNIX中的目录项,数据对象(git对象)则大致上对应文件内容。一个树对象包含了一条或多条记录(每条记录含有一个指向 git对象或者子树对象的 SHA-1指针,以及相应的模式、类型、文件名信息)。
一个树对象也可以包含另一个树对象。
构建树对象
我们可以通过 update-index;write-tree;read-tree等命令来构建树对象并塞入到暂存区。
假设我们做了一系列操作之后得到一个树对像。
操作一:
利用 update-index命令为test.txt文件的首个版本【在本地库中存储】——创建一个暂存区。并通过 write-tree命令生成树对像。
test.txt文件的首个版本
# 创建test.txt文件,并将其内容存储在本地库
git init
echo 'test content' > test.txt
git hash-object -w ./test.txt
# 返回:test.txt内容在本地库存储对应的key ->git对象
d670460b4b4aece5915caf5c68d12f560a9fe3e4
把test.txt放到暂存区,并生成树对象
# 把test.txt放到暂存区:
git update-index --add --cacheinfo 100644 \
d670460b4b4aece5915caf5c68d12f560a9fe3e4 test.txt
# 生成树对象-》为暂存区生成快照并保存到objects
git write-tree
查看暂存区
git ls-files -s
说明:
文件模式为
100644,表明这是一个普通文件
100755,表示一个可执行文件;
120000,表示一个符号链接。
--add选项:因为此前该文件并不在暂存区中首次需要--add
--cacheinfo选项:因为将要添加的文件位于Git本地库中,而不是位于当前目录下所有需要--cacheinfo。
注:树对象就是我们的项目版本快照!
80865964295ae2f11d27383e5f9c0b58a8ef21da 项目的第一个版本快照(树对象)
操作二:
新增 new.txt将 new.txt和 test.txt文件的第二个个版本塞入暂存区。并通过 write-tree命令生成树对像。
新增 new.txt将 new.txt和 test.txt文件的第二个个版本存入本地库
# 生成new.txt
echo 'new file' > new.txt
# 编辑修改test.txt的内容
vim test.txt
# new.txt和test.txt第二个版本存入本地库
git hash-object -w ./new.txt # 返回fa49b077972391ad58037050f2a75f74e3671e92
git hash-object -w ./test.txt # 返回c46f9d567f02066137fe1786bf1aa7873b510b67
目前本地库里面的数据
# 查看
find .git/objects -type f
.git/objects/80/865964295ae2f11d27383e5f9c0b58a8ef21da 项目的第一个版本快照(树对象)
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 test.txt文件的第一个版本(git对象)
.git/objects/c4/6f9d567f02066137fe1786bf1aa7873b510b67 test.txt文件的第二个版本(git对象)
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 new.txt文件的第一个版本(git对象)
将 new.txt和 test.txt文件的第二个个版本塞入暂存区,并生成树对象
# 把test.txt塞入暂存区
git update-index --cacheinfo 100644 \
c46f9d567f02066137fe1786bf1aa7873b510b67 test.txt
# 把new.txt塞入暂存区
git update-index --add --cacheinfo 100644 \
fa49b077972391ad58037050f2a75f74e3671e92 new.txt
git write-tree
# 第二份版本 95c70f16d00402c9530d4313d4463a3d60c74f8b
操作三:
将第一个树对象加入第二个树对象,使其成为新的树对象
# 命令:read-tree命令,可以把树对象读入暂存区
git read-tree \
--prefix=bak 80865964295ae2f11d27383e5f9c0b58a8ef21da
git write-tree
图示(最后的树对象)
问题:现在有三个树对象(执行了三次 write-tree),分别代表了我们想要跟踪的不同项目快照。然而问题依旧:若想重用这些快照,你必须记住所有三个SHA-1哈希值。并且,你也完全不知道是谁保存了这些快照,在什么时刻保存的,以及为什么保存这些快照。而以上这些,正是提交对象(commit object)能为你保存的基本信息。
查看树对象
git cat-file -p 95c70f16d00402c9530d4313d4463a3d60c74f8b
100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b simplegit.rb
解析树对象
Git根据某一时刻暂存区(即 index区域)所表示的状态创建并记录一个对应的树对象,如此重复便可依次记录(某个时间段内)一系列的树对象。
其实树对象是对暂存区内操作的抽象,这颗树对象相对于就是快照。当我们的工作区有任何更改同步到暂存区时。便会调用write-tree命令通过 write-tree命令向暂存区内容写入一个树对象。它会根据当前暂存区状态自动创建一个新的树对象。即每一次同步都产生一颗树对象。且该命令会返回一个hash指向树对象。在Git中每一个文件(数据)都对应一个hash(类型 blob)每一个树对象都对应一个hash(类型 tree)
总结:我们可以认为树对象就是我们项目的快照
提交对象
我们可以通过调用 commit-tree命令创建一个提交对象,为此需要指定一个树对象的 SHA-1值,以及该提交的父提交对象(如果有的话第一次将暂存区做快照就没有父对象)
创建提交对象
# 创建提交对象,并添加注释
echo 'first commit' | git commit-tree 80865964295ae2f11d27383e5f9c0b58a8ef21da
# 返回:提交对象的hash
dcb30ef1a7385d63dbf99e20a8bc485ef9f4e636
# 查看提交对象类型
git cat-file -t dcb30ef1a7385d63dbf99e20a8bc485ef9f4e636
# 返回
commit
查看提交对象
# 查看提交对象内容
git cat-file -p dcb30ef1a7385d63dbf99e20a8bc485ef9f4e636
# 返回:包含树对象、作者、提交者、注释
tree 80865964295ae2f11d27383e5f9c0b58a8ef21da
author jingerbo <jingerbo@newcapec.net> 1598001199 +0800
committer jingerbo <jingerbo@newcapec.net> 1598001199 +0800
first commit
提交对象的格式
提交对象的格式很简单:它先指定一个顶层树对象,代表当前项目快照;然后是作者 /提交者信息(依据你的user.name和user.email配置来设定,外加一个时间戳);留空一行,最后是提交注释。接着,我们将创建另两个提交对象,它们分别引用各自的上一个提交(作为其父提交对象):
# 第二次提交,包含第一个提交对象
echo 'second commit' | git commit-tree 95c70f16d0 -p dcb30ef1a738
# 返回第二个提交对象的hash
175c6b1226168f74ed039a0c72ec547ae35dba97
# 查看
git cat-file -p 175c6b1226168f7
# 返回
tree 95c70f16d00402c9530d4313d4463a3d60c74f8b
parent dcb30ef1a7385d63dbf99e20a8bc485ef9f4e636
author jingerbo <jingerbo@newcapec.net> 1598001561 +0800
committer jingerbo <jingerbo@newcapec.net> 1598001561 +0800
second commit
第三次类似...
图示
注意:git commit-tree不但生成提交对象而且会将对应的快照(树对象)提交到本地库中