Git笔记(16) 变基

  • 1. 整合分支
  • 2. 基本操作
  • 3. 指定目标分支
  • 4. 变基的风险
  • 5. 用变基解决变基
  • 6. 手动解决变基
  • 7. 整合原则

1. 整合分支

在 Git笔记(12) 分支使用 中提及到使用merge 来整合来自不同分支的修改

在 Git 中还有一种常见的方法:rebase 变基


2. 基本操作

回顾之前在 Git笔记(12) 分支使用 中的一个例子
会看到开发任务分叉到两个不同分支,又各自提交了更新

整合分支最容易的方法是 merge 命令
它会把两个分支的最新快照(C3C4)以及二者最近的共同祖先(C2)进行三方合并
合并的结果是生成一个新的快照(并提交)

然而还有一种方法:
可以提取在 C4 中引入的补丁和修改,然后在 C3 的基础上应用一次

在 Git 中,这种操作就叫做 变基
可以使用 rebase 命令将提交到某一分支上的所有修改都移至另一分支上
就好像“重新播放”一样

在上面这个例子中,运行:
先切到experiment分支,再变基

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

首先找到两个分支(当前分支 experiment、目标基底分支 master)的最近共同祖先 C2
然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件
然后将当前分支指向目标基底 C3
最后以此将之前另存为临时文件的修改依序应用

现在回到 master 分支,进行一次快进合并

$ git checkout master
$ git merge experiment


此时,C4’ 指向的快照就和上面使用 merge 命令的例子中 C5 指向的快照一样了

这两种整合方法的最终结果没有任何区别
但是变基 使得提交历史更加整洁

在查看一个经过变基的分支的历史记录时会发现
尽管实际的开发工作是并行的,但提交历史是一条直线没有分叉

无论是通过变基,还是通过三方合并
整合的最终结果 所指向的快照始终是一样 的,只不过提交历史不同罢了
变基是将一系列提交按照原有次序依次应用到另一分支上
而合并是把最终结果合在一起


3. 指定目标分支

在对两个分支进行变基时
所生成的“重放”并不一定要在目标分支上应用,可指定另外的一个分支进行应用

如:从一个特性分支里再分出一个特性分支的提交历史
创建了一个特性分支 server,为服务端添加了一些功能,提交了 C3C4
然后从 C3 上创建了特性分支 client,为客户端添加了一些功能,提交了 C8C9
最后,回到 server 分支,又提交了 C10

假设希望将 client 中的修改合并到主分支并发布
但暂时并不想合并 server 中的修改,因为它们还需要经过更全面的测试

这时就可以使用 git rebase 命令的 --onto 选项
master 分支上将在 client 分支里但不在 server 分支里的修改( C8C9)重放:

$ git rebase --onto master server client

以上命令的意思是:取出 client 分支,找出处于 client 分支和 server 分支的共同祖先之后的修改,然后把它们在 master 分支上重放一遍


现在可以快进合并 master 分支了

$ git checkout master
$ git merge client


这样即使提交历史简洁,并且不会影响 server 分支

接下来决定将 server 分支中的修改也整合进来
使用命令可以直接将特性分支(即本例中的 server)变基到目标分支(即 master)上

git rebase [basebranch] [topicbranch]

这样做能省去先切换到 server 分支,再对其执行变基命令的多个步骤

$ git rebase master server

server 中的代码被“续”到了 master 后面

然后就可以快进合并主分支 master 了:

$ git checkout master
$ git merge server

至此,clientserver 分支中的修改都已经整合到主分支里了
可以删除这两个分支:

$ git branch -d client
$ git branch -d server


4. 变基的风险

要用变基得遵守准则:

不要对在仓库外有副本的分支执行变基

变基操作的实质:
丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交

如果已经将提交推送至某个仓库,而其他人也已经从该仓库拉取提交并进行了后续工作
此时,如果用 git rebase 命令重新整理了提交并再次推送
同伴因此将不得不再次将他们手头的工作与你的提交进行整合
如果接下来还要拉取并整合他们修改过的提交,事情就会变得一团糟

来看一个在公开的仓库上执行变基操作所带来的问题
假设从一个中央服务器克隆然后在它的基础上进行了一些开发


