### 理解分支
一开始的时候,`master`分支是一条线,Git用`master`指向最新的提交,再用`HEAD`指向`master`,就能确定当前分支,以及当前分支的提交点:
![git-br-initial](http://www.liaoxuefeng.com/files/attachments/919022325462368/0)
每次提交,`master`分支都会向前移动一步,这样,随着你不断提交,`master`分支的线也越来越长。
当我们创建新的分支,例如`dev`时,Git新建了一个指针叫`dev`,指向`master`相同的提交,再把`HEAD`指向`dev`,就表示当前分支在`dev`上:
![git-br-create](http://www.liaoxuefeng.com/files/attachments/919022363210080/0)
你看,Git创建一个分支很快,因为除了增加一个`dev`指针,改改`HEAD`的指向,工作区的文件都没有任何变化!
不过,从现在开始,对工作区的修改和提交就是针对`dev`分支了,比如新提交一次后,`dev`指针往前移动一步,而`master`指针不变:
![git-br-dev-fd](http://www.liaoxuefeng.com/files/attachments/919022387118368/0)
假如我们在`dev`上的工作完成了,就可以把`dev`合并到`master`上。Git怎么合并呢?最简单的方法,就是直接把`master`指向`dev`的当前提交,就完成了合并:
![git-br-ff-merge](http://www.liaoxuefeng.com/files/attachments/919022412005504/0)
所以Git合并分支也很快!就改改指针,工作区内容也不变!
合并完分支后,甚至可以删除`dev`分支。删除`dev`分支就是把`dev`指针给删掉,删掉后,我们就剩下了一条`master`分支:
![git-br-rm](http://www.liaoxuefeng.com/files/attachments/919022479428512/0)
真是太神奇了,你看得出来有些提交是通过分支完成的吗?
### 分支命令
* 查看分支:`git branch`
* 创建分支:`git branch <name>`
* 切换分支:`git checkout <name>`
* 创建+切换分支:`git checkout -b <name>`
* 合并某分支到当前分支:`git merge <name>`
* 删除分支:`git branch -d <name>`
### 冲突处理
准备新的`feature1`分支,继续我们的新分支开发:
~~~
$ git checkout -b feature1
Switched to a new branch 'feature1'
~~~
修改`readme.txt`最后一行,改为:
~~~
Creating a new branch is quick AND simple.
~~~
在`feature1`分支上提交:
~~~
$ git add readme.txt
$ git commit -m "AND simple"
[feature1 14096d0] AND simple
1 file changed, 1 insertion(+), 1 deletion(-)
~~~
切换到`master`分支:
~~~
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
~~~
Git还会自动提示我们当前`master`分支比远程的`master`分支要超前1个提交。
在`master`分支上把`readme.txt`文件的最后一行改为:
~~~
Creating a new branch is quick & simple.
~~~
提交:
~~~
$ git add readme.txt
$ git commit -m "& simple"
[master 5dc6824] & simple
1 file changed, 1 insertion(+), 1 deletion(-)
~~~
现在,`master`分支和`feature1`分支各自都分别有新的提交,变成了这样:
![git-br-feature1](http://www.liaoxuefeng.com/files/attachments/919023000423040/0)
这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,我们试试看:
~~~
$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.
~~~
果然冲突了!Git告诉我们,`readme.txt`文件存在冲突,必须手动解决冲突后再提交。`git status`也可以告诉我们冲突的文件:
~~~
$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
~~~
我们可以直接查看readme.txt的内容:
~~~
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1
~~~
Git用`<<<<<<<`,`=======`,`>>>>>>>`标记出不同分支的内容,我们修改如下后保存:
~~~
Creating a new branch is quick and simple.
~~~
再提交:
~~~
$ git add readme.txt
$ git commit -m "conflict fixed"
[master cf810e4] conflict fixed
~~~
现在,`master`分支和`feature1`分支变成了下图所示:
![git-br-conflict-merged](http://www.liaoxuefeng.com/files/attachments/919023031831104/0)
### 不使用fast forward 模式合并分支
通常,合并分支时,如果可能,Git会用`Fast forward`模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用`Fast forward`模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
下面我们实战一下`--no-ff`方式的`git merge`:
首先,仍然创建并切换`dev`分支:
~~~
$ git checkout -b dev
Switched to a new branch 'dev'
~~~
修改readme.txt文件,并提交一个新的commit:
~~~
$ git add readme.txt
$ git commit -m "add merge"
[dev f52c633] add merge
1 file changed, 1 insertion(+)
~~~
现在,我们切换回`master`:
~~~
$ git checkout master
Switched to branch 'master'
~~~
准备合并`dev`分支,请注意`--no-ff`参数,表示禁用`Fast forward`:
~~~
$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
readme.txt | 1 +
1 file changed, 1 insertion(+)
~~~
因为本次合并要创建一个新的commit,所以加上`-m`参数,把commit描述写进去。
合并后,我们用`git log`看看分支历史:
~~~
$ git log --graph --pretty=oneline --abbrev-commit
* e1e9c68 (HEAD -> master) merge with no-ff
|\
| * f52c633 (dev) add merge
|/
* cf810e4 conflict fixed
...
~~~
可以看到,不使用`Fast forward`模式,merge后就像这样:
![git-no-ff-mode](https://www.liaoxuefeng.com/files/attachments/919023225142304/0)