git基本概念与原理

  git基本概念与原理

-----------------------------------------------------------------------------------------------------------------------------------------------

一、git的安装与简单配置

1、安装

apt install -y git-core

2、配置git名称与邮箱

git config --global user.name "chenux"

git config --global user.email "json_chenux@163.com"

git config --global --list   

mkdir 目录 && cd 目录

git init git_repo   #git_repo就是仓库,其目录下有个.git

1.png

3、git设置优先级

git config --system:使对应配置针对系统内所有的用户有效

git config --global:使对应配置针对当前系统用户的所有仓库生效

git config --local:使对应配置只针对当前仓库有效

local选项设置的优先级最高。

4、仓库中有文件后,保存库状态

在仓库中执行命令git add . && git commit -m "status01"

给仓库里加了些文件

2.png

5、gitk,图形化git管理工具,apt install -y gitk

 3.png

6、此图状态为在4中已经执行过的状态,此时输入指令更改文件内容

echo “aa11a1” >1.txt \

echo “b2b2b2” >> 2.txt \

touch project/pro-2.txt

执行后保存状态git add. && git commit -m “status02”,此时再启动gitk

 4.png

再次稍作修改后保存一次状态

 5.png

二、git对象

1、三种不同类型的git对象:

(1)blob:一个blob就是由一个文件转换而来,blob中只会存储文件的数据,而不会存储文件的元数据

(2)tree:一个tree就是有一个目录转换而来,tree只会存储一层目录信息,它只存储它的直接文件和直接子目录的信息,但子目录中的内容它不会保存

(3)commit:一个commit就是我们的git commit提交,它指向了一个tree,这个tree保存了某一时刻项目根目录中的直接文件和直接目录信息

总而言之,commit指向了tree,tree又指向了自己根下直接文件的blob或者子目录的tree,子目录的tree对象指向了子目录的文件blob和子子目录的tree,以此类推

2、每个git对象都有一个唯一的哈希码(SHA1算法),它是一个40位的16进制数

3、在初始提交后再进行二次提交,若存在

文件f1没有修改,在此过程后,它的blob哈希没有改变;

文件f2修改内容,在此过程后文件为f22,它的blob哈希发生改变;

存放f1和f2的目录ALPHA,在此过程中它的tree哈希发生改变,但指向文件f1的blob仍为a以前的blob

 6.png

三、git过程

1、目录结构

 7.png

可以看到,仓库内部除了自己建的文件,还有一个.git目录,该隐藏目录在git init时候就已经生成了。

.git目录:可以称之为版本库

2、在之前提交的命令中,输入的是

(1)git add -A:选择将哪些变化的内容加入到下一次提交中,这些变化内容会被索引或者暂存起来,此时生成文件的blob

(2)git commit -m “STATUSNAME”:创建commit提交对象,执行命令后git会根据索引/暂存中的结构,创建出对应的tree对象,之后git会创建一个commit对象,将新创建的commit对象指向新创建的tree

(3)这个新的提交产生后,记录了一个状态,该提交也会指向前一次的提交,每个提交都会指向自己的父提交

 8.png

(4)图片所示,当修改3.txt后,由于它是位于上一次状态中,修改它后会变红,等git add 3.txt后它的状态变为绿色,表明已加入暂存区,做好随时被提交的准备。4.txt由于没有提交,一直是红色,表明还在工作区

 9.png

四、git使用

1、git log

显示所有的git提交,git log --oneline,git log的简单模式

 10.png

git cat-file -t 哈希值 查看对象的类型

git cat-file -p 哈希值 查看对象的内容

git rev-parse 13614 通过简短的哈希值获取到整个哈希值

2、有如下操作:

git init git_test

cd git_test

echo f1>f1                                                                                         echo f2>f2

git add .

git commit -m "test01"

echo -e "haha \nheihei" >> f1

git add .

git commit -m "test02"

我们可以得知f1产生了变化,如果我们此时后悔了对f1的操作,执行命令

git log --oneline,先找出对应提交的哈希值,之后git reset --hard 82a23e7

 12.png

