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

Git 进阶使用

一、git merge

1、git merge 原理

  1. 找到当前分支的 HEAD 提交
  2. 找到要合并的 feature 分支的 HEAD 提交
  3. 找到这两个分支的最近公共祖先
  4. 三方合并
    1. 如果这三份内容在某个文件的某处不一致 → Git 自动尝试合并
    2. 如果某一行在两个分支中都被不同地修改过 → 冲突,你必须手动解决

2、merge 遇到的坑——手动改动

假如你有两个分支,分别是 main 和 feature.

main:hello = "world"feature:hello = "earth"

这两个分支都改了 hello 的值,然后这两个分支的最近公共祖先提交的快照都是没有 hello 的。如果 main 和 feature 合并,git 以最近公共祖先为基础,分别比较这两个分支的 HEAD 节点有什么地方跟公共祖先是不一样的。然而 git 会发现这两个 HEAD 都对 hello 改过,所以就不知道该应用哪个更改,于是就会这样:

<<<<<<< HEAD
hello = "world"
=======
hello = "earth"
>>>>>>> feature

然后你就要手动更新这个文件来告诉 git 应该应用哪个分支的更改。 

二、git fetch

1、git fetch 原理

  1. git 先看当前分支和要 fetch 的分支有没有最近公共祖先节点
  2. 如果有最近公共祖先节点,那就对比远程和本地有哪些节点是不同的,然后在本地的最近公共祖先节点的基础上新建一个分支,然后把远程和本地比不同的节点都加到这个分支里
  3. 如果没有最近公共祖先节点,就新建一个分支来存远程分支的所有节点,但这个分支跟本地的分支是平行的,不相交的。

三、git rebase

1、git rebase 原理

  1. 找到这两个分支的最近公共祖先
  2. 把 (公共祖先 ,  当前分支 HEAD 节点 ] 区间的所有节点一个个地通过复制更改的方式追加到 rebase 那个分支的末尾。

注意:新复制的节点的 commit id 已经和原来的不一样了,所以旧的节点看起来就消失了。但其实旧的节点并没有消失,它会临时被 git reflog 引用,此时节点的引用计数不为 0;所以你依然可以通过 git reflog 来恢复旧的节点。但如果过了一定时间后,git reflog 就会收回对这个节点的引用,此时节点的引用计数为 0,节点就会被销毁了。

2、git rebase VS git merge

综合来讲,git rebase 这个指令比较鸡肋。就是虽然可以把一个分支的所有节点追加到另一个分支上,但是追加的部份是这两个分支共有的,浪费当前分支。

四、git cherry-pick

1、git cherry-pick 有什么用

如果你想把另一个分支的某个特定节点相对于前一个节点的更改应用到当前分支的 HEAD 节点上,那么 git cherry-pick 是最适合的。

2、git cherry-pick 原理

cherry-pick 之前:A--B--C <- main\\  D--E--F <- feature在 main 分支 cherry-pick E 后:A--B--C--E' <- main\\  D--E--F <- feature

这里我们以在 main 分支 cherry-pick E 为例:

  1. git 先算出来 diff(E, D) 的部份
  2. git 在最新提交(C节点)的上下文定位到要加 diff(E, D) 的地方
  3. 如果定位到地方,就直接新建一个节点,应用这个 diff。
  4. 如果定位不到,就人工编辑,然后再执行 cherry-pick --continue

3、git cherry-pick 定位不到的情况

  • 同一段代码被改动但方式不同

在 feature E 中
- return fetchDataFromDB();
+ return fetchDataFromAPI(); // E 相对 D 比改为 fetch API在 main C 中
- return fetchDataFromDB();
+ return fetchDataFromCache(); // C 相对 B 比改为 fetch cache此时会定位失败,因为 git 在 C 中找不到 return fetchDataFromDB() 了
  • 当前分支的最新节点找不到上下文
feature E 中,跟 D 相比,在 func4() 下面加了 func5():func4() {}
func5() {}main C 中,没有 func4() 那堆代码此时因为 git 找不到 func4(),就不知道 func5() 应该加到哪个位置,定位失败,所以会造成冲突

4、在 merge commit 处如何 cherry-pick 

A -- B -- C ------- G(HEAD)  <- main\           /D -- E -- F    <- feature

假如我们要 cherry-pick G,那么 git 因为不知道取 diff(G, F) 还是 diff(G, C),所以会报错。不过我们可以通过 git cherry-pick -m 1 G 来让 git 取 diff(G, F),git cherry-pick -m 2 G 来让 git 取 diff(G, C).

五、git reset

1、git reset --soft

HEAD 指针回退,工作区的内容不会变化。但暂存区存的东西变成了新 HEAD 后面的当前分支的所有节点内容相对于 HEAD 的 diff。然后新 HEAD 当前分支后面的节点都被移除。

所以在 soft & mixed & hard 当中,有且只有 soft 才能做到把多个节点合成一个节点。mixed 和 hard 都不行。

2、git reset --mixed

HEAD 指针回退,工作区的内容不会变化。但暂存区并没有存后面当前分支所有节点相对于新 HEAD 的 diff。然后新 HEAD 当前分支后面的节点都被移除。

3、git reset --hard

