git系列---分支

注: 该系列文章整理自《Pro Git》,非原创。html

git系列:

git系列--基础使用

git系列--分支

一 Git分支简介

几乎全部的版本控制系统都以某种形式支持分支。使用分支意味着你能够把你的工做从开发主线上分离开来,以避免影响开发主线。git

与许多其它版本控制系统不一样,Git处理分支的方式可谓是难以置信的轻量,且鼓励在工做流程中频繁地使用分支与合并,哪怕一天以内进行许屡次。github

首先回顾下Git保存数据的方式:Git保存的不是文件的变化或差别,而是一系列不一样时刻的文件快照。Git进行提交操做的时候,会保存一个提交对象,该对象会包含一个指向内容快照的指针,还包含了做者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针(首次提交产生的提交对象没有父对象,普通提交操做产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象),相似于链数据结构。bash

Git的分支,在本质上仅仅只是指向提交对象的可变指针!它会在每次的提交操做中自动向前移动。服务器

注:Git的默认分支是“master”,并无什么特殊意义,只是git init命名默认建立的,通常懒得去更名字数据结构

1.1 分支建立

Git分支的建立,能够理解为就是简单的建立了一个基于当期提交对象的能够移动的新指针。post

好比,新建一个testing分支,用git branch测试

$ git branch testing
复制代码

这会在当前所在的提交对象上建立一个新的指针fetch

那么问题来了,git怎么知道当前本身在哪个分支指针上呢?它用一个名为“HEAD”的特殊指针来区分——它指向当前所在的本地分支(可将其想象成当前分支的一个别名)。切换到其余分支,能够当作就是HEAD指向另外一个分支指针ui

1.2 切换分支

新建分支,并不会自动切换到新分支,须要使用git checkout切换。

$ git checkout testing
复制代码

这样,就切到咱们刚才新建的testing分支上,即HEAD指向了testing分支

Git的分支功能十分强大有用。好比,你须要在testing上开发一个测试功能,但不想影响到主线master,或master先进行额外的更新发布。此时,你就能够分别在两个分支上操做,待时机成熟,再将你的工做合并起来:

首先切换到testing分支,并进行了一次提交,此时,testing分支就向前移动领先master(此时master是不移动的)

而后咱们切换回到主线master上

$ git checkout master
复制代码

这条命令作了两件事。

  • 一是使 HEAD 指回 master 分支,
  • 二是将工做目录恢复成 master 分支所指向的快照内容。 也就是说,你如今作修改的话,项目将始于一个较旧的版本。 本质上来说,这就是忽略 testing 分支所作的修改,以便于向另外一个方向进行开发。

注意: 在切换分支时,必定要注意你工做目录里的文件会被改变。 若是是切换到一个较旧的分支,你的工做目录会恢复到该分支最后一次提交时的样子。 若是 Git 不能干净利落地完成这个任务,它将禁止切换分支。

如今,若在master上,又进行了一个更新提交,则项目上就会产生分叉。你能够在不一样分支间不断地来回切换和工做,并在时机成熟时将它们合并起来。

因为 Git 的分支实质上仅是包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件,因此它的建立和销毁都异常高效。任何规模的项目都能在瞬间建立新分支。同时,因为每次提交都会记录父对象,因此寻找恰当的合并基础(即共同祖先)也是一样的简单和高效。这些高效的特性使得 Git 鼓励开发人员频繁地建立和使用分支。

二 分支的新建和合并

在实际开发过程当中,经常会忽然遇到一个紧急问题须要修复。这时,能够先切换到主分支,再为这个紧急的任务新建一个分支来解决问题。待问题解决后,再将其合并到主分支中。

2.1 新建分支和分支合并

假设项目已有一些提交

如今你正在解决公司的问题#53,所以,你新建了一个issue53分支。(想新建一个分支,并同时切换道这个分支上,可使用带-b参数的git checkout命令:

$ git checkout -b iss53
Switched to a new branch "iss53"
复制代码

它是下面两条命令的简写

$ git branch iss53
$ git checkout iss53
复制代码

继续在issue53上工做并提交了一些新提交。

如今忽然接到紧急任务,须要快速修复主分支的问题。有了Git的帮助,你没必要把这个紧急问题和iss53的修改混在一块儿,你也不须要花大力气来还原关于 53# 问题的修改,而后再添加关于这个紧急问题的修改,最后将这个修改提交到线上分支。 你所要作的仅仅是切换回 master 分支,并新建hotfix分支,再修复合并。

$ git checkout master
$ git checkout -b hotfix
复制代码

你能够在hotfix分支上修复问题,并运行你的测试,确保你的修改是正确的,而后将其合并回你的 master 分支来部署到线上。 你可使用 git merge 命令来达到上述目的:

$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
 index.html | 2 ++
 1 file changed, 2 insertions(+)
复制代码

如今这个紧急问题已修复并合并到主分支中了,所以能够删除该分支。 你可使用带 -d 选项的 git branch 命令来删除分支:

$ git branch -d hotfix
Deleted branch hotfix (3a0874c).
复制代码

