当前位置: 首页 > news >正文

【Git从入门到精通】分支机制

文章目录

  • 简述
    • 创建新分支
    • 切换分支
  • 基本的分支与合并操作
    • 基本的分支操作
    • 基本的合并操作
    • 基本的合并冲突解决
  • 远程分支
    • 推送
    • 跟踪分支
    • 拉取
    • 删除

Git的分支模型是Git的杀手锏特性

简述

首先我们来看一下Git是如何存储数据的。

Git通过一系列的快照的方式来存储数据,当你发起提交时,Git存储的事提交对象,其中包含了只想了暂存区的快照的指针。提交对象也包括作者姓名和邮箱地址、已输入的提交信息,以及指向其父提交的指针(上一次提交)。

下面我们通过一个图来解释,假设我们有一个包含了3个文件的目录,这三个文件都加入了暂存区并进行了提交,暂存时会计算校验和,并把文件的当前版本保存到Git仓库中(这些数据叫做blob对象),然后吧检验和添加到暂存区。

假如我们将以下三个文件进行提交。

git add README.MD Git基础.md 分支机制.md
git commit - m 'first commit'

GIt会进行以下操作:

  • 计算每个子目录的检验和
  • 将这些树对象保存进Git仓库中
  • 创建提交对象,其中包括元数据以及指向目录跟目录的树对象的指针

现在Git仓库中有五个对象:3个blob对象(文件内容),一个树对象(记录目录结构,以及和blob对象和文件的关系)以及一个提交对象(其中包含提交的全部元数据和指向根目录对象的指针)

大概如下图所示:

在这里插入图片描述

之后你又做了一些更改,并又进行了一次提交,这第二次提交就会指向他的上一次提交。

Git分支只不过是一个指向某次提交的轻量级的可移动指针。Git默认的分支名称是master,当你发起提交的时候,就有了一个指向最后一次提交的master的分支。每次提交之后他都会自动向前移动。

创建新分支

当你创建新的分支的时候,Git会创建一个可移动的新指针供你使用。我们可以通过git branch命令来创建一个名为testing的分支。

git branch testing

这会创建一个指向当前提交的新分支

在这里插入图片描述

那么就有了一个问题:GIt如何知道你现在处于哪个分支上呢?其实Git维护着一个HEAD指针,这个指针指向的就是你当前所处的分支。

在这里插入图片描述

可以简单的通过git log命令来查看各个分支当前所指向的对象。可以使用--decorate来查看

在这里插入图片描述

可以看到master和testing分支都处于最新的一次提交之上。并且HEAD处于master分支。

切换分支

要进行分支的切换,可以使用git checkout命令。

git checkout testing

这会使HEAD指针指向testing分支

在这里插入图片描述

这样做的意义是什么呢?当我们再次提交一次快照。

在这里插入图片描述

这个时候发生的是testing分支已经向前移动,master分支还处于原来的位置。

此时如果我们切换到master分支。

git checkout master

在这里插入图片描述

HEAD指针也会发生移动。当然你的工作目录也会变回master分支所处的状态。上述操作回滚了你在testing分支所做的工作,使你能在另一个方向进行开发。

切换分支会更改工作目录,如果你切换到较旧的分支,工作目录也会被恢复到该分支上最后一次提交的状态。

如果此时你在master上进行了一次修改提交。

在这里插入图片描述

现在的项目历史已经发生了分叉,这两次修改是在不同的分支上进行的,彼此互相分离,你可以在分支上来回切换。当你准备好了之后你就可以合并这两次修改。

使用git log --oneline --decorate --graph --all 命令,会输出提交历史,显示出分支指向以及项目历史的分叉情况

在这里插入图片描述

Git分支其实是一个简单的文件,其中包含了该分支所指向提交的校验和。所以Git创建和删除分支的成本很低,你可以随便的创建分支来进行开发。

基本的分支与合并操作

基本的分支操作

假设你已经在你的项目上有了一些提交

在这里插入图片描述

这个时候你想要去修复一个新的问题。你可以使用-b选项的git checkou命令来创建并切换分支。

git checkout -b iss53

在这里插入图片描述

接下来你在新的分支上进行了工作并进行了几次提交。这样会让iss53分支向前移动。

在这里插入图片描述

假设此时需要你紧急修复一个补丁,此时你就需要切换到master分支。

需要注意的是,如果你的工作目录或者暂存区存在着未提交的更改,并且这些更改与你要切换的分支冲突,Git就不允许你切换分支。在切换分支时,最好保证一个干净的工作区域。假设我们已经提交了修改,然后切换到master分支。

此时的状态就和处理iss53之前的状态一样。此时你可以基于master分支重新创建一个分支来修复补丁。

git checkout -b hotfix

然后进行你的工作进行提交。

在这里插入图片描述

当你测试你的补丁无误后,你可以将其合并到maser分支,以便部署到生产环境。使用git merge命令来完成上述操作。