然后,某人又向中央服务器提交了一些修改,其中还包括一次合并

现在抓取了这些在远程分支上的修改,并将其合并到本地的开发分支


接下来,这个人又决定把合并操作回滚,改用变基
继而又用 git push --force 命令覆盖了服务器上的提交历史

之后从服务器抓取更新,会发现多出来一些新的提交


结果十分尴尬
如果自己执行 git pull 命令抓取,将合并来自两条提交历史内容,生成一个新的合并提交


此时如果执行 git log 命令,会发现有两个提交(C6C4’)的作者、日期、日志相同
此外,如果将这一堆又推送到服务器上,实际上是将那些已经被变基抛弃的提交又找了回来
很明显对方并不想在提交历史中看到 C4C6
因为之前那个人就是把这两个提交通过变基丢弃的


5. 用变基解决变基

如果团队中的某人强制推送并覆盖了一些你所基于的提交
需要做的就是检查自己做了哪些修改,以及他们覆盖了哪些修改

实际上,Git 除了对整个提交计算 SHA-1 校验和以外
对本次提交所引入的修改计算了校验和—— 即 “patch-id

如果拉取被覆盖过的更新并将手头的工作基于此进行变基的话
一般情况下 Git 都能成功分辨出哪些是你的修改,并把它们应用到新分支上

如果遇到前面提到的情境:
有人推送了经过变基的提交,并丢弃了你的本地开发所基于的一些提交

如果不是执行合并,而是执行

git rebase teamone/master

Git 将会:

  • 检查哪些提交是我们的分支上独有的(C2,C3,C4,C6,C7)
  • 检查其中哪些提交不是合并操作的结果(C2,C3,C4)
  • 检查哪些提交在对方覆盖更新时并没有被纳入目标分支(只有 C2 和 C3,因为 C4 其实就是 C4’)
  • 把查到的这些提交应用在 teamone/master 上面

从而将得到与 将相同的内容又合并了一次,生成了一个新的提交 中不同的结果
如图 在一个被变基然后强制推送的分支上再次执行变基 所示:

要想上述方案有效,还需要对方在变基时 确保 C4’ 和 C4 是一样的
否则变基操作将无法识别,并新建另一个类似 C4 的补丁
而这个补丁很可能无法整洁的整合入历史,因为补丁中的修改已经存在于某个地方了


6. 手动解决变基

在本例中另一种简单的方法是使用 git pull --rebase 命令而不是直接 git pull
又或者可以手动完成这个过程,先 git fetch,再 git rebase teamone/master

如果习惯使用 git pull ,同时又希望默认使用选项 --rebase
可以执行这条语句来更改 pull.rebase 的默认配置

git config --global pull.rebase true

只要把变基命令当作是在推送前清理提交使之整洁的工具
并且只在从未推送至共用仓库的提交上执行变基命令,就不会有事

假如在那些已经被推送至共用仓库的提交上执行变基命令
并因此丢弃了一些别人的开发所基于的提交,那就麻烦了

如果你或你的同事在某些情形下决意要这么做,请一定要通知每个人执行命令

 git pull --rebase

这样尽管不能避免伤痛,但能有所缓解


7. 整合原则

变基 与 合并,到底哪种方式更好

首先讨论一下提交历史到底意味着什么

有一种观点认为,仓库的提交历史即是 记录实际发生过什么
这些痕迹就应该被保留下来,让后人能够查阅

另一种观点则正好相反,认为提交历史是 项目过程中发生的事
没人会出版一本书的第一版草稿,软件维护手册也是需要反复修订才能方便使用

现在,让回到之前的问题上来,到底合并还是变基好?
这并没有一个简单的答案
总的原则是,只对尚未推送或分享给别人的本地修改 执行变基操作清理历史
从不对已推送至别处的提交 执行变基操作
这样,才能享受到两种方式带来的便利


参考: git
以上内容,均根据git官网介绍删减、添加和修改组成


相关推荐:

Git笔记(15) 远程分支
Git笔记(14) 分支开发工作流
Git笔记(13) 分支管理
Git笔记(12) 分支使用
Git笔记(11) 分支简介


谢谢

