git 0基础入门—git入门与实践(3)—详细图解git分支操作及超详细的解决分支冲突及reset和checkout原理深究和详细操作(2021.04.15更新)

1. git入门与实践(3)

2. 分支入门操作

某天完成了项目的一次开发后,准备开始第二次开发,但是如果直接在一次开发的基础上更改,我们的文件就是1+2,管理起来相当不方便。


2.1 查看分支: git branch

前面*代表当前所处的分支

image-20200630115535628


2.2 创建分支:git branch 分支名称

创建出来的分支,并不会改变我们当前所在的位置。

git branch branch1

git branch

image-20200630115714216

git是如何判断我们所处当前哪个分支上呢?

(HEAD -> master, branch1) git是通过HEAD指向获取当前分支的。

image-20200630115835314


2.3 切换分支:git checkout 分支名

HEAD指向切换的分支

将工作目录恢复成当前分支的快照

简写命令:git checkout -b 分支名 创建分支 + 切换分支

image-20200630122154489

之后提交一个文件,之后看日志,发现HEAD已经指向branch1了。再往后仔细看标注,就是master分支的日志了。

image-20200630122707791

除此之外,如果是branch1分支,切换过去,只会显示该分支所有的文件。

image-20200630123518902

image-20200630123505313

切换master分支,则是它所有的文件。

image-20200630123533974

image-20200630124449317

branch1分支下删除所有文件

git rm * -r

image-20200630124132532

image-20200630124145865

再切回去:

image-20200630124351654

文件都在!因此各个分支是互不影响的!


2.3.1 分支流程详解

首先有一次c1的提交,在c1的基础上还有c2的提交,这些操作都是在默认的master分支上进行的,在c2的提交上又创建新分支branch1git如何判断在当前在哪个分支上,就是通过HEAD指针。当前HEAD指向master

image-20200630125634988

我们切换分支后,HEAD就指向branch1了。

image-20200630125740429

我们再提交c3,此时branch1向前移动,并带着HEAD一块移动了。此时branch1的路线就是c1->c2->c3

image-20200630130034936

此时我们想操作master分支,就需要切换过去了。

image-20200630130300992

之后再进行c4提交。此时master的路线就是c1->c2->c4

image-20200630165248943

image-20200630130540706


2.3.1.1 流程动态演示


3. 分支合并

假如我们开始开发,基础功能是c1提交和c2提交之间的内容,处理的是第一个版本的内容。

image-20200630153333597

为了便于后面的开发,我们将其分成两个分支,一个master,一个branch1

image-20200630153356644

image-20200630153405315

紧接着我们在分支branch1上开始开发,然后进行功能提升,进行第二个版本开发,提交c3c5

image-20200630153421423

现在又出现了问题,第一个版本开发的时候,有一些小的bug。这个时候需要提交一些补丁,这可怎么呢?

首先需要回退到c2上,这上面有第一个版本开发的时候所有的记录,这个时候我们实际不用回退。

直接切换到master的分支,在这个分支修改bug并提交c4。这个时候就可以将c1->c2->c4的代码进行打包,替换线上的代码了。

image-20200630153434584

第二个版本开发完成至c5提交。唯独缺少了c4的补丁,你可能会想到再创建一个提交修改bug。明显是不科学的,这样重复处理,是非常耽误时间和工作效率的。因此git就提供了一个很好用的功能,即合并分支。

模拟一下以上操作,新建一个文件夹。

image-20200630154304428

image-20200630154311763

c1提交

image-20200630154401011

c2提交

image-20200630154346653

git checkout -b branch1 创建分支并使HEAD指向新分支,-b就代表 branch

image-20200630154458131

完成c3提交

image-20200630154716582

切换回master打补丁,c4提交

image-20200630154858983


3.1 HEAD查看命令

3.1.1 git log --decorate

--decorate 参数用来显示一些相关的信息(带有分支信息),如HEAD、分支名、tag名等

image-20200630155229540


3.1.2 git log --oneline --decorate

--oneline 校验和(哈希)

查看分支指向+哈希

以上显示不太明显,我们可以使用该命令

image-20200630155627931

切换回branch1看看

image-20200630155853565

我们继续提交c5

image-20200630160016535

image-20200630160051029

注意:在开发中,一定有一个主分支,利于对当前分支的管理,因为分支不会影响主分支。一般都将master设置为主分支


3.1.3 git log --oneline --decorate --all --graph

查看命令的完美组合—> 包含了所有分支的提交,以树形图形形式展现。

--all 所有分支信息

--graph 打印出树形图

image-20200630160143289


3.2 git merge 分支合并

注意:在我们日常开发中最好必须有一个主分支,有一个主分支就利用我们对分支进行管理。比如平时打游戏,就会有一个主线任务和一个支线任务。然后支线任务也不会影响主线,实际上项目中主线就是项目的版本。默认情形下,大家都把默认分支master作为主分支。

git merge 目标分支

将目标分支的内容合并到当前分支

image-20200630160943907

git merge branch1branch1分支合并到主分支master上来。

image-20200630161010099

image-20200630161135347

此时完成了合并,解决了第二个版本中第一个版本的bug,我们想让第二个版本上线,就打包c6代码即可。


image-20200630161317364


3.2.1 模拟合并小结

其实最终合并关注3个点,一个是必须有共同父节点c2,其次是master末尾节点c4brach末尾节点c5,将c4c5实际整合成了c6c6就完美解决了 => S第二个开发版本c2->c3->c5中需要修复第一个开发版本bug的问题了。


3.3 快速前移

masterbranch没有形成分叉,依旧是处于一条路径线,当HEAD落后于所要合并的分支,将会形成快速前移。

先初始化目录

image-20200630165623735

进行两次提交

image-20200630165809901

创建分支branch1,并切换过去,做第三次提交

git checkout -b branch1

image-20200630170029685

git log --oneline --decorate --all --graph

image-20200630170215832

image-20200630171144789

切换到master,并与branch1合并

image-20200630171240557

image-20200630170412680

git log --oneline --decorate --all --graph

我们发现了奇怪的问题,masterbranch1都跑到c3了。为啥成这样了, 因为快速前移,我们仔细观察上图,就有提示:Fast-forward

image-20200630170457597

仔细观察上图发现合并前并没形成分叉,branch1只是在master的上开了一个分支。

master:c1->c2

branch1:c1->c2->c3

master上的提交都包括在branch1里了,这时执行的合并就和之前不一样了,不会出现新的提交,而是将master的指针往前移动了,同样HEAD会跟随master往前移。我们把这个过程称为**“快速前移”**。

image-20200630171357291

实际快速前移在合并操作里是不友好的,我们打印log,它这没有任何信息(有关合并的信息)可以表示出来。

image-20200630182842189

那如何解决呢?


3.3.1 --no-ff

禁止快速前移(可以commit记录描述为合并操作)

我们回退回去!

image-20200630183659778

image-20200630183638221

git merge --no-ff -m '这是一次合并操作' branch1 (要加描述)

image-20200630184018334

image-20200630184035268

image-20200630184109198


3.3.2 模拟快速前移


4. 分支冲突

4.1 分叉冲突

初始化git仓库

image-20200630194716040

创建并切换一个分支

image-20200630194837077

修改a文件,并提交

image-20200630195055584

image-20200630195030727

切换成master主线,a.txt内容为空,因为提交也分先后,在master中只是新建了a.txt,在branch1里才修改了内容。然后切换回master,会重置工作目录。我们添加a.txt的内容。

image-20200630200757949

image-20200630200740943

提交它!

image-20200630200909375

我们把branch1合并过来,发现冲突报错了。

image-20200630201019500

我们打开a.txt

image-20200630201105508

<<<<<<< HEAD master添加的内容,截止到分隔符

======= 分隔符

>>>>>>> branch1 从分隔符到当前,是branch1添加的内容

<<<<<<< HEAD
1
2
3
4
5
第二次修改
=======
a
b
c
第1次修改
>>>>>>> branch1

想要解决冲突,打开文本,可将内容合并或删除后不需要的内容,再提交即可,这里我们只保留master添加的内容。

image-20200630201836938

image-20200630200740943


4.2 快速前移(不产生冲突)

有的时候修改,不会产生冲突。

我们新建并提交一个b文件

image-20200630202532916

添加内容,并提交

image-20200630202616546

image-20200630202744771

再开一个分支branch2,对b进行修改!再提交。

image-20200630203218956

image-20200630203305572

切换到master再合并

提示Fast-forward(快速前移),并没有产生冲突!

image-20200630203518373

image-20200630203218956


4.3 如何判断是否会引起冲突

4.3.1 快速前移不会引起冲突

如上master1次提交:新建文件并添加内容

再创建分支,修改文件中的内容

最后切换到master合并分支,合并之前,git会判断两个分支是什么关系?

如果是祖先级关系,masterbranch2处于同一条commit路径上(直接级祖先关系),就进行了快速前移。

✔️ masterbranch2直接进行合并,不会引起冲突

image-20200630204554374

4.3.2 分叉冲突可能会引起冲突

如上上面的操作是,master1次提交:新建文件并创建并切换分支,添加文件中的内容

再切换回master,修改文件中的内容,提交之后最后进行合并。这时候出现了分叉,则不会进行快速平移了,git进行如下的分支判断:

image-20200630204956306

  1. 分析masterbranch1中的修改是否一致,如果一致合并将会成为一次空合并(因为内容完全一致,没有合并的需要)
  2. 如果不一致,是否修改的同一个文件内容,如果是,产生冲突

5. 解决分支冲突

解决分支冲突:

​ ① 手动解决冲突部分

​ ② 解决完成后再次提交,会以这次提交内容为准


6. 删除分支

合并完成后,分支就没有任何的后期用途了(实际就是打了补丁的作用),这时候我们需要手动删除分支,也是以防止自己词穷的时候出现重复命名分支的情况。

git branch -d 分支名称 -d -> delete

image-20200630210133151


6.1 无法删除的分支

注意:HEAD所指向的分支,无法删除

image-20200630210258162

还有一种情形也无法删除:

如果你的分支,从未合并:git branch -D 分支名称

image-20200630210533515

切换回master,再删除分支,都失败了。提示从来没合并过的分支,git会提示你是否真的要删除一个分支。它防止我们删错分支,导致分支上的内容丢失。

image-20200630210720645

这个时候就需要强制删除分支命令了。 => -D

git branch -D branch3

image-20200630211050875


7. 取消合并

比如合并产生冲突,就不想再合并了。

git merge --abort

image-20200630212032265

修改文件内容

image-20200630212054369

image-20200630212234574

切换回master,再修改内容,提交合并

image-20200630212357015

合并必然失败

image-20200630212617697

image-20200630212655152

可以取消合并!

git merge --abort

image-20200630212805558

image-20200630212357015

日志也不会提示进行过合并操作。

image-20200630213037438


8. 撤销合集

8.1 撤销上一次添加暂存区的内容

8.1.1 git rm --cached

image-20200630224042977

我们参考上面提示说明:git rm --cached <file> 将暂存区文件变为未追踪状态(删除暂存区分支上的文件, 但本地又需要使用),这其实就是一种撤销

image-20200630224433659


8.1.2 将文件内容手动修改回之前的状态

提交文件至暂存,再修改内容。

image-20200630225137836

修改a.txt的内容

image-20200630225217946

image-20200630225330107

我们再把文件中的内容清空

image-20200630225405180

image-20200630230751919

以上这种方法是不推荐的,因为一旦修改的内容比较庞大又有跳跃性,就不容易记住到底修改了哪些地方了。

=> git checkout -- 撤销对文件的修改


8.2 git checkout – 撤销对文件的修改

从先从缓存区中拉取版本还原,如果没有再到版本库中拉取还原。参考廖雪峰老师的官网如下:

修改文件内容。

image-20200630231118806

image-20200630231203805

image-20200630233205018

image-20200630225405180


8.3 取消暂存

8.3.1 git reset HEAD

git reset HEAD <file>

image-20200630234308145

修改a.txt

image-20200630234349059

本想分开提交,但是一不小心全部放到了暂存区。

image-20200630234522281

如果提交的话,两个文件都得提交

那我们尝试 git rm --cached a.txt 删除暂存区分支上的文件, 但将本地文件留存。

image-20200630235035102

相当于删除的文件和提交的文件一起进行提交,但这不符合需求,我只需要提交b.txt

image-20210405111000186

我们还是回到原来的状态,把a.txt添加回暂存区

image-20200630235329759

git reset HEAD a.txt => 取消暂存

这样a.txt 回到了添加到暂存区的状态。

image-20200630235809457

注意:当一个文件第一次进入暂存区实际上无需用reset命令的,直接remove就行,因为文件中还没有任何东西。

假设我们当前提交的记录写错了怎么整?请看下面的内容。

image-20200701000308170


8.4 撤销上一次提交信息

8.4.1 git commit --amend

8.4.1.1 修改提交信息

git commit -m '新建一个文件b,a文件待提交' --amend

注意:上次commit提交错后,不能再执行任何操作,必须紧接着它执行我们上面的修改提交信息命令才行。