如今切回以前工做的分支,即issue53,并继续工做

$ git checkout iss53
复制代码

在hotfix分支上作的修复工做并无包含到issue53分支中。若是须要拉取所作的修复,可使用git merge master将master合并入issue53分支中(由于hotfix已删除,因此合并master分支)。 或者,能够等到issue53分支完成任务后再合并回master分支中。

假设你已经修正了 #53 问题,而且打算将你的工做合并入 master 分支。 为此,你须要合并 iss53 分支到 master 分支,这和以前你合并 hotfix 分支所作的工做差很少。 你只须要检出到你想合并入的分支,而后运行 git merge 命令:

$ git checkout master
$ git merge iss53

复制代码

这和你以前合并 hotfix 分支的时候看起来有一点不同。 在这种状况下,你的开发历史从一个更早的地方开始分叉开来(diverged)。 由于,master 分支所在提交并非 iss53 分支所在提交的直接祖先,Git 不得不作一些额外的工做。 出现这种状况的时候,Git 会使用两个分支的末端所指的快照(C4 和 C5)以及这两个分支的工做祖先(C2),作一个简单的三方合并。

和hotfix单纯的推动分支指针不一样,Git 将这次三方合并的结果作了一个新的快照而且自动建立一个新的提交指向它。 这个被称做一次合并提交,它的特别之处在于他有不止一个父提交。

最后删除分支

$ git branch -d iss53
复制代码

三 分支管理

3.1 查看分支列表

git branch命令,不加任何参数的状况下,会当到当前全部分支的列表(本地分支):

$ git branch
  iss53
* master
  testing
复制代码

notemaster分支前的*字符,表明当前检出的那个分支(即,当前HEAD指针所指向的分支)

--merged--no-merged这两个有用的选项能够过滤这个列表中已经合并或还没有合并到当前分支的分支。 若是要查看哪些分支已经合并到当前分支,能够运行 git branch --merged:

$ git branch --merged
  issue53
* master
复制代码

由于以前已经合并了issue53分支,因此如今它在列表中。一般能够用git branch -d删除这些已经合并了的分支

若要查看未合并的分支,能够运行git branch --no-merged

$ git branch --no-merged
  testing
复制代码

注:对于这些还未合并的分支,若是它包含了还未合并的工做,尝试使用git branch -d来删除时会提示失败。(可使用-D选项强制删除,不推荐)

四 远程分支

能够经过 git remote show (remote) 查看远程仓库的具体远程分支状况

4.1 远程分支介绍

上一篇讲过远程仓库,也就是你服务器的git仓库,如GitHub上的项目。远程分支能够理解为,是将远程仓库上的分支的拷贝到本地目录中,但对于这些拷贝下来的远程分支,你只能去查看,而不能主动去提交修改,只有当你与服务器同步时,拷贝下来的分支才会有新的提交,即分支指针才会有移动。

简单而言,就是你仅仅只有只读权限,能够读取服务器的最新代码状态,并将其合并到本身的本地分支中。或者自行推送最新的修改到服务器中(此时,拷贝的远程分支也会自动更新到最新状态)

远程分支以(remote)/(branch) 形式命名

举个例子,假设你有一个git.ourcompany.com的git远程服务器,且上面已经有工做基础了的,而不是个空项目,此时你能够经过 git clone 克隆下来,拉取它的数据。此时,git为帮你将这个远程服务器命名为origin,同时拉取它的全部数据,并建立一个origin/master分支指针指向它,而且还会建立一个master本地分支,指向相同的地方,使得有工做的基础

如今你在本地master分支上作了些提交,与此同时,其余人推送更新了git.ourcompany.com远程仓库。(只要你不与服务器同步,拷贝的远程分支origin/master指针就不会有移动)

要抓取服务器的最新代码,能够运行git fetch origin。抓取并更新本地拷贝下来的远程分支。

fetch命令只会抓取数据并更新到远程分支,但并不会自动帮你合并到你的工做的本地分支master中,须要你自行合并(pull命令则能够自动合并,但不推荐使用)。

4.2 推送

本地的分支并不会自动与远程仓库同步——你必须显式地推送想要分享的分支。当你想要分享公开一个分支时,你可使用git push推送上去。

例如,但愿和其余人在serverfix分支上工做,能够运行git push (remote) (branch)

$ git push origin serverfix
Counting objects: 24, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (24/24), 1.91 KiB | 0 bytes/s, done.
Total 24 (delta 2), reused 0 (delta 0)
To https://github.com/schacon/simplegit
 * [new branch]      serverfix -> serverfix
复制代码

你也能够运行 git push origin serverfix:serverfix,它会作一样的事——也就是说“推送本地的 serverfix 分支,将其做为远程仓库的 serverfix 分支” 能够经过这种格式来推送本地分支到一个命名不相同的远程分支。 若是并不想让远程仓库上的分支叫作 serverfix,能够运行 git push origin serverfix:awesomebranch 来将本地的 serverfix 分支推送到远程仓库上的 awesomebranch 分支

