Git——分支管理(2)

提示:图床在国外且动图比较多的情况下,需要时间加载。

目录:

Git基础

关于Git的基础请参考上一篇基础的教程,这里主要是关于Git的分支管理的内容,在讲解完成之后会以实战的形式让各位更好的理解如何使用Git这一个强大的工具。

Git的分支与HEAD

Git的存储机制

当你发起提交的时候,Git存储的是提交对象,其中包含了指向暂存区快照的指针。

Git在仓库区一般会这样保存对象:

graph LR
A("提交对象") --> C("Tree对象") -->B("Blob1对象")
C-->D("Blob2对象")

  • 提交对象,一次提交包含着元数据以及指向项目根目录的树对象的指针,以便需要的时候重新创建这次快照。

  • Blob 对象,只用于存储单个文件内容,一般都是二进制的数据文件,不包含任何其他文件信息,比如不包含文件名和其他元数据,可以同时有多个。

  • Tree 对象,对应文件系统的目录结构,里面主要有:子目录 (tree),文件列表 (blob),文件类型以及一些数据文件权限模型等。

Git将文件的快照存储为对象,每个对象都有一个唯一的哈希值。当文件发生变化时,Git会计算新文件的哈希值,并将其作为新对象存储在对象数据库中。当Git检测到两个文件版本相似时,它会计算差异(即两个版本之间的不同之处),并将这些差异作为一个新的对象存储。这个差异对象会引用原始文件的哈希值,以便可以重构原始文件的内容。

Git的分支指针

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

举个例子:

graph LR
H("HEAD") -->G
G("master") ==> Commit_C
direction LR
subgraph Commit_A
direction TB
A("提交对象A") --> B("文件对象A")
end
subgraph Commit_B
direction TB
C("提交对象B") --> D("文件对象B")
end
Commit_A --> Commit_B
subgraph Commit_C
direction TB
E("提交对象C") --> F("文件对象C")
end
Commit_B --> Commit_C

也就是说分支其实就是指针,指针指向某一次提交。其中比较特殊的是HEAD指针,是用来指向当前分支指针的指针,表示当前在master分支上。

Git的远程仓库

远程分支是指远程仓库的分支指针,这些指针存在于本地且无法被移动,当你与服务器进行网络通信的时候,他会自动更新,也就是使用pull等命令的时候,远程分支有点像书签,会提示你上一次连接服务器时远程仓库中每个分支的位置。

graph LR
G("master") ==> Commit_C

direction LR
subgraph Commit_A
direction TB
A("提交对象A") --> B("文件对象A")
end
subgraph Commit_B
direction TB
C("提交对象B") --> D("文件对象B")
end
Commit_A --> Commit_B
subgraph Commit_C
direction TB
E("提交对象C") --> F("文件对象C")
end
Commit_B --> Commit_C
H("remote") ==> Commit_C

remote指针同master指针一样,都是一个指针而已。

Git的远程分支管理

合并冲突

Fast-forward操作指的是当前分支所在位置是在已有要合并分支的后面,打个比方:

graph LR
G("main") ==> Commit_B

direction LR
subgraph Commit_A
direction TB
A("提交对象A") --> B("文件对象A")
end
subgraph Commit_B
direction TB
C("提交对象B") --> D("文件对象B")
end
Commit_A --> Commit_B
subgraph Commit_C
direction TB
E("提交对象C") --> F("文件对象C")
end
Commit_B --> Commit_C
H("orgin/main") ==> Commit_C

main节点落后于服务器上的main(以后都称之为origin/main),此时执行Fast-forward操作就是把服务器的远程分支合并到main之上,此时main指针会向前走一个,这样的操作就是Fast-forward。

合并之后结果如下:

graph LR
G("main") ==> Commit_C