image-20200701000308170
在这里插入图片描述


8.4.1.2 修正紧挨着的一次的提交与本次提交合并

image-20200701001141727

我们再修改b文件

image-20200701001446854

image-20200701001507257

我们本想将ab文件一起提交,结果只提交了a文件

image-20200701001637991

我们把b文件也加入暂存区,再修正合并上次提交(将两个文件一起提交的内容补齐)

image-20200701001950061

image-20200701002032968

我们可以补充记录!

image-20200701002115263

image-20200701002153566


9. reset使用

9.1 git reset HEAD

撤销我们提交最新的一次提交

git reset HEAD^

如果撤销最新的两次提交

git reset HEAD^^

如果撤销最新的三次提交

git reset HEAD^^^

git reset HEAD^

在这里插入图片描述

可能有的编译器,会提示More ,是因为windows将其认为是换行符了,所有加上"…"即可

image-20200701010538724

解决:

image-20200701010605874

回归正题,看下日志

image-20200701010709212

image-20200701010808598

提交内容

image-20200701093539835

撤销两次提交

image-20200701093629170

注意:该提交并未丢失,可以通过哈希找回。(git中只要通过commit进行提交的,都可以找回)

=> 查看历史提交记录

git reflog


9.1.1 多条回退

git reset HEAD~n n代表最近删除的条数

image-20200701095229406

image-20200701095248358


9.1.2 git reset --hard (尽量避免使用)

重置工作目录,丢失暂存:
git reset --hard (尽量避免使用)

修改了a.txt

image-20200701102747341

image-20200701102941378

git reset HEAD^ --hard

image-20200701103042287

并且发现一个问题,对a.txt的修改没有了。

image-20200701103126737

image-20200701103205444

因此尽量避免使用--hard,丢失的暂存,是不能找回的。


9.1.3 git reset --soft(建议使用)

git reset --soft

保留工作目录,与原分支差异将放到暂存区


撤销回去

image-20200701103546623

我们打开a.txt,也很容易发现,之前里写的内容,是不存在的,因为丢失的内容,是找不回的,因此建议尽量避免使用--hard

image-20200701103813135

git reset HEAD^ --soft

并且发现暂存区文件内容还在!因此它不会造成文件的丢失。

image-20200701103929008

image-20200701103813135


9.1.4 git reset --mixed(默认)

git reset --mixed(默认) 这是默认的,平时直接reset就是这种命令了。

保留工作目录,并且清空暂存区


9.2 通过哈希找回历史记录

image-20200701094104112

git reset cc12a6c13af1f5b135f531617b0ce346bb8d46ca

image-20200701094123142

但是如果没有打印日志,不知道哈希,如何回退呢?


9.3 查看历史提交记录

git reflog

image-20200701095407256

我们回到三次提交的日志。

git reset 4180e8e

image-20200701095657346

注意:reset并不会导致commit提交的记录弄丢失。


10. reset与checkout的区别

10.1 reset本质

可以用来撤销commit,但实质行为上并不是撤销操作,而是移动HEAD并且带上所指向的分支,重置HEAD及分支

即在HEAD之后的提交,因为当前不在任何分支上,就不会出现在工作目录中,起到撤销效果。


10.2 reset过程分析

假设进行c1c2c3次提交,从c3回退到c2,实际上就是HEAD带上master一块移动到c2,此时c3就不在任何分支上了。因此打印log,就看不到c3了。如果想找回的话,即可通过reset重置回c3,即HEAD带上master一块移动到c3


10.3 checkout过程对比

撤销除了reset,checkout也与它类似,但是两者也是不一样的,之前我们学习发现。checkout一般撤销的是对文件的修改,它前提是没有加入暂存区或者没有提交(commit)的内容。

如果我们用checkout完成上述分析,从c3回退到c2,实际HEAD指向了c2,而master没有变化,打印log的时候,一切照旧。

因此我们才用checkout切换分支,因为checkout不会偏移master(分支)指针移动,只会改变HEAD指向,并不会影响主分支。

git checkout --文件名实际会去找当前文件最近的一次提交,如果最近的提交内容为空,则把工作目录中的东西重置为仓库中记录的内容。

10.4 小结

  • checkout本质
    • 签出指定commit,只会改变HEAD指向,并不影响分支指向
  • reset本质
    • 会改变HEAD指向,也会带上分支指向,一起改变



(后续待补充)
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值