Git应用详解第三讲:本地分支的重要操作
前言
分支是git
最核心的操作之一,了解分支的基本操作能够大大提高项目开发的效率。这一讲就来介绍一些分支的常见操作及其基本原理。
一、分支概述
在开发当中,往往需要分工合作。比如:小红开发A
功能,小明开发B
功能,小刚开发C
功能。如何才能做到三者并行开发呢?git
为我们提供的分支功能就能实现这一需求,如下图所示:
在实际的开发过程中,master
分支是用来发布项目稳定版本的。新的功能往往是在一个新建的分支上进行开发,等到新功能开发完毕并经过测试,表现稳定后,才会合并到master
分支上进行版本更新。这样就可以在保持一款软件发行的同时,同步进行新功能的开发。
通常来说,远程仓库的Git
分支会有如下几种:master
分支、test
分支、develop
分支,除此之外可能还有紧急修复bug
的hotfix
分支;但是,本地的分支可以有很多;本文主要介绍Git
本地分支的内容。
二、查看本地分支
1.git branch
查看当前版本库中的所有分支:
其中的 *
表示当前处于的分支,可见当前处于master
分支;
使用git init
初始化git
仓库时,git
会自动创建一个master
分支。但是,如果没有在master
分支上进行任何提交就切换到其他分支,那么在切换分支的时候master
分支会被销毁。并且,无法查看没有提交记录的分支,如下图所示:
2.git branch -a
查看所有本地分支,包括本地分支和本地远程分支:
3.git branch -v
查看所有本地分支上最近一次的提交记录:
但是,该指令无法查看本地远程分支:
4.git branch -r
-r
参数用于单独查看本地远程分支:
5.git branch -av
该指令不仅可以显示所有的本地分支,包括本地远程分支,以及对应分支上的最新提交信息:
6.git branch -vv
-vv
参数表示查看所有本地分支与远程分支的关联情况。如图所示,本地master
分支有本地远程分支origin/master
与之关联,说明它已与远程master
分支建立了关联;
至于上面提到的本地远程分支,将在下一讲中详细介绍。
三、创建本地分支
1.git branch <branch_name>
可通过上述命令创建新分支new_branch
:
由于是在master
分支上创建的new_branch
分支,所以new_branch
分支与master
分支有着部分共同的提交历史;所以,master
分支上的文件,new_branch
分支上都有。但是,在new_branch
分支上添加的new_branch
文件,不会存在于master
分支上:
此时两分支的状态为:
2.git branch -b <branch_name>
通过上述命令可创建并切换到new_branch
分支:
如图所示,本来所在分支为master
,并且没有new_branch
分支。执行上述命令后,创建并切换到了new_branch
分支上。
四、切换本地分支
1.git checkout <branch_name>
比如切换到new_branch
分支:
2.git checkout -
切换回上次操作的分支:
五、重命名本地分支
1.git branch -m <oldName> <newName>
如下图所示,将本地分支master
重命名为master2
:
六、删除本地分支
1.git branch -d new_branch
删除new_branch
分支:
注意点:
不能删除当前所处的分支;
当需要删除的分支上有
master
分支没有的内容,并且删除前没有进行合并(merge
)时,会报错:
此时可以通过git branch -D new_branch
使用参数D
,在不合并的情况下强制删除分支;
七、合并分支
注意:这里所讲的分支指的是有公共提交节点的分支,如下图中的dev
与master
分支所示,提交节点A
为它们的公共提交节点:
当两分支没有公共提交节点,如下图所示,应采用rebase
进行合并,后面会详细介绍:
1.git merge <branch_name>
首先,创建并切换到新分支
dev
中,并为test.txt
文件添加内容dev1
:注意:要将
dev
分支上的这一修改提交到版本库,才能进行后续合并。因为合并的是提交对象链,详情见后面讲解的合并原理:然后,切换回
master
分支,通过git merge dev
指令,将dev
分支中的内容合并到当前所处的master
分支中;合并后master
分支与dev
分支上test.txt
文件的内容达到了同步:
2.分支合并的原则
git
分支的合并采用的是三方合并的原则:找到两分支最新提交A
和B
的公共父节点C
,在这三个节点的基础上合并为节点D
。这个节点D
就包含了两个分支上的所有内容:
八、分支的本质
分支:指向一条commit
对象链或一条工作记录线的指针;
快照A~D
分别表示四次提交(commit
),注意提交的顺序为:A -> B -> C -> D
:
从图中可以看到每一次提交的对象内都会保存上一次提交的commit id
,由此可以从后往前把所有的提交(commit
)串起来形成一条链(类似单向链表),这条链就组成了一条完整的分支信息:
当版本库中只有一条分支:该分支的最新提交就包含了整条分支的所有内容,代表版本库的当前状态。如上图的快照
D
,里面包含了快照A~C
中的所有内容,此时快照D
中的内容就是整个版本库中的内容:当版本库中有多条分支:每条分支上的最新提交包含了所处分支的全部内容,将各个分支的最新提交进行合并。合并的节点就包含了所有分支的内容,也就是现阶段的版本库本身;如下图中的
d1
、m2
、t3
分别包含了dev
、master
、test
分支上的所有内容:
1.分支 ==
指针
情景一:
从图中可以看到:
HEAD
为一个指针:指向当前分支;master
也为一指针:指向提交;
情景二:
上图中,dev
为master
分支上创建的新分支,可知:
git
在创建新分支时,文件本身不变化,只需要创建一个代表新分支,并指向当前分支的指针;如图中的dev
与master
指向同一个提交,文件没有发生任何变化;HEAD
指向dev
分支,表示当前所处分支为dev
,相当于执行了:git checkout -b dev
后的状态;
相信你已经发现:
HEAD
是一个始终指向当前分支的指针;
2.HEAD
标识符
HEAD
文件是一个指向当前所在分支的引用标识符,也可以理解为一个指针,它与分支之间的关系是这样的:
查看HEAD
HEAD
文件中并不包含SHA1
值(每次提交的commit ID
),而是包含一个指向另外一个引用的指针。我们可以查看.git
目录下的HEAD
文件:
可见HEAD
指向的是当前所在的master
分支;
当我们通过git checkout -b dev
创建并切换到dev
分支后,再次进入.git
文件夹查看HEAD
,会发现此时HEAD
指向了dev
:
由此证明了HEAD
始终指向当前分支;
当执行git commit
命令时,git
会创建一个commit
对象(比如下图D
)。并且将这个commit
对象的parent
指针指向HEAD
所指向的引用(master
)指向的提交(也就是C
),这样就能形成一条提交链:
我们对于HEAD
修改的任何操作,都会被git reflog
完整记录下来:
但是手动地修改HEAD
文件,这些信息就不会被记录下来,所以十分不建议手动修改git
相关的配置文件,而是应该尽量采用命令行的方式来修改。
修改HEAD
实际上,我们可以通过git
的底层命令symbolic-ref
来实现对HEAD
文件内容的修改;
git
中的命令可分为两类:高级命令和底层命令;之前介绍的git add
等都是高级命令;
读取:
写入:
要注意格式:refs/heads/develop
;
查看ORIG_HEAD
文件:
里面是一个SHA1
值,查看当前的提交信息:
可以发现,ORIG_HEAD
里面的SHA1
值就是最新一次提交的SHA1
值。
查看FETCH_HEAD
文件:
里面有两个信息,一个是最新提交的commit ID
,另一个是提交信息。
所以,对于git
而言commit ID
是十分重要的信息,通过这个SHA1
值可以回溯或查找需要的提交。
3.git merge
原理
过程图解
在新分支上进行提交操作
上图表示在
dev
分支上进行了一次提交,此时:- 分支
master
的提交记录由:A
、B
和C
组成; - 而分支
dev
的提交历史则由:A
、B
、C
和D
组成;
- 分支
对两分支进行合并操作
在
master
分支上执行:git merge dev
将dev
分支的内容合并到了master
分支上;这种合并方式叫做:Fast-forward
,没有冲突,改变的只是master
指针的指向;
详细过程
在执行合并操作前:
- 在
master
分支上查看该分支的提交记录:
- 在
dev
分支上查看该分支的提交记录:
可以看到dev
分支只是比master
分支多进行了一次提交(dev1
),两分支状态如下图所示:
执行合并操作:
先切换到master
分支,然后执行git merge dev
合并dev
分支:
可以看到使用了Fast-forward
方式进行合并,合并后两分支状态如下图所示:
合并后,HEAD
同时指向了master
和dev
分支;并且master
和dev
分支的提交历史完全一致;这就说明了:使用Fast-forward
(快进合并)方式进行分支合并,只会改变master
分支指针的指向;
4.Fast-forward
- 默认情况下,合并分支时
git
会使用Fast-forward
模式; - 在这种模式下,删除分支会丢弃分支信息;
- 进行分支合并操作时加上
--no-ff
参数会禁止使用Fast-forward
方式,这样会多出一次提交记录;
ff
表示Fast-forward
。
具体演示如下:
使用Fast-forward
首先,查看master
分支上最新的3
次提交:
此时两分支的状态为:
随后在dev
分支上新增一次提交:dev2
。查看dev
分支上最新的3
次提交:
此时两个分支的状态为:
切换回master
分支,通过git merge dev
合并dev
分支,此时默认采用Fast-forward
方式:
可以看到合并后,master
直接指向了dev
的最新提交,并没有产生新的提交。合并后两分支的状态如下所示:
由此验证了Fast-forward
方式只会改变分支指针的指向。
禁用Fast-forward
合并时可以通过:
git merge --no-ff dev
禁用Fast-forward
模式。
继续在
dev
分支新增一次提交:dev3
。然后查看dev
分支上最新的3
次提交:不修改
master
分支,查看其最新的3
次提交:此时两个分支的状态为:
然后,在
master
分支上不使用Fast-forward
方式合并dev
分支。合并命令采用:git merge --no-ff dev
执行后进入如下的
vim
编辑器界面,要求我们填写提交注释:
这说明不使用Fast-forward
方式合并分支,会触发了一次提交。填写提交注释后完成提交操作,合并完成后,查看master
分支的提交记录:
可以发现禁用了Fast-forward
模式的合并会比dev
分支多产生一次提交:Merge branch 'dev'
,即使合并后的内容是一样的。此时两分支的状态为:
由此验证了,禁用Fast-forward
方式合并,会多出一个表示合并的提交记录。
5.合并冲突
合并的两分支只有一条分支发生了改变,并且其中一分支是基于另一分支创建的。比如上述的master
与dev
分支,两分支没有分岔,此时不会出现合并冲突;git
会通过Fast-forward
方式自动完成合并操作;
但是,当合并的两分支都发生改变时,即分支出现分岔,如下图所示。此时就需要解决冲突后手动合并分支了:
具体演示如下:
合并前
首先,分别对两分支上的test.txt
文件进行修改,并分别将修改提交到各自的分支;
- 在
master
分支上进行新的提交:mas3
,然后查看文件test.txt
内容和分支提交记录:
- 在
dev
分支上进行新的提交:dev1
,然后查看文件test.txt
内容和分支提交记录:
可见两分支的提交记录只有最新一次提交不一样:
合并后
在master
分支上,通过git merge dev
合并dev
分支时,会在共同修改的test.txt
文件中出现合并冲突,如下图所示:
出现冲突的原因为:两个分支都对同一个文件test.txt
进行了修改,git
合并时并不知道以哪个分支的修改为标准。所以不能采用Fast-forward
方式自动合并,需要解决冲突,手动合并。
手动合并过程
手动合并操作需要分如下三步进行:
- 第一步:选择需要保留的内容,手动解决合并冲突;
此时进入发生合并冲突的test.txt
文件:
会出现典型的冲突呈现方式(此时HEAD
指向的是master
分支),其中:
<<<HEAD
与>>>dev
之间的内容表示:两分支上test.txt
文件的不同之处;<<<HEAD
与===
之间的内容表示:当前分支master
对test.txt
文件的修改;===
与>>>dev
之间的内容表示:dev
分支对test.txt
文件的修改;
此时只需要在test.txt
中保留想要的内容即可,例如:将两分支对test.txt
的修改都进行保存,删除3、5、7
行多余的内容:
除此之外,还可以通过git mergetool
指令,调用vimdiff
工具进入vim
编辑器,来解决test.txt
文件的冲突:
在实际开发中,我们很少手动进行合并,而是借助于一些工具来实现。
- 第二步:使用
git add test.txt
将手动解决冲突时对test.txt
的修改提交到暂存区;
编辑完毕后,可以看到此时仍然处于合并过程中(MERGING
)。通过git status
查看状态,发现手动解决冲突时对test.txt
文件的修改操作还在工作区中,需要通过git add test.txt
将这一修改纳入暂存区,继续进行合并:
- 第三步:通过
git commit -m '合并注释'
将手动解决冲突时对test.txt
的修改进行提交,完成合并操作;
如果需要填写较多的合并注释,可以通过git commit
进入vim
编辑器编辑。提交后,完成整个手动合并过程。
完成手动合并分支后,查看两分支的提交历史:
master
分支上的提交历史:
dev
分支上的提交历史:
可以发现此时两分支转变为了可以通过Fast forward
方式合并的形式了,如图所示:
- 手动解决冲突前:
手动解决冲突后:
同步两分支
若想将dev
和master
分支上的内容进行同步,只需要在dev
分支中通过git merge master
合并master
分支即可。此时就可以使用Fast-forward
方式进行合并了,合并结果如下图所示:
验证:
合并后,查看dev
分支的提交历史:
可以看到HEAD
同时指向dev
与master
,即三个指针都指向了最新的一次提交,符合上述分析得出的结论;
经过上面的讨论,不难看出:合并分支的实质就是不同提交的合并,以及HEAD
和分支指针的移动;
以上就是今天介绍的本地分支重要操作,相信看到这里的你已经对
git
本地分支的操作了如指掌了。在下一讲中将介绍git
最神奇的功能:版本回退。俗话说得好:世上没有后悔药。但是在git
中,就存在所谓的"后悔药"!那么我们下一节再见。
Git应用详解第三讲:本地分支的重要操作的更多相关文章
- git 使用详解(9)-- 分支的新建与合并 git branch -d、merge、 --merged/--no-merged/-v
现在让我们来看一个简单的分支与合并的例子,实际工作中大体也会用到这样的工作流程: 开发某个网站. 为实现某个新的需求,创建一个分支. 在这个分支上开展工作. 假设此时,你突然接到一个电话说有个很严重的 ...
- git 使用详解(8)—— 分支HEAD、branch/checkout
有人把 Git 的分支模型称为"必杀技特性",而正是因为它,将 Git 从版本控制系统家族里区分出来.Git 有何特别之处呢?Git 的分支可谓是难以置信的轻量级,它的新建操作几乎 ...
- git 使用详解(6)—— 3种撤消操作
接下来,我们会介绍一些基本的撤消操作相关的命令.请注意,有些操作并不总是可以撤消的,所以请务必谨慎小心,一旦失误,就有可能丢失部分工作成果. 修改最后一次提交 git commit --amend 有 ...
- Git应用详解第四讲:版本回退的三种方式与stash
前言 前情提要:Git应用详解第三讲:本地分支的重要操作 git作为一款版本控制工具,其最核心的功能就是版本回退,没有之一.熟悉git版本回退的操作能够让你真真正正地放开手脚去开发,不用小心翼翼,怕一 ...
- Git应用详解第七讲:Git refspec与远程分支的重要操作
前言 前情提要:Git应用详解第六讲:Git协作与Git pull常见问题 这一节来介绍本地仓库与远程仓库的分支映射关系:git refspec.彻底弄清楚本地仓库到底是如何与远程仓库进行联系的. 一 ...
- Git初探--笔记整理和Git命令详解
几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...
- iOS开发——开发实战篇&版本控制SVN和Git使用详解
版本控制SVN和Git使用详解 公司的实际开发中,在天朝使用较多的还是SVN,因为SVN是集中式的,在天朝上班你们都懂的! -----------------svn--------- ...
- Git命令详解
一个中文git手册:http://progit.org/book/zh/ 原文:http://blog.csdn.net/sunboy_2050/article/details/7529841 前面两 ...
- git命令详解( 七 )
此为git命令详解的第七篇 这章我们可以来逐渐揭开 git push.fetch 和 pull 的神秘面纱了.我们会逐个介绍这几个命令,它们在理念上是非常相似的. git push的参数 git ...
随机推荐
- Linux 文件系统及 ext2 文件系统
linux 支持的文件系统类型 Ext2: 有点像 UNIX 文件系统.有 blocks,inodes,directories 的概念. Ext3: Ext2 的加强版,添加了日志 ...
- Spark实战--搭建我们的Spark分布式架构
Spark的分布式架构 如我们所知,spark之所以强大,除了强大的数据处理功能,另一个优势就在于良好的分布式架构.举一个例子在Spark实战--寻找5亿次访问中,访问次数最多的人中,我用四个spar ...
- mysql那些事之索引篇
mysql那些事之索引篇 上一篇博客已经简单从广的方面介绍了一下mysql整体架构以及物理结构的内容. 本篇博客的内容是mysql的索引,索引无论是在面试还是我们日常工作中都是非常的重要一环. 索引是 ...
- python中那些让开发事半功倍的模块
1. Map Map会将一个函数映射到一个输入列表的所有元素上 ex: 有一个列表: [1,2,3,4,5,6], 现在要求把列表每个元素乘以10 如果你还不知道Map,那你可能会这样做: list1 ...
- markdown样式测试
------------恢复内容开始------------ 标题1 Aaaaaaaaaaa 引用内容 Aaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ...
- OneNote代码高亮
向OneNote 2016安装NoteHighlight 下载.msi 文件,下载链接 下载之前查看自己的电脑上安装的OneNote版本以及位数(32-64) 查看方法:文件->选项->关 ...
- C语言学生管理系统
想练习一下链表,所以就有了这个用C写的学生管理系统 没有把它写入文件,才不是因为我懒哈哈哈,主要是为了练习链表的 #include<stdio.h> #include<stdlib. ...
- [Unity] Unity 2019.1.14f 在Terrain中使用Paint Texture的方法
1.点击Terrain中的Paint Texture按钮2.将按钮下面的下拉菜单选择paint texture3.点击Edit Terrain Layers按钮T4.点击弹出菜单的Create Lay ...
- 微服务实战——高可用的SpringCloudConfig
管理微服务配置 对于单体应用架构来说,会使用配置文件管理我们的配置,这就是之前项目中的application.properties或application.yml.如果需要在多环境下使用,传统的做法是 ...
- effective-java学习笔记---使用枚举类型替代整型常量34
1.要将数据与枚举常量相关联,请声明实例属性并编写一个构造方法,构造方法带有数据并将数据保存在属性中. // Enum type with data and behavior public enum ...