【Git】二、分支管理详解
文章目录
- Ⅰ. 理解分支
- Ⅱ. 理解HEAD指针
- Ⅲ. 创建、切换、合并、删除分支
- 1、创建分支 -- `git branch [name]`
- 2、切换分支 -- `git checkout`
- 3、合并分支 -- `git merge`
- 4、删除分支 -- `git branch -d <branch-name>`
- Ⅳ. 合并冲突
- Ⅴ. 分支管理策略
- 一、分支合并模式
- 二、分支策略
- Ⅵ. bug分支 -- `git stash` && 合并策略
- Ⅶ. 删除临时分支 -- `git branch -D [name]`
- 小结
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/351a8419e73244f8ac0e1ae8ed74ebf3.gif#pic_center)
Ⅰ. 理解分支
分支是什么呢,其实 分支就像是分身术,想象一下,我们学编程语言的时候,如果能进行分身,也就是分支,此时本体去学 c++
,而分身去 java
,甚至再分身去学 php
……
当我们多个分身都学完了,再全部合体,此时我们本体就同时学会了多种语言,是不是很吊!
分支就是这个道理!那 git
中的分支在哪里呢❓❓❓
其实我们已经接触过了,但是当时没有细讲,还记得吗,在版本库中,有一个 HEAD
指针指向 master
分支(其实这个 master
分支不一定是主干分支,但是 master
分支通常是默认的主分支名称),它就相当于是本体,然后此时创建了多个分支出来去各自完成代码的编写等操作,这相当于影分身!最后我们将多个分支融合,其实就是合体的过程!
在 git
中,分支可以看作是一个指向某个提交记录的指针(提交都是放在 .git/objects/
中管理的),它指向当前工作目录中的版本。当你创建一个新的分支时,这个分支实际上是从当前分支分离出来的一个新的指针。
因此,在某种程度上,每个分支都可以看作是一条主干。
再来理解一遍,HEAD
严格来说不是指向提交,而是指向 master
,master
才是指向提交的,所以,HEAD
指向的就是当前分支。
说了这么多废话,下面我们先来看一下 master
分支:
首先 HEAD
所指的就是 master
分支的开头,也就是哈希值为 c82c……6f6
的节点,而这个节点其中通过 parent
指针连接到 ac00……f34
节点上,其实这是前一个节点,相当于一条时间线上,这些节点是按顺序排布的,而 master
指向最新的那个节点,如下图所示:
此时我们是可以创建新分支的,还能融合分支等操作,差不多如下图所示:
而我们接下来就要讲如何进行分支的创建、切换等一系列管理分支的操作!
Ⅱ. 理解HEAD指针
在 git
中,HEAD
是一个特殊的指针,它指向当前所在的分支或提交。作用是跟踪当前工作目录的状态,并确定下一次提交将基于哪个分支或提交。
在 git
中进行提交时,实际上是将新的 提交的索引
添加 HEAD
所指的分支上。而当 创建一个新的分支时,我们要注意使用指令的不同,HEAD
的指向也是不同的,可能是指向当前分支,也可能是指向新的分支,这个是要时刻注意的,因为如果错误地在其它分支上提交了东西,那么分支就会变乱的,需要花时间去 “修补” 它!
除了指向当前分支或提交,
HEAD
也可以指向一个提交的哈希值。这种情况是处于一个 “分离头指针” 的状态,这意味着你不在任何分支上,而是直接基于某个提交进行开发和修改。 除此之外,
HEAD
还可以指向一个标签,这种情况下,你处于一个标签的 “分离头指针” 状态。
简单地说,HEAD
指针指向的分支就是当前正在工作的分支,可以参考上面的图片!理解 HEAD
指针的作用是很重要的,因为下面我们创建分支、切换分支等操作的时候,如果不清楚 HEAD
指针的指向,那么很可能导致几条分支都凌乱了,那是非常麻烦的!
Ⅲ. 创建、切换、合并、删除分支
首先我们得先知道当前我们本地仓库中有哪些分支,这时候就要用到 git branch
指令查看,如下所示:
[liren@VM-8-7-centos gitcode]$ git branch
* master
master
前面的 星号
,表示的是当前 HEAD
指针的指向,在 master
前面表示当前 HEAD
指向的就是 master
分支,这里因为只有一个分支,所以看起来不是那么的明显,下面创建其它分支之后我们就会看得更清晰!
另外这里重新介绍一个 git log
指令中非常好用的选项,就是帮助我们在查看日志的时候,以时间线的形式打印出不同分支的提交记录!
整个指令就是这样子的:git log --graph --abbrev-commit
[liren@VM-8-7-centos gitcode]$ git log --graph --abbrev-commit
* commit c82c67b
| Author: lirendada <2916776007@qq.com>
| Date: Mon Jul 10 16:16:14 2023 +0800
|
| change readme
|
* commit ac00e61
| Author: lirendada <2916776007@qq.com>
| Date: Mon Jul 10 15:10:04 2023 +0800
|
| add file5
|
* commit ced585b
……
因为现在我们只有一个分支,所以看起来效果不是很明显!
1、创建分支 – git branch [name]
使用 git branch [name]
就可以创建一个分支。下面我们创建一个叫做 dev
的分支出来:
[liren@VM-8-7-centos gitcode]$ git branch dev
[liren@VM-8-7-centos gitcode]$ git branch
dev
* master
[liren@VM-8-7-centos gitcode]$
现在我们就可以看到星号当前还是在 master
分支上的,也就是说此时 HEAD
指向的还是 master
分支,我们可以花个示意图来看看:
如何验证此时 master
和 dev
的索引都是之前 master
的最新提交呢,此时很好验证,就是看看索引一不一样嘛,如下所示:
[liren@VM-8-7-centos gitcode]$ git log --pretty=oneline # 先查看一下日志,看看最新的提交记录
c82c67bb370034ef67d0bde191252742fcc7d6f6 change readme
ac00e613b660a4d916a166d64ef518132d04bf34 add file5
ced585b446ebc42530ca850efc02195ae709cbad add file4
7eb10f50de9781d05a4a4af2134a8673352e89fa add 3 files
cdc96e6d432ea74b61b4bf36de4dc19f07310eb0 first add file:readme
[liren@VM-8-7-centos gitcode]$ cat .git/refs/heads/master # 查看master分支的索引,发现就是最新的提交记录
c82c67bb370034ef67d0bde191252742fcc7d6f6
[liren@VM-8-7-centos gitcode]$ cat .git/refs/heads/dev # 查看dev分支的索引,是和master一样的,说明此时指向同一个节点!
c82c67bb370034ef67d0bde191252742fcc7d6f6
💥💥💥注意:以上图片其实是采用 --no-ff
的方式来合并分支的,这样子画出来更便于理解,而实际情况是这样子的:
但是为了便于理解,我们都采用之前那种更像分支的方式来画图!我们后面会讲这两种方式的区别!
2、切换分支 – git checkout
我们在之前的撤销修改操作中有接触过这个指令,但是作用却截然不同,撤销工作区修改的指令中必须得有双横线,即 git checkout -- [filename]
,而我们这里用来切换分支则不需要加上双横线,只需要跟上对应的分支名即可!
git checkout
命令用于 切换分支、恢复文件 或 检出提交 等操作。其常见用法如下:
切换分支:git checkout <branch-name> # 切换到指定的分支并更新工作目录
创建新分支:git checkout -b <new-branch-name> # 创建一个新的分支并切换到该分支
强制切换分支:git checkout -f <branch-name> # 强制切换到指定分支并丢弃所有未提交的更改
恢复文件:git checkout <file-name> # 恢复指定文件的内容到最近的提交状态
检出提交:git checkout <commit> # 将工作目录中的所有文件恢复到指定提交的状态,但不会创建新的分支
切换到标签:git checkout <tag-name> # 切换到指定的标签并更新工作目录
对于切换分支来说,我们常用的是第一个指令;而如果想一步到位,既要创建分支又想切换到该新分支上,那么就用第二条指令!
需要注意的是,git checkout
命令会修改工作目录中的文件内容,因此在 使用该命令时需要注意备份重要的文件,并确保已提交或保存所有的更改。
下面我们就来演示一下如何将 HEAD
指针指向我们上面创建的 dev
分支:
[liren@VM-8-7-centos gitcode]$ git branch # 切换前
dev
* master
[liren@VM-8-7-centos gitcode]$ git checkout dev # 切换分支
Switched to branch 'dev'
[liren@VM-8-7-centos gitcode]$ git branch # 切换后
* dev
master
用图来表示的话,其实就是 HEAD
指针改变了指向 罢了,如下所示:
为了测试是否两个分支上提交是互不影响的,下面我们对两个分支来进行测试:
[liren@VM-8-7-centos gitcode]$ git branch # 先看看分支信息
* dev
master
[liren@VM-8-7-centos gitcode]$ cat readme # 然后看看即将修改的文本的内容
lirendada
[liren@VM-8-7-centos gitcode]$ echo "hello world" >> readme # 向readme插入数据后进行提交
[liren@VM-8-7-centos gitcode]$ git add readme
[liren@VM-8-7-centos gitcode]$ git commit -m 'dev insert into readme'
[dev 1e0a028] dev insert into readme
1 file changed, 1 insertion(+)
[liren@VM-8-7-centos gitcode]$ git log --pretty=oneline # 查看日志,会发现多了提交记录更新成当前插入数据的提交记录了
1e0a028cf4c9ac4566c365e09cbb360bffa34afd dev insert into readme
c82c67bb370034ef67d0bde191252742fcc7d6f6 change readme
ac00e613b660a4d916a166d64ef518132d04bf34 add file5
ced585b446ebc42530ca850efc02195ae709cbad add file4
7eb10f50de9781d05a4a4af2134a8673352e89fa add 3 files
cdc96e6d432ea74b61b4bf36de4dc19f07310eb0 first add file:readme
[liren@VM-8-7-centos gitcode]$ git checkout master # 切换到master分支
Switched to branch 'master'
[liren@VM-8-7-centos gitcode]$ cat readme # 查看readme文件,发现还是原来的样子
lirendada
[liren@VM-8-7-centos gitcode]$ git log --pretty=oneline # 查看日志发现没有dev分支的提交记录,说明已经独立了!
c82c67bb370034ef67d0bde191252742fcc7d6f6 change readme
ac00e613b660a4d916a166d64ef518132d04bf34 add file5
ced585b446ebc42530ca850efc02195ae709cbad add file4
7eb10f50de9781d05a4a4af2134a8673352e89fa add 3 files
cdc96e6d432ea74b61b4bf36de4dc19f07310eb0 first add file:readme
这里的操作,如果画图来表示的话,如下所示:
可以看到此时 dev
分支的指向其实已经变了,但是 master
分支的指向还是不变,我们可以验证一下:
[liren@VM-8-7-centos gitcode]$ cat .git/refs/heads/master # master的指向还是原来那个
c82c67bb370034ef67d0bde191252742fcc7d6f6
[liren@VM-8-7-centos gitcode]$ cat .git/refs/heads/dev # dev的指向就是新的版本提交
1e0a028cf4c9ac4566c365e09cbb360bffa34afd
而 Fast-forward
方式的图如下所示:
3、合并分支 – git merge
git merge <branch-name>
命令用于 将两个或多个分支的历史记录合并成一个新的提交。因此需要仔细审查合并结果,确保合并后的代码能够正常工作,并保证项目的稳定性和可维护性。
这个指令还支持一些选项,例如 --no-commit
选项可以在合并后不自动提交合并结果,--squash
选项可以将多个提交合并为一个单独的提交,等等。
要注意的是,合并后的提交记录,是放到当前工作的分支上面去的!
下面我们就把之前的 master
分支和 dev
分支合并起来,然后将该提交记录放到 master
分支中去:
[liren@VM-8-7-centos gitcode]$ cat .git/refs/heads/dev # 首先查看一下dev的记录索引
1e0a028cf4c9ac4566c365e09cbb360bffa34afd
[liren@VM-8-7-centos gitcode]$ cat readme # 看一下此时master分支下的readme中的内容
lirendada
[liren@VM-8-7-centos gitcode]$ git merge dev # 合并分支
Updating c82c67b..1e0a028
Fast-forward
readme | 1 +
1 file changed, 1 insertion(+)
[liren@VM-8-7-centos gitcode]$ cat readme # 合并后可以看到readme中就变成了新版本的内容了
lirendada
hello world
[liren@VM-8-7-centos gitcode]$ git log --pretty=oneline # 并且查看日志也能看到当前提交日志和dev分支是一样的
1e0a028cf4c9ac4566c365e09cbb360bffa34afd dev insert into readme
c82c67bb370034ef67d0bde191252742fcc7d6f6 change readme
ac00e613b660a4d916a166d64ef518132d04bf34 add file5
ced585b446ebc42530ca850efc02195ae709cbad add file4
7eb10f50de9781d05a4a4af2134a8673352e89fa add 3 files
cdc96e6d432ea74b61b4bf36de4dc19f07310eb0 first add file:readme
[liren@VM-8-7-centos gitcode]$ cat .git/refs/heads/master # master的指向也变成了新的一次提交
1e0a028cf4c9ac4566c365e09cbb360bffa34afd
我们可以看到合并分支时候,打印的内容中有一串叫做
Fast-forward
的东西,这是啥呢❓❓❓ 在
git merge
中,如果使用了 快进合并(Fast-forward Merge
),则 当前分支的指针会直接指向被合并的分支的最新提交,而不会创建新的合并提交。 例如,假设你当前在
master
分支上,而dev
分支是从master
分支上分出来的,且在dev
分支上有一些新的提交。如果你执行git merge dev
命令,并且dev
分支的历史记录可以直接被master
分支所包含,那么git merge
将会执行一个快进合并,直接将master
分支指针指向dev
分支的最新提交,而不会创建新的合并提交。
后面我们会讲另一种合并方式也就是我们上面有提及到的 --no-ff
,那种打印日志出来会更加好看一些,如下所示:
4、删除分支 – git branch -d <branch-name>
合并完之后,如果被合并的分支已经不需要了,那么可以使用 git branch -d <branch-name>
来进行删除分支,因为我们合并完分支之后,那些被合并的分支并不会自动删除,需要我们手动去删除!
注意事项:
- 删除分支之前需要确认该分支的历史记录不再被其他分支所依赖,以免影响项目的稳定性和可维护性。
- 当前工作分支是无法删除自己的!
下面我们就把之前的 dev
分支删除:
[liren@VM-8-7-centos gitcode]$ git branch
* dev
master
[liren@VM-8-7-centos gitcode]$ git branch -d dev #当前工作分支是无法删除自己的
error: Cannot delete the branch 'dev' which you are currently on.
[liren@VM-8-7-centos gitcode]$ git checkout master #切换为其它分支
Switched to branch 'master'
[liren@VM-8-7-centos gitcode]$ git branch -d dev #重新进行删除dev分支
Deleted branch dev (was 1e0a028).
[liren@VM-8-7-centos gitcode]$ git branch #删除成功
* master
此时的状态如下所示:
Ⅳ. 合并冲突
可是,在实际分支合并的时候,并不是想合并就能合并成功的,有时候可能会遇到代码冲突的问题。
为了演示这问题,创建一个新的分支 dev1
,并切换至目标分支,我们可以使用上面提到过的 git checkout -b dev1
一步完成创建并切换的动作,实例如下:
[liren@VM-8-7-centos gitcode]$ git checkout -b dev1 # 创建和切换,一步到位!
Switched to a new branch 'dev1'
[liren@VM-8-7-centos gitcode]$ git branch
* dev1
master
在 dev1
分支下修改 readme
文件,更改文件内容如下,并进行一次提交,如:
[liren@VM-8-7-centos gitcode]$ cat readme # 当前是dev分支
lirendada
hello world
[liren@VM-8-7-centos gitcode]$ vim readme # 使用编辑器将内容改变
[liren@VM-8-7-centos gitcode]$ cat readme
lirendada
hello dev1
[liren@VM-8-7-centos gitcode]$ git add . # 提交新内容
[liren@VM-8-7-centos gitcode]$ git commit -m 'modify readme:dev1'
[dev1 0cd9936] modify readme:dev1
1 file changed, 1 insertion(+), 1 deletion(-)
然后切换到 master
分支下,将其内容也稍加修改,保持和 dev1
修改内容中有一丝不同,并进行一次提交:
[liren@VM-8-7-centos gitcode]$ git checkout master #切换为master分支
Switched to branch 'master'
[liren@VM-8-7-centos gitcode]$ cat readme
lirendada
hello world
[liren@VM-8-7-centos gitcode]$ vim readme #使用编辑器将内容改变
[liren@VM-8-7-centos gitcode]$ cat readme
lirendada
hello master
[liren@VM-8-7-centos gitcode]$ git add . #提交新内容
[liren@VM-8-7-centos gitcode]$ git commit -m 'modify readme:master'
[master c7bfef3] modify readme:master
1 file changed, 1 insertion(+), 1 deletion(-)
此时 dev1
和 master
各自都有了新的提交,如下所示:
下面我们将它们两个合并起来,合并到 master
分支上去:
[liren@VM-8-7-centos gitcode]$ git branch
dev1
* master
[liren@VM-8-7-centos gitcode]$ git merge dev1 # 合并分支
Auto-merging readme
CONFLICT (content): Merge conflict in readme
Automatic merge failed; fix conflicts and then commit the result.
[liren@VM-8-7-centos gitcode]$ cat readme # 查看文本内容,发现奇奇怪怪的东西出现了!
lirendada
<<<<<<< HEAD
hello master
=======
hello dev1
>>>>>>> dev1
readme
文件中的 <<<<<<< HEAD
到 =======
中间的内容是 master
分支中冲突的内容,而从 =======
到 >>>>>>> dev1
中间的内容则是 dev1
分支中冲突的内容!
为什么它们都写到了 readme
文件中呢❓❓❓
仔细想想,git
可不知道我们在合并了两个文件中,长得非常相似的内容到底要留下哪个,所以 git
索性就将它们都写入到文件中,并且用标识边界给区分开,剩下的工作就需要我们自己手动去删掉不需要的内容是哪项!
所以此时我们 必须要手动调整冲突代码,并再次提交修正后的结果!!(再次提交很重要,切勿忘记)
[liren@VM-8-7-centos gitcode]$ vim readme
[liren@VM-8-7-centos gitcode]$ cat readme # 调整冲突代码
lirendada
hello master
[liren@VM-8-7-centos gitcode]$ git add readme # 重新提交修正后的内容
[liren@VM-8-7-centos gitcode]$ git commit -m 'merge, and modify readme'
[master 3800d19] merge, and modify readme
此时我们可以通过 git log --graph --abbrev-commit
打印出分支线来看看变化,可以看到这次它们 不再是之前的 Fast forward
方式进行合并了,因为这次是因为合并冲突!
[liren@VM-8-7-centos gitcode]$ git log --graph --abbrev-commit
* commit 3800d19
|\ Merge: c7bfef3 0cd9936
| | Author: lirendada <2916776007@qq.com>
| | Date: Tue Jul 11 15:51:59 2023 +0800
| |
| | merge, and modify readme
| |
| * commit 0cd9936
| | Author: lirendada <2916776007@qq.com>
| | Date: Tue Jul 11 15:31:41 2023 +0800
| |
| | modify readme:dev1
| |
* | commit c7bfef3
|/ Author: lirendada <2916776007@qq.com>
| Date: Tue Jul 11 15:33:00 2023 +0800
|
| modify readme:master
………………
最后,不要忘记 dev1
分支使用完毕后就可以删除了:
[liren@VM-8-7-centos gitcode]$ git branch -d dev1
Deleted branch dev1 (was 0cd9936).
[liren@VM-8-7-centos gitcode]$ git branch
* master
Ⅴ. 分支管理策略
一、分支合并模式
在 git merge
指令中,有三种主要的合并模式,它们分别是:
Fast-forward
合并模式(默认模式):- 如果 被合并的分支的历史记录可以直接被当前分支所包含,那么
git merge
直接将当前分支的指针指向被合并分支的最新提交。这种模式下 不会创建新的合并提交。
- 如果 被合并的分支的历史记录可以直接被当前分支所包含,那么
- 普通合并模式:
- 如果 被合并的分支的历史记录不能直接被当前分支所包含,那么
git merge
将会创建一个新的合并提交,用于记录两个分支的合并历史,包含了被合并分支和当前分支的所有历史记录。这种模式下 会创建一个新的合并提交!
- 如果 被合并的分支的历史记录不能直接被当前分支所包含,那么
- 简单合并模式:
- 如果 被合并的分支只有一个提交和当前分支有共同的提交,那么
git merge
会将被合并分支的提交合并到当前分支上,并创建一个新的提交,但不会创建新的合并提交。这种模式下,被合并分支的历史记录将会被包含在当前分支的提交历史中。
- 如果 被合并的分支只有一个提交和当前分支有共同的提交,那么
💥需要注意的是,如果执行普通合并或简单合并,可能会出现合并冲突(Merge Conflict
),需要手动解决冲突后再提交合并结果。
所以在执行合并操作时,需要根据实际情况选择合适的合并模式,以免影响项目的稳定性和可维护性。
其中这里的前两点我们在前面就遇到了,通常合并分支时,如果可能,Git
会采用 Fast forward
模式,因为它很快!
在这种 Fast forward
模式下,删除分支后,查看分支历史时,会丢掉分支信息,看不出来最新提交到底是 merge
进来的还是正常提交的。
但在讲合并冲突部分的时候,我们也看到通过解决冲突问题,会再进行一次新的提交,得到的最终状态为:
那么这就不是 Fast forward
模式了,这样的好处是,从分支历史上就可以看出分支信息。例如我们现在已经删除了在合并冲突部分创建的 dev1
分支,但依旧能看到 master
其实是由其他分支合并得到:
[liren@VM-8-7-centos gitcode]$ git log --graph --pretty=oneline --abbrev-commit
* 3800d19 merge, and modify readme
|\
| * 0cd9936 modify readme:dev1
* | c7bfef3 modify readme:master
|/
* 1e0a028 dev insert into readme
* c82c67b change readme
* ac00e61 add file5
* ced585b add file4
* 7eb10f5 add 3 files
* cdc96e6 first add file:readme
[liren@VM-8-7-centos gitcode]$
Git
支持我们 强制禁用 Fast forward
模式,那么就会在 merge
时生成一个新的 commit
,这样,从分支历史上就可以看出分支信息。
下面我们实战以下 --no-ff
方式的 git merge
!
首先,创建新的分支 dev2
,并切换至新的分支:
[liren@VM-8-7-centos gitcode]$ git checkout -b dev2
Switched to a new branch 'dev2'
[liren@VM-8-7-centos gitcode]$ git branch
* dev2
master
接着修改 readme
文件,并提交一个新的 commit
:
[liren@VM-8-7-centos gitcode]$ cat readme
lirendada
hello master
[liren@VM-8-7-centos gitcode]$ echo "add one line" >> readme
[liren@VM-8-7-centos gitcode]$ cat readme
lirendada
hello master
add one line
[liren@VM-8-7-centos gitcode]$ git add readme
[liren@VM-8-7-centos gitcode]$ git commit -m 'add one line: dev2'
[dev2 ab04ce4] add one line: dev2
1 file changed, 1 insertion(+)
切回 master
分支,开始合并:
[liren@VM-8-7-centos gitcode]$ git checkout master
Switched to branch 'master'
[liren@VM-8-7-centos gitcode]$ git merge --no-ff -m 'merge with no fast forward' dev2 # 非ff合并需要生成一个合并提交,要有记录
Merge made by the 'recursive' strategy.
readme | 1 +
1 file changed, 1 insertion(+)
[liren@VM-8-7-centos gitcode]$ cat readme # 可以看到已经合并成功,并且不是ff方式
lirendada
hello master
add one line
[liren@VM-8-7-centos gitcode]$ git log --pretty=oneline --graph --abbrev-commit
* 1b27e08 merge with no fast forward
|\
| * ab04ce4 add one line: dev2
|/
* 3800d19 merge, and modify readme
|\
| * 0cd9936 modify readme:dev1
* | c7bfef3 modify readme:master
|/
* 1e0a028 dev insert into readme
* c82c67b change readme
* ac00e61 add file5
* ced585b add file4
* 7eb10f5 add 3 files
* cdc96e6 first add file:readme
请注意 --no-ff
参数,表示禁用 Fast forward
模式。禁用 Fast forward
模式后合并 会创建一个新的 commit
,所以加上 -m
参数,把描述写进去。
所以在合并分支时,加上 --no-ff
参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而 fast forward
合并就看不出来曾经做过合并。
二、分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活,那在哪干活呢?
干活都在 dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如 1.0
版本发布时,再把 dev
分支合并到 master
上,在 master
分支发布 1.0
版本;
你和你的同事每个人都在 dev
分支上干活,每个人都有自己的分支,时不时地往 dev
分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
Ⅵ. bug分支 – git stash
&& 合并策略
假如我们现在正在 dev2
分支上进行开发,开发到一半,突然发现 master
分支上面有 bug
需要解决。在 Git
中,每个 bug
都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
可现在 dev2
的代码在工作区中开发了一半,还无法提交,怎么办❓❓❓
例如:
[liren@VM-8-7-centos gitcode]$ git branch
* dev2
master
[liren@VM-8-7-centos gitcode]$ cat readme
lirendada
hello master
add one line
i am coding...
[liren@VM-8-7-centos gitcode]$ git status
# On branch dev2
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: readme
#
no changes added to commit (use "git add" and/or "git commit -a")
此时如果我们直接切换到 master
的话,dev2
中工作区和暂存区的文件就没了!
为了不让 dev2
分支中开发了很久的文件就此消失。我们可以用 git stash
指令,将当前的工作区信息进行储藏,被储藏的内容可以在将来某个时间恢复出来。
[liren@VM-8-7-centos gitcode]$ git stash
Saved working directory and index state WIP on dev2: ab04ce4 add one line: dev2
HEAD is now at ab04ce4 add one line: dev2
[liren@VM-8-7-centos gitcode]$ git status # 发现工作区和暂存区的内容都被拿走,存储起来了
# On branch dev2
nothing to commit, working directory clean
其存储本质就是在 .git/refs
目录下创建了一个 stash
文件,里面放着此时分支存储的内容:
其文件内部存的就是该存储文件的索引:
[liren@VM-8-7-centos gitcode]$ cat .git/refs/stash
2c625bd178126e9e4e0efc67e7affdb1db3d43aa
[liren@VM-8-7-centos gitcode]$
储藏 dev2
工作区之后,由于我们要基于 master
分支修复 bug
,所以需要切回 master
分支,再 新建临时分支来修复 bug
,一般这个临时分支我们给它命名为 fix_bug
。
可能有人就会问,为什么不直接在
master
分支上修改呢❓❓❓ 原因很简单,因为
master
分支是需要保持稳定的,就算其出了bug
,我们也不能直接在它上面动手脚,因为可能会改出更大的bug
,所以此时我们需要用临时分支来代替完成修改bug
的工作!
举个例子:
[liren@VM-8-7-centos gitcode]$ git branch
dev2
* master
[liren@VM-8-7-centos gitcode]$ cat readme # 查看readme文件,发现最后一行少写了e,是个bug
lirendada
hello master
add one line
a, b, c, d,
[liren@VM-8-7-centos gitcode]$ git checkout -b fix_bug # 创建出一个临时分支,并且切换过去
M readme
Switched to a new branch 'fix_bug'
[liren@VM-8-7-centos gitcode]$ vim readme # 进行修改和查看
[liren@VM-8-7-centos gitcode]$ cat readme
lirendada
hello master
add one line
a, b, c, d, e
[liren@VM-8-7-centos gitcode]$ git add readme # 提交修改完的结果
[liren@VM-8-7-centos gitcode]$ git commit -m 'fix bug'
[fix_bug 1405e9d] fix bug
1 file changed, 1 insertion(+)
修复完成后,切换到 master
分支,并完成合并,记得 最好要使用 非fast forward
方式合并,最后删除 fix_bug
分支:
[liren@VM-8-7-centos gitcode]$ git checkout master # 切换为master分支
Switched to branch 'master'
[liren@VM-8-7-centos gitcode]$ git merge --no-ff -m 'merge fix_bug' fix_bug # 合并分支
Merge made by the 'recursive' strategy.
readme | 1 +
1 file changed, 1 insertion(+)
[liren@VM-8-7-centos gitcode]$ git log --pretty=oneline --graph --abbrev-commit # 打印分支提交记录
* 658c712 merge fix_bug
|\
| * 1405e9d fix bug
|/
* 1b27e08 merge with no fast forward
|\
| * ab04ce4 add one line: dev2
|/
* 3800d19 merge, and modify readme
|\
| * 0cd9936 modify readme:dev1
* | c7bfef3 modify readme:master
|/
* 1e0a028 dev insert into readme
……
[liren@VM-8-7-centos gitcode]$ cat readme # 查看内容,看到已经改过来了
lirendada
hello master
add one line
a, b, c, d, e
[liren@VM-8-7-centos gitcode]$ git branch -d fix_bug # 删除临时分支
Deleted branch fix_bug (was 1405e9d).
至此,bug
的修复工作已经做完了!
但是我们还要继续回到 dev2
分支进行开发,所以切换回 dev2
分支:
[liren@VM-8-7-centos gitcode]$ git checkout dev2
Switched to branch 'dev2'
[liren@VM-8-7-centos gitcode]$ git status
# On branch dev2
nothing to commit, working directory clean
工作区是干净的,刚才的工作现场存到哪去了?用 git stash list
命令看看:
[liren@VM-8-7-centos gitcode]$ git stash list
stash@{0}: WIP on dev2: ab04ce4 add one line: dev2
可以看到工作现场还在,Git
是把 stash
内容存在 .git/refs/stash
中了,但是需要恢复一下,如何恢复现场呢❓❓❓
我们可以使用 git stash pop
命令,恢复的同时会把 stash
也删了,实例如下:
[liren@VM-8-7-centos gitcode]$ git stash pop
# On branch dev2
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: readme
#
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (2c625bd178126e9e4e0efc67e7affdb1db3d43aa)
另外,恢复现场也可以采用
git stash apply
恢复,但是恢复后,stash
内容并不删除,此时需要用git stash drop
来删除,如下所示:[liren@VM-8-7-centos gitcode]$ git stash list stash@{0}: WIP on dev2: ab04ce4 add one line: dev2 [liren@VM-8-7-centos gitcode]$ git stash apply #恢复现场,但不删除stash内容 # On branch dev2 # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: readme # no changes added to commit (use "git add" and/or "git commit -a") [liren@VM-8-7-centos gitcode]$ git stash list stash@{0}: WIP on dev2: ab04ce4 add one line: dev2 [liren@VM-8-7-centos gitcode]$ git stash drop #手动删除 Dropped refs/stash@{0} (76e4351a0cdf678e1efbef6396a2462d9c36d729) [liren@VM-8-7-centos gitcode]$ git stash list [liren@VM-8-7-centos gitcode]$
恢复完代码之后我们便可以继续完成开发,开发完成后便可以进行提交,例如:
[liren@VM-8-7-centos gitcode]$ cat readme #查看内容,假设此时已经开发完
lirendada
hello master
add one line
i am coding... Done!!!
[liren@VM-8-7-centos gitcode]$ git add readme #提交
[liren@VM-8-7-centos gitcode]$ git commit -m 'modify readme'
[dev2 7f3aebd] modify readme
1 file changed, 1 insertion(+)
但我们注意到了,修复 bug
的内容,并没有在 dev2
上显示。此时的状态图为:
master
分支目前最新的提交,是要领先于新建 dev2
时基于的 master
分支的提交的,所以我们 在 dev2
中当然看不见修复 bug
的相关代码。
我们的 最终目的是要让 master
合并 dev2
分支,那么正常情况下我们切回 master
分支直接合并即可,但这样其实是 有一定风险!
是因为在合并分支时可能会有 合并冲突,而代码冲突需要我们手动解决(在 master
上解决)。我们无法保证对于冲突问题可以正确地一次性解决掉,因为在实际的项目中,代码冲突不只是一两行那么简单,有可能几百上千条,甚至更多,解决的过程中难免手误出错,导致错误的代码被合并到 master
上。此时的状态为:
解决这个问题的一个好的建议就是:最好在 dev2
分支上先试探性地合并下 master
,如果没问题的话,再让 master
去合并 dev2
,这样做的好处是有冲突的话可以在 dev2
中解决并进行测试,而不影响 master
分支 。此时的状态为:
对应的实操演示如下,要说明的是,以下演示的 merge
操作中,没有使用 --no-ff
,但上述的图示是使用了禁用 Fast forward
模式后得出的,主要是为了方便解释问题。
[liren@VM-8-7-centos gitcode]$ git branch # 确保当前是dev2分支
* dev2
master
[liren@VM-8-7-centos gitcode]$ git merge master # 合并master分支到dev2分支下
Auto-merging readme
CONFLICT (content): Merge conflict in readme
Automatic merge failed; fix conflicts and then commit the result.
[liren@VM-8-7-centos gitcode]$ cat readme # 查看错误内容
lirendada
hello master
add one line
<<<<<<< HEAD
i am coding... Done!!!
=======
a, b, c, d, e
>>>>>>> master
[liren@VM-8-7-centos gitcode]$ vim readme # 修改错误内容
[liren@VM-8-7-centos gitcode]$ cat readme # 查看修改后内容
lirendada
hello master
add one line
a, b, c, d, e
i am coding... Done!!!
[liren@VM-8-7-centos gitcode]$ git add readme # 重新提交
[liren@VM-8-7-centos gitcode]$ git commit -m 'merge master'
[dev2 8b781ad] merge master
[liren@VM-8-7-centos gitcode]$ git status
# On branch dev2
nothing to commit, working directory clean
[liren@VM-8-7-centos gitcode]$ git checkout master # 切换到master分支
Switched to branch 'master'
[liren@VM-8-7-centos gitcode]$ git merge dev2 # 合并dev2分支
Updating 658c712..8b781ad
Fast-forward
readme | 1 +
1 file changed, 1 insertion(+)
[liren@VM-8-7-centos gitcode]$ git status
# On branch master
nothing to commit, working directory clean
[liren@VM-8-7-centos gitcode]$ git branch -d dev2 # 删除dev2分支
Deleted branch dev2 (was 8b781ad).
Ⅶ. 删除临时分支 – git branch -D [name]
软件开发中,总有无穷无尽的新的功能要不断添加进来。
添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以,每添加一个新功能,最好新建一个分支,我们可以将其称之为 feature
分支,在上面进行开发,完成后合并,最后删除该 feature
分支即可。
可是,如果我们今天正在某个 feature
分支上开发了一半,被产品经理突然叫停,说是要停止新功能的开发。虽然白干了,但是这个 feature
分支还是必须就地销毁,留着没用了。
这时使用传统的 git branch -d
命令删除分支的方法是不行的。因为 -d
选项只有在合并了分支以后才能用来删除指定分支。
[liren@VM-8-7-centos gitcode]$ git checkout -b dev # 创建并切换到dev分支开发新模块
Switched to a new branch 'dev'
[liren@VM-8-7-centos gitcode]$ touch newfile
[liren@VM-8-7-centos gitcode]$ echo "i am iron man" >> newfile
[liren@VM-8-7-centos gitcode]$ cat newfile
i am iron man
[liren@VM-8-7-centos gitcode]$ git add newfile # 提交更改
[liren@VM-8-7-centos gitcode]$ git commit -m 'give a new file'
[dev bf27408] give a new file
1 file changed, 1 insertion(+)
create mode 100644 newfile
[liren@VM-8-7-centos gitcode]$ git checkout master # 此时要求删除dev分支,则切换到master分支
Switched to branch 'master'
[liren@VM-8-7-centos gitcode]$ git branch -d dev # 进行删除,发现失败,并且提示说要用-D选项去删除
error: The branch 'dev' is not fully merged.
If you are sure you want to delete it, run 'git branch -D dev'.
根据提示,我们得用 git branch -D dev
去删除 dev
分支,如下所示:
[liren@VM-8-7-centos gitcode]$ git branch -D dev
Deleted branch dev (was bf27408).
[liren@VM-8-7-centos gitcode]$ git branch
* master
[liren@VM-8-7-centos gitcode]$
小结
分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了 50%
的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
并且 Git
无论创建、切换和删除分支,Git
在 1
秒钟之内就能完成!无论你的版本库是1
个文件还是1
万个文件。