不难发现,虽说文件恢复到test01的状态了,但是刚才test02没了,现在如果想再次回到test02,该如何操作?

(1)git reflog,输入后可以查看之前的test02哈希码,查询到后 git reset --hard 哈希码便可回复

 13.png

(2)验证下,恢复成功

 14.png

五、git分支

1、在实际环境中,文件的修改是纵容交错的,所以存在一个问题:文件回退时,是否会影响其它文件?这里就会引入一个概念:分支

git status,显示位于主分支master

 15.png

创建新分支时,默认以当前所在分支为基础创建,创建后分支的最新提交与master一样

(1)创建分支,使用命令git branch分支名

 16.png

创建后工作区仍处在master分支上,未切换到新建的slave分支。分支的创建,并不是说明slave复制了master的分支,而是git只是创建了slave分支指针,指向了master分支对应的最新提交。

逻辑图如下:

17.png

(2)查看分支情况

git branch、git branch -v、git branch -vv

18.png

(3)分支切换

git checkout 分支名

切换后工作区也随之切换,之后的git add与git commit命令影响切换后的分支

 19.png

现有操作

 20.png

切换到我的分支slave后,输入指令后再提交,此时逻辑图就变为了

21.png

master上有5个提交,而slave上有6个提交。这时在切回master分支,看下f2文件,内容并没有改变。

 22.png

 同理,若此时在master分支修改任何文件,切换到slave分支是看不到master修改的文件,因此在项目时可以利用分支,负责某个项目a模块开发的修改文件在分支1,负责b模块开发的修改文件在分支2,后期项目合并时分支合并即可。具体的合并分支在后面。

六、HEAD

(1)HEAD指向分支

我们从之前图中可以看到,通常情况下,当处于哪个分支,HEAD就指向了哪个分支,所以git是通过HEAD找到自己所处的工作区,在git仓库下的.git里,可以查看HEAD的文件内容,其内部显示就是目前指向的分支。

 23.png

(2)HEAD指向提交

特殊情况下,HEAD可以指向某个提交,这种情况称作头分离,detached HEAD。

24.png

如上图所示,HEAD没有指向任何一个分支,而是指向了commit2这个提交。怎样才能出现这种效果呢?git checkout COMMIT_HASH,如图示

 25.png

此时输入指令,git log --oneline --all --graph

 27.png

再看下此时的git status

 26.png

分离头使用场景:

对该提交点的文件可以随便看看,或者做一些实验性的修改,并且可以将这些修改做成 提交,提交后会组成匿名分支,如果最后实验结果不满意可以丢弃之前实验行的提交, 如果实验结果满意,就可以创建一个新的分支,来永久保存这些提交。

示例:首先分离头已经指向了51fe144,此时输入了以下指令:

sed -i "s/\:/\-/g" 1.txt

git add 1.txt  && git commit -m "head_modify_1.txt"

echo headtest2 > 1.txt

git add 1.txt  && git commit -m "head_modify_1.txt-02"

修改了两次,提交了两次,我们此时通过图形工具看一下

gitk --all

 1.png

注:这里分离头选的提交刚好选到master分支的最新提交了,这能说明分离头是从master分支分出来的

修改完成后,随便切换一个分支,会出现提示,比如我在git checkout test后,有图

 2.png

如果修改项目后实验结果满意,就可以使用提示的命令

git branch 分支名 哈希值

输入git branch headfile 65a96e4后

3.png

如果不想保存实验性修改,切换分支后不用去管提示即可

七、差异比较

1、为比较差异,我们此处创建一个新的用来比对仓库

#git init git_test && cd git_test

#cat > test1 << EOF

> 1

> 2

> 3

> EOF

#cat > test2 << EOF

> a

> b

> c

> EOF

#git add . && git commit -m “new test1&2”

之后做出修改:

#sed -i “s/2//” test1 && echo haha >> test1

#sed -i "s/c/cdef/" test2

做出修改后,我们并没有git add将变化添加到暂存区,所以当前工作区和暂存区的内容是不同的,查看不同,使用命令git diff

