分支模型是Git的必杀技特性,也正因为这一特性,使得 Git 从众多版本控制系统中脱颖而出。Git 处理分支的方式可谓是难以置信的轻量,只是简单地指向某个提交纪录。创建新分支这一操作几乎能在瞬间完成,并且在不同分支之间的切换操作也是一样便捷。
Git 鼓励在工作流程中频繁地使用分支与合并,因为即使创建再多分支也不会造成储存或内存上的开销,并且按逻辑分解工作到不同的分支要比维护那些特别臃肿的分支简单多了。
正常来说,每次提交是有先后关系的,所有提交将会串成一条直线。而分支就是在原本的直线上分出去的岔路。一个分支从分出去的那一刻起,在其上的修改将完全独立于其他分支(除非你显式地将2个分支合并到一起)。使用分支可以把你的工作从开发主线上分离开来,方便了自己的开发,同时也不会影响开发主线。当大家在各自的分支上开发完后,又可以合并到开发主线上,整合所有的开发。
初始化仓库的时候git会自动创建一个master分支
HEAD
git中有一个HEAD的概念,它是一个指针,通常情况下,它指向当前分支。而分支又指向一个commit提交。
分支常用命令
创建分支
创建分支要明确基于哪个分支哪次提交(默认为最新提交)创建新分支。
git branch 分支名 //只创建一个分支
git checkout -b 分支名 //创建分支,并切换到此分支。-b即为branch
例:在master分支的最新一次提交的基础上创建feature分支,并切换到feature分支
git checkout -b feature
切换分支
git checkout 分支名
查看分支
git branch //查看本地仓库分支
git branch -r //查看远程的分支信息
git branch -a //查看本地和远程的分支
git log --graph //查看分支合并图
保留&恢复工作现场
当手头工作没有完成时,先把分支工作现场git stash一下,然后切换到别的分支去干别的事,再git stash pop,回到工作现场。
//保留工作现场
git stash
//查看保留的工作现场
git stash list
//恢复工作现场
git stash apply //恢复现场
git stash drop //删除保留的stash内容
git stash pop //恢复现场并删除保留的stash内容
合并分支
git merge 分支名
删除分支
git branch -d 要删除的分支名 //删除合并过的分支
git branch -D 要删除的分支名 //删除还没有合并过的分支
查看远程分支
git remote //查看远程分支的名称
git remote -v //查看远程分支的具体提交过去的地址
合并分支
git rebase vs git merge详解(好文推荐)
合并分支有两种方式:git merge 和 git rebase
假设有两个分支master和feature,feature为基于master分支的最近提交而创建的分支。feature分支开发完要并入master分支(下文都用这两个分支名来做示例)。同时也许还有其他基于master分支的别的分支在同时开发其他功能,完成后也要并回master分支。
1、git merge
merge合并分支采取两种策略。fast forward
模式和true merge
模式
fast forward
模式
如果feature分支有新的提交,而master分支不动,则表示无分叉,master分支所在的提交对象是要并入的feature分支的直接上游(顺着A分支走下去可以到达feature分支),这时Git在合并两者时,只会简单地把指针右移,因为这种单线的历史分支不存在任何需要解决的分歧,所以这种合并过程可以称为快进(Fast forward)。fast forward模式合并分支时,删除没用的分支后,会丢掉分支信息,这在分支仅作为技术分支而不需要保留此分支的信息时非常有用,可以保持干净的分支结构。如果分支信息有用,希望保留时,可以加上 --no-ff参数 强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
merge合并用法
//切换到要合并的分支上(如上文提到的master分支)
git merge 被合并的分支名 //fast forward模式
git merge --no-ff -m "描述" 被合并的分支名 //强制禁用fast forward模式

运行git-merge时含有大量的未commit文件很容易让你陷入困境,这将使你在冲突中难以回退。因此非常不鼓励在使用git-merge时存在未commit的文件,建议使用git-stash命令将这些未commit文件暂存起来,并在解决冲突以后使用git stash pop把这些未commit文件还原出来。
true merge
模式
如果master分支所指向的提交对象不是feature分支的直接祖先,那么Git 就不得不进行一些额外处理。Git 会用两个分支的末端以及它们的共同祖先(Git自己裁决哪个共同祖先才是最佳合并基础而不像CVS等需要开发者手工指定合并基础)进行一次简单的三方合并计算,并对三方合并后的结果重新做一个新的快照,并自动创建一个指向它的提交对象。这个提交对象比较特殊,它有两个祖先(master和feature分支的提交对象)。
merge合并用法
//切换到要合并的分支上(如上文提到的master分支)
git merge 被合并的分支名

