创建分支
git branch
没有参数,显示本地版本库中所有的本地分支名称。
当前检出分支的前面会有星号。
git branch newname
在当前检出分支上新建分支,名叫newname。
git checkout newname
检出分支,即切换到名叫newname的分支。
git checkout –b newname master
这个命令将上面两个命令合并:在master分支上创建分支newname分支并检出到该分支。
合并分支间的修改 Merge
合并操作将两条或多条分支合并到一起,实际上有好几种分支合并方法,下面介绍主要的三种:
1.直接合并(straight merge):
把两条分支上的历史轨迹合并,交汇到一起。
比如要把dev分支上的所有东东合并到master分支:
首先先到master分支:git checkout master
然后把dev给合并过来:git merge dev
注意没参数的情况下merge是fast-forward的,即Git将master分支的指针直接移到dev的最前方。
换句话说,如果顺着一个分支走下去可以到达另一个分支的话,那么Git在合并两者时,只会简单移动指针,所以这种合并成为快进式(Fast-forward)。
2.压合合并(squashed commits):
将一条分支上的若干个提交条目压合成一个提交条目,提交到另一条分支的末梢。
把dev分支上的所有提交压合成主分支上的一个提交,即压合提交:
git checkout master
git merge --squash dev
此时,dev上的所有提交已经合并到当前工作区并暂存,但还没有作为一个提交,可以像其他提交一样,把这个改动提交到版本库中:
git commit –m “something from dev”
3.拣选合并(cherry-picking):
拣选另一条分支上的某个提交条目的改动带到当前分支上。
每一次提交都会产生一个全局唯一的提交名称,利用这个名称就可以进行拣选提交。
比如在dev上的某个提交叫:321d76f
把它合并到master中:
git checkout master
git cherry-pick 321d76f
要拣选多个提交,可以给git cherry-pick命令传递-n选项,比如:
git cherry-pick –n 321d76f
这样在拣选了这个改动之后,进行暂存而不立即提交,接着可以进行下一个拣选操作,一旦拣选完需要的各个提交,就可以一并提交。
删除分支
有些分支没有必要长期保存,比如分支中的代码已经打了标签并已发布,或者实验分支已经成功完成工作或中途废弃等等。
注意:打了标签的分支,Git在删除该分支时,从版本树起始到此标签间的全部历史轨迹均会保留,此时删除分支操作只是删除分支本身的名称,因此可以说该分支没有必要长期保存。
而在其他版本控制工具中,删除分支通常意味着删除分支上的所有历史轨迹,所以不能因为打了标签就认为其没有必要保存。
删除一个分支dev2:
git branch –d dev2
注意不能删除当前所在分支,需要转到别的分支上。
如果要删除的分支已经成功合并到当前分支,删除分支的操作会直接成功。
如果要删除的分支没有合并到当前所在分支,则会出现提示,如果确定无须合并而要直接删除,则执行命令:
git branch –D dev2
进行强删。
分支重命名
重命名分支:
git branch –m oldname newname
-m不会覆盖已有分支名称,即如果名为newname的分支已经存在,则会提示已经存在了。
如果改成-M就可以覆盖已有分支名称了,即会强制覆盖名为newname的分支,这种操作要谨慎。
冲突处理
当两条分支对同一个文件的同一个文本块进行了不同的修改,并试图合并时,Git不能自动合并的,称之为冲突(conflict)。解决冲突需要人工处理。
比如当前在master分支,想把dev分支merge过来,结果产生了一个冲突,打开文件内容可以看到这么一个冲突:
<<<<<<< HEAD test in master ======= test in dev >>>>>>> dev
<<<<<<<标记冲突开始,后面跟的是当前分支中的内容。
HEAD指向当前分支末梢的提交。
=======之后,>>>>>>>之前是要merge过来的另一条分支上的代码。
>>>>>>>之后的dev是该分支的名字。
对于简单的合并,手工编辑,然后去掉这些标记,最后像往常的提交一样先add再commit即可。
冲突的产生 很多命令都可能出现冲突,但从根本上来讲,都是merge 和 patch(应用补丁)时产生冲突。 而rebase就是重新设置基准,然后应用补丁的过程,所以也会冲突。 git pull会自动merge,repo sync会自动rebase,所以git pull和repo sync也会产生冲突。当然git rebase就更不用说了。 冲突的类型 逻辑冲突 git自动处理(合并/应用补丁)成功,但是逻辑上是有问题的。 比如另外一个人修改了文件名,但我还使用老的文件名,这种情况下自动处理是能成功的,但实际上是有问题的。 又比如,函数返回值含义变化,但我还使用老的含义,这种情况自动处理成功,但可能隐藏着重大BUG。这种问题,主要通过自动化测试来保障。所以最好是能够写出比较完备的自动化测试用例。 这种冲突的解决,就是做一次BUG修正。不是真正解决git报告的冲突。 内容冲突 两个用户修改了同一个文件的同一块区域,git会报告内容冲突。我们常见的都是这种,后面的解决办法也主要针对这种冲突。 树冲突 文件名修改造成的冲突,称为树冲突。 比如,a用户把文件改名为a.c,b用户把同一个文件改名为b.c,那么b将这两个commit合并时,会产生冲突。 $ git status added by us: b.c both deleted: origin-name.c added by them: a.c 如果最终确定用b.c,那么解决办法如下: git rm a.c git rm origin-name.c git add b.c git commit 执行前面两个git rm时,会告警“file-name : needs merge”,可以不必理会。 树冲突也可以用git mergetool来解决,但整个解决过程是在交互式问答中完成的,用d 删除不要的文件,用c保留需要的文件。 最后执行git commit提交即可。 内容冲突的解决办法 发现冲突 一般来讲,出现冲突时都会有“CONFLICT”字样: $ git pull Auto-merging test.txt CONFLICT (content): Merge conflict in test.txt Automatic merge failed; fix conflicts and then commit the result. 但是,也有例外,repo sync的报错,可能并不是直接提示冲突,而是下面这样: error: project mini/sample 注:无论是否存在冲突,只要本地修改不是基于服务器最新的,它都可能报告这个错误,解决方法都是一样。 这个时候,需要进入报错的项目(git库)目录,然后执行git rebase解决: git rebase remote-branch-name 冲突解决的一般过程 merge/patch的冲突解决 先编辑冲突,然后git commit提交。 注:对于git来讲,编辑冲突跟平时的修改代码没什么差异。修改完成后,都是要把修改添加到缓存,然后commit。 rebase的冲突解决 rebase的冲突解决过程,就是解决每个应用补丁冲突的过程。 解决完一个补丁应用的冲突后,执行下面命令标记冲突已解决(也就是把修改内容加入缓存): git add -u 注:-u 表示把所有已track的文件的新的修改加入缓存,但不加入新的文件。 然后执行下面命令继续rebase: git rebase --continue 有冲突继续解决,重复这这些步骤,直到rebase完成。 如果中间遇到某个补丁不需要应用,可以用下面命令忽略: git rebase --skip 如果想回到rebase执行之前的状态,可以执行: git rebase --abort 注:rebase之后,不需要执行commit,也不存在新的修改需要提交,都是git自动完成。 编辑冲突的方法 直接编辑冲突文件 冲突产生后,文件系统中冲突了的文件(这里是test.txt)里面的内容会显示为类似下面这样: a123 <<<<<<< HEAD b789 ======= b45678910 >>>>>>> 6853e5ff961e684d3a6c02d4d06183b5ff330dcc c 其中:冲突标记<<<<<<< (7个<)与=======之间的内容是我的修改,=======与>>>>>>>之间的内容是别人的修改。 此时,还没有任何其它垃圾文件产生。 最简单的编辑冲突的办法,就是直接编辑冲突了的文件(test.txt),把冲突标记删掉,把冲突解决正确。