文章目录
一、GIT体系介绍
1.1 代码管理工具:SVN、CVS、GIT
GIT和SVN有什么区别:
- 存储方式不一样
- 使用方式不一样
- 管理模式不一样
1.2 存储方式
GIT把内容按元数据方式存储类似k/v数据库,而SVN是按文件(新版svn已改成元数据存储)
git hash-object -w 文件
将文件夹写入到数据库中(本地数据库.git/object
)
git cat-file -p Hash值
在数据库(hashmap)中根据hash值查找文件内容
保存到数据库但是还没有保存到暂存区中(类似C语言中的指针)
git add 文件
只是让本地指针跟踪到到了该文件,但是还没有写入到版本中
一旦git commit 文件 -m message
就会在文件中的refs/heads
中写入该此提交(保存的是key(hash值))
SVN没用过,不清楚,听说是cp readme.md readme.md.version1
的保存方式
1.3 使用方式区别
从本地把文件推送远程服务,SVN只需要commint 而GIT需要 add、commint、push 三个步骤
-
SVN基本使用过程
-
Git基本使用过程
git add 文件是添加到暂存区
git rm --cached 文件
是从暂存区删除
//待考证 只要不push,代码都可以找回,并修改,一旦push到远程仓库就可能控制不了远程的文件了(没有权限)
1.4 版本管理模式区别
git 是一个分布式的版本管理系统,而要SVN是一个远程集中式的管理系统
集中式
分布式
二、核心命令
2.1 安装git 客户端安装
2.1.1 Windows
官方客户端
其它客户端
2.2.2 linux
$ git
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git
或者
sudo apt-get install git-core
因为Git是分布式版本控制系统,所以,每个机器都必须自报家门:你的名字和Email地址。你也许会担心,如果有人故意冒充别人怎么办?这个不必担心,首先我们相信大家都是善良无知的群众,其次,真的有冒充的也是有办法可查的。
注意git config
命令的--global
参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。
2.2 认识GIT的基本使用
2.2.1 基本配置
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"
2.2.2 git 项目创建与克隆
或者
克隆:
git clone 项目地址
2.3 文件提交与推送
完整模拟从项目添加到push 过程
- 创建项目
- 初始化git仓库
- 提交文件
- 远程关联
- push 至远程仓库
- 过程:init/clone add commit remote push
本地初始化GIT 仓库:
-
基于远程仓库克隆至本地
git clone <remote_url>
-
当前目录初始化为git 本地仓库
git init <directory>
-
基于mvn 模板创建项目
mvn archetype:generate
-
将本地仓库和yuanchengck进行关联
git remote add origin https://XX
本地添加
-
添加指定文件至暂存区
git add <fileName>
-
添加指定目录至暂存区
git add <directory>
-
添加所有
git add -A
或
git add .
-
将指定目录及子目录移除出暂存区
git rm --cached 文件/-r
git rm --cached
文件或者git rm --cache -r
移除还没有提交所有的文件 -
添加勿略配置文件
.gitignore
本地提交
-
提交至本地仓库
git commit file -m '提交评论'
-
快捷提交至本地仓库, 提交所有 -am或者.(.代表本目录下的所有内容)
git commit -am '快添加与提交'
-
查看本地仓库状态
git status
远程推送 -
设置远程分支并推送
git push --set-upstream origin master
命令解释git push
推送命令,第一次推送需要设置推送的远程分支,后面不改远程分支就不需要了。--set-upstream
设置远程分支,master
远程分支名称
推送不成功,说明本仓库的内容和远程仓库中的内容不一致。需要git pull
还不如直接git clone 项目地址
不用初始化,直接。
另外:
我们提交代码,有时有编译时生成的修改项并不想提交,因为每次编译都会生成不一样的东西,我们可以进行过滤,步骤如下:
-
在工程根目录下新建 .gitignore文件
vim .gitignore
此处默认使用linux指令新建,需要会vim编辑器的基本用发(我也是不久前刚系统的学习了一下 😂)
当然如果是其他的文本编辑器也行,如notepad++ -
在生成的.gitignore文件中输入你不想上传的文件,我的如下:
*.iml .gradle /local.properties /.idea/workspace.xml /build
设置完之后,下次上传之后这些目录的修改就不在版本控制范围之内了
2.4 分支管理
git branch 查看分支
:-a
所有分支 -av
详细信息 -avv
远程仓库和本地仓库的关系
创建分支:
基于当前分支 创建一个新分支
基于远程分支 创建一个新分支
基于一个提交 创建一个新分支
基于Tag 创建一个新分支
基于当前分支
基于远程分支
基于一个提交
查看分支:
删除分支:
切换分支:
解决冲突:
当前分支一样,证明没有冲突。
本地分支修改:不push
远程修改:
文件冲突:
本地提交:本地分支改变,远程分支远程修改,导致不一致。
不能push
能pull
查看状态:
提示两边都修改了,需要merge。
修改为:
#基于当前分支新建分支
git branch
#基于提交新建分支
git branch
$ git branch -d {dev}
#切换分支
git checkout
#合并分支
git merge
#解决冲突,如果因冲突导致自动合并失败,此时 status 为mergeing 状态.
#需要手动修改后重新提交(commit)
2.5 远程仓库管理
-
查看远程配置
git remote [-v]
-
添加远程地址
git remote add origin http:xxx.xxx
-
删除远程地址
git remote remove origin
-
上传新分支至远程(与本地建立关联)
git push --set-upstream origin master
-
将本地分支与远程建立关联
git branch --track --set-upstream-to=origin/test test
2.6 tag 管理
跟分支类似,不过tag是只读的。
- 查看当前
git tag
- 创建分支
git tag <tag name> <branch name>
基于当前分支创建tag
基于tag创建tag
基于提交创建tag
- 删除分支
git tag -d <tag name>
2.7 日志管理(打印提交日志)
-
查看当前分支下所有提交日志
git log
-
查看当前分支下所有提交日志
git log {branch}
-
单行显示日志
git log --oneline
-
比较两个分支的区别
git log master..experiment
后一个有多少提交没有提交到前一个
git log master..test
: test有多少提交没有提交到master
git log test..master
: master有多少提交没有提交到test
合并分支:合并以后无差别
-
以图表的方式显示提交合并网络
git log --pretty=format:'%h %s' --graph
-
git show 分支名
:最后一次提交的详细的内容
-
查看文件:显示本地文件和本地仓库内部的不同
git status
命令可以让我们时刻掌握仓库当前的状态,上面的命令输出告诉我们,README.MF
被修改过了,但还没有准备提交的修改。虽然Git告诉我们readme.txt被修改了,但如果能看看具体修改了什么内容,自然是很好的。比如你休假两周从国外回来,第一天上班时,已经记不清上次怎么修改的readme.txt,所以,需要用git diff这个命令看看:
2.7 管理修改
-
第一次修改 -> git add -> 第二次修改 -> git commit
-
Git管理的是修改,当你用
git add
命令后,在工作区的第一次修改被放入暂存区,准备提交,但是,在工作区的第二次修改并没有放入暂存区,所以,git commit
只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。 -
提交后,用
git diff HEAD -- 文件
命令可以查看工作区和版本库里面最新版本的区别 -
那怎么提交第二次修改呢?你可以继续
git add
再git commit
,也可以别着急提交第一次修改,先git add
第二次修改,再git commit
,就相当于把两次修改合并后一块提交了: -
步骤:第一次修改 -> git add -> 第二次修改 -> git add -> git commit
总结:每次提交都要先git add
然后在git commit
命令解释git checkout -- file
git checkout -- file
可以丢弃工作区的修改:
$ git checkout -- README.MF
把本地的内容修改了,想恢复到上一次提交的内容,此时还没有添加到暂存区,没有执行git add
命令。此时恢复的是上一次提交的暂存区的文件内容。
命令解释git reset HEAD 文件
:撤回git add
操作
git reset HEAD
: 回退到暂存区
若本地文件修改了,不小心git add
,就使用该命令,作用撤销git add
若本地文件还想变回原来的文件(上一次提交的内容),则执行git checkout -- file
若不想放弃修改,则就在本地文件继续修改,修改好了在git add
即可
命令解释git reset --hard hash值
结合git reflog
可去任何地方:回退commit
版本
可以去到之前任意一个执行过的git commit
的地方
-
git relog
记录每一次命令
在Git中,总是有后悔药可以吃的。当你用$ git reset --hard HEAD^回退到add distributed版本时,再想恢复到append GPL,就必须找到append GPL的commit id。Git提供了一个命令git reflog用来记录你的每一次命令:
-
git diff HEAD file
查看文件修改情况
解释:绿色代表加入到暂存区还没有提交,红色代表还没有添加到暂存区
简单说就是没有执行git add
是红色,执行git add
是绿色
+:号代表添加的内容
-:代表删除的内容
黑白色的内容就是代表此时本地仓库(最后一次提交)里面的内容
三、git 底层原理
3.1 GIT存储对像(hashMap)
Git 是一个内容寻址文件系统,其核心部分是一个简单的键值对数据库(key-value data store),你可以向数据库中插入任意内容,它会返回一个用于取回该值的hash 键。
-
git 键值库中插入数据
根据内容生成hash值,内容一样hash值一样。同一个操作系统的值一样的,window和Ubuntu不一样,亲自测试。
echo 'luban is good man' | git hash-object -w --stdin
79362d07cf264f8078b489a47132afbc73f87b9a
-
基于键获取指定内容
git cat-file -p 79362d07cf264f8078b489a47132afbc73f87b9a
Git基于该功能 把每个文件的版本中内容都保存在数据库中,当要进行版本回滚的时候就通过其中一个键将期取回并替换。
模拟演示git 版写入与回滚过程
-
查找所有的git 对像
find .git/objects/ -type f
-
写入版本1
echo 'version1' > README.MF; git hash-object -w README.MF
;
5bdcfc19f119febc749eef9a9551bc335cb965e2
-
写入版本2
echo 'version2' > README.MF; git hash-object -w README.MF;
df7af2c382e49245443687973ceb711b2b74cb4a
-
写入版本3
echo 'version3' > README.MF; git hash-object -w README.MF;
777d3c2b51e73cc9177c34d14d9b6079b7c3ae7d
-
回滚指定版本
git cat-file -p df7af2c382e49245443687973ceb711b2b74cb4a> README.MF
只是查了该hash值的内容,没有写入文件,顾问兼内容不败你
所以我们平常用的git add
其实就是把修改之后的内容 插入到键值库中。当我们执行git add README.MF
等同于执行了git hash-object -w README.MF
把文件写到数据库中。
我们解决了存储的问题,但其只能存储内容同并没有存储文件名,如果要进行回滚 怎么知道哪个内容对应哪个文件呢?接下要讲的就是树对象,它解决了文件名存储的问题 。
- 所有指针(所有hash值)
- 版本回滚:
将hash值存储在README.MF文件中,实现版本回滚
3.2 GIT树对像
树对像解决了文件名的问题,它的目的将多个文件名组织在一起,其内包含多个文件名称与其对应的Key和其它树对像的用引用,可以理解成操作系统当中的文件夹,一个文件夹包含多个文件和多个其它文件夹。
- 查看对象类型
git cat-file -t hash值
每一个分支当中都关联了一个树对像,他存储了当前分支下所有的文件名及对应的 key.
3.3 git提交对象
三次版本所以有三个节点
- 少了写入数据库命令
git hash-object -w hello.txt
git hash-object -w hello.txt
git hash-object -w hello.txt
git hash-object -w hello.txt
git hash-object -w hello.txt
git hash-object -w hello.txt
git hash-object -w hello.txt
git hash-object -w hello.txt
所以这个节点在object里面没有值
- 执行
git add
添加到暂存区,其实就是写入到object(数据库中Key/Value)里面去。
git add hello.txt
多了一行。
已经写入了数据库中,不会重复写入数据库。
增加两个对象,一个提交对象,一个树对像。
一次提交即为当前版本的一个快照,该快照就是通过提交对像保存,其存储的内容为:一个顶级树对象、上一次提交的对像啥希、提交者用户名及邮箱、提交时间戳、提交评论。
- 查看分支树
git cat-file -p master^{tree}
- 查看提交生成的对象(重要)
git cat-file -p hash值
- 查看树对像
通过上面的知识,我们可以推测出从修改一个文件到提交的过程总共生成了三个对像:
一个内容对象 ==> 存储了文件内容
一个树对像 ==> 存储了文件名及内容对像的key
一个提交对像 ==> 存储了树对像的key 及提交评论。
演示文件提交过程 - 两次提交对象,我们选取第一个画图
- tree是树对像,本对象是此次提交的内容
- parent是上次提交的树对像
- 在src下创建新文件,查看变化
- 只显示一个parent
- 红颜色代表变化,黑色代表不变
3.4 GIT引用
当我们执行 git branch {branchName}
时创建了一个分支,其本质就是在git 基于指定提交创建了一个引用文件,保存在 .git\refs\heads\
下。
-
演示分支的创建
git branch dev
cat.git\refs\heads\dev
-
git 总共 有三种类型的引用:
- 分支引用
- 远程分支引用
- 标签引用(只读不写)
3.4 GITGC
GC清理无用的引用并打包后删除
- 创建一个本地仓库test, 创建一个readme.md文件并提交
- 查看对象
- 创建分支dec,切换分支dec创建文件并提交
- 查看对象和日志
- 切换分支master, 并删除分支dec
git gc
后查看对象
tag和branch都是一个引用,文件内部存储的是hash的key。
tag是只能读(不能操作在该对象树下的所有文件),branch是可以修改的。