git基本概念
git其实就是一个版本控制器,所以我们先从版本控制讲起:
版本控制
什么是版本控制?
我们为什么要关心它呢?版本控制是一种记录一个或若干文
件内容变化,以便将来查阅特定版本修订情况的系统
为什么要使用版本控制?
软件开发中采用版本控制系统是个明智的选择。
有了它你就可以将某个文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态。就算你乱来一气把整个项目中的文件改的改删的删,你也照样可以轻松恢复到原先的样子。但额外增加的工作量却微乎其微。你可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致怪异问题出现的原因,又是谁在何时报告了某个功能缺陷等等
版本控制系统分类:
-
集中式版本控制系统
集中化的版本控制系统诸如 CVS,svn 以及 Perforce 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。多年以来,这已成为版本控制系统的标准做法优点: 每个人都可以在一定程度上看到项目中的其
他人正在做些什么。而管理员也可以轻松掌控每个开发者的权限,并且管理一个集中化的版本控制系统; 要远比在各个客户端上维护本地数据库来得轻松容易
缺点:
1、是中央服务器的单点故障,如果服务器宕机一小时,那么在这一小时内,谁都无法提交更新,也就无法协同工作
2、中央服务器的磁盘发生故障,碰巧没做备份,或者备份不够及时,就会有丢失数据的风险。最坏的情况是彻底丢失整个项目的所有历史更改记录
3、最大的毛病就是必须联网才能工作 -
分布式版本控制系统
像 Git,BitKeeper等都是分布式版本控制系统,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的提取操作,实际上都是一次对代码仓库的完整备份
分布式的版本控制系统在管理项目时 存放的不是项目版本与版本之间的差异.它存的是索引(所需磁盘空间很少 所以每个客户端都可以放下整个项目的历史记录)分布式的版本控制系统出现之后,解决了集中式版本控制系统的缺陷:
1、断网的情况下也可以进行开发(因为版本控制是在本地进行的)
2、使用 github 进行团队协作,哪怕 github 挂了 每个客户端保存
的也都是整个完整的项目(包含历史记录的!!!)
git简介
Git 是目前世界上最先进的分布式版本控制系统。同生活中的许多伟大事件一样,Git 诞生于一个极富纷争大举创新的年代。Linux 内核开源项目有着为数众广的参与者。绝 大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002 年间)。到 2002 年,整个项目组开始启用分布式版本控制系统 BitKeeper 来管理和维 护代码。
到了 2005 年,开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结 束,他们收回了免费使用 BitKeeper 的权力。这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds )不得不吸取教训,只有开发一套属于自己的版本控制系统才
不至于重蹈覆辙。他们对新的系统制订了若干目标:
- 分支切换速度快
- 容量小(压缩)
- 简单的设计
- 完全分布式
- 对非线性开发模式的强力支持(允许上千个并行开发的分支)
- 有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)
自诞生于 2005 年以来,Git 日臻成熟完善,在高度易用的同时,仍然保留着初期设 定的目标。它的速度飞快,极其适合管理大项目,它还有着令人难以置信的非线性分支管理 系统可以应付各种复杂的项目开发需求。
git安装
windows安装:windows安装地址
下载完安装包之后,双击 exe 安装包,一直qq式安装(下一步)即可
mac安装:mac安装地址
下载下来之后可以看到一个 dmg 文件,双击打开 压缩文件,可以看到里面有
一个文件, 再次双击 pkg 文件,就可以进行安装,然后按照引导一直点击继续
按钮就可以完成安装了.
linus常见命令
-
clear: 清除屏幕
-
echo ‘output content’ :往控制台输出’‘output content’’
-
echo ‘output content’ > demo1.txt: 新建一个demo.txt文件,文件内容是output content
-
ll : 将当前目录下的子文件和子目录的详情信息(时间、读写权限、大小)展平输出到控制台
-
ls: 只是将当前目录下的子文件和子目录输出,功能类似于ll,只不过输出的信息并没有ll那么详细
-
find 目录名: 将该目录名下的所有子孙目录或子孙文件都展平输出到控制台
-
rm 文件名: 删除指定文件
-
mv 源文件 重命名文件:文件重命名
-
cat 文件名 :查看文件的内容
-
vim 文件名:编辑文件
- 按i进入编辑状态
- 按esc退出编辑状态
- 非编辑模式下按:wq ----- 保存并退出
- 非编辑模式下按:q! ----- 强制退出,不保存
- 非编辑模式下按:set nu ----- 设置行号
git底层概念与原理
git初始化:随便在本地新建个文件夹,使用命令git init
来初始化一个项目,此时会在该文件夹下生成一个默认隐藏的.git的文件
- config: git仓库的配置信息
- description:仓库的描述信息
- HEAD:映射到ref引用,能够找到下一次commit的前一次哈希值
- hooks:目录包含客户端或服务端的钩子脚本(hook scripts)
- info:包含一个全局性排除文件,用以放置那些不希望被记录在 .gitignore 文件中的忽略模式(ignored patterns)
- objects:存放所有的数据内容,包括git对象、树对象、提交对象(这三种对象的概念后面会讲到)
- refs文件夹下的headers文件夹:存放最新一次提交的哈希值
- index:这个文件只有当暂存区有内容时才会出现,它就是用来存放暂存区内容的文件
用户信息配置:
该配置的是你个人的用户名称和电子邮件地址。这两条配置很重要,每
次 Git 提交时都会引用这两条信息,说明是谁提交了更新,所以会随更新内容一 起被永久纳入历史记录:
$ git config --global user.name '用户名' // 用户名配置
$ git config --global user.email '用户名@example.com' // 用户邮箱配置
配置完成后,通过以下命令检查已有配置信息:
$ git config --list
删除配置信息
$ git config --global --unset user.name
$ git config --global --unset user.email
git对象
Git 的核心部分是一个简单的键值对数据库(key-value data store)。 你可以向 Git 仓库中插入任意类型的内容,它会返回一个唯一的键,通过该键可以在任意时刻再次取回该内容
可以通过底层命令 git hash-object 来演示上述效果——该命令可将任意数据保存于 .git/objects 目录(即 对象数据库),并返回指向该数据对象的唯一的键。
就拿上面新建的那个git仓库来看,首先先确认该仓库objects目录是空的(刚初始化的状态)
$ cd gitDemo // 进入刚新建的文件夹
$ find .git/objects // 查看改目录下所有子孙文件、目录
可以看到 Git 对 objects 目录进行了初始化,并创建了 pack 和 info 子目录,但均为空。 接着,我们用 git hash-object
创建一个新的数据对象并将它手动存入你的新 Git 数据库中:
$ echo 'init content' | git hash-object -w --stdin
048b6a9fe22a319647dd670e8eafa744ac6689db
git hash-object 会接受你传给它的东西,而它只会返回可以存储在 Git 仓库中的唯一键。 -w 选项会指示该命令不要只返回键,还要将该对象写入数据库中。 最后,–stdin 选项则指示该命令从标准输入读取内容;若不指定此选项,则须在命令尾部给出待存储文件的路径。
现在我们再来看下此时objects文件夹下是否有内容了
$ find .git/objects -type f
.git/objects/04/8b6a9fe22a319647dd670e8eafa744ac6689db
可以看出objects文件夹中增加了一个04文件夹,该文件夹下有一个8b6a…开头的文件,这个文件夹名以及文件名其实就是刚才往数据库写入内容时所返回的哈希值,这个哈希值是每个文件的唯一标识
既然,我们可以往数据库中存入数据当然也可以读取数据,可以用cat-file
命令,为 cat-file 指定 -p 选项可指示该命令自动判断内容的类型,并为我们显示大致的内容:
$ git cat-file -p 048b6a9fe22a319647dd670e8eafa744ac6689db
init content
至此,你已经掌握了如何向 Git 中存入内容,以及如何将它们取出。 我们同样可以将这些操作应用于文件中的内容。 例如,可以对一个文件进行简单的版本控制。 首先,创建一个新文件haha.txt并将其内容存入数据库,它同样也会返回一个哈希值
$ echo haha > haha.txt
$ git hash-object -w haha.txt
5ad28e22767f979da2c198dc6c1003b25964e3da
这时我们再来看下objects文件夹下的内容:
$ find .git/objects -type f
.git/objects/04/8b6a9fe22a319647dd670e8eafa744ac6689db
.git/objects/5a/d28e22767f979da2c198dc6c1003b25964e3da
从结果中可以看出,此时出现了两个文件,5a文件夹下的文件就是刚新建的haha.txt所产生的一条数据记录
我们现在用git cat-file -t
再来看下这两个文件对象的类型:
返回都是blob类型,所以我们把这种blob类型的键值对对象称为git对象
注意:git hash-object 命令会生成一个哈希值,以这个哈希值为键,文件内容为值, 生成一个 blob类型的git对象,放到git数据库中去,它不会 影响暂存区,它只能代表是一个文件的版本,并不能代表一个仓库的版本
好了,我们再来回顾下前面用到的命令:
$ echo '内容' | git hash-object -w --stdin // 将内容存入数据库中,-w表示会存入数据库,如果不加,则只是将哈希值返回,并不会往数据库中添加一条数据
$ git hash-object -w -文件名 // 将该文件存入数据库
git cat-file -p 哈希值 // 读取指定哈希值文件的内容
git cat-file -t 哈希值 // 读取指定哈希值文件的类型
树对象
Git 以一种类似于 UNIX 文件系统的方式存储内容,但作了些许简化。 所有内容均以树对象和数据对象(git对象)的形式存储,其中树对象对应了 UNIX 中的目录项
构建树对象
我们可以通过update-index、write-tree、read-tree
等命令来构建 树对像并塞入到暂存区
注意:为了更好的演示,我这里对本地gitDemo文件夹清空,重新操作
$ git init //初始化git仓库
$ echo "haha v1" > haha.txt // 新建一个haha.txt文件,内容为haha v1
$ git hash-object -w haha.txt //将haha.txt文件存入git数据库中,生成一个git对象
$ git update-index --add --cacheinfo 100644 ae3828798fd14ab207a587fb10f98b8e6c1b5081 haha.txt // 从数据库中找到该git对象,然后给该git对象赋予一个文件名,然后将其存入暂存区
$ git ls-files -s // 查看暂存区内容
这里对以上命令涉及几个参数的做下说明:
文件模式为 100644,表明这是一个普通文件; 100755表示一个可执行文件; 120000表示一个符号链接。
--add 选项: 因为此前该文件并不在暂存区中 首次需要—add
–cacheinfo 选项: 因为将要添加的文件位于 Git 数据库中,而不是位于当前 目录下 所有需要—cacheinfo,相当于我们保存到暂存区的内容,需要从根据哈希值去数据库中拿
此时,需要注意,当前操作只是往暂存区中存了一份记录,还并没有生成一份快照保存到数据库中,我们可以来看下此时数据库中的情况:
$ find .git/objects -type f // 查看数据库内容
.git/objects/ae/3828798fd14ab207a587fb10f98b8e6c1b5081 //只有最初生成的一个git对象
所以我们还需要git write-tree
来给暂存区生成一个快照,然后将这个快照存入数据库中,这样就类似于实际开发中对项目的一次版本控制
$ git write-tree // 生成快照,存入数据库中
69e4ad4aebb369fc5100796bdcaa87b82cf6662d //又生成了一个哈希值
$ find .git/objects -type f // 查看数据库内容,此时就多了一条
.git/objects/69/e4ad4aebb369fc5100796bdcaa87b82cf6662d // 刚生成的树对象
.git/objects/ae/3828798fd14ab207a587fb10f98b8e6c1b5081 // 之前生成的git对象
这是我们再来看下这个哈希值的类型:
$ git cat-file -t 69e4ad4aebb369fc5100796bdcaa87b82cf6662d //查看该对象类型
tree
好,到这,基本树对象的概念也讲的差不多了,为了巩固,我们再来看个需求,如果此时需要对haha.txt文件进行修改(改为v2版本),并又多了一个xixi.txt文件,然后需要将v2版本的haha.txt,以及xixi.txt生成一个项目快照,存入数据库中(这就接近于实际开发过程中项目文件的修改与新增了,然后将操作后的最新文件提交)
不废话,直接撸…
step1:先修改haha.txt,将其改为v2版本,然后存入数据库
$ vim haha.txt //点i进入编辑模式,修改内容后:wq保存退出
$ git hash-object -w haha.txt //将修改后的v2版本haha.txt存入数据库中,生成一条新的git对象
d4240af6ecc4cf70116d2cce6c7f0bd902017b07
$ git cat-file -p d4240af6ecc4cf70116d2cce6c7f0bd902017b07 // 查看此时haha.txt的内容
haha v1
haha v2
step2:再生成一个新文件xixi.txt
$ echo "xixi" > xixi.txt
$ git hash-object -w xixi.txt
c79b51681cf62e928e77736a905ddc84492e7b82
step3:先来梳理下此时数据库中的情况,从下图中可以看到有4个对象了
69…为haha.txt文件的v1版本的树对象
ae…为haha.txt文件v1版本的git对象
c7…为xixi.txt文件的git对象
d4…为haha.txt文件v2版本的git对象
step4:将d4…和c7…两个对象内容存入暂存区,并生成一份新的暂存区快照
上图中最终生成的95…哈希值就是我们想要的haha.txt v2版本以及xixi.txt的一次快照(树对象)
我们再来看下它的类型:
所以我们称这种类型为tree的键值对为数对象,虽然树对象相比于git对象来说,增加了文件名的体现,但是我们依然不太清楚其内部每个文件的提交人、提交日期以及相关提交注释等信息,所以这就需要我们对树对象进行一下包装,也就引出了最后一个提交对象
提交对象
提交对象的格式很简单:
它先指定一个顶层树对象,代表当前项目快照;然后是作者/提交者信息(依
据你的user.name和user.email的配置来设定,外加一个时间戳)留空一行,最后是提交注释
好,既然这样,我们就来用刚才生成的一个最新树对象来生成一个提交对象: