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

git入门与实践(3)

1. 分支入门操作

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

1. 1 查看分支: git branch

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

image-20200630115535628

1. 2 创建分支:git branch 分支名称

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

git branch branch1

git branch

image-20200630115714216

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

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

image-20200630115835314

1. 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

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

1. 3. 1 分支流程详解

  首先有一次c1的提交,在c1的基础上还有c2的提交,这些操作都是在默认的master分支上进行的,在c2的提交上又创建新分支branch1。git如何判断在当前在哪个分支上,就是通过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

1. 3. 1. 1 流程动态演示

2. 分支合并

  假如我们开始开发,基础功能是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

2. 1 HEAD查看命令

2. 1. 1 git log --decorate

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

image-20200630155229540

2. 1. 2 git log --oneline --decorate

--oneline 校验和(哈希)

  查看分支指向+哈希

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

image-20200630155627931

切换回branch1看看

image-20200630155853565

我们继续提交c5

image-20200630160016535

image-20200630160051029

2. 1. 3 git log --oneline --decorate --all --graph

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

--all 所有分支信息

--graph 打印出树形图

image-20200630160143289

2. 2 git merge 分支合并

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

git merge 目标分支

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

image-20200630160943907

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

image-20200630161010099

image-20200630161135347

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

image-20200630161317364

2. 2. 1 模拟合并小结

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

2. 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  那如何解决呢?

2. 3. 1 --no-ff

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

  我们回退回去!

image-20200630183659778

image-20200630183638221

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

image-20200630184018334

image-20200630184035268

image-20200630184109198

2. 3. 2 模拟快速前移

3. 分支冲突

3. 1 分叉冲突

  初始化git仓库

image-20200630194716040

  创建并切换一个分支

image-20200630194837077

  修改a文件,并提交

image-20200630195055584

image-20200630195030727

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

3. 2 快速前移

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

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

image-20200630202532916

  添加内容,并提交

image-20200630202616546

image-20200630202744771

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

image-20200630203218956

image-20200630203305572

  切换到master再合并

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

image-20200630203518373

image-20200630203218956

3. 3 如何判断是否会引起冲突

3. 3. 1 快速前移不会引起冲突

  如上master第1次提交:新建文件并添加内容

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

  最后切换到master合并分支,合并之前,git会判断两个分支是什么关系?如果是祖先级关系,masterbranch2处于同一条commit路径上(直接级祖先关系),就进行了快速前移。

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

image-20200630204554374

3. 3. 2 分叉冲突可能会引起冲突

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

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

image-20200630204956306

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

4. 解决分支冲突

解决分支冲突:

​ ① 手动解决冲突部分

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

5. 删除分支

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

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

image-20200630210133151

5. 1 无法删除的分支

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

image-20200630210258162

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

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

image-20200630210533515

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

image-20200630210720645

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

git branch -D branch3

image-20200630211050875

6. 取消合并

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

7. 撤销合集

7. 1 撤销上一次添加暂存区的内容

7. 1. 1 git rm --cached

image-20200630224042977

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

image-20200630224433659

7. 1. 2 将文件内容手动修改回之前的状态

image-20200630225137836

修改a.txt的内容

image-20200630225217946

image-20200630225330107

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

image-20200630225405180

image-20200630230751919

7. 3 git checkout – 撤销对文件的修改

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

修改文件内容。

image-20200630231118806

image-20200630231203805

image-20200630233205018

image-20200630225405180

7. 4 取消暂存

7. 4. 1 git reset HEAD

git reset HEAD <file>

image-20200630234308145

修改a.txt

image-20200630234349059

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

image-20200630234522281

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

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

image-20200630235035102

  我们发现,也无法提交,这里git会帮我们重命名了,完全不符合我们的需求。我们需求是仅仅提交b.txt

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

image-20200630235329759

git reset HEAD a.txt

image-20200630235809457

注意:当一个文件第一次进入暂存区实际上无需用reset命令的,直接remove就行,因为暂存区没有其之前的信息。

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

image-20200701000308170

7. 5 撤销上一次提交信息

7. 5. 1 git commit --amend

7. 5. 1. 1 修改提交信息

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

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

image-20200701000308170

image-20200701001403170

7. 5. 1. 2 修正紧挨着的一次的提交与本次提交合并

image-20200701001141727

我们再修改b文件

image-20200701001446854

image-20200701001507257

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

image-20200701001637991

我们把b文件也加入暂存区,再修正合并上次提交

image-20200701001950061

image-20200701002032968

我们可以补充记录!

image-20200701002115263

image-20200701002153566

8. reset使用

8. 1 git reset HEAD

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

git reset HEAD^

如果撤销最新的两次提交

git reset HEAD^^

如果撤销最新的三次提交

git reset HEAD^^^

git reset HEAD^

image-20200701010208748

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

image-20200701010538724

解决:

image-20200701010605874

回归正题,看下日志

image-20200701010709212

image-20200701010808598

提交内容

image-20200701093539835

撤销两次提交

image-20200701093629170

注意:该提交并未丢失,可以通过哈希找回。

8. 1. 1 多条回退

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

image-20200701095229406

image-20200701095248358

8. 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,丢失的暂存,是不能找回的。

8. 1. 3 git reset --soft(建议使用)

git reset --soft

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


  撤销回去

image-20200701103546623

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

image-20200701103813135

git reset HEAD^ --soft

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

image-20200701103929008

image-20200701103813135

8. 1. 4 git reset --mixed(默认)

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

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

8. 2 通过哈希找回历史记录

image-20200701094104112

git reset cc12a6c13af1f5b135f531617b0ce346bb8d46ca

image-20200701094123142

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

8. 3 查看历史提交记录

git reflog

image-20200701095407256

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

git reset 4180e8e

image-20200701095657346

9. reset与checkout本质

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

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

9. 1 reset过程分析

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

9. 2 checkout过程对比

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

  如果我们用checkout完成上述分析,从c3回退到c2,实际HEAD指向了c2,而master没有变化,打印log的时候,一切照旧。因此我们才用checkout切换分支,因为checkout不会偏移master(分支)指针。

  所以,签出指定commit,只会改变HEAD指向,并不会影响分支指向。



(后续待补充)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章