2、git rebase
对一个分支做变基
操作,这意味着改变这个branch的初始commit(commits本身组成了一颗树)。它会在新的base上一个一个地运行这个分支上的所有commits。
rebase原理:feature分支执行变基操作,把feature的提交取消掉,并把它们临时保存为补丁,然后把feature更新到分支master的最新提交状态,再把保存的补丁应用到feature分支。最后把master更新到最新状态。
rebase合并和merge合并得到的结果没有任何区别,但rebase可以把提交历史整理成直线,使代码库的提交历史变得清晰直观,仿佛所有修改都是在一根线上先后完成的,尽管实际上它们原来是同时并行发生的。通常在当本地的工作(由一些列的commits组成)被认为是在一个过时的base基础上做的工作的时候才需要用rebase。

rebase黄金定律
永远不要rebase一个已经分享的分支,也就是说永远不要rebase一个已经在中央库中存在的分支,只能rebase自己使用的私有分支。
最佳实践
既然merge和rebase都能够实现合并分支的功能,那么到底应该选择哪种方式来合并分支呢?
1. 当merge一个不需要在版本变更历史图谱中显示的临时的本地branch时,我总是使用一个fast-forward merge策略来merge这类branch,而这往往需要在merge之前做一个rebase;
2.当我需要merge一个项目组都知道的local branch时。。。我得确保这个branch的信息会在历史图谱中一直展示,我总是执行一个true merge;
3.当我准备push我的本地工作时。。。我得首先清理我的本地历史信息以便我总是push一些清晰易读有用的功能;
4.当我的push由于和别人已经发布的工作相冲突而被拒绝时,我总是rebase更新到最新的remote branch以避免用一些无意义的micro-merge来污染历史图谱
分支的目的是什么?这个分支需要在历史图谱中展示吗?
如果是临时性的技术性质的不需要添加到版本图谱里的分支,创建它仅仅是为了在开发它的同时又能防止master变得不稳定。那么这种分支就不要产生图谱信息,只要完成开发任务就可以了。考虑到分叉问题,如果merge的目标分支(比如说master分支)在我们创建feature分支之后一直没有改变,那么合并分支用一个fast-forward merge就足够了。如果master分支在这个feature分支创建后又往前走了,也就是说master分支(头tip)已经不再是这个临时feature分支的直接祖先了,我们会认为我们这个feature分支too old了,所以我们往往需要使用git rebase -i master梳理历史信息(比如合并成一个commit),在master的tip上重新运行我们feature分支上的commit以便保持一个线性的历史,然后再用git merge产生一个fast forward,最终以一个commit展示在master分支上。
//没分叉的情况
git merge feature //fast forward merge
//分叉的情况
git rebase -i master //feature变基,保持线性的历史
git merge feature //fast forward merge
如果是一个特别活动的跟踪,可能代表了一个sprint或者说user story的实现过程,或者说代表了我们的一个bug fix过程。那么永远不要使用rebase,而是git merge --no-ff,这样该分支历史永远存续在主分支上
git merge --no-ff -m "实现某功能" feature
一般我们使用rebase的目的,是想要得到一个能在远程分支上干净应用的补丁,比如某个项目你不是维护者,但是想帮点忙,最好使用rebase处理。先在自己的一个分支进行开发,当准备向主项目提交补丁的时候,根据最新的orgin/master进行一次衍合操作然后再提交,这样维护者就不需要任何整合工作。
实际为:把解决分支补丁同最新主干代码之间的冲突的责任,划转给由提交补丁的人来解决。
作为维护项目的人只需要根据你提供的仓库地址做一次快进合并,或者直接采纳你提交的补丁。
使用rebase请务必遵循如下准则:一旦分支中的提交对象发布到公共仓库,就千万不要对该分支进行rebase操作。
网友评论