图片1.png

查看单个文件变化的命令:git diff -- 文件名,该命令可以跟多个文件,git diff -- test1 test2 ...

图片2.png

2、查看工作区与当前分支的区别,使用命令git diff HEAD,查看工作区与某个分支的区别,如果HEAD未指向需要查看的那个分支,使用命令git diff 分支hash,比如说我目前在test分支,我查找test分支与之前status02分支的区别,找到status02的哈希码,之后便可以使用命令查看区别了。具体如下图:(使用另一个提交比较多的git仓库作示例)

图片3.png

结论:git diff比较的是工作区与暂存区的差异,git HEAD比较的是工作区与提交的区别

3、再次回到新建的git_diff库,我们继续做出两次修改

#echo test1 >>test1

#echo test2 >test2

#git add -A

#git commit -m "first modify"

#echo test1-2 >> test1

#echo test2-2 >> test2

#git add -A

#git commit -m “second modify”

查看git log --oneline后获得两次提交的哈希码,之后可以通过命令

git diff 提交HASH1  提交HASH2,此命令查看两次提交的差异

如下图所示:

图片4.png

当然,图中7ac86fc也是我们HEAD的指向处,此处使用命令git diff cd692e5 HEAD也可以

如果比对这次提交与前一次的提交,使用命令git diff HEAD HEAD~,这是一种简易写法,其它简易写法如下:

HEAD:等于HEAD~0,表示最新的一次提交

HEAD~:等于HEAD~1,表示最新提交的前一次提交

HEAD~~:等于HEAD~2,表示最新提交的前前一次提交

HEAD~~~:等于HEAD~3,表示最新提交的前前前提交

另:根据提交名获取对应的哈希码,使用命令举例:

git rev-parse HEAD~

git rev-parse master

4、如果是分支之间的比对,比如说git diff master slave,此时对比的是两个分支各自最新提交的比对。

如图所示

图片5.png

总结:

#git diff==>比较工作区和暂存区

#git diff HEAD==>比较工作区和当前分支最新的提交,HEAD可以换成别的分支的名字,比如test分支,"#git diff test"表示比较当前工作区和test分支最新的提交之间的差异,也可以把HEAD替换成任何一个commit的ID,表示比较当前工作区

和对应提交之间的差异。

#git diff --cached==>比较暂存区和当前分支最新的提交

#git diff -- file1==>比较工作区和暂存中file1文件的差异

#git diff -- ./file1==>只比较工作区和暂存区中file1文件的差异

#git diff -- file1 file2==>比较暂存区file1和file2文件差异

#git diff -- dir1/ ==>只比较工作区和暂存区中dir1目录中所有文件的差异

#git diff HEAD -- ./file1 ==>只比较工作区和当前分支最新的提交中file1文件的差异,HEAD可以替换成分支名或者commitID

#git diff branch01 -- ./file1==>只比较工作区和branch01分支最新的提交中file1文件的差异

#git diff --cached branch01==>比较暂存区和branch01分支最新的提交

#git diff --cached branch01 --./file1==>只比较暂存区和branch01分支最新的提交中file1文件的差异

#git diff HEAD~ HEAD==>比较当前分支中最新的两个提交之间的差异

#git diff HEAD~ HEAD -- file1==>比较当前分支中最新的两个提交中的file1文件的差异

#git diff commit01 commit02==>比较两个commit之间的差异

#git diff commit01..commit02==>同比较两个commit之间的差异,与上个命令等效

#git diff branch01 branch02==>比较两个分支上最新提交之间的差异

#git diff branch01..branch02==>比较两个分支上最新提交之间的差异,与上个命令等效

三、撤销暂存中的变更

1、平时进行版本管理中,修改某处代码提交了,之后后悔了怎么办?

使用命令,git reset HEAD,此操作为“文件修改已记录到暂存中,但后悔这么做,恢复到git add之前”,也就是把暂存区恢复到与最新的commit状态保持一致。如图所示

图片6.png

从图中我们看出,git reset HEAD撤销了之前的git add操作,f1和f2的修改又回到了工作区。

