Git大法好——3.Git分支本地操作详解

来源:互联网 发布:csgo淘宝 编辑:程序博客网 时间:2024/06/05 16:27

转自

http://blog.csdn.net/coder_pig/article/details/50744842


引言

在上一节中我们对Git的常用本地操作的命令进行详解,而本节要讲解的是Git的分支, 
在讲解之前补充两点概念性的东西:

第一个: 
第一节中一个读者提出的疑问,Git和SVN在版本控制中存储方式版本信息的差异。 
答:Git关心文件的整体是否发生变化,而SVN则关心的是文件内容的具体差异! 
SVN每次记录的是有哪些文件进行了修改,以及修改了哪些行的哪些内容:

如上图,比如版本2中记录的是文件A以及文件C的变化,而版本3中仅仅记录文件C 
的变化这样,以此类推;而Git并不保存这些前后变化的差异数据,而是保存整个当前 
的工作空间(暂存区)所有文件,又叫快照,有变化的文件保存,没变化的文件就不保存, 
而是对上一次保存的快照作一个链接。

如上图,每一次保存的都是所有文件,改变的保存,没改变的链接指向上一次提交的文件! 
因为这种不同的保存方式,Git切换分支的速度比SVN快几条街!

第二个: 
Git每次commit时候,在仓库中的数据结构

如上图,Blob对象存储的是文件的快照内容,tree对象则是记录快照索引的目录 . 
当然,上面的内容知道下就好了,也不必过于深究,好的,开始学习本节Git分支的相关 
内容吧~


1.什么是分支?

我们知道每次我们commit的时候都会生成一个快照,或者说一个版本库,从引言我们也知道 
了通过Blob对象存储文件快照内容,然后Tree对象记录快照索引目录,通过索引找到文件快照; 
那么问题来了:每一个快照(版本库)又是怎样的组合到一起的呢? 
还记得我们上一节讲解的版本回退吗?我们可以根据一个版本号,让当前工作空间的文件回退 
到某个版本,其实Git会将这些快照串成一条条的时间线,而这些时间线就是我们的:分支。 
假如我们不创建并切换到其他分支上,那么每次commit生成的快照都会被串到一条线上,而这 
条时间线又叫master分支或者主分支,除了这个master分支外,我们还要知道一个东西 
就是HEAD指针,这个指针是指向正在工作的本地分支,我们前面的版本回退,其实就是修改 
HEAD指针的指向而已!比如:git reset HEAD^就是将HEAD指针前移,指向上一个快照 
而已,可能你还不是很理解,没事,我们撸一发命令,然后来波图解就好~

这里我们新建四个文件,然后每次add一个文件后commit,然后我们键入:git log –graph –all

上面的这道红线就是版本的时间线(分支)了,而上面的每一个节点则是某一版本的快照, 
这条红线就是我们的master分支,而上面的结点就是我们的每个版本,下面我们画图帮助大家理解下:

第一次提交: 第二次提交: 
第三次提交: 
第四次提交:

由上面的图,我们不难发现这样的规律:

  • 当我们每次commit,我们的master都会向前移动一步,即指向最新的提交
  • HEAD则指向你正在工作的本地分支,而git reset修改的就是HEAD 
    指针的指向而已!

2.为什么要创建其他分支?

看到这个标题,读者可能会有疑问:不是已经有一个master分支了吗,为什么还要另外创建 
其他分支?我们每次commit就好,假如是远程协作的话,就都Push到远程服务器上,有冲突 
就解决冲突,然后每个人在pull一下服务器上的代码不就好了,另外创建新分支好像没什么 
必要吧?