这里需要注意的是master处于我们要合并分支的上游。所以Git会将master分支指针向前移动。

在这里插入图片描述

当你部署完这次的修改之后。你想要回到之前的工作。你可以切换到未完成的iss53分支继续你的工作。在这之前你可以删除hotfix分支 ,因为它用不着了,因为他和master指向的位置相同。

你可以使用git branch -d hotfix来删除分支,然后使用git checkout iss53继续你的工作
在这里插入图片描述

需要注意的是iss53分支并不包含你在hotfix分支上所作的工作,如果你需要把上述的修复工作并入iss53,就需要执行git merge master使得master分支合并到iss53中。或者最后将你的修改合并到master分支。

基本的合并操作

假设现在iss53的工作已经完成,可以合并回master分支了。现在只需要切回到master分支,并执行git merge操作即可。

git checkout master

git merge iss53

在这里插入图片描述

这次的合并有一点的不同,开发历史从最早的祖先发生了分叉。master分支并不是iss53分支的直接祖先,因此Git必须做一写额外的工作。

与之前简单的移动指针不同,Git会基于三方合并的结果创建一个新的快照。然后创建一个提交指向新建的快照。这个操作叫做 合并提交 。特殊性在于它拥有不止一个父提交。

在这里插入图片描述

Git会自己判断最优的共同祖先并将其作为合并基础。这个时候如果你用不到iss53分支,你就可以将其删除。

基本的合并冲突解决

有时候合并并不会这么顺利,如果两个分支同事修改了同一个文件的同一部分内容,Git就没办法干净的合并这部分内容,Git也不清楚你需要的是那次修改。这个时候就需要你手动的进行解决。

假设基于master分支创建了一个test分支,随后在分别在两个分支修改并进行提交,然后需要合并test分支到master。

在这里插入图片描述

Git并没有自动创建新的合并提交。它会暂停整个合并过程,等待你来解决冲突。

要查看哪些文件没有合并,你可以使用git status来查看

On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")

可以看到README.md还没有合并。

任何存在着未解决的合并冲突的文件都会显示成未合并状态。Git会给这些文件添加标准的待解决冲突标记,以便你手动打开这些文件来解决冲突。

打开文件你可以看到下面的区域

<<<<<<< HEAD
master commit
=======
test commit
>>>>>>> test

当前分支的内容显示在上半部分,test分支的在下半部分。此时你就可以根据实际情况来选择你想要保留的内容,假如两个修改我们都进行保留。

master commit
test commit

我们选择删除多余的部分。

当你解决冲突之后,使用git add将文件加入暂存区,然后commit来完成此次合并提交。

默认的提交信息如下,如果你想要提供更多的信息,你也可以添加这次合并的细节。

Merge branch 'test'

# Conflicts:
#       README.md
#
# It looks like you may be committing a merge.
# If this is not correct, please run
#       git update-ref -d MERGE_HEAD
# and try again.


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
#       modified:   README.md
#

这样你就完成了合并。此时文件的内容就是你刚才保留的内容。

远程分支

远程分支指的是指向远程仓库的分支的指针。这些指针存在于本地并且无法被移动。当你与服务器进行任何网络通信时,它们会自动更新。

远程分支的表示形式为(remote)/(branch)。remote指的是远程仓库的名称。

假设你有远程的Git仓库。然后你将其内容克隆到了本地。Git的克隆命令会把这台服务器命名为origin,并拉取他的全部数据,然后会在本地创建指向服务器上master的指针,并命名为origin/master,Git接着也会帮你创建你自己的本地master分支。刚开始这两个指针指向同一个位置。

在这里插入图片描述

假设你在master上进行了工作,那么你的master指针就会向前移动。但是origin/mastet并不会发生移动,就算别人给远程服务器推动了数据,只要你不更新,那么你的远程指针在本地就不会发生移动。

在这里插入图片描述

要与服务器通信,需要执行git fetch origin命令,这条命令会查询 'origin’对应的服务器地址,并从服务器取得所有本地尚未包含的数据,然后更新本地数据库,最后把origin/mastert指针移动到最新的上面去。
在这里插入图片描述

然后你可以执行merge操作,将其合并到你的master分支。

如果有多个远程服务器(一个项目但是有多个仓库)。你可以使用git remote add将新的远程服务器添加到正在进行的项目中。

然后执行git fetch 新仓库名获取服务器上本地不存在的数据。如果远程仓库的内容本地都有,Git并不会真正拉取数据,只会创建名为 远程仓库/master的远程分支。指向此仓库的最新提交。

推送

当你需要和别人共享某个分支上的工作成果时,就要把它推送到某一个具有写权限的远程仓库。你的本地分支并不会自动同步到远程仓库,必须显式地推送你想要共享的分支。

你可以使用git push remote branch命令