我们这里是撤销了所有文件的更改,如果只想撤销一个文件的更改,使用命令

git reset HEAD -- f1

图片7.png

2、git reset、git reset --hard和git reset --soft

(1)git reset = git reset --mixed

git reset HEAD,将所有已经暂存的内容从暂存区撤销了(即暂存区与最近提交中的状态一致)

git reset COMMIT_HASH,将HEAD指针指向该次提交,并且将提交中的内容同步到暂存区,但工作区中内容不受影响。产生的结果是工作区记录的是reset前的最新提交与reset后的提交的变化内容,在git add和git commit之后,文件依然是reset前的文件,但HEAD指向了reset后的提交。此处示例git reset COMMIT_HASH:

首先我们新建了一个新git库,并做了7次提交,之后git reset到第3次提交

 图片8.png

此时我们git diff,可以看下工作区与第3次提交的差异

图片9.png

这也是第3次提交与我们git reset前的最新提交相比产生的差异,哪怕是git add之后继续git commit,结果也是git reset前的结果,但是HEAD此时在第3次的提交上。

 图片10.png

git reset --hard HEAD,将所有区域恢复成了最近的提交中的状态(即工作区、暂存区都与最近提交中的状态一致)

(2)git reset --hard COMMIT_HASH,将HEAD指向该次提交,并且将所有区域内容进行同步,也就是恢复到该次提交状态。此处示例git reset --hard COMMIT_HASH:

#echo "1 2 3" > f1

#echo "a b c" > f2

#git add .

#git commit -m "first add f1&f2"

 

#echo "456" >>f1

#git add f1

#git commit -m "second modify f1"

 

#echo "def" > f2

#git add f2

#git commit -m "third update f2"

                                                                                                              

#echo "linux" >> f1

#echo "chen" >>f2

#git add .

#git commit -m "4th update f1&f2"

图片11.png

git reset --hard COMMIT_HASH,回到first f1&f2这个提交点

图片12.png

(2)git reset --soft

此命令只将HEAD指针指向commitID对应的提交,但是不会操作暂存区和工作区,也就是说,当前分支中的最新提交会变成commitID对应的提交,工作区和暂存区中的内容或者变更不会受到任何影响。此处示例:git reset --soft

如图中所示,我们将提交指向了最初新建的commit,文件结果没有改变

图片13.png

3、git checkout撤销文件

撤销工作区已修改但未git add进入暂存区的文件,使其回到上一次暂存后的状态,如果不存在上一次的暂存,则回到最新的提交状态。该命令的使用,对象文件必须之前被git add追踪过,若是新建的文件没有被追踪过,此撤销方法对其无效。

git checkout -- FILE,若是该目录全部撤销更改,git checkout -- .

图片14.png

四、git合并分支

1、合并方向

图片15.png

如图示,有两种情况如左右两图,分支B是基于分支A产生的分支,红色部分是分支A的提交,橙色部分是分支B的提交,左图为合并分支后合并分支的HEAD在A上,分支B的HEAD仍在原处;右图同理亦然。

2、Fast-foward

图片16.png

上图所示即为分支A与分支B的合并,不过我们也可以使用一种快捷的合并方式:Fast-forward,也译为“快进”或者“快速重定向”,这种情况下的快速合并,最终结果会变为如图:

图片17.png

此种情况为:基于分支A创建的分支B,分支A没有任何新提交产生,如果此时将分支A合并到分支B,只需要将分支A的指针指向到B分支的最新提交即可。这就是Fast-forward。

图片18.png

如图所示,右图分支A产生分支B后又产生了新的提交,所以不能用fast-forward的合并方式

示例:首先输入以下指令,slv1从master分出去之后,master再无新的提交,slv1产生了新的提交

#touch f1

#git add f1  

#git commit -m "add f1"

#echo f1> f1 && git add . && git commit -m "insert f1 in f1"

#git branch slv1

#git status

#git checkout slv1

#touch f2 && git add f2 && git commit -m "touch f2 at slv1"