:我以前也是这样想的,在上一家公司,我和另外的同事就是这样干的…把东西都丢到 
master分支上,感觉没什么不对,当然,这种做法是可以的,项目小可以,整个项目就一个master分支, 
但是这样做其实并不好!下面列举两点吧: 
第一点,我们一般的项目都是一步步迭代升级的,一般都会有大版本和小版本的更新, 
大版本更新一般是改头换面的一个更新,比如UI大改或者架构大改之类的,然后版本是:v2.0.0这样; 
而小版本的更新一般是一些细节的小改,比如UI修改和bug的修复,或者优化等,然后版本是:v2.0.11这样; 
只有一条master分支,意味着你的分支线会非常非常的长,假如你发布了第二个大版本,而用户反馈 
你的第一个版本有一个很严重的Bug,你要切回之前的版本,够呛的哈! 
第二点,效率问题,假如某一次提交后出现冲突了,而这个冲突很难解决,那么就会卡在这里, 
那么就无法向后再开发了,又或者说master上的分支出现了很大的问题,同样也无法接着开发。

当然,不好的地方远远不止上面两个,我们得想办法来解决这个问题,而一个简单而有效的 
方法就是创建其他的分支,然后按照一定的分支策略来管理我们的项目版本!一种最简单和 
常用的分支策略就是:

master分支上开辟一个新的develop分支,然后我们根据功能或者业务,再在develop 
分支上另外开辟其他分支,完成分支上的任务后,再将这个分支合并到develop分支上!

master分支和develop分支都是长期分支,而我们创建的其他分支则是临时性分支

简单概括下各个分支都拿来干嘛吧:

master分支:可直接用于产品发布的代码,就是正式版的代码 
develop分支:日常开发用的分支,团队中的人都在这个分支上进行开发 
临时性分支:根据特定目的开辟的分支,包括功能(feature)分支,或者预发布(release)分支, 
又或者是修复bug(fixbug)分支,临时性分支用完之后一般都会删除,使得代码库的常用分支始终 
只有两个长期分支!

PS:关于分支管理的详细策略,我们后面讲多人协作再细讲,这里知道最简单的这种就可以了!


3.分支的创建与切换

1)创建分支

<code class="hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">git branch 分支名</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

我们可以直接简单git branch或者git branch -a来查看所有分支,而此时分支和HEAD 
的情况如下:

此时,尽管我们创建了develop分支,但是HEAD指针还指向master分支,我们继续commit 
的话,都会在master分支上进行,我们需要切换一下当前分支,即修改HEAD指针的指向!

2)切换分支

<code class="hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">git checkout 分支名</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

好的,通过上面的命令我们就切换到develop分支下了,切换后的情况是这样的:

其实,分支的创建和切换只需要下面的一个指令就可以完成了:

<code class="hljs lasso has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">git checkout <span class="hljs-attribute" style="box-sizing: border-box;">-b</span> 分支名</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

接着我们来修改下某个文件的内容,改点东西,然后commit,然后此时版本线的情况如下:

接着我们切回master分支,键入:git checkout master,打开我们的note_1.txt,这个时候 
你会发现并没有发生更改,因为我们刚刚的提交是在develop分支上进行的,而master分支上 
没有变化,此时的版本线情况如下:


4.分支的合并

在Git中,我们可以使用git mergegit rebase两个命令来进行分支的合并。 
而本节我们主要讲解如何使用merge指令来合并分支,另外合并的方式又分为两种: 
快速合并和普通合并,两者的区别在于前者合并后看不出曾经做过合并,而后者合并 
后的历史会有分支记录!作图是快速合并,右图是普通合并!

 _______ 

1.快速合并

我们把develop分支合并到master分支上,来到master分支后,键入下述命令:

<code class="hljs sql has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">git <span class="hljs-operator" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">merge</span> develop</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

合并成功,此时我们打开note_1.txt文件,可以看到:

嘿嘿,果然develop分支上的做的更改都合并到master分支上了!这里的cat命令是linux 
下用来打印文件内容的一个指令!

2.普通合并

这里的话我们切到develop分支下,修改note_2.txt的内容,然后再通过下面的指令合并分支: 
–no-ff参数表示禁用快速合并!

<code class="hljs sql has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">git <span class="hljs-operator" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">merge</span> --<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">no</span>-ff -m <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"合并的信息(TAG)"</span> develop</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

成功合并,然后我们可以键入:git log –graph -all来查看版本状态,当然这里我们只 
关心的是分支线的情况,我们可以键入:

