背景
好处
过程
开始的时候,master
分支是一条线,Git用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点:
每次提交,master
分支都会向前移动一步,这样,随着你不断提交,master
分支的线也越来越长
当我们创建新的分支,例如dev
时,Git新建了一个指针叫dev
,指向master
相同的提交,再把HEAD
指向dev
,就表示当前分支在dev
上:
你看,Git创建一个分支很快,因为除了增加一个dev
指针,改改HEAD
的指向,工作区的文件都没有任何变化!
不过,从现在开始,对工作区的修改和提交就是针对dev
分支了,比如新提交一次后,dev
指针往前移动一步,而master
指针不变:
假如我们在dev
上的工作完成了,就可以把dev
合并到master
上。Git怎么合并呢?最简单的方法,就是直接把master
指向dev
的当前提交,就完成了合并:
所以Git合并分支也很快!就改改指针,工作区内容也不变!
合并完分支后,甚至可以删除dev
分支。删除dev
分支就是把dev
指针给删掉,删掉后,我们就剩下了一条master
分支:
创建与合并
查看分支
git branch
git branch -v //显示详情
创建分支
git branch [name]
切换分支
git checkout [name]
创建+切换分支:
git checkout -b <name>
合并某分支到当前分支
git merge <name>
第一步,切换到接受修改的分支
第二步,执行 merge 命令
删除分支
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告诉我们,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 log --graph
命令可以看到分支合并图
总结
编辑冲突文件,删除特殊符号并修改至合适版本,保存退出。
将冲突文件添加至暂存区,git add
将冲突文件提交到版本库,git commit
注意:commit 后面不带具体文件名
分支管理策略
Fast forward
通常,合并分支时,如果可能,Git会用Fast forward
模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward
模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
git merge --no-ff -m [日志信息] [分支信息]
首先,仍然创建并切换dev
分支,修改readme.txt文件,并提交一个新的commit。
然后切换回master
,准备合并dev
分支,请注意--no-ff
参数,表示禁用Fast forward
可以看到,不使用Fast forward
模式,merge后就像这样:
分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本;
你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
Bug分支
软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
当前正在dev
上进行的工作还没有提交:
$ git status
On branch dev
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: hello.py
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.txt
创建储藏
$ git stash
Saved working directory and index state WIP on dev: f52c633 add merge
现在,用git status
查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。
首先确定要在哪个分支上修复bug,假定需要在master
分支上修复,就从master
创建临时分支:
git checkout master
git checkout -b 【临时分支】
现在修复bug,需要把“Git is free software ...”改为“Git is a free software ...”,然后提交:
git add readme.txt
git commit -m "日志信息"
修复完成后,切换到master
分支,并完成合并,最后删除临时分支
git checkout master
git merge --no-ff -m "日志信息" [临时分支]
git branch -d dev
查看储藏
git stash list
恢复储藏
- 用
git stash apply
恢复储藏(工作现场),但是恢复后,stash内容并不删除,你需要用git stash drop
来删除
git stash apply 【储藏序号:stash@{0}】 //恢复
git stash drop 【储藏序号:stash@{0}】 //删除
- 用
git stash pop
,恢复的同时把stash内容也删了
git stash pop
取消储藏
在某些情况下,你可能想应用储藏的修改,在进行了一些其他的修改后,又要取消之前所应用储藏的修改。Git没有提供类似于 stash unapply
的命令,但是可以通过取消该储藏的补丁达到同样的效果:
$ git stash show -p stash@{0} | git apply -R
同样的,如果你沒有指定具体的某个储藏,Git 会选择最近的储藏:
$ git stash show -p | git apply -R
小结
修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;
当手头工作没有完成时,先把工作现场git stash
一下,然后去修复bug,修复后,再git stash pop
,回到工作现场。
Feature分支
添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以,每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。
如果在Feature分支还没有被合并时,需要将分支删除,需要进行强删:
git branch -D feature-vulcan
开发一个新feature,最好新建一个分支。
Rebase
merge 和 rebase关系
git rebase 和git merge 做的事其实是一样的。它们都被设计来将一个分支的更改并入另一个分支,只不过方式有些不同。
merge
命令示例
git checkout feature
git merge master
这样feature 分支中新的合并提交(merge commit)将两个分支的历史连在了一起
Merge 好在它是一个安全的操作。现有的分支不会被更改
每次合并上游更改时 feature 分支都会引入一个外来的合并提交。如果上游分支 非常活跃的话,这或多或少会污染你的分支历史
Rebase
命令示例
git checkout feature
git rebase master
它会把整个 feature 分支移动到 master 分支的后面,有效地把所有 master 分支上新的提交并入过来
但是,rebase 为原分支上每一个提交创建一个新的提交,重写了项目历史,并且不会带来合并提交。
rebase的优点和缺点
优点
- rebase最大的好处是你的项目历史会非常整洁
- rebase 导致最后的项目历史呈现出完美的线性——你可以从项目终点到起点浏览而不需要任何的 fork。这让你更容易使用 git log、git bisect 和 gitk 来查看项目历史
缺点
- 安全性,如果你违反了 rebase 黄金法则,重写项目历史可能会给你的协作工作流带来灾难性的影响
- 可跟踪性,rebase 不会有合并提交中附带的信息——你看不到 feature 分支中并入了上游的哪些更改
交互式的 rebsase
允许你更改并入新分支的提交。这比自动的 rebase 更加强大,因为它提供了对分支上提交历史完整的控制
一般来说,这被用于将 feature 分支并入 master 分支之前,清理混乱的历史。
命令示例:
git checkout feature
git rebase -i master
使用rebase
合并多个commit为一个完整的commit
git rebase