#git log --oneline

#echo "f2 in slv1" > f2 && git add f2 && git commit -m "insert f2 in f2 at slv1"

输入指令之后,我们的git符合了可用fast-forward的模型

图片19.png

此时我们进行分支合并

git merge 分支名

先git cheout 分支A ,再git merge 分支B:将分支B合并到分支A,

图片20.png

图中所示,这种方式即为Fast-forward,git log --oneline可用看到HEAD同时指向了两个分支

图片21.png

3、正常分支合并

建立个如下图的模型

图片22.png

首先有如下操作:

#echo 1 > 1.txt && git add . && git commit -m "touch 1"

#echo 2 >> 1.txt && git add . && git commit -m "insert 2 to 1"                                                                             

#git branch slv01

#echo 3 >> 1.txt  && git add 1.txt && git commit -m "insert 3 to 1"

#git checkout slv01

#echo a > a.txt && git add a.txt && git commit -m "touch a"

#echo ab >> a.txt && git add a.txt && git commit -m "insert ab to a"

gitk --all看到模型

图片23.png

此时再合并分支:git checkout master && git merge slv01

输入后会弹出nano编辑器,编辑器是需要为合并后的新提交而准备。

图片24.png

不添加就用它的默认注释即可。

gitk后看到结构已变为:

图片25.png

4、常用合并命令

#git merge 分支A:表示将分支A合并到当前分支。

#git merge --no-ff 分支A:表示将分支A合并到当前分支,但是明确指定不使用"Fast-forward"的模式进行合并

 

#git merge --ff-only 分支A:表示将A分支合并到当前分支,但是只有在符合"Fast-forward"模式的前提下才能合并成功,在不符合"Fast-forward"模式的前提下,合并操作会自动终止,换句话说就是,当能使用"Fast-forward"模式合并时,合并正常执行,当不能使用"Fast-forward"模式合并时,则不进行合并。

 

#git merge --no-edit 分支A:表示将A分支合并到当前分支,但是没有编辑默认注释的机会,也就是说,在创建合并提交之前不会调用编辑器(上文的示例中会默认调用vim编辑器,以便使用者能够有机会编辑默认的注释信息),换句话说就是,让合并提交直接使用默认生成的注释,默认注释为" Merge branch 'BranchName' "

 

#git merge 分支A --no-ff -m "merge A into master,test merge message":表示将A分支合并到当前分支,并且使用-m参数指定合并提交对应的注释信息。

注意,为了保险起见,需要同时使用"--no-ff"参数,否则在满足"Fast-forward"模式的情况下,会直接使用"Fast-forward"模式进行合并,从而忽略了-m选项对应的注释信息(因为使用"Fast-forward"模式合并后不会产生新提交,所以为提交准备的注释信息会被忽略)

5、合并冲突解决

如果两个分支中同一个文件中的同一行内容不一样,合并分支时就会出现冲突,此时需要我们认为介入确认,这就是解决冲突的过程。

示例:首先输入指令造成两条分支同一文件同一行内容不同

#git init git_test

#echo 1 > f1.txt && git add . && git commit -m "add 1 in f1"

#echo 2 >> f1.txt && git add f1.txt && git commit -m "add 2 in f1"

#sed -i "s/2/2master/" f1.txt

#git add f1.txt && git commit -m "modify 2 at master"

#git checkout -b slv01        --------------->新建并切换新分支                                                                                    

#sed -i "s/2/2slv01/" f1.txt

#git add f1.txt && git commit -m “modify 2 at slv01”

得到结果:

图片26.png

切到master分支,进行合并,有结果

图片27.png

(1)对于一样的内容已经合并,对于冲突内容给出提示,修复成一样的内容或者git merge --abort放弃合并。

此时再看f1.txt,会标出冲突结果:

当前分支中的冲突内容: "<<<<<<< HEAD"与"=======" 之间。

另一条合并分支中的冲突内容: "======="与"<<<<<<< new" 之间。

图片28.png

我们取消冲突,git merge --abort后查看git status,已经提示干净的工作区了