direction LR
subgraph Commit_A
direction TB
A("提交对象A") --> B("文件对象A")
end
subgraph Commit_B
direction TB
C("提交对象B") --> D("文件对象B")
end
Commit_A --> Commit_B
subgraph Commit_C
direction TB
E("提交对象C") --> F("文件对象C")
end
Commit_B --> Commit_C
H("orgin/main") ==> Commit_C

merge操作指的是在本地分支和远程分支在同一个基点产生分歧的时候如下图所示:

graph LR
G("main") ==> Commit_B

direction LR
subgraph Commit_A
direction TB
A("提交对象A") --> B("文件对象A")
end
subgraph Commit_B
direction TB
C("提交对象B") --> D("文件对象B")
end
Commit_A --> Commit_B
subgraph Commit_C
direction TB
E("提交对象C") --> F("文件对象C")
end
Commit_A --> Commit_C
H("orgin/main") ==> Commit_C

在你的第一次提交之后本地已有个第二次提交,远程也有一个基于第一次提交的远程提交,此时就需要merge将两次提交合并成新的提交:merge_commit,来进行合并。

graph LR

direction LR
subgraph Commit_A
direction TB
A("提交对象A") --> B("文件对象A")
end

subgraph Commit_B
direction TB
C("提交对象B") --> D("文件对象B")
end

subgraph Commit_C
direction TB
E("提交对象C") --> F("文件对象C")
end

subgraph merge_commit
direction TB
G("提交对象C") --> H("文件对象C")
end

Commit_A --> Commit_B
Commit_A --> Commit_C
Commit_C --> merge_commit
Commit_B --> merge_commit
merge_commit --> J("orgin/main")
merge_commit --> I("main")

Rebase操作指的是在上述情况之上直接将某个分支之上所有提交的更改在另一个分支上重现一遍。

为了Rebase需要先找到两个要整合的分支的共同祖先,然后取得当前所在分支的每次提交引入的变更,并把这些变更保存成文件,这之后将当前分支重置为要整合到的分支,然后在该分支上依次引入之前保存的每个更改。

在合并之前分支已成这个样子:

graph LR
G("main") ==> Commit_B

direction LR
subgraph Commit_A
direction TB
A("提交对象A") --> B("文件对象A")
end
subgraph Commit_B
direction TB
C("提交对象B") --> D("文件对象B")
end
Commit_A --> Commit_B
subgraph Commit_C
direction TB
E("提交对象C") --> F("文件对象C")
end
Commit_A --> Commit_C
H("orgin/main") ==> Commit_C

当我们想把远程分支的内容整合到main分支之上(就是将orgin/main rebase到 main)的话就会有如下情况,合并之后:

graph LR
G("main") ==> Commit_B

direction LR
subgraph Commit_A
direction TB
A("提交对象A") --> B("文件对象A")
end
subgraph Commit_B
direction TB
C("提交对象B") --> D("文件对象B")
end
Commit_A --> Commit_B
subgraph Commit_C+
direction TB
E("提交对象C") --> F("文件对象C")
end
Commit_B --> Commit_C+
H("orgin/main") ==> Commit_C+

可以看到这里实际上变化成了Fast-forward所处理的情况,再次执行Fast-forward(merge)即可完成操作。

远程分支和本地仓库的冲突处理

查看当前分支情况

查看当前分支信息:

(base) neo@NeoNeuxs:~/git_test/Essays$ git branch
* main
test

加上参数v可以显示当前每个分支的最新提交和其的提交信息,加上a则会包括当前的远程分支:

(base) neo@NeoNeuxs:~/git_test/Essays$ git branch -av
* main dd0258f [ahead 3] Merge branch 'test'
test 8eafb31 update git rm pycharm
remotes/origin/HEAD -> origin/main
remotes/origin/main e5d18d7 del copy

*代表了HEAD指针所处位置,也就是当前所在的分支。 update git rm pycharm就是对应的提交信息。e5d18d7就是提交对象的散列值。

如果要查看已并入当前的分支的所有分支信息可以使用:

