分支就是暂时从主线代码离开,去专注于开发一个新特性或者解决一个小 bug,git 创建和切换分支都能在瞬间完成,默认的 git 仓库,输入 git branch 可以看到只有一个 master 分支,通常这就是主分支。

wangbo@wangbo-VirtualBox:~/test/git-demo$ git branch * master

假设现在需要开发一个打印的新特功能,分支名称为 feature_print,从 master 分支离开去新的分支有几个写法:

1. git branch feature_print(或 git branch feature_print master ) && git checkout feature_print(或 git switch feature_print)

2. git checkout -b feature_print(或 git checkout -b feature_print master)

3. git switch -c feature_print (或 git switch -c feature_print master)

其中 git switch 是新版本的 git 命令,以前的章节讲过 git checkout 用来恢复文件,checkout 本身是签出文件的意思,用来切换分支语义上有点模糊,后连就增加了语义更加明确的 switch 切换,随时都可以使用 git branch 查看分支的情况,前面的 * 表示你当前所在的分支。

wangbo@wangbo-VirtualBox:~/test/git-demo$ git branch * feature_print master

在新的分支下,可以专注于开发打印的功能了,我们添加一个 print.c 文件提交,从提交日志上看,工作成果是放到了 feature_print 分支上。

git 常用分支(大白话git教程-08-分支合并和典型用法)(1)

如果想知道两个分支的变化,可以使用 git diff 分支1 分支2,比如输入 git diff master feature_print,由于我们当前就在 feature_print 分支上,可以简化输入 git diff master 来和 master 分支相比较。

git 常用分支(大白话git教程-08-分支合并和典型用法)(2)

可以非常清楚的看到了新增的打印相关的代码,假设打印的功能已经开发完成了,我们需要希望在主干分支上合并新的打印功能的代码,以便下一步的开发或是发布,首先切换到主分支:

1. git switch master

2. git checkout master

wangbo@wangbo-VirtualBox:~/test/git-demo$ git switch master Switched to branch 'master' wangbo@wangbo-VirtualBox:~/test/git-demo$ git branch feature_print * master

git branch 命令清楚的显示当前已经在 master 主干分支了,现在需要将 feature_print 分支的代码合并到 master 分支上,怎么做呢? 有 2 种做法,我们分别来介绍。

第一种: 使用 git merge 指令,默认如果执行 git merge feature_print 就可以,但是这样不会为这次合并单独生成一个 commit 节点,为了以后能清楚的看到分支合并的情况,通常会使用一个参数 --no-ff,也就是使用 git merge --no-ff feature_print 来执行。

git 常用分支(大白话git教程-08-分支合并和典型用法)(3)

合并完成以后,提示消息如下:

wangbo@wangbo-VirtualBox:~/test/git-demo$ git merge --no-ff feature_print Merge made by the 'recursive' strategy. print.c | 6 1 file changed, 6 insertions( ) create mode 100644 print.c

通过 tig 看看分支时间线的形态,可以清楚的看到了,master 分支合并了 feature_print 分支的情况。

git 常用分支(大白话git教程-08-分支合并和典型用法)(4)

通常很多功能分支或者解决 bug 的分支,我们在合并完成以后,会将它删除,因为它的代码已经被合并到主分支了,以后需要继续开发或者有 bug 的话可以从主分支再次创建单独的分支来解决。使用 git branch -d feature_print 来删除打印功能的分支,使用 tig 查看一下分支时间线的形态,发现feature_print 分支这个名称不会再显示了,但是形态并没有变化,依靠提交的注释能够清楚的知道了合并了 feature_print 分支的代码。

git 常用分支(大白话git教程-08-分支合并和典型用法)(5)

分支的时间线形态非常重要,尤其是我们需要回到历史的时候,实际工作的时候,可能同时有多个分支存在,如果合并后的时间线形态杂乱无章,对我们代码的维护会产生非常大的副作用。

上面是理想的 merge 合并的情况,处于 master 分支和 feature_print 分支的代码完全是新增的包含关系,也就是没有修改到重叠的代码,如果合并feature_print 分支的时候,master 分支上的打印功能也被修改了,那么合并就会产生冲突,而 git merge 会直接合并文件,并且报告冲突的代码情况,这时候需要手动修改文件解决冲突,完成后再次做一次提交。如下图,master 和 feature_print 都同时修改了同一行代码,在 master 分支上查看区别:

git 常用分支(大白话git教程-08-分支合并和典型用法)(6)

执行 git merge --no-ff feature_print 后显示:

git 常用分支(大白话git教程-08-分支合并和典型用法)(7)

提示有冲突尝试自动合并,但是自动合并失败了(如果没有修改到重叠的代码 git 可以自动化的成功合并),需要手动解决文件的差异了,因为 git 并不知道保留哪个分支的代码才是需要保留的,打开文件发现长这个样子,用 === 分割了 2 个分支的代码,使用 <<<< 表示当前工作分支, >>>> 表示了被合并的 feature_print 分支。

git 常用分支(大白话git教程-08-分支合并和典型用法)(8)

我们觉得 master 分支添加的换行和 feature_print 分支的 ok 文本都是需要保留的,修改文件如下:

git 常用分支(大白话git教程-08-分支合并和典型用法)(9)

使用 git status 查看一下仓库的状态:

wangbo@wangbo-VirtualBox:~/test/git-demo$ git status On branch master You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge) Unmerged paths: (use "git add <file>..." to mark resolution) both modified: print.c no changes added to commit (use "git add" and/or "git commit -a")

显示 print.c 的文件状态是 both modified,需要暂存提交,执行 git add . && git commit -m 'fix print text' 后使用 tig 查看分支形态:

git 常用分支(大白话git教程-08-分支合并和典型用法)(10)

所以 git merge 的缺点就是有冲突的时候,所有文件都被合并了,没有给用户留机会做出选择,并且单独生成了一个合并的 commit 提交节点(显示 M)。如果希望渐进式的合并,就需要使用第二种方法。

第二种: 使用 git rebase 命令,切换到 master 分支上,执行 git rebase feature_print 合并,如果没有冲突,直接合并成功是最理想的情况,有冲突的情况如下:

git 常用分支(大白话git教程-08-分支合并和典型用法)(11)

git 给了我们三种选择:

1. 自己解决文件冲突后执行 git rebase --continue 后继续尝试后面的合并

2. 执行 git rebase --skip 直接跳过后继续尝试合并,回头再来收拾残局

3. 执行 git rebase --abort 放弃本次合并,代码切换到 master 为合并前的状态

这时候我们可以打开文件 print.c,修复文件冲突,执行下面 2 个命令继续合并:

git add . git rebase --continue

continue 后 git 会继续尝试合并,如果还有文件冲突,重复执行这个过程,也可以随时 git rebase --abort 放弃,不想立刻解决就执行 git rebase --skip,直到整个合并完成。

wangbo@wangbo-VirtualBox:~/test/git-demo$ git status rebase in progress; onto 5a8323a You are currently rebasing branch 'master' on '5a8323a'. (fix conflicts and then run "git rebase --continue") (use "git rebase --skip" to skip this patch) (use "git rebase --abort" to check out the original branch) Unmerged paths: (use "git restore --staged <file>..." to unstage) (use "git add <file>..." to mark resolution) both modified: print.c no changes added to commit (use "git add" and/or "git commit -a") wangbo@wangbo-VirtualBox:~/test/git-demo$ git add . wangbo@wangbo-VirtualBox:~/test/git-demo$ git rebase --continue Applying: add blank line

执行完成以后我们看看分支的时间线形态:

git 常用分支(大白话git教程-08-分支合并和典型用法)(12)

重点来了,我们发现 git rebase 和 git merge 生成的时间线并不相同,merge 保留了提交历史和操作过程,rebase 去掉了合并过程的提交更加简洁,但是合并后的 commit 信息可能需要修改,比如这个例子的 add blank line 已经不能同时表达文本被更新成 ok 的含义了,应该改为 fix print text 可能更合适(取决于你合并到底保留了什么样的代码)。

merge 和 rebase 如何选择呢?

1. 如果需要一个干净的时间线形态选择 rebase,如果希望渐进式的合并使用 rebase,可能会需要更新一次 commit 信息。

2. 如果需要保留完整的历史记录使用 merge,有多个文件冲突的时候可能比较棘手,但是 commit 信息都会保留下来。

关于 git 分支还有一些命令需要掌握:

1. 查看所有为合并的分支 git branch --no-merged

2. 查看合并过的分时 git branch --merged

3. 强行删除分支 git branch -D 分支名称

4. 而 git branch -v 可查看每个分支最后一次提交的情况

特别说明,一般我们不会在 master 分支上直接修改代码,本章节为了简化说明问题(避免太多的分支扰乱你的视线),在 master 上直接修改了代码,第 12 节学习科学的分支开发模型,也就是如何更加合理规范的使用 git 分支。

,