(2)更改冲突文件使其不冲突

将两个分支中的f1都修改后提交,变为2master 2slv01,之后git merge后就进入了nano编辑器编辑自己想要的注释了。

(3)合并后对于之前的slv01分支,就可以不要了,处于非slv01分支下删除slv01分支,git branch -d slv01。

当存在某个分支没有合并过并且需要删除时,git会提示,如果不予理会这种提示,可以使用命令:git branch -D 分支名

五、github

github:git的远程仓库

1、使用前准备

(1)https://github.com注册账号

注册好后登录账号,右上方选new repository

图片29.png

继续进行创建远程仓库

图片30.png

创建好以后屏幕左侧会有自己的远程仓库名,点击进去可以看到连接自己远程仓库的两种方式,https或者ssh

图片31.png

(2)ssh方式添加公钥

右上角姓名处下箭头点击settings,之后选择SSH and GPG keys

图片32.png

建立公钥私钥:

ssh-keygen -t rsa,建立密钥对

图片33.png

去查看公钥文件,将文件内容写入github的ssh下

图片34.png

点击add ssh key

至此,github上的远程仓库可以使用ssh方式连接了。

2、git clone

git clone git@github.com:json-chenux/test.git

注意:如果这里提示ssh拒绝连接的权限问题,需要将之前本机做过ssh免密登录时生成的id_rsa.pub文件中公钥内容粘贴至列表

git remote -v,可以看到本地仓库与远程仓库的对应关系

图片35.png

图中orgin为远程仓库名称,可以更改

3、推送至远程仓库

git push,推送本地仓库至远程仓库

刚在github上的仓库建立好后,进入该仓库目录,输入一些命令:

#touch t1

#git add . && git commit -m "build t1"

#echo 123 > t1 && git add . && git commit -m "add 123 in t1"

#git checkout -b slv

#echo abc > t2 && git add t2 && git commit -m "built add t2"

之后测试远程连接的仓库,使用命令:

git push origin master:master,将本地master分支推送到远程master分支,

"master:master"中冒号左边的master代表你要推送的本地master分支,冒号右边的master代表推送到远程仓库中的master分支

命令结果:

图片36.png

如果本地仓库master 分支推送至远程仓库并不存在的分支,即使用命令:

git push origin master:m1,远程仓库会自动创建新的分支m1。

#git push:如果git中只有一条分支,推送时可以使用命令git push,当远程仓库没有该分支,会自动新建这个分支。

当我们把新建的分支推向远程仓库时,需要输入指令

#git push --set-upstream origin slv:slv   ----------->git 1.x版本

#git push -u orgin slv:slv  ------------->git 2.x版本

,将此分支slv推到远程仓库后,等下次在slv分支文件做出改变时,位于slv分支时输入git push,便可以自动推送slv分支到远程slv分支了。

图示:

#git branch -vv:显示本地分支信息

#git branch -vva:显示本地与远程分支信息

图中origin只有master有,slv没有,说明slv没有推送至远程仓库

图片37.png

将slv分支推入远程仓库,之后再git branch -vva,发现本地slv分支已经至远程仓库的myremote-slv分支。

图片38.png

4、他人加入该远程仓库

使用命令:git remote add origin git@github.com:json-chenux/test.git

5、git pull

#git push --all:此命令表示当本地各分支与远程仓库各分支同名时,push所有分支的更新到对应的远程分支。

#git fetch:此命令表示获取远程仓库的更新到本地,但是不会更新本地分支中的代码。

#git pull:此命令表示当本地分支与上游分支同名时,对当前分支执行pull操作,对其他分支执行fetch操作,具体的差异主要取决于对应的远程分支有没有更新。

 #git pull remote branchA:此命令表示将remote仓库的A分支pull到本地当前所在分支,如果你想要pull到本地的A分支,需要先checkout到本地A分支中。

 #git pull remote branchA:branchB:此命令表示将远程仓库的A分支pull到本地的B分支,成功之后(如果操作失败,则后面的操作不会执行)再将远程A分支pull到本地的当前所在的分支。       

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值