git入门五(分支合并冲突和衍合)
分支合并冲突的处理
合并分支的冲突时在不同的分支中修改了同一个文件的同一部分,程序无法把两份有差异的文件合并,这时候需要人为的干预解决冲突。当前处于master 分支,当dev 分支和master 分支对相当部分test1.txt 都做了修改,当合并dev 分支的时候,合并会出现分支冲突如下:查询当前工作区的状态可以显示那些文件发生合并冲突,任何包含未解决冲突的文件都会以未合并(ummerged)的状态列出,git 会加入标准冲突解决标记,可以通过手工定位来解决这些冲突。可以看大 =======隔开以上部分就是当前活动分支,也是合并的基准分支(head 指向的master分支),======分隔符以下的是dev分支中的内容。解决冲突的办法无非是二者选其一或者由你亲自整合到一起。比如你可以两部分内容合并成 一部分内容。
$ git branch
dev
* master
testing
$ git merge dev
Auto-merging test1.txt
CONFLICT (content): Merge conflict in test1.txt
Automatic merge failed; fix conflicts and then commit the result.
$ git status
# On branch master
# Unmerged paths:
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: test1.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
$ notepad test1.txt
<<<<<<< HEAD
now this is bug fix branch
=======
this is branch merge conflict problem
>>>>>>> dev
now this is bug fix branch
=======
this is branch merge conflict problem
>>>>>>> dev
同时还可以用图形化界面的工具来处理分支,git mergetool 命令会调用时当前系统配置的合并工具。合并完成后可以查询状态git status 来确认所有冲突都已经解决。如果冲突解决都已完成,可以把合并后的内容提交到暂存区,可以用git commit 完成这次合并提交。针对冲突合并,需要写好注释说明,后续查看会更加简单方便。
$ git commit -m "master merge dev branch"
[master 05a2f29] master merge dev branch
1 files changed, 4 insertions(+), 1 deletions(-)
分支的管理
git branch 是查询当前所有分支的清单,*号的表示当前的活动分支,也就是当前所在的分支。也就是说如果现在有提交更新,当前的工作分支master 分支或向前移动。若要看各个分支最后一个提交对象的新,可以通过git branch -v 来查看。
$ git branch
dev
* master
testing
$ git branch -v
dev be70ec8 dev
* master 05a2f29 master merge dev branch
testing 0c8f2de testing branch change
在所有分支清单中,可以筛选出那些与当前分支尚未合并,通过参数--merge 可以筛选出那些分支在当前分支的上游,这些分支只需要通过fast-forward 移动指正就可以移动当当前最后提交给对象。--no-merged 可以查看还位和当前分支合并的分支。如dev分支和当前分支还未合并。如果以前是无效的分支,可以通过git branch -d 删除制定的分支。
$ git branch --merged
* master
testing
$ git branch --no-merged
dev
$ git branch -d testing
Deleted branch testing (was 0c8f2de).
长期分支
git只是简单的三方合并分支的特性,所以在脚长的一段时间内,把多个分支合并到一个分支或者同时拥有多发分支进行开发。由于每个分支都有特定的任务,随着开发的推进,随时可以把某个特性分支合并到其它分支中。需求使用git 的开发者都喜欢用这种方式开发,一般来说仅仅在master 分支保留稳定的代码,就是已经发布或者经过测试的代码。与此同时,你可以同时拥有多个开发分支。每个开发分支都有特定的任务。如还有一个叫develop 的平行分支,专门拥有后续的开发,仅拥有稳定性的测试。一旦到达某种稳定的状态就可以合并到master 分支。如果有其它特性的短缺分支能够通过测试,并且不会引如更多错误后,就可以并到主干master分支中。等待下一次发布。
随着提交对象的不断右移指针,稳定分支总是在提交历史中落后一大截,而且前言分支总是比较靠前。稳定分支总是滞后,经过测试比较稳定的对象或者集合才被合并到稳定的分支上。这样可以维护不同层次的稳定。
特性分支
在任何规模的项目中可以使用特性分支(topic).一个特性是指一个短期的,用来实现单一特性与其相关的工作分支,你可以在以前版本中从未做过类似的这样事情,因为创建和合并分支的消耗太大。然而在git中,一天之内创建,删除和合并多个分支是常见的事情。在创建特性分支后,你可以提交合并到主干分支,然后删除他,该技术让你迅速且完全进行语境切换。因为你的工作分散在不同的流水性力,每个分支力改变都和他的目标特性相关。你可以把做出的改变保持在特性分支几分钟。几天甚至几个月。等他们成熟以后再合并。而不用在乎他们建立的顺序和进度。
一般分支都是在本地。大部分都是本地分支。这一点很重要。当前使用合并分支的时候,一切都在你的git 仓库中进行的,完全不与服务器交互。只有当你有固定的分支或者分享需要和其它合作伙伴共享的时候,才需要推送到中心服务器。
远程分支:
remote branch 是对远程仓库中的分支的索引。他们是无法移动本地分支。只有在git进行网络交互时才会更新。远程分支就是书签,提醒着你上次链接远程仓库时上面各个分支的位置。 我们用仓库名/分支名 这样的形式表示远程分支。比如我们想想上次同 origin 仓库进行通讯时master 分支的样子。就应该查看origin/master 分支。如果你和同伴一起修复某个问题。他们推送一个iss53分支到远程仓库。虽然你可能也有一个本地的iss53分支,但指向服务器上最新的更新的英是origin/iss53分支。
假如团队中心服务器git地址:git.ourcompany.com.。如果你从这里克隆,git会自动在为你将次远程仓库吗命名为origin。并下载其中的数据,建立一个指向它的master分支指针。在本地命名为Origin/master。但你无法再本地更改数据,接着git建立一个属于你自己本地的master分支。始于origin上master分支相同的位置。如下图
![](http://images0.cnblogs.com/i/71375/201403/132303567065955.png)
如果你在本地master 分支做了些改动。与此同时本地分支向前推进。只要本地没有向远程服务器推送,origin/master 分支指针任然保持在原位不会移动。
![](http://images0.cnblogs.com/i/71375/201403/132304172954398.png)
在本地工作同时有人向远程仓库推送内容会让历史开始分流。可以允许git fethch origin 来同步远程服务器上的数据到本地。该命令首先找到origin 是哪个服务器。从上面获取你未拥有的数据。更新你本地的数据库。然后把Origin/master的指针移动到他最新的位置上。
![](http://images0.cnblogs.com/i/71375/201403/132306266326387.png)
如果有多个远程分支的项目是如果进行工作的。我们假设你还有另外一个内部使用的远程服务器。通过git remote add 命令吧它加为当前项目的远程分支之一。我们把它命名为teamtone,以便代替完整的git url。
![](http://images0.cnblogs.com/i/71375/201403/132304467136874.png)
现在把另外一个远程服务器添加为远程仓库了,现在可以用git fetch teamtone 来获取小组服务器你还没有的数据,由于当前服务器的内容是origin服务器上的子集,git不会下载任何数据。而只是简答创建一个名为teamone/master 的远程分支。指向teamone 服务器上master 分支所有在的提交对象 31b3e 如下图:现在你在本地就有了一个指向teamone 的索引。
推送本地分支
要想和其它人分享某个本地的分支,你需要把它推送到一个拥有些权限的远程仓库。你创建的本地分支部会因为你的写入操作而被自动同步到你引入的远程服务器上,你需要明确的执行推送分支的操作。换句话说,对于无意分享的分支,你尽管保留私人分支好了。而只推送那些协同工作要用到的特性分支。如果有个交severfix 的分支需要和他人一起开发,可以运行git push (远程仓库名) 分支名。
$ git push origin serverfix
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:andy/test.git
* [new branch] serverfix -> serverfix
git 自动把serverfix 分支名扩展为refs/heads/serverfix:refs/heads/serverfix,意思是“取出我在本地的serverfix分支,推送到远程仓库的serverfix分支中区,一般在同一分支上可以省略, git push origin serverfix:serverfix,还可以把本地分支推送到远程不同的分支。可以用已经存在的新远程分支或新的远程分支。
当你再次从远程获取服务器上数据的时候,同伴会获取到origin/serverfix 和 origin/newfix 的分支,并指向服务器上serverfix 所指向的版本。在fetch操作下载好新的远程分支之后。你任然无法再本地编辑远程仓库中的分支。换句话说你不会有一个新的serverfix 分支。有的只是一个你无法移动的Origin/serverfix指针。你如果需要把该远程分支的内容合并到当前分支,可以运行git merge origin/serverfix ,如果想要一份自己的serverfix来开发。可以在远程分支的基础上分化一个新的分支来。这会切换到新的serverfix 的本地分支。其内容同远程分支 origin/serverfix 一致。这样可以继续开发了。
$ git push origin serverfix:newfix
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:andyi/test.git
* [new branch] serverfix -> newfix
$ git fetch origin
remote: Counting objects: 19, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 17 (delta 6), reused 16 (delta 5)
Unpacking objects: 100% (17/17), done.
From github.com:andyi/test
* [new branch] dev -> origin/dev
894ed8b..37b40ce master -> origin/master
跟踪远程分支
从远程分支checkout 出来的本地分支。称为跟踪(tracking branch),跟踪分支是一种和某个远程分支有直接联系的本地分支。在跟踪分支里输入git push,git 会自行推断应该向那个服务器的那个分支推送数据。同样,在这些分支里运行git pull 会回去远程索引,并把它们的数据合并到本地分支中。
在克隆仓库时,git 通常会自创建一个名master 的分支来跟踪,这正是git push 和 git pull 一开始就能正常工作的原因。当然,你可以随心所欲设定其为跟踪分支,比如在origin 上除了master 之外的其它分支。刚才我们已经开到了这样的一个例子。 git checkout -b 分支名 远程名/分支名, 还可以用 --track 选项。 如果本地分支和远程分支的名称不一样,可以本地分支换个名称。
$ git checkout -b serv origin/serverfix
Branch serv set up to track remote branch serverfix from origin.
Switched to a new branch 'serv'
$ git branch
master
* serv
serverfix
删除远程分支
如果不再需要摸个远程分支了,比如搞定某个特性并合并进了远程的master 分支(或任何其他存放稳定的代码分支),可以用这个命令 git push 远程名:分支名。如果运行这个命令,服务器上的分支就没了,git puhs 远程名 本地分支:远程分支 语法,如果省略本地分支。那就是等提前空白然后把它变成远程分支。
分支的衍和
把一个分支中的修改整合到另一个分支的办法由两种:merge 和 rebase(翻译为衍合)。
基本的衍合操作,当开发进程分叉到两个不同的分支,有各自提交了更新。最简单的整合方式是合并merege 命令。他会把两者共同的祖先ac631f6进行三方合并。并合后产生一个结果就是两条分的合并点。
![](http://images0.cnblogs.com/i/71375/201403/132307321681096.png)
其实除了合并以外,还有另外一个选择,可以把7599941产生变化的补丁在4632de基础上重新打一遍。在git 里着叫衍合(rebase),有了rebase命令,就可以在把在一个分只里提交的改变移动到另一个分支重方一遍。他们的原理是回到两个分支最近共同的祖先。根据当前分支(也就是要进行衍合的分支)后续历次提交的对象在这里分支只有一个提交。生产一系列文件补丁,然后基地分支(也就是主干分支master 的最后一个提交对象为新的出发点,逐个应用之前准备好的补丁文件,最后会生产一个新的合并提交对象。从而改写需要衍合分支的提交历史。使他成为master 分支的直接下游。
![](http://images0.cnblogs.com/i/71375/201403/132307420213187.png)