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. JNDI注入分析

    JNDI介绍 JNDI(Java Naming and Directory Interface,Java命名和目录接口)是为Java应用程序提供命名和目录访问服务的API,允许客户端通过名称发现和查找 ...

  2. KingbaseES V8R6集群运维系列 -- connect_check_type参数

    案例说明: 在KingbaseES V8R6C7的版本中,repmgr.conf增加了connect_check_type参数可以指定参数值,默认值是'mix',以前版本为隐藏参数,默认值'ping' ...

  3. KingbaseES V8R6运维案例之---MySQL和KingbaseES字符串排序规则对比

    案例说明: 相同数据排序后查询,在MySQL和KingbaseES下得到的排序顺序不一致,本案例从MySQL和KingbaseES的排序规则分析,两种数据库排序的异同点. 适用版本: Kingbase ...

  4. 前端 Typescript 入门

    前端 Typescript 入门 Ant design vue4.x 基于 vue3,示例默认是 TypeScript.比如 table 组件管理. vue3 官网介绍也使用了 TypeScript, ...

  5. 深入理解HashMap和TreeMap的区别

    目录 简介 HashMap和TreeMap本质区别 排序区别 Null值的区别 性能区别 共同点 深入理解HashMap和TreeMap的区别 简介 HashMap和TreeMap是Map家族中非常常 ...

  6. Django集成layui 的 layedit 之图片上传接口

    # a.html <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  7. Qt数据结构-QString一:常用方法

    一.拼接字符串 拼接字符串有两种方法: +=  .  append QString s; s = "hello"; s = s + " "; s += &quo ...

  8. 异步、多线程、Java爬取某网站图片

    一.网页图片爬取类 package com.yhyl.utils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import or ...

  9. 掌握 xUnit 单元测试中的 Mock 与 Stub 实战

    引言 上一章节介绍了 TDD 的三大法则,今天我们讲一下在单元测试中模拟对象的使用. Fake Fake - Fake 是一个通用术语,可用于描述 stub或 mock 对象. 它是 stub 还是 ...

  10. 重新点亮linux 命令树————文件列表查看命令[二]

    前言 整理一下文件查看命令 正文 主要是pwd和ls命令 pwd 这个是一个非常常用的命令,在shell脚本中基本都有,表示的是当前目录. 这是一个非常简单,但是非常实用的命令. 通过使用pwd -- ...