HEAD 指针回退,工作区的内容会变成当前新 HEAD 的内容;暂存区没有存后面当前分支所有点相对于新 HEAD 的 diff,然后新 HEAD 当前分支后面的节点都被移除。

4、从分支开始处向前 reset

HEAD 回退,但 git 并不会删除任何分支,所以分支开始处不会被删;取而代之的是 git 会把这个交点直接改为不属于当前分支的节点,但不会删除这个节点。然后新 HEAD 节点成为新的分支开始处节点。(这样可以确保其他分支的历史内容不会变化)。

reset 前(当前在 main 分支):A -- B(HEAD)           <- main\           D -- E -- F    <- feature在 main 分支 reset 后:A(HEAD) <- main\          B -- D -- E -- F <- feature此时 B 节点已不属于 main 分支的了,但 B 节点不会被删

5、从 merge commit 向前 reset

HEAD 回退,并且虽然这个节点是个 merge commit,但 git 会删掉这个节点。可能是因为删了这个节点并不会引起分支的删除,所以就删掉了。

reset 前(当前在 main 分支):A -- B -- C ------- G(HEAD)  <- main\           /D -- E -- F    <- feature在 main 分支 reset 后:A -- B -- C(HEAD)   <- main\          D -- E -- F <- feature

六、git revert

1、git revert 基本原理

git revert 某节点的原理是:git 会算这个节点跟前一个节点的 diff,然后新建一个节点,内容是 revert 节点的内容 - diff。

原来的提交历史:A -- B -- C -- D (HEAD)|  git revert C|现在的提交历史:A -- B -- C -- D -- C' (HEAD)其中 C' 是 C - diff(C, B) 后的内容

2、对一个 merge-commit revert 怎么解决

A -- B -- C ------- G (main)\           /D -- E -- F (feature)

就像这个例子,我要 git revert G 的话,git 就会感到很疑惑:G 相对 F 和 C 都有 diff,那我新建节点时应该减哪个分支的 diff 呢,于是就拿不准就报错了。但幸运的是,我们可以用 git revert -m <数字> G 来告诉 git 要保留哪个分支的 diff,其中数字就是分支的编号,然后 1 就是 main 分支。

所以如果执行 git revert -m 1 G 的话,意思就是保留 diff(G, C);然后新建的节点内容就是 G - diff(G, F) 了。

七、git stash

1、git stash 原理

git 会保留当前工作区的所有代码和暂存区的所有更改,然后工作区会变成 HEAD 的内容,暂存区没有更改。

2、git stash pop & git stash apply

git stash pop 会应用最近一次 stash,然后删除最近的 stash;git stash apply 应用最近一次 stash,但不删除最近的 stash.

八、git flow 规范

1、feature

用来开发某个功能的分支。每个 feature 实现完,都要合并到 develop 里。

2、develop

所有功能的集成分支。

3、release

当 develop 的某个提交足够成熟了,才分出 release 分支进行 debug,如果 release 没问题,就同时向 develop 跟 master 合并分支。

4、hotfix

紧急处理 bug 的分支,一旦处理完同时向 master 和 develop 合并。

5、master 

给用户用的版本。

http://www.xdnf.cn/news/16778.html

相关文章:

  • 算法篇----位运算
  • 【Mysql】字段隐式转换对where条件和join关联条件的影响
  • Oracle EBS 缺少adcfgclone.pl文件
  • 链接脚本中. = ALIGN(4);的作用?
  • 北斗变形监测在地质灾害监测中的应用
  • 浅谈低代码平台涉及的一些技术选型
  • AI Agent 视角:可执行程序的二进制格式,是一场「结构化语言」与「智能解析」的双向奔赴
  • UE5多人MOBA+GAS 番外篇:同时造成多种类型伤害,以各种属性值的百分比来应用伤害(版本二)
  • 流式编程的中间操作
  • linux编译基础知识-编译时路径和运行时路径
  • 在Idea中,配置maven
  • Galaxea机器人由星海图人工智能科技有限公司研发的高性能仿人形机器人
  • 【C语言】预处理详解
  • 高防服务器租用:保障数据安全
  • Nginx跨域问题与 MIME 类型错误深度排错指南:解决 MIME type of “application/octet-stream“ 报错
  • PyTorch分布式训练深度指南
  • 26数据结构-顺序表
  • 【数据结构与算法】21.合并两个有序链表(LeetCode)
  • 如何将消息转移到新 iPhone
  • 深入剖析Spring IOC容器——原理、源码与实践全解析
  • Linux---编辑器vim
  • 嵌入式学习笔记-MCU阶段-DAY10ESP8266模块
  • 初识微服务
  • 飞算 JavaAI 中 SQL 另存为脚本功能详解
  • ZKmall开源商城微服务架构电商平台:服务注册与配置中心设计
  • 充电桩与照明“联动”创新:智慧灯杆破解新能源基建难题
  • 微服务消息队列之RabbitMQ,深入了解
  • 【unity小技巧】封装unity适合2D3D进行鼠标射线检测,获取鼠标位置信息检测工具类
  • Java设计模式之行为型模式(解释器模式)实现方式详解
  • Elasticsearch 集群管理核心 API 指南:健康、状态、分片诊断与运维实战