git分支详解

来源:互联网 发布:肌肉纬度 知乎 编辑:程序博客网 时间:2024/06/05 15:30

概述

我们首先来回顾下SVN的分支是什么样的。假如说我们现在要开发一个新的功能,需要用到svn的分支,与主分支进行并行开发,我们是怎么做的呢?我们首先要创建一个分支,然后checkout分支,checkout完成后我们会发现分支branch在我们本地其实就是对trunk的一个完全拷贝。我们本地会多出一个branch的文件夹,目录结构跟trunk一模一样。那么怎么样来进行并行开发呢?试想一下,分支在开发的同时,trunk也在开发,最后trunk开发完成需要合并到主分支时,可能会存在一堆的冲突。这个怎么避免呢?是这样,在并行开发的同时,我们也要时常更新本地trunk并且把trunk合并到branch上,保持分支与仓库的实时更新,这样才不会产生大量冲突。等功能开发和测试完成,就可以把branch内容合并到trunk中,然后删除branch,提交trunk。这样才是一个完整的svn分支开发流程。我们注意到,本地的branch其实是对trunk的一个完全拷贝。会发现,使用SVN的分支开发还是挺麻烦的。

git分支

git分支与SVN分支显然大不一样。git创建一个本地分支只需要在初始化一个本地git仓库后执行 git branch <branchName>,就可以创建一个名字为branchName的本地分支。使用checkout命令可以切换分支,这个在上篇已经介绍过了。我们会发现,创建分支过后,本地仓库并没有多出分支文件夹,也就是说git创建分支并没有对主分支进行完全拷贝,这个跟svn是大便不相同的。在git分支上进行的操作,提交过后,可以合并到主分支上,合并完成后,创建的git分支即可删除。

在这里多说一下,使用git作为版本控制,每开发一个新功能或者修复一个bug都应该新建一个分支来进行操作。不然几个模块并行开发的时候,你都在主分支上操作,到时候有一个功能好了,需要提交测试,你应该怎么提交?需要人工识别哪些文件能提交,哪些文件不能提交。这显然不是一种好的做法。好的做法是每个功能模块使用分支进行开发,功能好了过后在分支提交然后往主分支master上合并,这样不用纠结那些文件能有提交推送哪些不能。

git分支原理

要使用好git分支,首先要弄明白分支原理。svn的分支是在本地进行一个完全拷贝,git分支则不是。下面介绍下git分支原理。

在介绍原理之前需要介绍几个概念。

  • HEAD:我们使用git的时候发现好多命令例如git reset - - hard HEAD^(会退到上一个版本)里面都含有HEAD, 那么HEAD是什么呢?HEAD指向的是当前分支。另外在./.git/HEAD文件中可以看到当前分支:

    ➜  gitlecture git:(test) cat .git/HEAD ref: refs/heads/test➜  gitlecture git:(test) 

    可以看到HEAD文件是一个指向所在分支的引用标识符,该文件内部并不包含SHA-1值,而是指向另外一个引用的指针。
    另外,凡是修改了HEAD指向的操作,都会在git reflog中被记录下来。

    d70e644 HEAD@{0}: checkout: moving from master to testd70e644 HEAD@{1}: checkout: moving from test to masterd70e644 HEAD@{2}: checkout: moving from master to testd70e644 HEAD@{3}: checkout: moving from test to masterd70e644 HEAD@{4}: checkout: moving from develop to testd70e644 HEAD@{5}: checkout: moving from test to developd70e644 HEAD@{6}: checkout: moving from master to test
  • 提交:当执行git commit命令时,git会创建一个commit对象,并且将这个commit对象的parent指针设置为HEAD所指向的引用的SHA-1值。也就是说除了第一个提交,每一个提交都有一个parent指针指向它的上一次提交,可以理解为一种链式结构。因此我们使用git log命令就可以查看到完整的提交信息。

  • 分支:分支指向的是当前分支上的最新一次提交。分支指向的是提交。

    下面通过一组图来解释git 分支提交合并原理。

    1. 当前处在master分支。
      这里写图片描述

    2. 创建一个新的分支dev,并切换到dev(git checkout -b dev)。
      这里写图片描述

    3. 在分支dev上进行一次提交。
      这里写图片描述

    4. 讲dev合并到master。由于master没有进行任何提交,此次合并相对与master而言是进行了一次快进操作(fast-forword)。
      这里写图片描述

    5. 如果分支在合并到master之前,master也做了提交,这种合并就要重新一个提交要记录合并后的状态。如果有冲突的话,还需要解决冲突。
      这里写图片描述

    以上就是分支提交合并的原理。

