目录
Git 分支模型被称为“必杀技特性”,与其他版本控制系统相比可谓是难以置信的轻量级,本文是 《Git 使用指南》 后续:图解Git的分支管理。
本文是 《Pro Git》 阅读笔记整理,文中不详细之处,可阅读原著补充。
一、Git 分支本质
1.1、首次提交后,各对象间关系
- 生成blob类型对象,它存储着当前文件状态的快照。(暂存操作)
- 生成tree对象,该对象中的每个文件,包含着指向blob对象的索引。(提交)
- 创建commit对象,它包含指向tree对象的索引和其他元数据。(提交操作)

1.2、多次提交后,提交对象间关系
修改后再次提交,那么这次的提交对象会包含一个指向上次提交对象的指针。

1.3、本地master主分支
Git分支本质:是一个指向commit 对象的可变指针,例如master为默认分支名称。

1.4、创建新分支
创建新分支本质:是创建一个指向当前commit对象的指针。
git branch testing #创建testing分支
1.5、当前分支
HEAD 指针指向当前分支,默认指向 master 分支。

1.6、切换分支
checkout 切换至 testing 分支,于是 HEAD 指针也指向了testing分支。(图1-6-1)
git checkout testing #切换至testing分支
修改后再提交,HEAD 依然随着testing分支一起向前移动(图1-6-2)
vim test.rb
git commit -a -m 'made a change'
返回原来的master主分支继续工作。(图1-6-3)
git checkout master
当在主分支中修改提交后,分叉出"87ab2"的提交对象,但不会干扰到testing分支。(图1-6-4)
vim test.rb
git commit -a -m 'made other changes'
二、分支工作流程
- 开发某个项目 (master主分支,生产服务器)
- 为该项目增加某个新需求 (iss53分支)
- 已上线项目需要修补,于是为本次修补建立新分支(hotfix分支)
- 合并补丁(hotfix分支)到master分支,完成已上线项目Bug修补
- 继续开发新需求(iss53分支),并且纳入已发布的补丁
2.1、开发某个项目(master分支)
首先,我们正在开发某个项目,并推送到了生产服务器。

2.2、为该项目增加某个新需求 (iss53分支)
创建并切换至iss53分支,注意要加"-b"参数。
git checkout -b iss53
修改后再次提交。
vim index.html
git commit -a-m 'addedanewfooter [issue53]' 
2.3、修补上线项目Bug(hotfix分支)
已上线项目需要修补,于是为本次修补建立新分支(hotfix分支)
git checkout master #首先切换到主分支
git checkout -b 'hotfix' #然后创建hotfix分支
2.4、合并补丁(hotfix分支)到master分支
补丁(hotfix分支)测试完毕,于是合并到master分支,完成Bug修补。
git checkout master #首先切换到主分支
git merge hotfix #测试完毕后,合并hotfix分支
git branch -d hotfix #删除已合并的hotfix分支合并时会出现“Fast forward”(快进)提示,表示无分歧需要解决,直接向前移动指针。

2.5、继续开发新需求,并纳入已发布的补丁
git checkout master #切换至master
git merge iss53 #合并iss53到master注意:这次与之前hotfix合并不同,由于当前master分支直接祖先是C2,iss53分支直接祖先是C3,所以Git需要和它们的共同祖先C2进行一次简单的三方合并计算。

解决合并冲突
git status #合并失败的文件以"unmerged"标出
git add . #标记冲突已解决,并暂存文件
#git mergetool Git默认图形化冲突解决工具
三、远程分支
远程分支(remote branch)是对远程仓库状态的索引,格式如下:
格式:远程仓库名/分支名
默认:origin/master3.1、克隆远程分支
Git clone 会克隆远程origin/master分支,并建立一个相同位置的本地master分支。

正在本地master分支工作,与此同时,其他人向远程仓库推送了内容,从f42c5更新到了190a3,但是本地origin/master指针依然会停留在f42c5位置。

