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

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.dtcms.com/a/309598.html

相关文章:

  • ansible 在EE 容器镜像中运行
  • C primer plus (第六版)第十章 编程练习第7,8,9,10,11题
  • Linux基本服务——web服务解析
  • 如何管理数据足迹,实现SAP S/4HANA的无缝迁移
  • Solana: 逐行解读 solana-test-validator 输出, 本地节点日志完全指南
  • oracle备库主机断电重启后IO异常报错
  • 【C#学习Day16笔记】XML文件、 事件Event 、Json数据
  • Sqlserver备份恢复指南-完整备份恢复
  • 从零到英雄:掌握神经网络的完整指南
  • Qt Quick 自定义组件开发
  • 江协科技STM32 11-4 SPI通信外设
  • Android SDK 版本差异与兼容方案:从适配到实践
  • gitlab 开发人员无法创建分支,管理员配置分支权限
  • flutter-boilerplate-project 学习笔记
  • 嵌入式学习笔记-MCU阶段--DAY09
  • STM32-ESP8266Wi-Fi模块使用USART实现通信/创建AP和STA模式配置教程(寄存器版)
  • 从0开始学习R语言--Day64--决策树回归
  • 流式编程的中间操作
  • 机器学习sklearn:随机森林的决策树
  • 低通滤波器的原理以及作用
  • C# 引用外部项目
  • 切比雪夫不等式
  • 网页从点击到显示:前端开发视角下的旅程
  • 在SQL SERVER 中如何用脚本实现每日自动调用存储过程
  • 大模型开发框架LangChain之构建知识库
  • 高速公路桥梁安全监测系统解决方案
  • 技术栈:基于Java语言的搭子_搭子社交_圈子_圈子社交_搭子小程序_搭子APP平台
  • 安全专家发现利用多层跳转技术窃取Microsoft 365登录凭证的新型钓鱼攻击
  • 【C#学习Day14笔记】泛型、集合(数组列表Arraylist、列表list)与字典
  • Python 中的可迭代、迭代器与生成器——从协议到实现再到最佳实践