(base) neo@NeoNeuxs:~/git_test/Essays$ git branch -av --merged
* main dd0258f [ahead 3] Merge branch 'test'
test 8eafb31 update git rm pycharm
remotes/origin/HEAD -> origin/main
remotes/origin/main e5d18d7 del copy

相反的,显示没有并入分支的命令可以使用:

(base) neo@NeoNeuxs:~/Desktop/Essays$ git branch -av --no-merged
remotes/origin/HEAD -> origin/main
remotes/origin/main e5d18d7 del copy

但是以上的方式都不够明显展示分支的变化及历史,所以推荐还是使用如下命令:

git log --graph --decorate --oneline --all

如图展示了两个本地的仓库,其中二者的远程仓库是保持一致的,也就是说二者都有一样的远程信息,但是本地却差别很大,我们的工作就是尽量合并二者的内容并优化分支结构。

在看到二者的分支图的时候我们就能看到:

简单来说我们的解决方案如下

首先rebase仓库A,将仓库A的main分支rebase到远程上然后提交到服务器。

然后将远程新的内容pull到仓库B,再进行一次rebase然后将内容上传到服务器。最终再pull仓库A、仓库B、服务器就可以实现同步。

(base) neo@NeoNeuxs:~/Desktop/Essays$ git rebase origin/main
Successfully rebased and updated refs/heads/main.

reabase之后:

由于仓库B还有部分内容没有提交,这里就再提交一次:

(base) neo@NeoNeuxs:~/git_test/Essays$ git commit -a -m "finished git 1"
[main e2fdc30] finished git 1
1 file changed, 61 insertions(+), 6 deletions(-)

提交之后分支如下,本次提交并不影响合并方案。

git pull --rebase
# 等价于:
git fetch origin/main
git rebase origin/main

其中的分支变化如下:

关于rebase的变化的解释:

Git会将该合并提交的更改拆分成多个单独的提交,并将它们重新应用在远程分支的顶部。这意味着原始的合并提交(在这个例子中是 dd0258f)不再存在于变基后的分支历史中。

在git的变基过程遵循如下方式,在两个分支的共同祖先开始提取被变基分支(master)上的有效修改,然后将被变基分支的有效更改指向基分支(orgin/master)的最新提交。

245df5e提交和e5d18提交完全一致,并不是有效提交,所以被舍弃。

需要注意的是,没有手动推送上图中test分支,服务器上实际上是没有245df5e,8eafb31的信息的。

参考:精通Git(第二版)