3.2、更新本地分支
git fetch 从远程仓库更新你本地还没有的数据。(新增了31b8e,190a3)

注意:在fetch操作抓来新远程分支XX之后,本地不会新增XX分支,有的只是一个你无法移动的origin/XX指针。
如果要把远程分支合并到当前分支,可以运行:
git merge [远程仓库名]/[分支名]
git merge origin/serverfix。3.3、跟踪分支
从远程分支检出的本地分支,称为跟踪分支(tracking branch)。在跟踪分支里git push可推送本地分支到相应的远程仓库,反过来可直接运行git pull,可获取远程分支所有更新,并且合并到当前本地分支。
注:克隆操作会自动创建一个master分支来跟踪origin/master。
如果想要在远程分支的基础上分化出一个新分支,可以运行:
方法一:
git checkout -b [分支名] [远程名]/[分支名]
git checkout -b serverfix origin/serverfix方法二:(适用1.6.2以上版本Git)
git checkout --track [远程名]/[分支名]
git checkout --track origin/serverfix3.4、推送本地分支
方法一:
语法:git push (远程仓库名) (本地分支名)
实例:git push origin serverfix方法二:(可自定义远程分支名)
语法:git push (远程仓库名) (本地分支名):(远程分支名)
实例:git push origin serverfix:serferfix四、衍合
把一个分支整合到另一个分支的办法有两种:merge(合并) 和rebase(衍合),两者得到的结果一样,但衍合能产生一个更为整洁的提交历史。
4.1、合并与衍合区别
合并(merge):通过C2-C3-C4三方合并计算生成C5。

衍合(rebase):把在C3 里产生的变化补丁重新在C4 的基础上打一遍。
衍合原理:从两个分支共同祖先(C2)开始,提取你所在分支(experiment)每次提交时产生的差异(diff),然后回到需要衍合入的分支(master),按顺序施用每一个差异补丁文件。
git checkout experiment
git rebase master
最后回到master分支进行一次快进合并,结果同三方合并例子中的C5内容一模一样,但衍合能产生一个更为整洁的提交历。
4.2、衍合(rebase)风险
注释事项:永远不要衍合那些已经推送到公共仓库的更新。
常见用法:一般把衍合当成一种在推送之前清理提交历史的手段,而且仅仅衍合那些永远不会公开的commit
五、小结
5.1、新建本地分支
git branch testing #创建testing分支
git checkout testing #切换至testing分支
git checkout -b iss53 #创建并切换至iss53分支。5.2、合并本地分支
git checkout master #首先切换到主分支
git merge hotfix #测试完毕后,合并hotfix分支
git branch -d hotfix #删除已合并的hotfix分支5.3、解决合并冲突
git status #合并失败的文件以"unmerged"标出
git add . #标记冲突已解决,并暂存文件
git mergetool #Git默认图形化冲突解决工具5.4、合并远程分支
origin/master #格式:远程仓库/分支名
git merge origin/serverfix #合并远程分支serverfix到当前分支5.5、删除远程分支
#git push [远程仓库名] :[远程分支]
#注意区别:git push [远程名] [本地分支]:[远程分支]
git push origin :serverfix #删除远程分支5.6、创建跟踪分支
方法一:
git checkout -b [分支名] [远程名]/[分支名]
git checkout -b serverfix origin/serverfix方法二:(适用1.6.2以上版本Git)
git checkout --track [远程名]/[分支名]
git checkout --track origin/serverfix5.7、查看分支
git branch #列出所有分支,星号"*"表示当前所在分支。
git branch -v #查看各分支最后一次提交信息
#查看已和当前分支合并的分支
#可用git branch -d 删掉未标星号的分支,因为已经合并了。
git branch --merged
#查看未和当前分支合并的分支
#强制删除未合并的分支git branch -D
git branch --no-merged 5.8、衍合所在分支
git checkout experiment #切换到需要衍合的分支
git rebase master #衍合experiment分支到主分支