git分支常用命令

  • git branch:查看本地分支
  • git branch <branchName>:在本地创建一个名字为branchName的分支。
  • git checkout <branchName>:切换到名字为branchName的本地分支。
  • git checkout -b <branchName>:创建一个名字为branchName的分支并且切换到改分支。
  • git branch -d <branchName>:删除一个名字为branchName的分支。如果该分支有提交未进行合并,则会删除失败。
  • git branch -D <branchName>:强制删除一个名字为branchName 的分支。如果该分支有提交未进行合并,也会删除成功。
  • git merge <branchName>:将名字为branchName的分支合并到当前分支。

git远程分支、refspec

我们在本地新建一个分支develop,然后执行git push命令,会发现推送失败。

➜  gitlecture git:(develop) git push fatal: The current branch develop has no upstream branch.To push the current branch and set the remote as upstream, use    git push --set-upstream origin develop

可以看出错误提示,当前分支没有上游分支,因此推送失败。那么怎么创建一个上游分支呢,可以使用git提示对推荐的命令。

➜  gitlecture git:(develop) git push --set-upstream origin develop Counting objects: 8, done.Delta compression using up to 4 threads.Compressing objects: 100% (8/8), done.Writing objects: 100% (8/8), 820 bytes | 0 bytes/s, done.Total 8 (delta 2), reused 0 (delta 0)remote: Resolving deltas: 100% (2/2), done.To https://github.com/aronykl/gitlecture.git * [new branch]      develop -> developBranch develop set up to track remote branch develop from origin.

看提示,创建了一个新的分支,从本地的develop分支到远程的develop分支,分支develop设置追踪远程分支develop。

这里讲远程分支,其实涉及到了一个refspec的概念。我们想远程仓库进行推送的时候,执行的是git push命令。其实这个只是一个简写的命令,完整的命令是:

$ git push origin <src>:<dst>

其中, <src> 代表的是本地分支;<dst> 代表的是远程服务器的分支,意思是讲本地<src> 到远程分支<dst>

git pull 也是一样。

$ git pull origin <src>:<dst>

意思是讲远程的<src> 分支拉取到本地<dst> 分支,并且进行合并。注意:git pull拉取实际是两个操作:首先将远程分支的更新fetch到本地的远程分支上(一般remotes/origin/开头,可用过git brach -av命令查看本地远程分支),然后再把本地远程分支合并到本地分支。而且我们是不能直接修改本地本地远程分支的内容的,只能由git修改。

再来说refspec。refspec其实是git的一个引用规范。如果服务器上有一个 master 分支,我们可以在本地通过下面这种方式来访问该分支上的提交记录:

$ git log origin/master$ git log remotes/origin/master$ git log refs/remotes/origin/master

上面的三个命令作用相同,因为 Git 会把它们都扩展成refs/remotes/origin/master。

若要将远程的 master 分支拉到本地的 origin/mymaster 分支,可以运行:

$ git fetch origin master:refs/remotes/origin/mymaster

其实也可以写成:

$ git fetch origin master:origin/mymaster$ git fetch origin master:remotes/origin/mymaster

最终git都会把origin/mymaster 分支扩展成refs/remotes/origin/mymaster。

关于更多的refspec内容,可参考:git refspec

git 远程分支常用命令

  • 给本地分支设置一个远程上游分支:git push –set-upstream origin <remoteBranchName>
  • 推送一个本地分支到远程分支:git push origin <localBranch>:<remoteBranch>
  • 从远程分支拉取到本地分支:git push origin <remoteBranch>:<localBranch>
  • 在执行git pull将远程分支拉取到本地远程分支后,在本地创建分支追踪远程分支:git checkout - -track <remoteBranch>。这里的<remoteBranch> 其实是本地远程分支。
  • 删除远程分支:
    1. git push origin :<remoteBranch>,意思是将本地空分支推送到远程分支,即可删除远程分支。
    2. 2.git push origin - -delete <remoteBranch>