本篇继续上面所讲的内容,我们理解了Git的基本原理,进行的Git的相关操作。本篇进入到Git最重要的章节,Git的分支管理。前面所有的我们都是在master分支上面进行的操作,master分支可以理解为一个稳定发行的版本,所以不可能在上面进行修改。这样我们就需要建立其它的分支用于开发,测试,Bug修复等。
通常我们的分支策略是
PRD ——> UAT ——> ST ——> DEV(1 … n)
| |
| |——>ST_BUGFIX
|
| ——>UAT_BUGFIX
但是在Git里面可能有一些差别,不过本质上的思想是一致的。
根据前面内容,我们知道每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。在Git里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。
每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。
创建新的分支
我们创建一个UAT的分支,并且查看版本库的所有分支信息,*代表的时当前分支,即uat
xq@xq-VPCEG17YC:~/git/test$ git branch dev
xq@xq-VPCEG17YC:~/git/test$ git checkout dev
切换到分支 'dev'
xq@xq-VPCEG17YC:~/git/test$ git branch
* dev
master
st
uat
当我们创建新的分支dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:
Git创建一个分支很快,因为除了增加一个dev指针,改变HEAD的指向,工作区的文件都没有任何变化!
不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:
假如我们在dev上的工作完成了,就可以把dev合并到master上。Git就是直接把master指向dev的当前提交,就完成了合并:
合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:
下面我们在Dev上面修改hello.txt文件,然后commit。
切换到master分支下面查看修改,发现master下面并没有更新。
因为我们commit的分支时Dev而不是master。
xq@xq-VPCEG17YC:~/git/test$ vi hello.txt
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my sixth try.
1111111111111111111
xq@xq-VPCEG17YC:~/git/test$ git add hello.txt
xq@xq-VPCEG17YC:~/git/test$ git commit -m "branch test"
[dev a1cadc0] branch test
1 file changed, 1 insertion(+)
xq@xq-VPCEG17YC:~/git/test$ git checkout master
切换到分支 'master'
您的分支与上游分支 'origin/master' 一致。
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my sixth try.
分支合并
在Dev流中完成开发后,需要将代码提交到master流。也就是分支合并。
git merge命令用于合并指定分支到当前分支。合并后,再查看hello.txt的内容,就可以看到,和dev分支的最新提交是完全一样的。
合并分支时,如果可能,Git会用Fast forward模式,也就是“快进模式”,删除分支后,会丢掉分支信息。也就是直接把master指向dev的当前提交,所以合并速度非常快。如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my sixth try.
xq@xq-VPCEG17YC:~/git/test$ git branch
dev
* master
st
uat
xq@xq-VPCEG17YC:~/git/test$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
dev_rickey.txt | 1 +
hello.txt | 1 +
2 files changed, 2 insertions(+)
create mode 100644 dev_rickey.txt
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my sixth try.
1111111111111111111
合并并不总是友好的,有多人参与的项目,有多个分支,就非常容易产生冲突。
我们新建一个st_bugfix的分支,在该分支修改文件hello.txt,加上st_bugfix,并commit;然后切换到st分支,也在文件hello.txt,加上不一样的st,并commit,最后合并两个分支,Git提示合并失败,因为两个分支上的文件存在冲突。
Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容。
我们修改如下后保存。并且查看版本数。
也可通过git branch -d branchName删除分支
xq@xq-VPCEG17YC:~/git/test$ git checkout st_bugfix
切换到分支 'st_bugfix'
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my sixth try.
1111111111111111111
st_bugfix
xq@xq-VPCEG17YC:~/git/test$ git checkout st
切换到分支 'st'
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my sixth try.
1111111111111111111
st
xq@xq-VPCEG17YC:~/git/test$ git merge --no-ff -m "st merger st_bugfix test" st_bugfix
自动合并 hello.txt
冲突(内容):合并冲突于 hello.txt
自动合并失败,修正冲突然后提交修正的结果。
xq@xq-VPCEG17YC:~/git/test$ git status
位于分支 st
您有尚未合并的路径。
(解决冲突并运行 "git commit")
未合并的路径:
(使用 "git add <文件>..." 标记解决方案)
双方修改: hello.txt
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
test/
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my sixth try.
1111111111111111111
<<<<<<< HEAD
st
=======
st_bugfix
>>>>>>> st_bugfix
xq@xq-VPCEG17YC:~/git/test$ vi hello.txt
xq@xq-VPCEG17YC:~/git/test$ git add hello.txt
xq@xq-VPCEG17YC:~/git/test$ git commit -m "st add conflict test"
[st b7ca7d4] st add conflict test
1 file changed, 3 deletions(-)
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my sixth try.
1111111111111111111
st
st_bugfix
xq@xq-VPCEG17YC:~/git/test$ git log --graph --pretty=oneline --abbrev-commit
* b7ca7d4 st add conflict test
* 595fbcb st add conflict test
|\
| * 52caed4 st_bugfix branch test
* | 8f57e6a st branch merge test
|/
* c3fa3c8 merge dev
|\
| * a1cadc0 branch test
| * f26e626 branch test
|/
* 3742dca commit to GitHub
* 1a465a0 stage test
* 7740455 update again
* 9d94f3b update hello.txt
* b10613a add a file[hello.txt]
PRD ——> UAT ——> ST ——> DEV(1 … n)
| |
| |——>ST_BUGFIX
|
| ——>UAT_BUGFIX
master作为稳定的发行版本,即上图的PRD,PRD分成UAT和UAT_BUGFIX两个分支;UAT分为ST和ST_BUGFIX两个分支;ST分为n个开发流,可以对应不同的人,不同的项目,不同的功能。通过这样的策略层层提交,向上合并。每一个新的版本发布,向下rebase。
有一种情况,工作只进行到一半,还没法提交,但是有一个bug需要修复,Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作。
一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;
另一种方式是用git stash pop,恢复的同时把stash内容也删了:
你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:
$ git stash apply stash@{0}
xq@xq-VPCEG17YC:~/git/test$ git stash
Saved working directory and index state WIP on dev: 7a623b1 merge master
HEAD 现在位于 7a623b1 merge master
xq@xq-VPCEG17YC:~/git/test$ git status
位于分支 dev
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
test/
提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)
xq@xq-VPCEG17YC:~/git/test$ git checkout st_bugfix
切换到分支 'st_bugfix'
xq@xq-VPCEG17YC:~/git/test$ vi hello.txt
xq@xq-VPCEG17YC:~/git/test$ git merge --no-ff -m "merge master" master
Merge made by the 'recursive' strategy.
hello.txt | 1 +
1 file changed, 1 insertion(+)
xq@xq-VPCEG17YC:~/git/test$ vi hello.txt
xq@xq-VPCEG17YC:~/git/test$ git add hello.txt
xq@xq-VPCEG17YC:~/git/test$ git commit -m "st bug fix stach test"
[st_bugfix 631eac1] st bug fix stach test
1 file changed, 1 insertion(+)
xq@xq-VPCEG17YC:~/git/test$ git checkout master
切换到分支 'master'
您的分支领先 'origin/master' 共 9 个提交。
(使用 "git push" 来发布您的本地提交)
xq@xq-VPCEG17YC:~/git/test$ git merge --no-ff -m "merge st_bugfix" st_bugfix
Merge made by the 'recursive' strategy.
hello.txt | 1 +
1 file changed, 1 insertion(+)
xq@xq-VPCEG17YC:~/git/test$ cat hello.txt
this is my sixth try.
1111111111111111111
st
st_bugfix
stash_test_on_st_bugfix
xq@xq-VPCEG17YC:~/git/test$ git checkout dev
切换到分支 'dev'
xq@xq-VPCEG17YC:~/git/test$ git status
位于分支 dev
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
test/
提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)
xq@xq-VPCEG17YC:~/git/test$ git stash list
stash@{0}: WIP on dev: 7a623b1 merge master
xq@xq-VPCEG17YC:~/git/test$ git stash pop
删除 dev_rickey.txt
位于分支 dev
尚未暂存以备提交的变更:
(使用 "git add/rm <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
删除: dev_rickey.txt
修改: hello.txt
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
test/
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
丢弃了 refs/stash@{0} (fc73d8c7a60070ae1475361c7705126e16b88b47)
将分支push到远程服务器——GitHub
首先,可以试图用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。
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。
创建标签
默认标签是打在最新提交的commit上的
git tag tagId commitId
还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字
git tag -a v0.1 -m “version 0.1 released” 3628164
还可以通过-s用私钥签名一个标签:
$ git tag -s v0.2 -m “signed version 0.2 released” fec145a
签名采用PGP签名,因此,必须首先安装gpg(GnuPG),如果没有找到gpg,或者没有gpg密钥对,就会报错
xq@xq-VPCEG17YC:~/git/test$ git checkout dev
D dev_rickey.txt
M hello.txt
已经位于 'dev'
xq@xq-VPCEG17YC:~/git/test$ git tag v1.0
xq@xq-VPCEG17YC:~/git/test$ git tag
v1.0
xq@xq-VPCEG17YC:~/git/test$ git show v1.0
commit 7a623b1cb2fd9d56dbdfc4bda03c4a6c3f39dcd6
Merge: a1cadc0 e6f1dd6
Author: “rickey17” @163.com>
Date: Sat Apr 15 19:42:36 2017 +0800
merge master
操作标签
如果标签打错了,也可以删除:
git tag -d v0.1
因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。
如果要推送某个标签到远程,使用命令git push origin v1.0
或者,一次性推送全部尚未推送到远程的本地标签:
git push origin –tags
如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:
git tag -d v0.9
然后,从远程删除。删除命令也是push,但是格式如下:
git push origin :refs/tags/v0.9
是否真的从远程库删除了标签,可以登陆GitHub查看。