Git版本管理之旅(三)—— Git分支策略与管理

  • 创建与合并分支
  • 解决冲突
  • 分支管理策略
  • 标签管理

本篇继续上面所讲的内容,我们理解了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指向的就是当前分支。
Git版本管理之旅(三)—— Git分支策略与管理_第1张图片
每次提交,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版本管理之旅(三)—— Git分支策略与管理_第2张图片

Git创建一个分支很快,因为除了增加一个dev指针,改变HEAD的指向,工作区的文件都没有任何变化!

不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:

Git版本管理之旅(三)—— Git分支策略与管理_第3张图片
假如我们在dev上的工作完成了,就可以把dev合并到master上。Git就是直接把master指向dev的当前提交,就完成了合并:

Git版本管理之旅(三)—— Git分支策略与管理_第4张图片

合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:

Git版本管理之旅(三)—— Git分支策略与管理_第5张图片

下面我们在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功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作。

  1. 通过git stash保存工作空间
  2. 切换到Bug相应的分支,修改缺陷,commit修改
  3. 去master分支上合并该commit
  4. 切换回Dev分支
  5. 用git stash list命令看看,暂存的工作区
  6. 恢复工作区

一是用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查看。

你可能感兴趣的:(git)