凌云的博客

行胜于言

跟我一起学Git (五) 分支

分类:Git| 发布时间:2014-03-10 07:48:38


对于开发软件项目来说,分支(branch)是一个基本的概念。 Git的分支系统是简单和轻量级的。和其他版本管理系统不一样,Git的使用者经常会用到分支。

分支名称

为了支持可扩展性和分类组织,Git支持Unix路径名称类型的层级式的分支名称。 比如你有一类用于修改Bug的分支,你可以统一将这类分支的名称命名为bug/pr-17和bug/pr-108这种格式。 另一个使用层级式的分支名称的原因是Git和Unix Shell一样支持通配符。 比如你可以通过以下命令来查看所有的bug分支

$ git show-branch 'bug/*'

能用于和不能用于分支名称的字符

  • 你可以使用斜杠(/)来创建层级式的名称,但是不用用它作为分支名称的结尾
  • 点(.)不能紧跟在斜杠后面
  • 点点(..)不能存在分支名称中
  • 另外,分支名称中不能有任何的空格符和其他的空白符,对Git来说有特殊含义的字符, 包括波浪号(~),脱字符号(^),分号(:),问号(?)等等。

使用分支

虽然在Git仓库中可以有很多不同的分支,但是在任一时间点只有一个“活动”或“当前”分支。 默认情况下master是当前分支。 每个代码库中的分支都有一个唯一的名称,并且这个名称总是引用到这个分支最新的提交。

如果你不知道一个新的分支是从原有分支的哪一个提交开始分出来的,可以通过以下命令得到:

$ git merge-base original-branch new-branch

创建分支

可以通过以下命令创建分支:

$ git branch branch-name [starting-commit]

如果没有提供starting-commit参数,那么默认是当前分支的最新提交

列出分支名称

可以通过以下命令列出:

$ git branch

查看分支

git show-branch 可以提供比git branch更详细的输出。 对于git show-branch 来说还有两个比较常用的参数,-r表示显示远程分支,-a表示显示所有分支 例子:

$ git show-branch
! [bug/pr-1] Fix Problem Report 1
 * [dev] Improve the new development
  ! [master] Added Bob's fixes.
---
 *  [dev] Improve the new development
 *  [dev^] Start some new development.
+   [bug/pr-1] Fix Problem Report 1 
+*+ [master] Added Bob's fixes.

这个命令的输出一共有两部分:
第一部分列出分支,每个分支一行,每行分为三列, 第一列是占位符和第二部分的内容对应,第二列是用方括号包含的分支名称,第三列是最新的提交的日志。
第二部分是一系列的提交信息,每个提交一行,也分为三列。 第一列用于对应第一部分的第一列,这一列一共有三种符号,加号,减号和星号。 加号表示这个分支有这个提交,星号表示这是当前分支,减号表示这个分支通过合并有这个提交。

默认情况下,git show-branch会根据时间列出提交直到遇到一个所有分支都有的提交为止。 当然这个命令也可以通过参数来指定要显示的分支,比如:

$ git show-branch bug/pr-1 master

签出分支

简单的签出

可以通过git checkout branchname 来签出分支 当你签出分支时需要注意以下情况:

  • 不在当前分支但是在将要签出的分支的 文件/目录 会在签出时将其复制到你的工作目录
  • 在当前分支不在将要签出的 文件/目录 会在签出时从工作目录删除
  • 当前分支和将要签出的分支都有的文件会将其引用到签出的分支

当你有未提交的修改时签出

当你有未提交的修改时签出,如果自动合并时出现冲突, Git会输出错误信息并且拒绝这个操作(如果没有冲突签出会成功)

$ git branch
  bug/pr-1
  bug/pr-2
  dev
* master
$ git checkout dev
error: Entry 'NewStuff' not uptodate. Cannot merge. 

这时你的一个选择是将修改提交后再签出分支

合并修改到另一个分支

当你想将修改合并到要签出的分支而不是当前分支时, 你可以在git checkout时指定-m参数,这时git就会在你签出时进行合并

$ git checkout -m dev
M       NewStuff
Switched to branch "dev"

但是需要注意的是如果有冲突的话,Git仅仅会指出冲突的地方,你仍然需要手动解决冲突。

$ cat NewStuff
Something
<<<<<<< dev:NewStuff
A Change
=======
Something else
>>>>>>> local:NewStuff

创建并签出到新分支

可以通过以下命令创建并签出到新分支:

$ git checkout -b new-branch start-point

相当于以下命令:

$ git branch new-branch start-point
$ git checkout new-branch

和HEAD分离的分支

当你签出分支时,推荐的操作是使用分支名称指定分支,这时实际上是签出到分支的最新修改。 实际上你可以签出到分支的任一个提交, 这时Git会为你创建一系列的匿名分支(我们称为和HEAD分离的分支)。

当你进行以下操作时,Git会为你创建一个和HEAD分离的分支:

  • 签出一个分支的非HEAD提交
  • 签出一个追踪分支
  • 签出一个标签引用的提交
  • 使用git bitset
  • 使用git submodule update

删除分支

可以使用git branch -d branch 来删除分支。 需要注意的是这个命令不能删除当前分支:

$ git branch -d bug/pr-3
error: Cannot delete the branch 'bug/pr-3' which you are currently on.

这时你需要切换到其他分支进行这个操作。 另外,还有一个需要注意的地方:如果要删除的分支有些提交没有合并到当前分支Git也会拒绝操作。

$ git checkout master
Switched to branch "master"
$ git branch -d bug/pr-3
error: The branch 'bug/pr-3' is not an ancestor of your current HEAD.
If you are sure you want to delete it, run 'git branch -D bug/pr-3'.

这时你需要合并修改回来,或者,如果你确认这些修改是不需要的 你可以使用 git branch -D branch 强制删除分支。 Git不会维护分支的创建、重命名、删除的历史,如果你删除了分支那么它就不见了。 但是,关于这个分支的提交历史又另当别论了, 只要还有其他的分支、标签引用到这些提交,那么这些提交历史就会保留。

当你意外地删除了分支后,你可以通过以下工具帮助你恢复:

  • git reflog
  • git fsck
  • 配置选项:gc.reflogExpire 和 gc.pruneExpire