git push gst master
Enumerating objects: 31, done.
Counting objects: 100% (31/31), done.
Delta compression using up to 8 threads
Compressing objects: 100% (29/29), done.
Writing objects: 100% (29/29), 1.12 MiB | 22.99 MiB/s, done.
Total 29 (delta 4), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (4/4), completed with 1 local object.
To https://github.com/tanruike/gitstudy.git
   e0b2341..c9a8e1c  master -> master

上述的命名就是把本地的master分支推送到gst的master分支上。

如果你不想将远程分支的名称和本地相同。你可以使用git push remote branch:newname

跟踪分支

基于远程分支创建的本地分支会自动成为跟踪分支。

跟踪分支是远程分支直接关联的本地分支。如果你正处在一个跟踪分支上并键入git push,Git就会知道将数据推送到远程服务器的那个分支。同样git pull也会知道从那个服务器上拉取数据,并与本地分支进行合并。

当你克隆一个远程仓库时,Git默认情况下会自动创建跟踪远程origin/master分支的本地master分支。当然你也可以设置成不跟踪master分支,即执行git chechout -b [branch] [remote/branch]。对于这个命令Git提供了一种简略方式。git checkout --track origin/branch

当你创建分支时,如何你创建的分支名和远程分支名称一致,Git会帮你创建粉棕分支。

当你需要给本地已经存在的分支设置跟踪分支,或者更改本地分支对应的远程分支,可以使用git branch -u origin/branch 来设置任意的远程分支。

你可以使用 -vv选项来查看已经设置了哪些跟踪分支。

git branch -vv

拉取

git fetch会拉取本地没有的远程分支的所有数据。但这条命令不会更改本地目录,它只会从服务器读取数据,然后让你自己合并。你可以使用git merge origin/branch将拉取到的远程分支数据合并到你自己的本地分支。

你也可以使用git pull命令,这个命令相当时fetch之后紧跟着执行了merge操作。

删除

你可以使用git push origin --delete branch命令来删除你不需要的远程分支。

以上命令照顾只是删除了远程服务器上的分支指针,Git会保留数据一段时间,知道下一次出发垃圾回收。所以,即便误删了分支,一般来说也很容易进行恢复。

相关文章:

  • 【jvm系列-04】精通运行时数据区共享区域---堆
  • 【机器学习】P10 从头到尾实现一个线性回归案例
  • 四个常见的Linux面试问题
  • 【面试】TCP、UDP、Socket、HTTP网络编程面试题
  • Mysql查询截取分析_慢查询日志
  • Qt 中的信息输出机制:QDebug、QInfo、QWarning、QCritical 的简单介绍和用法
  • 50 Projects 50 Days - Expanding Cards 学习记录
  • Go语言精修(尚硅谷笔记)第十一章
  • 【EHub_tx1_tx2_E100】不止科技NVISTAR ROC 300激光雷达Ubuntu18.04+ROS1ROS2 评测
  • WebRTC API
  • 大数据之Spark开发环境准备
  • 什么是Java运算?Java运算好学吗?
  • abaqus子程序vumat安装使用
  • 【Java项目】Minio的安装部署以及SpringBoot整合Minio
  • 架构设计第一讲:架构设计相关面试题汇总
  • 【mysql】环境安装、服务启动、密码设置
  • 【跃迁之路】【519天】程序员高效学习方法论探索系列(实验阶段276-2018.07.09)...
  • Docker: 容器互访的三种方式
  • in typeof instanceof ===这些运算符有什么作用
  • iOS 颜色设置看我就够了
  • JavaScript DOM 10 - 滚动
  • JavaScript创建对象的四种方式
  • Js基础——数据类型之Null和Undefined
  • linux安装openssl、swoole等扩展的具体步骤
  • Median of Two Sorted Arrays
  • ng6--错误信息小结(持续更新)
  • Python十分钟制作属于你自己的个性logo
  • 阿里云前端周刊 - 第 26 期
  • 创建一个Struts2项目maven 方式
  • 大数据与云计算学习:数据分析(二)
  • 那些被忽略的 JavaScript 数组方法细节
  • 前端之Sass/Scss实战笔记
  • 设计模式(12)迭代器模式(讲解+应用)
  • 使用Swoole加速Laravel(正式环境中)
  • 算法---两个栈实现一个队列
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 在weex里面使用chart图表
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • ​一些不规范的GTID使用场景
  • (2009.11版)《网络管理员考试 考前冲刺预测卷及考点解析》复习重点
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • /usr/bin/python: can't decompress data; zlib not available 的异常处理
  • @property @synthesize @dynamic 及相关属性作用探究
  • [ 常用工具篇 ] AntSword 蚁剑安装及使用详解
  • [BUUCTF 2018]Online Tool
  • [BZOJ2281][SDOI2011]黑白棋(K-Nim博弈)
  • [C/C++]数据结构 堆的详解
  • [C++]STL之map
  • [CTO札记]盛大文学公司名称对联
  • [ffmpeg] x264 配置参数解析
  • [GN] 后端接口已经写好 初次布局前端需要的操作(例)
  • [IE9] IE9 Beta崩溃问题解决方案