Git分支
Git分支
几乎所有的版本控制系统都以某种形式支持分支。 使用分支意味着你可以把你的工作从开发主线上分离开来, 以免影响开发主线。
一.分支简介
Git 是如何保存数据的:GIt 保存的不是文件的变化或者差异,而是一系列不同时刻的快照。
在进行提交操作时,Git 会保存一个提交对象(commit object),包含一个指向内容快照的指针,还包含了作者的姓名和邮箱,提交时输入的信息以及指向它的父对象的指针。(首次提交产生的提交对象没有父对象)
- 暂存操作(git add .):为每一个文件计算校验和(哈希算法),然后会把当前版本的文件快照保存到 Git 仓库中(git 中使用 blob 对象来保存他们),最终将校验和加入到暂存区域等待提交。
- 提交操作(git commit -m “”):GIt 会先计算每一个子目录的校验和,然后在 Git 仓库中这些校验和保存为树对象。随后,Git 便会创建一个提交对象,它除了包含上面提到的那些信息外,还包含指向这个树对象的指针。如此一来,Git 就可以在需要的时候重现此次保存的快照。
做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针。


二.分支创建
Git 是如何创建新分支的:答案是它只是为你创建了一个可以移动的指针。
$ git branch testing
Git是如何知道当前在哪一个分支上的:答案是有一个名为HEAD的特殊指针,它指向当前所在的本地分支(可以理解为当前分支的别名)。
执行完git branch testing命令后,你仍然在 master 分支上,因为git branch命令仅仅创建了一个新分支,并不会自动切换到新分支去。
使用git log —oneline —decorate可以查看当前各个分支指向的对象。
三.分支切换
切换到一个已经存在的分支:
$ git checkout testing
或者
$ git switch testing
这样 HEAD就指向 testing分支了
此时,当你再修改并提交时:
切换回 master 后:
在切换分支时,一定要注意你工作目录里的文件会被改变。 如果是切换到一个比较旧的分支,你的工作目录会恢复到该分支最后一次提交时的样子。如果 Git 不能干净利落地完成这个任务,它将禁止切换分支。
切换回 master 后再次对文件进行修改并提交:
使用如下命令可以查看分支情况:
$ git log --oneline --decorate --graph --all
由于 Git 的分支实质上仅是包含所指对象校验和的文件,所以它的创建和销毁都异常高效。
使用如下命令,创建分支的同时切换过去:
$ git checkout -b <branchname>
四.分支的新建与合并
两种情况:
- 修复原 master 上的错误(hotfix)
修复完成后,切换到 master 分支,然后进行合并即可(fast-forward,此时只有一个父对象)
$ git checkout master
$ git merge hotfix
此时,hotfix 分支就不需要了,可以进行删除操作:
$ git branch -d hotfix
- 再修复完成原 master 上的错误后,此时已经出现了分叉,继续开发 issue53,然后进行合并(此时会拥有两个父对象)
$ git branch master
$ git merge iss53
此时,因为 master 分支所在提交并不是 iss53 分支所在提交的公共祖先,Git 不得不做一些额外的工作。出现这种情况的时候,Git 会使用两个分支的末端所指的快照(c4 和c5)以及这两个分支的公共祖先(c2),做一个简单的三方合并。
和之前将分支指针向前推进所不同的是,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。这个被称作一次合并提交,它的特别之处在于他不止一个父提交。
最后将 iss53 分支删除即可:
$ git branch -d iss53
五.分支管理
获取当前所有分支:
$ git branch
如果分支前带有*则代表当前分支(即 HEAD 所指向的分支)。
查看每个分支的最后一次提交:
$ git branch -v
查看哪些分支已经合并到当前分支:
$ git branch --merged
查看哪些分支没有合并到当前分支:
$ git branch --no-merged
这会显示未合并工作的分支尝试使用git branch -d命令删除会失败
如果想要强制删除,可以使用如下命令:
$ git branch -D branchname
六.远程分支
远程分支是对远程仓库的引用(指针),包括分支,标签等等。
可以通过以下命令获取远程引用的完整列表:
$ git ls-remote show <remote>
远程跟踪分支是远程分支状态的引用。它们是你无法移动的本地引用。一旦你进行了网络通信,Git 就会为你移动它们以反映远程仓库的状态。
它们以<remote>/<branch>的形式命名,例如要查看最后一次与远程仓库 origin 通信时master 分支的状态,你可以查看origin/master分支。
从远程服务器克隆,Git 的 clone 命令会为你自动将其命名为 origin,拉取它的所有数据,创建一个指向它的 master 分支的指针,并且在本地将其命名为 origin/master。Git 也会给你一个与 origin 的 master 分支在指向同一个地方的本地 master 分支,这样就有了工作的基础。
(origin 并无特殊含义,是你在运行git clone时默认的远程仓库名字,如果运行git clone -o booyah,那么你默认的远程分支名字将会是 booyah/master。同时 master 是当你运行 git init 时默认的起始分支名字,也无特殊含义)
如果你在本地的 master 分支做了一些工作,在同一段时间内有其他人推送提交到 git.ourcompany.com 并且更新了它的 master 分支,这就是说你们的提交历史已走向不同的方向。 即便这样,只要你保持不与 origin 服务器连接(并拉取数据),你的 origin/master 指针就不会移动。
如果要与远程服务器进行同步,运行git fetch <remote>(本例中为git fench origin)。这个命令查找‘origin’是哪一个服务器(git.ourcompany.com),从中抓取本地没有的数据,并且更新本地数据库,移动origin/master指针到更新之后的位置。
你可以通过如下命令添加一个新的仓库:
$ git remote add teamone git://git.team1.ourcompany.com
(其中 teamone 就是远程仓库的别名,像 origin 一样,后面是远程仓库的地址,以后就可以使用 teamone 来代替后面的地址)
使用git fetch teamone来抓取远程仓库 teamone 有而本地没有的数据。
(teamone 中的内容是 origin/master中的一个子集)
- 推送
当你想要公开分享一个分支时,需要将其推送到有写入权限的仓库上。本地的分支并不会自动与远程仓库同步,你必须显式地推送想要分享的分支。
$ git push <remote> <branch>
例如:
$ git push origin master
也可以这样:
$ git push origin serverfix:serverfix
也就是说推送本地的 serverfix 分支,将其作为远程仓库的 serverfix 分支。
如果并不想让远程仓库上的分支叫做 serverfix,也可以运行如下命令:
$ git push origin serverfix:awesommebranch
将本地的 serverfix 分支推送到远程仓库上的 awesomebranch 分支
- 跟踪分支
跟踪分支是与远程分支有直接关系的本地分支,如果在一个跟踪分支上输入git pull,Git 能自动识别去哪个服务器上抓取,合并到哪个分支。
当克隆一个仓库时,它通常会自动地创建一个跟踪origin/master的 master 分支。然而,如果你愿意的话,可以设置其他的跟踪分支,或是一个在其他远程仓库上的跟踪分支,又或者不跟踪 master 分支。
将当前分支跟踪到远程的 serverfix 分支的命令:
$ git checkout --track origin/serverfix
- 拉取
$ git pull
该命令会查找当前分支所跟踪的服务器与分支,从服务器上抓取数据然后尝试合并入那个远程分支。
- 删除远程分支
$ git push origin --delete serverfix
(这个命令做的只是从服务器上移除这个指针)
七.变基
通过命令:
$ git checkout experiment
$ git rebase master
得到:
原理:首先找到这两个分支的最近共同祖先 C2,然后对比当前分支相对与该祖先的历次提交,提交相应的修改并存为临时文件,然后将当前分支指向目标基底C3,最后以此将之前另存为临时文件的修改依序应用。
最后,回到 master 分支上进行合并:
$ git checkout master
$ git merge experiment
变基和第三方合并整合的最终结果所指向的快照始终是一样的,只不过提交历史不一样罢了。