推送以后,其余人使用git fetch抓取数据时,会在本地生成一个远程分支origin/serverfix,指向服务器的serverfix分支的引用

$ git fetch origin
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://github.com/schacon/simplegit
 * [new branch]      serverfix    -> origin/serverfix
复制代码

特别注意:抓取新的服务器分支后,只会拷贝一份可读的远程分支,本地不会自动生成可工做的server分支(clone命令,生成master命令是系统自动生成的) 换一句话说,这种状况下,不会有一个新的 serverfix 分支——只有一个不能够修改的 origin/serverfix 指针。

能够运行git merge origin/serverfix,将这些工做合并到当前所处的本地工做分支。或者,若是想在本身本地也建立一个serverfix分支工做,能够将其创建在远程跟踪分支上($ git checkout -b serverfix origin/serverfix):

$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'
复制代码

这会给你一个用于工做的本地分支,而且起点位于 origin/serverfix。

4.3 跟踪分支

从一个远程分支检出(checkout)一个本地分支会建立所谓的”跟踪分支“(它跟踪的分支叫作“上游分支”)。跟踪分支是与远程分支有直接关系的本地分支。 若是在一个跟踪分支上输入git pull,Git能自动地识别去哪一个服务器上抓取、合并到哪一个分支。

当clone克隆一个仓库时,一般会自动建立一个跟踪origin/master的master分支。你也能够设置其余的跟踪分支,如前面的$ git checkout -b serverfix origin/serverfix就会建立一个跟踪origin/serverfix的本地serverfix分支。(该命令的快捷方式:$ git checkout --track origin/serverfix

如今,在本地分支 serverfix运行pull命令 会自动从 origin/serverfix 拉取。

设置已有的本地分支跟踪一个刚刚拉取下来的远程分支,或者想要修改正在跟踪的上游分支,你能够在任意时间使用 -u--set-upstream-to 选项运行 git branch 来显式地设置。

$ git branch -u origin/serverfix
复制代码

当推送一个新分支到服务器时,也可使用带-upush命令,它不只会推送本地分支到服务器,也会跟踪它。

$ git push -u origin serverfix

复制代码

若是你有注意的话,在GitHub上新建一个空项目时,就有相关的提示

添加一个远程仓库,并推送本地master分支到服务器上,同时跟踪这个远程分支。

4.4 拉取

git fetch 命令从服务器上抓取本地没有的数据时,它并不会修改工做目录中的内容。 它只会获取数据而后让你本身合并。 然而,有一个命令叫做 git pull 在大多数状况下它的含义是一个 git fetch 紧接着一个 git merge 命令。 若是有一个像以前章节中演示的设置好的跟踪分支,无论它是显式地设置仍是经过 clone 或 checkout 命令为你建立的,git pull 都会查找当前分支所跟踪的服务器与分支,从服务器上抓取数据而后尝试合并入那个远程分支。

因为 git pull 常常使人困惑因此一般单独显式地使用 fetch 与 merge 命令会更好一些。

4.5 删除远程分支

假设你已经经过远程分支作完全部的工做了——也就是说你和你的协做者已经完成了一个特性而且将其合并到了远程仓库的 master 分支(或任何其余稳定代码分支)。 能够运行带有 --delete选项的 git push 命令来删除一个远程分支。

$ git push origin --delete serverfix
To https://github.com/schacon/simplegit
 - [deleted]         serverfix
复制代码

五 分支的另外一种整合:变基(rebase)

在 Git 中整合来自不一样分支的修改主要有两种方法:merge 以及 rebase。二者的主要区别在于提交历史的不一样。

5.1 变基(rebase)

变基rebase命令将分支上的全部修改移到另外一个你指定的分支中,就好像“从新播放”同样,跟以前的合并merge的主要区别就是最终提交历史记录不一样,但最终的代码结果是同样的。

假设如今开发任务有两个分叉

若是使用merge 命令。 它会把两个分支的最新快照(C3 和 C4)以及两者最近的共同祖先(C2)进行三方合并,合并的结果是生成一个新的快照(并提交)。

此时若使用变基rebase命令,则提取在 C4 中引入的补丁和修改,而后在 C3 的基础上应用一次,就好像“从新播放”同样。

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
复制代码

它的原理是首先找到这两个分支(即当前分支 experiment、变基操做的目标基底分支 master)的最近共同祖先 C2,而后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件,而后将当前分支指向目标基底 C3, 最后以此将以前另存为临时文件的修改依序应用

如今回到 master 分支,进行一次快进合并。

$ git checkout master
$ git merge experiment
复制代码

此时,C4' 指向的快照就和上面使用 merge 命令的例子中 C5 指向的快照如出一辙了。再次强调:两种整合方法的最终结果没有任何区别,可是变基使得提交历史更加整洁,通常使用变基的目的是为了确保在向远程分支推送时能保持提交历史的整洁,具体使用哪一种方法,看我的实际使用。

注:不要对在你的仓库外有副本的分支执行变基。 若是你遵循这条金科玉律,就不会出差错。不然,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。

更多可查看Git 分支 - 变基

相关文章
相关标签/搜索