Git⽀持我们查看或创建其他分⽀,在这⾥我们来创建第⼀个⾃⼰的分⽀dev ,对应的命令为:
#查看当前本地所有分⽀
[root@VM-16-15-centos gitcode]# git branch
* master
#新建分⽀dev
[root@VM-16-15-centos gitcode]# git branch dev
[root@VM-16-15-centos gitcode]# git branch
dev
* master
当我们创建新的分⽀后,Git新建了⼀个指针叫dev, *表⽰当前 HEAD 指向的分⽀是 master 分
⽀。另外,可以通过⽬录结构发现新的 dev 分⽀。
[root@VM-16-15-centos ~]# ls .git/refs/heads
dev master
那如何切换到dev分⽀下进⾏开发呢?使⽤ git checkout 命令即可完成切换,⽰例如下:
[root@VM-16-15-centos ~]# git checkout dev
Switched to branch 'dev'
[root@VM-16-15-centos ~]# git branch
* dev
master
[root@VM-16-15-centos ~]# cat .git/HEAD
ref: refs/heads/dev
[root@VM-16-15-centos ~]#
我们发现HEAD已经指向了dev,就表⽰我们已经成功的切换到了dev上!
接下来,在 dev 分⽀下修改ReadMe⽂件,新增⼀⾏内容,并进⾏⼀次提交操作:
[root@VM-16-15-centos gitcode]# vim readme
[root@VM-16-15-centos gitcode]# cat readme
hello world
alter readme 1
alter readme version1
alter readme version2
alter readme version3
write test for new branch
[root@VM-16-15-centos gitcode]# git add .
[root@VM-16-15-centos gitcode]# git commit -m "new branch"
[dev 58b2bfc] new branch
1 file changed, 2 insertions(+)
现在,dev分⽀的⼯作完成,我们就可以切换回master分⽀,切换回master分⽀后,发现ReadMe⽂件中新增的内容不⻅了。
因为我们是在dev分⽀上提交的,⽽master分⽀此刻的提交点并没有变,此时的状态如图如下所⽰
当切换到master分⽀之时,HEAD就指向了master,当然看不到提交了!
为了在master主分⽀上能看到新的提交,就需要将 dev 分⽀合并到 master 分⽀,⽰例如下
git checkout master # 切换到 master 上进⾏合并
git merge dev # 合并 dev 分⽀
git merge 命令⽤于合并指定分⽀到当前分⽀。合并后,master就能看到dev分⽀提交的内容了。此时的状态如图如下所⽰:
合并完成后,dev分⽀对于我们来说就没⽤了,那么dev分⽀就可以被删除掉,注意如果当前正处于该分⽀下,就不能删除当前分⽀,如:
[root@VM-16-15-centos ~]# git checkout dev
Switched to branch 'dev'
[root@VM-16-15-centos ~]# git branch
* dev
master
[root@VM-16-15-centos ~]# git branch -d dev
error: Cannot delete the branch 'dev' which you are currently on.
⽽可以在其他分⽀下删除当前分⽀,如:
[root@VM-16-15-centos ~]# git checkout master
Switched to branch 'master'
[root@VM-16-15-centos ~]# git branch -d dev
Deleted branch dev (was 36d40bc).
[root@VM-16-15-centos ~]# git branch
* master
因为创建、合并和删除分⽀⾮常快,所以Git⿎励你使⽤分⽀完成某个任务,合并后再删掉分⽀,这和直接在master分⽀上⼯作效果是⼀样的,但过程更安全。
可是,在实际分⽀合并的时候,并不是想合并就能合并成功的,有时候可能会遇到代码冲突的问题。为了演⽰这问题,创建⼀个新的分⽀ dev1 ,并切换⾄⽬标分⽀,我们可以使⽤ git checkout -b dev1
⼀步完成创建并切换的动作,⽰例如下:
[root@VM-16-15-centos ~]# git checkout -b dev1
Switched to a new branch 'dev1'
[root@VM-16-15-centos ~]# git branch
* dev1
master
在 dev1 分⽀下修改 ReadMe ⽂件,并进⾏⼀次提交。
[root@VM-16-15-centos ~]#cat readme
hello bit
hello git
hello world
hello version1
hello version2
hello version3
write bbb for new branch # 将 aaa 该为 bbb
[root@VM-16-15-centos ~]# git add .
[root@VM-16-15-centos ~]# git commit -m"modify ReadMe"
然后切换⾄ master 分⽀,此时在master分⽀上,我们对ReadMe⽂件再进⾏⼀次修改,并进⾏提交。
[root@VM-16-15-centos ~]# git checkout master
Switched to branch 'master'
[root@VM-16-15-centos ~]#cat readme
hello bit
hello git
hello world
hello version1
hello version2
hello version3
write ccc for new branch
[root@VM-16-15-centos ~]# git add .
[root@VM-16-15-centos ~]# git commit -m"modify ReadMe"
现在, master 分⽀和 dev1 分⽀各⾃都分别有新的提交(master是bbb,dev1是ccc),变成了这样:
这种情况下,Git只能试图把各⾃的修改合并起来,但这种合并就可能会有冲突(master是bbb,dev1是ccc)
此时我们必须要⼿动调整冲突代码,使其原本冲突的地方代码一致,并需要再次提交修正后的结果!!(再次提交很重要,切勿忘记)
到这⾥冲突就解决完成,此时的状态变成了:
最后,不要忘记dev1分⽀使⽤完毕后就可以删除了。
通常合并分⽀时,如果可能,Git会采⽤ Fast forward 模式。
我们看到,合并时采用的就是 Fast forward 模式。
在这种 Fast forward 模式下,删除分⽀后,查看分⽀历史时,会丢掉分⽀信息,看不出来最新提交的到底是merge进来的还是正常提交的。
但在合并冲突部分,我们也看到通过解决冲突问题,会再进⾏⼀次新的提交,得到的最终状态为:
那么这就不是 Fast forward 模式了,他会保留分支历史信息,这样的好处是,从分⽀历史上就可以看出分⽀信息。例如我们现在已经删除了在合并冲突部分创建的 dev分⽀,但依旧能看到master其实是由其他分⽀合并得到。
Git⽀持我们强制禁⽤ Fast forward 模式,那么就会在merge时⽣成⼀个新的 commit ,这样,从分⽀历史上就可以看出分⽀信息。
下⾯我们实战⼀下 --no-ff
⽅式的 git merge :
请注意 --no-ff
参数,表⽰禁⽤ Fast forward 模式。禁⽤ Fast forward 模式后合并会创建⼀个新的 commit ,所以加上 -m 参数,把描述写进去。
合并后,查看分⽀历史:
从图上我们可以看出来此时的master其实是由其他分⽀(dev2)合并得到的。
所以在合并分⽀时,加上 --no-ff
参数就可以⽤普通模式合并,能看出来曾
经做过合并,⽽ fast forward 合并就看不出来曾经做过合并。
在实际开发中,我们应该按照⼏个基本原则进⾏分⽀管理:
⾸先,master分⽀应该是⾮常稳定的,也就是仅⽤来发布新版本,平时不能在上⾯⼲活(线上环境是用户能看到的,如果你线上环境出现bug或不稳定情况是会直接你公司营收效益的);
那在哪⼲活呢?⼲活都在dev分⽀上,也就是说,dev分⽀是不稳定的,到某个时候,⽐如1.0版本发布时,再把dev分⽀合并到master上,在master分⽀发布1.0版本;
你和你的⼩伙伴们每个⼈都在dev分⽀上⼲活,每个⼈都有⾃⼰的分⽀,时不时地往dev分⽀上合并就可以了。
假如我们现在正在 dev2分⽀上进⾏开发,开发到⼀半,突然发现 master 分⽀上⾯有bug,需要解决。
在Git中,每个bug都可以通过⼀个新的临时分⽀来修复,修复后,合并分⽀,然后将临时分⽀删除。
可现在 dev2 的代码在⼯作区中开发了⼀半,还⽆法提交,怎么办?
解释:
我们在dev2分支进行开发,此时假设master出现了bug,那么我们就需要先创建新分支用来解决master的bug,然后继续回到dev2进行开发,直到开发完成将dev2合并到master。
但是我们在mastr上创建新分支用于解决bug之前,会看到开发到一半的dev2分支的内容已经在master的工作区可以看到了,很显然,这是我们不想看到的。
Git提供了 git stash
命令,可以将当前的⼯作区信息进⾏储藏,被储藏的内容可以在将来某个时间恢复出来。
我们进入dev2分支,将其工作区内容进行保存。
那这些内容储藏到哪里了呢?
我们通过tree .git
查看一下:
我们看到此时这里有一个stash文件,这里面就是保存的dev2分支的内容。
注意点: git stash
命令只会将目前已经追踪管理到master的分支的内容进行储藏,也就是说,你如果当前分支只是在工作区进行了新建,没有 add 、commit ,那么你的内容是不能被储藏的。
接着,假设我们在新建分支fix_bug
上进行了修复bug,然后重新add,commit。
# 切回master
git checkout master
# 新建并切换到 fix_bug 分⽀
git checkout -b fix_bug
# 修复bug
# 重新add,commit
git add ReadMe # 重新add,commit
git commit -m"fix bug"
修复完成后,切换到 master 分⽀,并完成合并,最后删除 fix_bug 分⽀:
git checkout master
git merge --no-ff -m"merge fix_bug branch" fix_bug
git branch -d fix_bug
⾄此,bug的修复⼯作已经做完了,我们还要继续回到 dev2 分⽀进⾏开发。切换回 dev2 分⽀:
hyb@139-159-150-152:~/gitcode$ git checkout dev2
Switched to branch 'dev2'
hyb@139-159-150-152:~/gitcode$ git status
On branch dev2
nothing to commit, working tree clean
我们发现⼯作区是⼲净的,刚才的⼯作现场存到哪去了?⽤ git stash list
命令看看:
hyb@139-159-150-152:~/gitcode$ git stash list
stash@{0}: WIP on dev2: 41b082f modify ReadMe
⼯作现场还在,Git把stash内容存在某个地⽅了,但是需要恢复⼀下,如何恢复现场呢?
我们可以使⽤ git stash pop
命令,恢复的同时会把stash也删了,⽰例如下:
hyb@139-159-150-152:~/gitcode$ git stash pop
On branch dev2
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git restore ..." to discard changes in working directory)
modified: ReadMe
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (4f873250b3503687b5efd26196776aee7e3724c2)
再次查看的时候,我们已经发现已经没有现场可以恢复了:
hyb@139-159-150-152:~/gitcode$ git stash list
hyb@139-159-150-152:~/gitcode$
另外,恢复现场也可以采⽤ git stash apply
恢复,但是恢复后,stash内容并不删除,你需要⽤ git stash drop
来删除;
你可以多次stash,恢复的时候,先⽤ git stash list 查看,然后恢复指定的stash,⽤命令git stash apply stash@{0}
。
恢复完代码之后我们便可以继续完成开发,开发完成后便可以进⾏提交。
需要注意一点,恢复完dev2分支的内容后,我们会发现修复bug的内容,并没有在 dev2 上显⽰。此时的状态图为:
Master 分⽀⽬前最新的提交,是要领先于新建 dev2 时基于的 master 分⽀的提交的,所以我们在 dev2 中当然看不⻅修复bug的相关代码。
我们的最终⽬的是要让 master 合并 dev2 分⽀的,那么正常情况下我们切回 master 分⽀直接合并即可,状态如下图。
但这样其实是有⼀定⻛险的。
是因为在合并分⽀时可能会有冲突,⽽代码冲突需要我们⼿动解决(在 master 上解决)。我们⽆法保证对于冲突问题可以正确地⼀次性解决掉,因为在实际的项⽬中,代码冲突不只⼀两⾏那么简单,有可能⼏⼗上百⾏,甚⾄更多,解决的过程中难免⼿误出错,导致错误的代码被合并到 master 上。
解决这个问题的⼀个好的建议就是:最好在⾃⼰的分⽀dev上先合并下master ,再让 master 去合并dev ,这样做的⽬的是有冲突可以在本地分⽀解决并进⾏测试,⽽不影响 master 。
状态演示:
软件开发中,总有⽆穷⽆尽的新的功能要不断添加进来。
添加⼀个新功能时,你肯定不希望因为⼀些实验性质的代码,把主分⽀搞乱了,所以,每添加⼀个新功能,最好新建⼀个分⽀,我们可以将其称之为 feature 分⽀,在上⾯开发,完成后,合并,最后,删除该 feature 分⽀。
可是,如果我们今天正在某个 feature 分⽀上开发了⼀半(该分支有提交未进行合并),被产品经理突然叫停,说是要停⽌新功能的开发。虽然⽩⼲了,但是这个 feature 分⽀还是必须就地销毁,留着⽆⽤了。这时使⽤传统的 git branch -d
命令删除分⽀的⽅法是不⾏的。需要用到 git branch -D
命令。
git branch -d
:删除一个名字为branchName的分支。如果该分支有提交未进行合并,则会删除失败。git branch -D
:强制删除一个名字为branchName 的分支。如果该分支有提交未进行合并,也会删除成功。the end!