<code class="hljs brainfuck has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">git</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">log</span> <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">-</span><span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">-</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">graph</span> <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">-</span><span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">-</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">pretty=oneline</span> <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">-</span><span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">-</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">abbrev</span><span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">-</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">commit</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

结果如下:

当然,不是每次合并分支都是顺顺利利的,有事会发生合并冲突,这个时候,我们 
需要处理冲突,完成后才能够进行合并!


5.解决合并冲突

这里我们切到master分支下,修改note_3.txt,写点东西,add后提交,然后切到develop分支, 
也是修改note_3.txt文件,add后commit,最后切回master分支,然后再执行merge合并分支。 
这个时候就会出现合并失败,需要我们手动解决冲突后才能提交!

可能命令行看的不是很清楚,我们打开note_3.txt文件:

选择要保留的内容,add后提交,成功后分支就合并成功了,接着键入git status看下状态, 
也可以键入:git log –graph –pretty=oneline –abbrev-commit 查看整个版本线的状态!


6.删除分支

删除分支就简单很多了,直接键入:

<code class="hljs bash has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">git branch <span class="hljs-operator" style="box-sizing: border-box;">-d</span> 分支名</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

这里我们把dev分支删除掉:


7.恢复误删分支

当然有时可能我们会手多,或者不小心把某些分支给删掉了,你后悔了,想恢复 
被删的分支,没关系,我们先键入:

<code class="hljs rust has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">git <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">log</span> --branches=<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"被删的分支名"</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

获取到该分支的最新版本的那个版本号id(取前七位即可),接着键入下述命令即可:

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">git branch develop 版本<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">id</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

结果如下:

无压力的说!


8.切换分支时保存未commit的更改

比如可能有这样一个场景: 
当你在某个分支上写代码写得很嗨的时候,这个时候你的同事过来找你,他看不懂你写的 
某个分支上的代码,要你解释一波,这时候你需要切换到另外一个分支上,此时,你的代码 
还没有提交,会提示切换失败,比如我这里在develop分支上新建一个Task分支,然后新建 
一个note_5.txt文件,add,commit,接着修改文件内容,add,commit,再接着add,不commit 
直接切换分支,就会出现切换分支失败,提示我们要么commit或者stash

你可以直接commit,不过,假如你的代码才写了几行或者未完成,一般都不想去提交的, 
你可能想保存当前的状态,然后跟同事BB完后,又回来当前的状态来,那么git stash指令 
能帮到你!直接键入:

<code class="hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">git stash</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

然后就可以切换分支了,切换分支后,招呼完同事,你可以键入:

<code class="hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">git stash apply</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

恢复你之前的状态,比如note_5.txt我们add后还没commit!

另外,可以保存多个stash哦,他们会放在一个stash的列表中你可以根据表示符 
来解除对应的stash并且恢复未提交的变更!键入下述命令可查看stash列表:

<code class="hljs php has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">git stash <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">list</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

标识符就是括号里的,如果你想回复某个stash的话,比如这里,你就可以键入:

<code class="hljs css has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">git</span> <span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">stash</span> <span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">apply</span> <span class="hljs-tag" style="color: rgb(0, 0, 0); box-sizing: border-box;">stash</span><span class="hljs-at_rule" style="box-sizing: border-box;">@<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">{0}</span></span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

你只要修改{}里的标识符(数字)即可!


小结:

本节给大家讲解了一波Git的本地分支操作的命令,基本涵盖了一些日常的本地分支操作, 
同样建议你跟着笔者的文章,一步步走指令,动手更有助于理解!下节我们来讲解远程 
仓库和多人协作的分支管理策略!敬请期待~ 
假如你觉得本文简单易懂,对你学习Git分支有帮助,点个赞留个评论如何?

,晚安!


参考文献

  • Pro Git(官方)
  • Pro Git(oschina)

——作者:coder-pig,本教程不收取任何费用,欢迎转载,转载请注明出处,尊重作者 
劳动成果,请勿用于商业用途,侵权必究!


0 0
原创粉丝点击