分支的作用很强大,假设你准备开发一个新功能,但是需要两周才能完成,第一周写了部分代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。现在有了分支,你可以创建一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全又不影响别人工作。
分支创建与切换
Git 是怎么创建新分支的呢? 很简单,它只是为你创建了一个可以移动的新的指针。一开始的时候,master
分支是一条线,Git用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点。每次提交,master
分支都会向前移动一步,这样,随着你不断提交,master
分支的线也越来越长。首先,我们来创建dev分支并切换到dev分支上
$ git checkout -b dev
$ git branch dev
$ git checkout dev
也就是说,创建分支的命令是git branch,
这会在当前所在的提交对象上创建一个指针。而要切换到一个已存在的分支,你需要使用 git checkout
命令。请牢记:当你切换分支的时候,Git 会重置你的工作目录,使其看起来像回到了你在那个分支上最后一次提交的样子。 Git 会自动添加、删除、修改文件以确保此时你的工作目录和这个分支最后一次提交时的样子一模一样。
你可以简单地使用 git log
命令查看各个分支当前所指的对象。 提供这一功能的参数是 --decorate
。
可以看到当前 “master” 和 “testdevng” 分支均指向校验和以 9b1105c
开头的提交对象。
查看分支
然后,用git branch
命令查看当前分支(加上-a参数可以查看远程分支,远程分支会用红色表示出来(如果你开了颜色支持的话)):$ git branch
git branch查看分支,会列出所有的分支,当前分支前面会添加一个星号。然后,我们就可以在
dev
分支上正常提交,比如对test.txt做个修改,加上一行“777777”,然后提交:现在,
dev
分支的工作完成,我们就可以切换回master
分支:切换回
master
分支后,再查看一个test.txt文件,刚才添加的内容不见了!因为那个提交是在dev
分支上,而master
分支此刻的提交点并没有变:$ git merge dev
git merge
命令用于合并指定分支到当前分支。合并后,再查看readme.txt的内容,就可以看到,和dev
分支的最新提交是完全一样的。
注意到上面的Fast-forward
信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master
指向dev
的当前提交,所以合并速度非常快。
当然,也不是每次合并都能Fast-forward
,我们后面会讲其他方式的合并。
删除分支
合并完成后,就可以放心地删除dev
分支了(在Git v1.7.0 之后,删除远程分支可以使用git push origin --delete <branchName>,否则,可以使用这种语法,推送一个空分支到远程分支,其实就相当于删除远程分支:git push origin :<branchName>):
$ git branch -d dev
删除后,查看
branch
,就只剩下master
分支了:因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在
master
分支上工作效果是一样的,但过程更安全。现在切换到master分支上,也在test.txt最后一行添加内容,内容为"999999",如下所示:
Git还会自动提示我们当前
master
分支比远程的master
分支要超前1个提交,在master上提交:现在,
master
分支和dev1
分支各自都分别有新的提交。这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突:Git告诉我们,test.txt文件存在冲突,必须手动解决冲突后再提交。
git status
也可以告诉我们冲突的文件:可以直接查看readme.txt的内容:
Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,其中<<<HEAD是指主分支修改的内容,>>>>>dev1 是指dev1上修改的内容,打开test.txt文件,修改下如下后保存:
现在已经是我们想要的效果了,提交:
用带参数的
git log
也可以看到分支的合并情况:最后,删除
dev1
分支:通常合并分支时,Git会用“Fast forward”
模式,但这种模式下,删除分支会丢掉分支信息。如果强制禁用“Fast forward"
模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。现在我们来使用带参数 –no-ff来禁用”Fast forward”模式。
首先,仍然创建并切换dev
分支:
修改test.txt文件,并提交一个新的commit:
现在切换回
master,合并dev
分支,使用“--no-ff”
参数。因为本次合并要创建一个新的commit,所以加上-m
参数,把commit描述写进去。
合并后,我们用git log看看分支历史:
分支策略:首先master主分支应该是非常稳定的,也就是用来发布新版本,一般情况下不允许在上面干活,干活一般情况下在新建的dev分支上干活,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
上发布1.0版本。你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
现在已经创建、合并、删除了一些分支,让我们看看一些常用的分支管理工具。
git branch
命令不只是可以创建与删除分支。 如果不加任何参数运行它,会得到当前所有分支的一个列表:
注意 master 分支前的 * 字符:它代表现在检出的那一个分支(也就是说,当前 HEAD 指针所指向的分支)。 这意味着如果在这时候提交,master 分支将会随着新的工作向前移动。 如果需要查看每一个分支的最后一次提交,可以运行 git branch -v 命令:
--merged 与 --no-merged 这两个有用的选项可以过滤这个列表中已经合并或尚未合并到当前分支的分支。 如果要查看哪些分支已经合并到当前分支,可以运行 git branch --merged:
因为之前已经合并了 dev 分支,所以现在看到它在列表中。 在这个列表中分支名字前没有 * 号的分支通常可以使用 git branch -d 删除掉——你已经将它们的工作整合到了另一个分支,所以并不会失去任何东西。
查看所有包含未合并工作的分支,可以运行 git branch --no-merged命令。
Bug分支
开发过程中,遇到bug是难免的,有了bug就需要修复,在Git中,分支是很强大的,每个bug都可以通过一个临时分支来修复,修复完成后,合并分支,然后将临时的分支删除掉。假设我在开发中接到一个404 bug,我们可以创建一个404分支来修复它,但是,当前的dev分支上的工作还没有提交。不是我不想提交,而是工作没有完成,我们还无法提交,怎么办呢?还好,Git还提供了一个stash功能,可以把当前工作现场 ”隐藏起来”,等以后恢复现场后继续工作
现在,用git status查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支issue-404,现在修复bug,然后提交:
修复完成后,切换到master分支,并完成合并,最后删除issue-404分支:
现在,我们可以回到dev分支上了
工作区是干净的,那么我们工作现场去哪里呢?我们可以使用命令 git stash list来查看下:
工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,可以使用如下2个方法:
现在再用git stash list查看,就看不到任何stash内容了
当你从远程仓库克隆时,实际上Git自动把本地的master
分支和远程的master
分支对应起来了,并且,远程仓库的默认名称是origin
。
origin
的地址。如果没有推送权限,就看不到push的地址。dev
,就改成git push origin dev
但并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?
master
分支是主分支,因此要时刻与远程同步;dev
分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
现在我们可以模拟另外一个同事,可以在另一台电脑上(注意要把SSH key添加到github上)或者同一台电脑上另外一个目录克隆,新建一个目录名字叫mygit2,克隆远程的库到本地来
现在目录下生成如下所示:
当你的小伙伴从远程库clone时,默认情况下,你的小伙伴只能看到本地的
master
分支小伙伴已经向
origin/dev
分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:
推送失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用
git pull
把最新的提交从origin/dev
抓下来,然后,在本地合并,解决冲突,再推送:
git pull
也失败了,原因是没有指定本地dev
分支与远程origin/dev
分支的链接,根据提示,设置dev
和origin/dev
的链接:
这回 git pull成功,但是合并有冲突,需要手动解决,解决的方法和分支管理中的 解决冲突完全一样。解决后,提交,再push,先来看下test.txt内容
手动解决完了再提交,再push到远程库里面去
因此,多人协作的工作模式通常是这样:
首先,可以试图用
git push origin branch-name
推送自己的修改;如果推送失败,则因为远程分支比你的本地更早,需要先用
git pull
试图合并;如果合并有冲突,则解决冲突,并在本地提交;
没有冲突或者解决掉冲突后,再用
git push origin branch-name
推送就能成功!
如果git pull
提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream branch-name origin/branch-name
。
这就是多人协作的工作模式,一旦熟悉了,就非常简单。