分布式版本控制系统Git|七Git分支介绍不同时间的文件快照。Git在执行提交操作时,会保存一个提交对象(commitobject)。提交对象包含指向暂存内容快照的指针、提交者的姓名、电子邮件地址、提交信息以及指向其父对象的指针。当然,第一次提交产生的??提交对象没有父对象。其他普通commit操作产生的对象有一个父对象,多个分支合并时产生的commit对象有多个父对象。分支创建与合并分布式版本控制系统Git|三在上面的文章中,我们提到对于每一次提交,Git都会将它们串成一条时间线,也就是一个分支。在创建新分支之前,这里是主分支,也就是master分支。Git是如何创建新分支的?使用gitbranch,在这里它创建了一个可以移动的新指针。比如创建一个dev分支:$gitbranchdev这时候会在commit对象上创建一个指针,如下图:现在创建了一个分支,但是Git是如何区分两个分支的,又是如何区分的呢?它知道它目前在哪个分支吗?这里提到了一个特殊的指针HEAD。严格来说,HEAD并不指向提交。默认指向master,而master或者新建的dev指向提交,HEAD指向当前分支。刚才gitbranchdev只是新建了一个分支,并不会自动切换到新分支。HEAD指向当前分支,可以使用命令gitlog查看各个分支当前指向的对象。带参数--decorate:$gitlog--oneline--decorate861b17e(HEAD->master,dev)项目的初始提交此时master和dev分支都指向校验和以861b17e开头的commit对象。切换分支要切换现有分支,您可以使用gitcheckout或gitswitch。这里使用gitswitch,比较好理解:$gitswitchdevSwitchedtobranch'dev'让HEAD指向dev分支。以上是将创建和切换操作分开。Git还提供了在创建时切换到分支的命令:$gitswitch-ciss007或$gitcheckout-biss007使用带有-c参数的gitswitch命令,或者使用带有-b参数的checkout命令。事实上,上面的命令可以分解为:$gitbranchiss007$gitswitchiss007$gitbranchiss007$gitcheckoutiss007假设你在iss007分支上工作并进行了提交。在此过程中,iss007分支将继续向前推进。$vimISSUE$gitcommit-a-m"Fixissue007[issue007]"假设此时还有一个紧急问题需要修复。此时使用Git不需要把这个issue和issue007混为一谈,不需要revert对iss007的修改,然后再添加对这个紧急问题的修改。只需要回到master分支新建一个分支就可以解决燃眉之急了。这里需要注意的是,在切换到master分支之前,需要注意工作目录和暂存区是否有未提交的文件。它会检测分支冲突,防止切换分支(这种情况后面会介绍)。这假设工作目录是干净的:$gitswitchmasterSwitched到分支'master'$gitswitch-chotfix切换到一个新分支'hotfix'$viISSUE$gitcommit-a-m"fixedthehotissue"现在紧急issue已经修复,此时可以合并回master分支并部署上线,如下:$gitswitchmaster$gitmergehotfixUpdating861b17e..48a6cfaFast-forwardISSUE|1+1filechanged,1insertion(+)createmode100644ISSUE注意Fast-forward,意思是“快速模式”,即直接将master指向当前提交的hotfix,合并速度快.注意:并非所有合并都可以快进。同时,我们可以删除hotfix分支,因为任务已经完成:$gitbranch-dhotfixDeletedbranchhotfix(was48a6cfa)。冲突解决现在紧急问题已经修复并部署上线,可以回到iss007分支继续修改文件。假设iss007分支也被修改以解决问题。这时候也认为mergemaster会部署上线:$gitswitchmaster$gitmergeiss007CONFLICT(add/add):MergeconflictinISSUEAuto-mergingISSUEAutomaticmergefailed;修复冲突然后提交结果。这时候会出现合并冲突,因为iss007分支和hotfix分支修改的是同一个文件。Git不会自动创建新的合并提交。提交前需要手动解决冲突。使用gitstatus查看处于未合并状态的文件:$gitstatus在分支master你有未合并的路径。(修复冲突并运行“gitcommit”)(使用“gitmerge--abort”中止合并)未合并的路径:(使用“gitadd...”标记解决方案)同时添加:问题未添加任何更改提交(使用“gitadd”和/或“gitcommit-a”)现在查看冲突文件:<<<<<<>>>>>>iss007Git用<<<<<<<,=======,>>>>>>>标记不同分支的内容,修改内容如下:修复热点问题。修复问题007。修复问题继续并完成。删除标记的部分,按照要求解决冲突,然后用gitadd标记为冲突已解决。$gitaddISSUE$gitstatusOnbranchmasterAll冲突已修复,但您仍在合并。(使用"gitcommit"结束merge)要提交的变更:modified:ISSUE此时可以提交部署:gitcommit-a-m"Mergebranch'iss007'"[master7059dd0]Mergebranch'iss007'分支管理策略gitbranch除了创建和删除分支。当不加参数时,该命令可以得到所有分支的列表:$gitbranchdeviss007*master*数字表示当前分支(即使是HEAD指针指向的分支)。添加-v参数可以查看各个分支的最终提交信息:$gitbranch-vdev861b17eInitialcommitofprojectiss0075267039Fixissue007again*master7059dd0Mergebranch'iss007'gitbranch命令多了两个选项——合并和--no-merged。这两个选项分别过滤合并或尚未合并到当前分支。例如:查看已经合并到当前的分支:$gitbranch--mergediss007*master因为已经和iss007合并了,所以此时也可以删除分支,这里什么都不会丢失:$gitbranch-diss007Deleted分支iss007(原为5267039)。要查看包含未合并工作的所有分支,可以使用gitbranch--no-merged:$gitbranch--no-mergeddev在这里显示dev分支,其中包含尚未合并的工作。现在尝试使用删除命令将失败:$gitbranch-ddeverror:Thebranch'dev'isnotfullymerged。如果您确定要删除它,请运行“gitbranch-Ddev”。如果你真的要删除这些未合并的作业,你可以根据下面的提示使用-D选项强制删除。在抓取分支多人协作的情况下,每个人将自己的修改推送到master和dev分支。但是当协作者修改了同一个文件,先提交到远程仓库,这时候你修改问题再提交,就会提示冲突。解决方法:使用gitpull抓取文件,在本地合并解决冲突,然后push。这里可以参考上面的冲突解决步骤。每次合并和推送后变基(Rebase),分支都会变得混乱。Git有一个操作叫做rebase,它可以让Git的commithistory变成一条直线。这里我用之前学过的例子来说明。与远程分支同步后,我对文件进行了两次提交并使用gitlog检查了它:*24be579(HEAD->master)addauthor*94448feaddcomment*6a7291astoreGitLearn*e36f4e9(origin/master)addcontentaboutcooperativeandupdatecatalog*6cd4087addknowledgeaboutfeaturebranch注意到Git使用(Head->master)和(origin/master)来标识当前分支的HEAD和远程origin的位置是24be579addauthor和e36f4e9添加内容..,本地3在远程分支之前提交现在尝试推送本地分支:$gitpushoriginmasterTogithub.com:username/git_learn.git![rejected]master->master(fetchfirst)error:failedtopushsomerefsto'git@github.com:username/git_learn.git'提示:更新被拒绝,因为远程包含你提示的工作:本地没有。这通常是由另一个存储库推送提示引起的:到相同的引用。您可能希望在再次推送之前首先集成远程更改提示:(例如,“gitpull...”)。提示:有关详细信息,请参阅“gitpush--help”中的“关于快进的注意事项”。失败,说明有人推送到远程库的第一个分支。先拉$gitpullremote:枚举对象:4,done.remote:计数对象:100%(4/4),done.remote:压缩对象:100%(1/1),done.remote:Total3(delta1),reused3(delta1),pack-reused0Unpackingobjects:100%(3/3),done.Fromgithub.com:damengsanqianqiu/git_learne36f4e9..538bd7cmaster->origin/masterCONFLICT(add/add):合并hello.py中的冲突自动合并hello.py自动合并失败;修复冲突然后提交结果。解决冲突,重新提交。然后使用gitstatus查看状态$gitstatusOnbranchmaster你的分支比'origin/master'提前了4个提交。(使用“gitpush”来发布你的本地提交)加上刚才的合并提交,现在我们的分支领先于远程分支4个提交。使用gitlog查看$gitlog--graph--pretty=oneline--abbrev-commit*2027993(HEAD->master)fixconflictofhello.py|\|*538bd7c(起源/主人)设置出口=1*|24be579添加作者*|94448fe添加评论*|6a7291astoreGitLearn|/*e36f4e9添加合作内容和更新目录现在分支很乱,这时候rebase就派上用场了,用gitrebase试试:$gitrebaseFirst,rewindingheadtoreplayyourworkontopof它...应用:添加评论使用索引信息重建基础树...Mhello.pyFallingbacktopatchingbaseand3-waymerge...Auto-merginghello.pyApplying:addauthorUsingindexinfotoreconstructabasetree...Mhello.pyFallingbacktopatchingbaseand3-waymerge...自动合并hello.py然后用gitlog看看,$gitlog--graph--pretty=oneline--abbrev-commit*2027993(HEAD->master)修复hello.py*冲突24be579添加作者*94448fe添加评论*6a7291a存储GitLearn*538bd7c(origin/master)setexit=1originalpoint分叉提交现在变成一条直线。我们注意到,Git“移动”了我们的本地提交,并将其放在538bd7c(origin/master)setexit=1之后,这样整个提交历史就变成了一条直线。rebase操作前后,最终提交的内容始终是一样的。但是本地的commit修改内容发生了变化。他们的修改不再是基于e36f4e9addcontent...,而是基于538bd7c(origin/master)setexit=1,但是最后的commit2027993是一致的。这就是rebase操作的特点:将分叉的提交历史“组织”成一条直线,看起来更直观。缺点是修改了本地的forkcommit。关于rebasing和merging,这里会有不同的说法。有人认为commithistory是对发生的事情的记录,使用rebase会抹去痕迹。还有人认为commithistory就是项目过程中发生的事情,没人在乎第一版手稿,而且大部分手册都修改过很多次才可以方便使用。这里没有必要追求一个完整而简单的答案。需要根据实际情况判断做出选择。但是总的原则是,对于本地没有推送或者共享的修改,可以根据情况进行rebase清理历史,对于已经推送到别处的commit,不要进行rebase操作。以上是本文的主要内容。目前Git的介绍已经更新。但是,这些内容还远远没有将Git讲解透彻。但是这些内容可以让你简单的了解Git,希望这些内容可以让你慢慢的学习和理解。这里如果想了解更多Git的使用,可以查看官方文档:https://git-scm.com/book/en/v2欢迎关注微信公众号《书所集录》