Git笔记(16) 变基相关推荐

  1. git rebase(变基)—— Git 学习笔记 19

    git rebase(变基) 认识 git rebase 假设你现在基于远程分支"origin",创建一个叫"mywork"的分支. $ git checkou ...

  2. Git合并和变基简介:它们是什么,以及如何使用它们

    by Vali Shah 通过瓦利沙阿 Git合并和Git变基简介:它们做什么以及何时使用它们 (An Introduction to Git Merge and Git Rebase: What T ...

  3. Git 分支 - rebase 变基

    变基 在 Git 中整合来自不同分支的修改主要有两种方法:merge 以及 rebase. 在本节中我们将学习什么是"变基",怎样使用"变基",并将展示该操作的 ...

  4. Git rebase(变基)操作详解

    目录 简单变基操作 复现 变基 更复杂的变基 复现 变基 变基操作建议 简单变基操作 复现 先复现一个变基操作的现场,具体做法如下: (1)创建一个testrebase目录,在该目录下执行git in ...

  5. 两条命令让你的git轻松自动变基,学到了!

    大厂技术  高级前端  Node进阶 点击上方 程序员成长指北,关注公众号 回复1,加入高级Node交流群 作者:张京 链接:https://segmentfault.com/a/1190000040 ...

  6. GIT学习笔记(5):变基

    GIT学习笔记(5):变基rebase 变基 引入变基 在Git中整合来自不同分支的修改主要有两种方法:merge以及rebase. 整合分支最容易的方法是merge,他会把两个分支的最新快照以及两者 ...

  7. git使用变基方式同步远程和本地副本的代码同步方式

    一.确保你父分支的本地副本与项目主仓库中最新的提交同步 git pull --rebase 若有必要, 请显式指定参数.在使用 pull 命令更新一个分支的本地副本时,参数中远程连接和远程分支的名称通 ...

  8. git Rebase 变基 教程

    在上一节我们看到了,多人在同一个分支上协作时,很容易出现冲突.即使没有冲突,后push的童鞋不得不先pull,在本地合并,然后才能push成功. 每次合并再push后,分支变成了这样: $ git l ...

  9. 3.6 Git 分支 - 变基

    变基 在 Git 中整合来自不同分支的修改主要有两种方法:merge 以及 rebase. 在本节中我们将学习什么是"变基",怎样使用"变基",并将展示该操作的 ...

最新文章

  1. TCP/IP四层模型和OSI七层模型
  2. 计算机加入到域的注意事项
  3. [HNOI 2011]XOR和路径
  4. 检测电脑硬件的软件_为电脑DIY爱好者推荐一款非常实用的硬件测试软件
  5. 2019 Multi-University Training Contest 1 - 1004 - Vacation - 二分 - 思维
  6. 微电网日前优化调度 。算例有代码(2)
  7. Python django-restframework 前后端分离实现在线教育课程平台
  8. html表格右键可编辑,Bootstrap table右键功能实现方法
  9. AFURLRequestSerialization
  10. 删除AdminServer logs下的log文件后开启AdminServer报错
  11. 使用taskset命令来限制进程的CPU
  12. 汇编语言INC DEC JGE JG JLE JL JNE JE 都是什么意思
  13. Hi3518E_V200 SDK编译笔记 第一季
  14. 映美精(IMAGINGSOURCE)相机与OPENCV库(包含工程源码)
  15. 索尼笔记本笔记本计算机在哪里设置,索尼vaio笔记本怎么进bios设置|索尼笔记本进bios按什么键...
  16. 自制力宛若受到控制的火焰
  17. Java UTC时间戳
  18. CMake来编译OpenCV,细致教程
  19. 50个最有价值的数据可视化图表(推荐收藏)
  20. 计算机网络第一章学习通题目及答案

热门文章

  1. Memcached内存管理机制浅析
  2. 冷热水龙头_冷热水龙头结构图是怎么样的
  3. python3的安装_python3快速安装
  4. UNIDAC如何驱动MSSQL2000
  5. 利用makefile构建c++项目的思路介绍
  6. preg_match_all中的标记
  7. linux基础-第十六单元 yum管理RPM包
  8. c#实现SQLServer存储照片,并读取照片
  9. error LNK2005 已经在***.obj中定义
  10. 服务器能做镜像文件吗,如何给服务器做镜像