Git 分支管理与多人协作

    这一篇文章主要学习和记录分支管理和多人合作。我们知道 Git 的分支管理非常快,这是因为 Git 只是生成一个指向当前版本的指针。但是以前的很多管理软件都是生成一份代码的物理拷贝,可以想象,如果代码很大,是有多慢。但 Git 的这个特点,也需要我们更好的掌握它的切换,否则很容易造成错误。

    在 SF 上看到了一张图片,觉得非常好,图片来源是SF的这篇文章,感谢:

    分支管理策略


    创建与合并分支

    创建和合并分支都可以看作是指针的移动。我们看下下面这样一套流程,这是最常见的:

    git checkout -b dev   //创建一个新的dev(-b创建并切换)
    git add file.txt //在dev分支中修改 file.txt 文件
    git commit -m "add change on dev"  //到dev分支提交新的代码
    git checkout master   //切换到主分支
    git merge dev  //在主分支上合并dev分支
    git branch -d dev  //dev工作完成,删除该dev分支
    

    我画了一张图用于描述这个过程:

    分支过程

    下面我们学习一些基本的命令:

    查看分支:git branch
    
    创建分支:git branch <name>
    
    切换分支:git checkout <name>
    
    创建+切换分支:git checkout -b <name>
    
    合并某分支到当前分支:git merge <name>
    
    删除分支:git branch -d <name>
    

    所以: git checkout -b dev = git branch dev + git checkout dev


    合并分支碰到的问题

    有时候我们会碰到下面这个问题:

    问题描述

    可以看到,我们新建了一个 feature1 分支,这个时候比如修改了 a 文件的第一行代码。然后当我们切换到 master 分支的时候,发现 master 也已经有了新版本。然后我们按照上面将的方法 切换到 master 分支,然后利用 git merge feature1 来进行合并。但是发现, master 分支也是修改了 a 文件的第一行代码,这时候我们就要手动去解决冲突了。并且会在 a 文件发现下列问题:

    <<<<<<< HEAD
    Creating a new branch is a
    =======
    Creating a new branch is b
    >>>>>>> feature1
    

    Git 利用 <<<<<<< ======= >>>>>>>来表明冲突的地方,我们手动解决后再进行简单的合并就没有问题了。然后再删除 feature1 分支。

    解决冲突后

    试试 git log --graph这个命令,你会发现分支合并图。

    git log --graph

    合并分支时,有时候 Git 会用 Fast forward 模式,但这种模式下,删除分支后,会丢掉分支信息。如果要强制禁用 Fast forward 模式,Git 就会在 merge 时生成一个新的 commit ,这样,从分支历史上就可以看出分支信息。可以用 –no-ff 参数,表示禁用 Fast forward 。

    git merge --no-ff -m "no-ff merge" develope  //or
    git merge --no-ff develop
    

    看下下面这张图开了解下是否通过 –no-ff 的区别。

    有无--no-ff的区别

    正确的分支管理策略应该是这样的。

    图片来自廖雪峰网站git教程

    每个人干自己的活,在自己的分支上工作,别人不受影响也看不到,然后一定时候合并到 dev 分支上,这样大家就可以看到了。然后要到一定的时候发版本再合并到 git 分支上。


    修复bug分支

    比如我当前在dev分支工作,此时工作还没有完成不能够 merge 到master 分支,但是这个时候发现有一个 bug 需要修复。怎么破呢?

    Git 还提供了一个 stash 功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作。

    git stash //保存现场
    git status //发现现场是干净
    git checkout master  //切换到 master 分支修复 bug
    git checkout -b bug  //在 master 分支上 新建分支 bug
    git add bug001.txt
    git commit -m "修复bug"  //提交修改
    git checkout master    //切换到主分支
    git merge --no-ff -m "merged bug" bug  //合并 bug 分支
    git branch -d bug  //删除 bug 分支
    

    好,这时候我们已经完成了 bug 分支的修改。我们要会到我们的 dev 分支来继续我们没有完成的工作了。进行如下的过程。

    git checkout dev 
    git status  //nothing to commit
    git stash list  //查看现场
    
    git stash apply //恢复现场
    git stash drop  //删除 stash
    git stash pop  //恢复并且删除 stash
    
    git stash list //再次查看现场
    

    过程解释如下:首先我们切换到了 dev 分支。但是查看 git status 发现却是 nothing to commit ,这是怎么回事呢? 我们可以利用利用 git stash list 查看下。发现 Git 会列出所有保存起来的现场:

    stash@{0}: WIP on dev: 13d436c 
    

    工作现场还在, Git 把 stash 内容存在某个地方了,但是需要恢复一下。

    git stash apply <现场名称>//恢复现场
    git stash drop  //删除 stash
    

    其中 git stash apply stash@{0} 的现场名称可以加可以不加,如果你 git stash 了多下,就需要指明了,如果只有一个,就直接 apply 了。 apply 后,保存的现场还在,但是我们是不需要的,所以我们删除它,再使用 git stash drop 即可。这两个命令合在一起可以用 git stash pop 代替。

    我们再次使用 git stash list 就会发现没有啦。

    所以我们总结下,如果我们要修复 bug,我们可以创建新的bug 分支,然后合并再删除。但如果当前手头工作没有完成时,先要用 git stash 保存现场,然后去修复bug,最后再利用 git stash pop 回到工作现场。

    这时候有人会问,那我不能这样吗? 虽然我在 dev 分支上进行了修改,我切换到 master 分支上去新建 bug 分支不行吗?这样或得到的不是从 master 分支的代码吗? dev 上的代码又没有提交到 master 分支上。

    答案是不行的。应为你在 dev 上的改变,即使切换到 master 分支去新建分支都不行,会把你 dev 上的更改到新建的 bug 分支上。可以试下。我做了实验是这样。

    测试的过程中, 我发现了一个好用的命令,比如你新建了一个 bug 分支,这个 bug分支做了修改,但是后来又不想用了,如果直接用普通的git branch -d bug 是删除不了的。要用下面这个命令。

    git branch -D <name>
    

    特别注意下,我们修补了的 bug 分支,最后不仅要同步到 master 分支,可能还要同步到当前工作的 dev 分支。我们可以起名为 fixbug-* 来命名。这样或许比较清楚。

    fix bug checkout


    多人协作

    两个小伙伴 A和B:

    A和B 都从远程库中 clone 代码,由于默认 clone 都是 把远程 master 分支和本地 master分支关联了起来,并且名称为 origin。此时 A 和 B 本地都有一个 master 分支,他们都要建立自己的 dev 分支。于是 A 和 B:

    git checkout -b dev
    

    此时 A 和 B 同事把修改推送到 远程 origin 的 dev 上,如果第一次没有 dev 分支,则会自动建立。

    git push origin dev
    

    若 A 和 B 同时修改了同一个地方。这时候 A 推送过去, B 再 git push origin dev
    的时候,会 push 失败,要解决冲突, 所以 B 需要先 git pull , 但此时 git pull 也会失败,原因是没有指定本地 dev 分支与远程 origin/dev 分支的连接。此时我们

    git branch --set-upstream dev origin/dev
    git pull
    git push origin dev
    

    总结

    到目前为止分支管理就也弄完了。还有一些其他功能,比如打 tag 等等。我觉得不会经常用到的,就不用写在这里了,因为不常用,肯定还会经常忘记,知道有这个东西,以后再来查就行啦。感觉写博客还是很好的。写完这两篇文章,基本上自己的思路就清楚了。以后再遇到问题就来这里面看就好啦。再次感谢廖雪峰老师的文章。