git merge,rebase跟*(no branch)

git merge,rebase和*(no branch)

        上一篇:http://blog.csdn.net/xiaoputao0903/article/details/23933589,说了git的分支,相关的用法没说到但是只要google就能搜出一大片,这里还有几个细节要注意一下,就是merge合并和rebase合并的区别,以及*(no branch)的处理。

1.merge

        在上篇介绍分支的时候有简单的说了一下分支的创建和合并,当时合并就是写的merge,这是根据两个不同分支的最后一次提交的commit对象c5,c7和两个分支的交叉点的commit对象c3进行一次简单的三方合并,最终得到一个新的commit来作为最终的提交commit对象c8,指针指向c8,而且c4,c5,c6,c7是存在于本地仓库的历史版本,我们可以通过日志查看找到这两个commit,同样也可以恢复到这两个版本。也就是下面这个图:

git merge,rebase跟*(no branch)

        上图是将test分支合并到master分支,然后我们来实现一次,假如现在已经到了c3,我新建一个分支test,然后先用master分支修改我的test.txt文件并提交重复两次,得到c4和c5,然后再切换到test分支同样对test.txt修改并提交两次,得到c6和c7,然后切换到master分支执行合并操作,这时会提示有冲突,最后我们解决冲突了再提交:

git merge,rebase跟*(no branch)

git merge,rebase跟*(no branch)

git merge,rebase跟*(no branch)

冲突:

git merge,rebase跟*(no branch)

日志:

git merge,rebase跟*(no branch)

2.rebase

        rebase称为衍合,这是个什么概念呢,如下图,当我们把test衍合到master的时候,是将c5,和c6中发生的变化打成补丁然后再c8的基础上做修改的,如果这个时候遇到冲突,就必须要我们自己决绝好冲突之后,添加到暂存区然后再继续合并,c5的变化补丁在c8上发生变化之后得到c5'这个commit对象,而c6的变化补丁再在c5'上修改得到c6',然后指针指向c6',这个过程我们称之为衍合,而且这个过程都是在一个*(no branch)分支上做的,这个后面会说到。而且有一个要注意的地方,如果git执行git gc或者我们手动执行git gc,那么c5和c6就不再存在于我们的本地仓库,我们就再也找不回这两个commit了,这个过程就是一个线性的过程,在test执行完之后,再在test的基础上执行master的操作。以上就是rebase和merge的区别所在。

git merge,rebase跟*(no branch)

        和merge一样,我们实际来做一次,假设现在我们已经在c4了,这时候创建test分支,所有修改操作和merge一样,然后我们用rebase这种方式来把test衍合到master上:

git merge,rebase跟*(no branch)

git merge,rebase跟*(no branch)

git merge,rebase跟*(no branch)

冲突:

git merge,rebase跟*(no branch)

日志:

git merge,rebase跟*(no branch)

3.*(no branch)

        在执行命令git branch查看分支的时候,如果出现*(no branch),则表示不在任何分支上进行工作。出现这种情况我也是在几次不经意之间,用git checkou回溯版本的时候,用git pull或者merge和rebase的时候会出现*(no branch)。目前我在rebase的时候都是在*(no branch)上进行的,当衍合完成后自动切到master上,我觉得这是个正常现象,但是其他几种方式就不正常了,具体原因我也不是很清楚。

        由于*(no branch)表示不在任何分支上进行,而有时我们不知道自己是在*(no branch)上进行操作的,而且可能我们已经进行很久的开发工作了,已经提交好几个版本的代码了,突然执行git branch发现在*(no branch)上,是不是一件很恐怖的事啊。

        当然经过提交的版本数据都会以快照的方式被记录在commit对象存在.git目录的objects子目录里,那么当我们发现是在*(no branch)时应该怎么解决呢。有两种情况。

        第一种情况是我们还没离开*(no branch),这个时候,我们可以执行git checkout -b mybranch命令,这个时候会创建新分支mybranch,并将*(no branch)里面的数据都checkout到mybranch分支上,然后我们再在mybranch上开发,最终合并到master上。

        第二种情况就不乐观了,我们已经离开*(no branch)了,然后发现用git log都找不到之前的提交了,当然了,在*(no branch)上提交的,在别的分支上怎么找的到在它上面提交的数据呢。不过也许还有救,如果git还没有执行git gc,那么我们可以通过执行git reflog找到在*(no branch)上提交的数据,然后根据找到的commit的id来恢复该数据,这也是最后唯一的希望了,如果git已经执行了git gc或者你手贱自己执行了git gc,那么就真的不能在一起愉快的玩耍了。

        所以在执行过git checkout恢复过以前的数据或者是做过合并分支的操作,那么不要吝啬你们的git branch,敲这个命令又不要钱,却能让你之后的提交高枕无忧。

4.总结

        merge的合并是三方合并,而且历史版本都在本地方库中,但是却比较繁琐,而且开发的过程是个网状结构,如果创建的分支比较多,进行的merge也比较多,那么就算我们在纸上画它的提交历史都会画的手疼,但是rebase就不一样,它是根据另外一个分支的修改内容进行打补丁然后在前一个分支的最后提交上进行修改,而且将另外一个分支的提交历史删除,这样就是一个线性的过程,很清晰。所以在推送到远程仓库之前尽量多用rebase来衍合分支,但是如果将一个commit推送到远程仓库之后,就不要再对它进行衍合操作了,因为这样的话,它很可能在你的某次衍合过程中被删除,那么再推送到远程仓库就会造成很大的损失。切记,rebase虽好可不要贪杯。