三、分支管理
何为分支管理?
就是通过创建、切换、合并不同的分支,让不同开发任务可以在相互隔离的环境中并行进行,最后将代码变化合并到目标分支。
比如:现在公司要求:开发新登录功能、修复线上支付 Bug、开发新的订单模块
如果所有人直接改 master。
会出现问题:
- 新功能代码和 Bug 修复混在一起
- 一个功能没完成,不能提交
- 多人修改容易冲突
- 线上版本不稳定
所以引入分支管理,多个任务就可以并行处理了。
1.查看当前版本库的分支和工作分支
命令 :git branch

master前面有一个*,表示当前HEAD指向的分支,为当前的工作分支。
查看远程分支
命令 :git branch -r
查看远程和本地的所有分支:
命令 : git branch -a
查看本地关联的远程分支
命令 :git branch -vv

上图显示,本地的master分支与远程的master分支建立了连接。
本地分支与远程分支建立连接关系(tracking relationship)后,就可以直接使用简短的:git push和git pull,因为在没有建立连接时,Git 不知道:我要把本地分支推到远程哪个分支?
2.分支创建
命令 :git branch 分支名

在.git隐藏文件中也新增了一个分支:


发现新增加的dev分支与master分支执行的commit id一样。这是因为我们是基于最新版本之下创建的分支。

3.切换分支
HEAD所指向的分支就是当前的工作分支,现在要求在dev分支下做开发,所以要分支切换。
命令 :git checkout 分支名


现在我们已切换到了dev分支下了。
我们对README.md文件做一下修改,然后再提交到版本库中。


切换到master分支下,查看README.md文件是否发生了变化。

发现并没有变化,这说明了不同分支之间 能相互的隔离。

创建新分支并切换到该新分支上:
git checkout -b 新分支名
4.合并分支
本质 :就是合并提交(commit),更准确地说,是把一个分支上的提交历史合入当前分支,使当前分支包含另一个分支的提交所带来的代码变化。
命令 :git merge 目标分支
比如:
git checkout A
git merge B
#含义:
把 B 合并到 A
分支合并的三种情况:
1)情况一:
基于master分支创建了dev分支,在dev分支上修改并提交代码后,回到master分支上,合并dev分支。


合并时,可以看到是fast forward模式,即master 直接移动到 dev 的最新提交,结果就是master和dev分支指向了同一个commit id。
2)情况二:(不产生冲突)
基于master分支创建了dev分支,在dev分支上修改并提交代码后,master分支也修改并提交了代码,但是两分支修改的是不同的地方的代码 (即不会产生冲突),回到master上,合并dev分支。


3)情况三:(产生冲突)
基于master分支创建了dev分支,在dev分支上修改并提交代码后,master分支也修改并提交了代码,**但是两分支修改的地方有重复(即会产生冲突),回到master上,合并dev分支。

5.删除分支
合并分支后,被合并的分支就没用了,使用git branch -d 删除分支名
注意:要删除dev分支,只能切换其他分支下删除,不能在dev分支下,删除dev分支。
6.合并冲突
产生冲突的原因:
Git 在合并两个分支时,发现同一个文件的同一范围内容被两个分支分别修改,并且无法自动判断应该保留哪个修改。需要开发者手动选择,然后 add + commit 完成合并。
产生冲突的条件:
口诀:同文件 + 同区域 + 两边改 + Git无法判断
-
修改同一个文件
-
修改同一位置附近(同一行修改)
-
两个分支都做修改
-
git无法判断该留那一部分
Git冲突不是看有没有修改同一句话,而是看两个分支的修改范围是否产生重叠。
案例 :
原本README.md文件中的内容。

在master分支修改README.md文件,并提交:

在dev分支修改README.md文件,并提交:

在master分支下合并dev分支:

合并分支在README.md文件中发生冲突。查看README.md文件。

需要手动编辑,决定保留的部分,然后删除这些标记。<<<<<<< HEAD
=======
dev

然后需要git add ,git commit提交到版本库中,冲突才算解决。

合并冲突时的状态:

以图形结构查看提交历史
使用git log --graph --abbrev-commit也可以把Git提交历史 + 分支关系 + 合并关系 用字符画出来。
- --abbrev-commit 的作用:显示短版本 commit id
- --graph 的作用:用 ASCII 字符画出 Git 提交分支图
为该命令设置别名,方便调用:
shell
git config --global alias.lg "log --graph --abbrev-commit --oneline"
以后执行git lg,就等价于执行git log --graph --abbrev-commit --oneline

7.合并模式
1)fast forword 快速合并模式:
fast forword模式,看不出来最新一次的提交到底是merge进来的,还是正常提交的。

上图中master分支实际上merge了dev分支,但是在图形结构提交历史图中只能看出此时master和dev分支指向了同一次提交,不能看出是否发生了merge操作。
2)no fast forward模式(只要涉及到merge,建议加上--no-ff):
使用 --no-ff 强制禁用快进模式合并,Git 会生成一个新的 commit,这样从历史上就能看出来最新一次的提交到底是merge进来的,还是正常提交的。
如果想使用这种模式:需要在git merge时,加上--no-ff -m "合并描述信息"
shell
git merge --no-ff -m "m描述信息" 合并分支
注 :合并冲突默认就是no fast forward 模式,因为能从结构提交图清晰的看出发生了合并。
示例 :
在dev修改README.md文件,并进行一次提交,然后切回到master分支下,使用no fast forward模式合并dev分支。

能从上图看出dev分支先提交了一次代码,然后master分支与dev分支发生了合并。此时HEAD执行的是master分支最新一次合并提交,dev指向的还是自己的提交。
8.使用分支的策略(工作中常用)
master主分支是稳定的,上面的代码是运行在线上的,比如发布的APP和网站等,所以不能在master分支上直接提交代码。
我们日常开发是在dev分支上,dev分支上的代码需要经过严格的测试,最终才能合并到master分支上。

1)情况一:master分支出现Bug的解决办法?
在dev分支下开发代码,dev分支是基于master分支创建的,此时master分支下出现了bug,如何解决bug还能保证开发的正常进行?

解决方式一:
可以先将开发的代码提交到版本库,再在master的最新一次提交下创建一个fix_bug分支修复bug,修复完bug后,master分支与fix_bug分支合并,解决了BUG,然后切换到dev分支下,继续开发代码,最后开发完成,由于dev分支下还存在BUG,所以先在dev分支下与master分支合并,解决完代码冲突后,再切回到master分支下,合并dev分支。最终代码开发完成,bug也修复了。

解决方式二:
在master分支发现BUG时,dev分支并没有提交代码,而是将开发的代码存到了缓存区,在解决完bug后,在回到dev分支,将之前开发的代码恢复,然后继续进行后面的开发,最后开发完成,由于dev分支下还存在BUG,所以先在dev分支下与master分支合并,解决完代码冲突后,再切回到master分支下,合并dev分支。最终代码开发完成,bug也修复了。

代码提交至缓存的命令 :git stash
- 注意 :
git stash默认只保存已经被 Git 跟踪的文件修改;如果需要保存新创建但未提交的文件,需要使用git stash -u;如果连.gitignore忽略的文件也需要保存,则使用git stash -a。 - 什么是跟踪(tracked) 文件 :曾经执行过
git add并提交到版本库的文件。 - 什么是未跟踪(untracked) 文件 :从来没有被添加到版本库。
从缓存区恢复代码到工作区 :git stash -pop
为什么需要将代码先保存到缓存区 ?
dev 分支未提交的修改虽然被 Git 跟踪,但仍属于工作区状态。如果直接切换分支,这些修改可能污染 master 分支,甚至导致提交时把两个任务的代码一起提交。stash 的作用是暂时把未完成代码移走,让修复 bug 的分支保持干净。
9.强制删除分支
如果我们新创建了一个分支,在分支上提交了代码,但是没有与其他分支合并 ,如果此时使用git branch -d 分支名,删除该分支,git是不允许,因为git认为,你既然创建了分支,也进行了代码提交,就是有用的,不能随便删除。如果你确实想删除,git也提供了命令:git branch -D 分支名,表示强制删除该分支。
