变基原理,理解: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 像导演一样,从某个场景开始,按照剧本(提交历史)重新拍摄每一个镜头(提交),生成新电影(新历史)。

相关推荐
purpleseashell_Lili10 小时前
commitizen,cz-git,husky 三者区别
git
摇滚侠10 小时前
零基础小白自学 Git_Github 教程,解决分支合并冲突,笔记14
笔记·git·github
我是李武涯13 小时前
如何修改git的commit消息
git
fruge13 小时前
Git 进阶技巧:分支管理、冲突解决、提交规范实操
大数据·git·elasticsearch
spencer_tseng14 小时前
Git-2.18.0-64-bit.exe client install
git
摇滚侠16 小时前
Idea Git 合并分支,rebase 和 merge 的区别,应该使用哪个,多人协作开发,禁止使用 rebase 合并分支
git·github
winfreedoms17 小时前
将 Git 恢复到「上一次提交(git commit)的版本」操作
git
系夏普17 小时前
Git 的三大工作区域:工作区、暂存区、本地仓库
git
摇滚侠19 小时前
零基础小白自学 Git_Github 教程,分支合并,笔记13
笔记·git·github