从撤销 rebase 谈谈 git 原理

假设我们有两个分支,a 和 b,它们的提交都有一个相同的父提交(master 指向的那次提交)。如图所示:

现在我们在分支 a 上,然后 rabase 到分支 b 上。如图所示:

平时开发中经常遇到这种情况,假设分支 a 和 b 是两个独立的 feature 分支,但是不小心被我们错误的 rebase 了。现在相当于两个 feature 分支中原本独立的业务被揉起来了,当然是我们不想看到的结果,那么如何撤销呢?

一种方案是利用 reflog 命令。

利用 reflog 撤销变基

我们先不考虑原理,直接上解决方案,首先输入 git reflog,你会看到如下图所示的日志:

最后的输出其实是最早的操作,我们逐条分析下:

  1. HEAD@{8}: 这里我们创建了初始的提交
  2. HEAD@{7}:检出了分支 a
  3. HEAD@{6}:在分支 a 上做了一次提交,注意 master 分支没有变动
  4. HEAD@{5}:从分支 a 回到分支 master,相当于向后退了一次
  5. HEAD@{4}:检出了分支 b
  6. HEAD@{3}:在分支 b 上做了一次提交,注意 master 分支没有变动
  7. HEAD@{2}:这一步开始变基到分支 a,首先切换到分支 a 上
  8. HEAD@{1}:把分支 b 对应的那次提交变基到分支 a 上
  9. HEAD@{0}:变基结束,因为是在 b 上发起的变基,所以最后还切回分支 b

如果我们想撤销此次 rebase,只要输入以下命令就可以了:

  • git reset --hard HEAD@{3}

此时再看,已经“恢复”到 rebase 前的状态了。的是不是感觉很神奇呢,先别着急,后面会介绍这么做的原理。

git 工作原理简介

为了搞懂 git 是如何工作的,以及这些命令背后的原理,我想有必要对 git 的模型有基础的了解。

首先,每一个 git 目录都有一个名为 .git 的隐藏目录,关于 git 的一切都存储于这个目录里面(全局配置除外)。这个目录里面有一些子目录和文件,文件其实不重要,都是一些配置信息,后面会介绍其中的 HEAD 文件。子目录有以下几个:

  1. info:这个目录不重要,里面有一个 exclude 文件和 .gitignore 文件的作用相似,区别是这个文件不会被纳入版本控制,所以可以做一些个人配置。
  2. hooks:这个目录很容易理解, 主要用来放一些 git 钩子,在指定任务触发前后做一些自定义的配置,这是另外一个单独的话题,本文不会具体介绍。
  3. objects:用于存放所有 git 中的对象,下面单独介绍。
  4. logs:用于记录各个分支的移动情况,下面单独介绍。
  5. refs:用于记录所有的引用,下面单独介绍。

本文主要会介绍后面三个文件夹的作用。

git 对象

git 是面向对象的!
git 是面向对象的!
git 是面向对象的!

没错,git 是面向对象的,而且很多东西都是对象。我举个简单的例子,来帮助大家理解这个概念。假设我们在一个空仓库里,编辑了 2 个文件,然后提交。此时都会有那些对象呢?

首先会有两个数据对象,每个文件都对应一个数据对象。当文件被修改时,即使是新增了一个字母,也会生成一个新的数据对象。

其次,会有一个树对象用来维护一系列的数据对象,叫树对象的原因是它持有的不仅可以是数据对象,还可以是另一个树对象。比如某次提交了两个文件和一个文件夹,那么树对象里面就有三个对象,两个是数据对象,文件夹则用另一个树对象表示。这样递归下去就可以表示任意层次的文件了。

最后则是提交对象,每个提交对象都有一个树对象,用来表示某一次提交所涉及的文件。除此以外,每一个提交还有自己的父提交,指向上一次提交的对象。当然,提交对象还会包含提交时间、提交者姓名、邮箱等辅助信息,就不多说了。

假设我们只有一个分支,以上知识点就足够解释 git 的提交历史是如何计算的了。它并不存储完整的提交历史,而是通过父提交的对象不断向前查找,得出完整的历史。

注意开头那张图片,分支 b 指向的提交是 9cbb015,不妨来看下它是何方神圣:

  • git cat-file -t 9cbb015
  • git cat-file -p 9cbb015

这里我们使用 cat-file 命令,其中 -t 参数打印对象的类型,-p 参数会智能识别类型,并打印其中的内容。输出结果如图所示:

可见 9cbb015 是一个提交对象,里面包含了树对象、父提交对象和各种配置信息。我们可以再打印树对象看看:

这表示本次提交只修改了 begin 这个文件,并且输出了 begin 这个文件对于的数据对象。

git 引用

top Created with Sketch.