变基原理,理解:Git 正在将你的提交一个一个重新应

Git 变基(Rebase)的工作原理:一个一个重新应用提交

1. 变基的核心过程

比喻:重播电影

想象你有一部电影(提交历史):

  • 原版电影:A → B → C → D
  • 你想从 B 开始重拍:变基到 B
  • Git 会:暂停电影 → 从B开始重新拍摄每个场景 → 生成新电影

2. 详细步骤分解

原始状态

复制代码
提交历史: A ← B ← C ← D ← E (HEAD/master)
           ↑
          你要变基到这里(比如B)

变基过程

bash 复制代码
git rebase B

Git 内部执行:

步骤1:保存要移动的提交
bash 复制代码
# Git 会:
1. 找到 B 之后的所有提交:[C, D, E]
2. 把这些提交"拆下来"临时保存
步骤2:重置到目标提交
bash 复制代码
# Git 会:
3. 把 HEAD 移动到 B(快退到B时刻)
4. 工作区变成 B 时的状态
步骤3:一个一个重新应用
bash 复制代码
# Git 会:
5. 取出 C,尝试应用到当前状态
6. 如果成功,创建新的 C'
7. 取出 D,尝试应用到 C' 上
8. 如果成功,创建新的 D'
9. 取出 E,尝试应用到 D' 上
10. 如果成功,创建新的 E'
步骤4:更新分支指针
bash 复制代码
# 最终:
旧历史:A ← B ← C ← D ← E
新历史:A ← B ← C' ← D' ← E'

3. "一个一个重新应用"的具体含义

对于每个提交,Git 做这些事:

python 复制代码
# 伪代码表示变基过程
def rebase_commit(old_commit, current_state):
    # 1. 获取这个提交的更改
    changes = get_changes(old_commit)
    
    # 2. 尝试将更改应用到当前状态
    try:
        new_state = apply_changes(current_state, changes)
        
        # 3. 创建新的提交
        new_commit = create_commit(
            message = old_commit.message,
            author = old_commit.author,
            changes = changes,
            parent = current_state.last_commit
        )
        
        return new_commit, new_state
        
    except ConflictError:
        # 4. 如果有冲突,暂停并等待用户解决
        pause_and_wait_for_user_to_resolve()

4. 实际例子:你的飞行项目

你的提交历史

bash 复制代码
# 原始提交链
3394272 ← 31cea98 ← 8f5b36e ← ... ← cc67b56 ← ... ← b095acf

当你执行 git rebase -i 3394272

bash 复制代码
# Git 的内部操作:
1. 保存这些提交:[31cea98, 8f5b36e, ..., cc67b56, ..., b095acf]
2. 重置到 3394272(工作区变成那时的状态)
3. 开始重新应用:
   - 尝试应用 31cea98 → 创建新的提交 31cea98'
   - 尝试应用 8f5b36e → 创建新的提交 8f5b36e'
   - ...
   - 现在正在尝试应用 cc67b56
   - 如果成功 → 创建 cc67b56'
   - 然后继续下一个...

5. 为什么需要"重新应用"而不是"移动"?

原因:提交是基于父提交的

bash 复制代码
# 每个提交都包含:
1. 当前文件状态
2. 与父提交的差异(diff)
3. 父提交的引用

# 所以不能直接移动,因为:
# 提交 C 是基于 B 的差异
# 如果要放到 A 后面,需要重新计算差异

示例:计算差异

复制代码
提交 B: 文件内容 = "Hello"
提交 C: 在 B 基础上添加 " World" → 内容 = "Hello World"
差异: + " World"

现在要放到 A 后面:
提交 A: 文件内容 = "Hi"
应用 C 的差异 (+ " World") → 内容 = "Hi World"

6. 交互式变基的特殊处理

当你有修改指令时:

bash 复制代码
git rebase -i 3394272
# 编辑器中:
pick 31cea98 完善mybatis-plus代码生成器
squash 8f5b36e 编写了docker脚本...
drop 98b50e9 忽略target文件夹
reword cc67b56 登录、分页查询、新增员工

Git 的处理:

bash 复制代码
# 对于每个指令:
1. pick: 正常重新应用
2. squash: 应用更改,但不立即提交,合并到前一个提交
3. drop: 跳过这个提交(不应用)
4. reword: 应用更改,但让你修改提交信息

7. 遇到冲突时的暂停

为什么停在 cc67b56

bash 复制代码
# 可能原因:
1. cc67b56 修改了某个文件
2. 之前的某个提交(比如 31cea98')也修改了同一行
3. Git 不知道该如何合并

# 所以 Git 说:
"我正在应用 cc67b56,但这里有个冲突,你来决定怎么处理"

8. 可视化理解

变基前:

复制代码
时间线:○──○──○──○──○──○
提交:  A   B   C   D   E   F
标签:      ↑目标

变基过程:

复制代码
1. 拆下 C,D,E,F:   A   B   [C][D][E][F]
2. 回到 B:         A   B
3. 重新贴 C:       A   B ── C'
4. 重新贴 D:       A   B ── C' ── D'
5. 重新贴 E:       A   B ── C' ── D' ── E'
6. 重新贴 F:       A   B ── C' ── D' ── E' ── F'

9. 实际执行的 Git 命令

Git 底层命令

bash 复制代码
# 变基大致相当于:
# 1. 创建临时分支
git checkout -b temp-branch 3394272

# 2. 逐个 cherry-pick
git cherry-pick 31cea98
git cherry-pick 8f5b36e
# ...
git cherry-pick cc67b56  # ← 停在这里
# ...

# 3. 移动分支指针
git branch -f master temp-branch
git checkout master

10. 关键要点总结

  1. 不是移动,是重演:每个提交都被重新创建
  2. 顺序重要:必须按顺序应用,因为每个提交基于前一个
  3. 可能冲突:重演时可能与当前状态冲突
  4. 创建新提交:即使内容相同,也是新的提交对象(不同哈希)
  5. 原提交还在 :直到垃圾回收前,原提交还在 .git/objects

简单说: Git 像导演一样,从某个场景开始,按照剧本(提交历史)重新拍摄每一个镜头(提交),生成新电影(新历史)。

相关推荐
先跑起来再说2 小时前
Git 入门到实战:一篇搞懂安装、命令、远程仓库与 IDEA 集成
ide·git·后端·elasticsearch·golang·intellij-idea
承渊政道5 小时前
Linux系统学习【Linux系统的进度条实现、版本控制器git和调试器gdb介绍】
linux·开发语言·笔记·git·学习·gitee
Doro再努力6 小时前
【Linux操作系统12】Git版本控制与GDB调试:从入门到实践
linux·运维·服务器·git·vim
摇滚侠8 小时前
MAC IDEA GIT 提交区显示了几个不存在的目录
git·idea
城东9 小时前
Git使用[远程仓库远端的head比本地和提交的head旧,其他人拉不到最新代码]
git·head·远程仓库远端·比本地和提交的head旧·其他人拉不到最新代码
何中应18 小时前
使用SSH地址拉取远程仓库代码报下面的错误
git
何中应18 小时前
Git本地仓库命令补充
git
sun00770020 小时前
执行repo sync -c -d -j4以后,提交未git push的代码看不到了。要怎么恢复?
git
胖虎11 天前
Git 一个本地仓库同时推送到两个远程仓库(详细教程)
git·多远程仓库·双远程仓库·git双远程·git备份
春日见1 天前
如何创建一个PR
运维·开发语言·windows·git·docker·容器