Git——分支管理(2)的更多相关文章

  1. GIT分支管理模型

    GIT分支管理模型 link: git-branching-model 主分支(Main branches) 项目两个常驻分支: master 主干分支(锁定),仅用于发布新版本,平时不能在上面干活, ...

  2. Git 分支管理是一门艺术

    转载: Git 分支管理是一门艺术 1 要确保:团队成员从主分支(master)获得的都是处于可发布状态的代码,而从开发分支(develop)应该总能够获得最新开发进展的代码. 2 "辅助分 ...

  3. Git 分支管理详解

    大纲: 1.前言 2.创建分支 3.切换分支 4.合并分支(快速合并) 5.删除分支 6.分支合并冲突 7.合并分支(普通合并) 8.分支管理策略 9.团队多人开发协作 10.总结 注,测试机 Cen ...

  4. Git分支管理[三]

    标签(linux): git 笔者Q:972581034 交流群:605799367.有任何疑问可与笔者或加群交流 git分支管理命令 git branch #创建分支 git branch -v # ...

  5. Git—分支管理

    Git—分支管理 分支学习:branch称为分支,默认仅有一个名为master的分支.一般开发新功能流程为:开发新功能时会在分支dev上进行,开发完毕后再合并到master分支. branch相关常用 ...

  6. [转载]理解 Git 分支管理最佳实践

    原文 理解 Git 分支管理最佳实践 Git 分支有哪些 在进行分支管理讲解之前,我们先来对分支进行一个简单的分类,并明确每一类分支的用途. 分支分类 根据生命周期区分 主分支:master,deve ...

  7. 02: git分支管理

    目录:GIT其他篇 01: git & github 02: git分支管理 目录: 1.1 Git常用命令 1.2 主要分支(保护分支) 1.3 特性分支:feature (开发分支合并到d ...

  8. git 分支管理——多人协作

    git 分支管理--多人协作 一般一个项目有一个master主分支,还有一个develop开发分支.主要是在develop分支上协作开发,然后merge合并到master主分支上. 当从远程仓库克隆时 ...

  9. Git分支管理及合并

    Git分支管理   建立分支 git branch [name]   切换到分支 git checkout [name]   查看有哪些分支 git branch   比较分支 git diff [b ...

  10. git学习------>Git 分支管理最佳实践

    ps:本文转载于 : https://www.ibm.com/developerworks/cn/java/j-lo-git-mange/index.html Git 是目前最流行的源代码管理工具.大 ...

随机推荐

  1. 嵌入式C语言设计学习之C语言回顾

    C的基本语法-回忆 1.C的结构 C语言的结构还是以函数为主体,通过其他资源的添加来实现高级语言逻辑.所有的操作都是基于主函数展开的.以主函数为顺序列表,其他函数作为功能模块,组成一个完整的系统.所以 ...

  2. 浅谈JVM整体架构与调优参数

    本文分享自华为云社区<[性能优化]JVM整体架构与调优参数说明>,作者: 冰 河. JVM的分类 这里,我们先来说说什么是VM吧,VM的中文含义为:虚拟机,指的是使用软件的方式模拟具有完整 ...

  3. #换根dp#洛谷 2986 [USACO10MAR]Great Cow Gathering G

    题目 分析 处理出所有点到根节点的答案,然后换根依次求最小值 代码 #include <cstdio> #include <cctype> #define rr registe ...

  4. Bootstrap实战 - 单页面网站

    一.介绍 单页面结构简单.布局清晰,常常用来做手机 App 或者某个产品的下载介绍页面.现在,展示型网页整体趋向于单页网站设计,这样一次性把核心信息展现出来,对于用户来说更加直观和简单,能够快速了解一 ...

  5. 【FAQ】调用应用内支付SDK时报错,如何用tag对问题进行排查和分析

    华为应用内支付服务(In-App Purchases,IAP)为开发者提供便捷的应用内支付体验和简便的接入流程.开发者的应用集成IAP SDK后,调用IAP SDK接口,启动IAP收银台,即可实现应用 ...

  6. DEB打包教程

    一.deb简介 deb是一种安装包的格式,linux上常见的安装包主要是deb.rpm 二.deb简单使用 # deb安装 sudo dpkg -i webcamera_1.0_amd64.deb # ...

  7. mybatis复习(一)

    原始连接数据库步骤: 1.导入JDBC驱动包 Class.forName("com.mysql.jdbc.Driver"); 2.获取连接对象(连接目标数据库) Connectio ...

  8. 构建动态交互式H5导航栏:滑动高亮、吸顶和锚点导航技巧详解

    功能描述 产品要求在h5页面实现集锚点.吸顶及滑动高亮为一体的功能,如下图展示的一样.当页面滑动时,内容区域对应的选项卡高亮.当点击选项卡时,内容区域自动滑动到选项卡正下方. 布局设计 css 布局 ...

  9. jenkins 持续集成和交付——pipeline(五)

    前言 整理一下pipeline. 正文 介绍 什么是pipeline呢? 根据前面的所得,我们知道,以前都是模板形式,但是如果有些复杂的项目,需要用更加自定义的写法,那么就有了pipeline,也就是 ...

  10. 力扣118(java)-杨辉三角(简单)

    题目: 给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行. 在「杨辉三角」中,每个数是它左上方和右上方的数的和. 示例 1: 输入: numRows = 5输出: [[1], ...