分支就是暂时从主线代码离开,去专注于开发一个新特性或者解决一个小 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 diff 分支1 分支2,比如输入 git diff master feature_print,由于我们当前就在 feature_print 分支上,可以简化输入 git diff master 来和 master 分支相比较。
可以非常清楚的看到了新增的打印相关的代码,假设打印的功能已经开发完成了,我们需要希望在主干分支上合并新的打印功能的代码,以便下一步的开发或是发布,首先切换到主分支:
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 来执行。
合并完成以后,提示消息如下:
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 分支的情况。
通常很多功能分支或者解决 bug 的分支git还原本地修改,我们在合并完成以后,会将它删除,因为它的代码已经被合并到主分支了,以后需要继续开发或者有 bug 的话可以从主分支再次创建单独的分支来解决。使用 git branch -d feature_print 来删除打印功能的分支,使用 tig 查看一下分支时间线的形态,发现feature_print 分支这个名称不会再显示了,但是形态并没有变化,依靠提交的注释能够清楚的知道了合并了 feature_print 分支的代码。
分支的时间线形态非常重要,尤其是我们需要回到历史的时候,实际工作的时候,可能同时有多个分支存在,如果合并后的时间线形态杂乱无章,对我们代码的维护会产生非常大的副作用。
上面是理想的 merge 合并的情况,处于 master 分支和 feature_print 分支的代码完全是新增的包含关系,也就是没有修改到重叠的代码,如果合并feature_print 分支的时候git还原本地修改,master 分支上的打印功能也被修改了,那么合并就会产生冲突,而 git merge 会直接合并文件,并且报告冲突的代码情况,这时候需要手动修改文件解决冲突,完成后再次做一次提交。如下图,master 和 feature_print 都同时修改了同一行代码,在 master 分支上查看区别:
执行 git merge –no-ff feature_print 后显示:
提示有冲突尝试自动合并,但是自动合并失败了(如果没有修改到重叠的代码 git 可以自动化的成功合并),需要手动解决文件的差异了,因为 git 并不知道保留哪个分支的代码才是需要保留的,打开文件发现长这个样子,用 === 分割了 2 个分支的代码,使用 > 表示了被合并的 feature_print 分支。
我们觉得 master 分支添加的换行和 feature_print 分支的 ok 文本都是需要保留的,修改文件如下:
使用 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 ..." 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 merge 的缺点就是有冲突的时候,所有文件都被合并了,没有给用户留机会做出选择,并且单独生成了一个合并的 commit 提交节点(显示 M)。如果希望渐进式的合并,就需要使用第二种方法。
第二种: 使用 git rebase 命令,切换到 master 分支上,执行 git rebase feature_print 合并,如果没有冲突,直接合并成功是最理想的情况,有冲突的情况如下:
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 ..." to unstage)
(use "git add ..." 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 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 分支。
限时特惠:本站每日持续更新海量设计资源,一年会员只需29.9元,全站资源免费下载
站长微信:ziyuanshu688