我们来详细讲解一下 git rebase 的使用。git rebase 是 Git 中一个非常强大但同时也需要谨慎使用的命令,它的核心作用是重新定义分支的基准点,从而整理提交历史。

1. 理解 Rebase 的核心思想

想象一下,你的提交历史是一条直线。当你基于一个旧的分支(比如 main)创建了一个特性分支 feature 后,main 分支可能已经有了新的提交。

  • 合并前的情况:
A---B---C  main
         \
          D---E---F  feature

Rebase 的目的是让 feature 分支的基点从 C 变成 main 分支最新的提交(比如 C‘),就好像你的工作 D-E-F 是在 C' 之后才开始的一样。

  • Rebase 后的情况:
A---B---C---C'  main
                 \
                  D'---E'---F'  feature

注意:D', E', F' 是新的提交,它们的哈希值和之前 (D, E, F) 不同,但内容基本相同。

2. 为什么要使用 Rebase?

主要有两个目的:

  1. 创造更清晰的历史:使用 rebase 后再合并,可以得到一条线性的、非常整洁的提交历史,没有不必要的合并提交。这对于代码审查和问题追溯非常友好。
  2. 在合并前同步上游变更:在将你的特性分支合并回主分支之前,先使用 rebase 将主分支的最新改动“融入”你的分支,这样可以在你的分支上解决潜在的冲突,使得最终的合并变得简单快速。

3. 基本使用方法

场景一:将特性分支变基到主分支

这是最常见的用法,用于同步最新代码并整理历史。

# 1. 切换到你的特性分支
git checkout feature

# 2. 执行变基,将当前分支变基到 main
git rebase main

这个命令的执行过程是:

  1. Git 会找到当前分支 (feature) 和目标分支 (main) 的最近共同祖先(提交 C)。
  2. 提取当前分支(feature)上从那个祖先之后的所有提交(D, E, F),并将它们临时保存为补丁。
  3. 将当前分支指针重置到目标分支的最新提交(C‘)。
  4. 按顺序将保存的补丁(D, E, F)重新应用到当前分支上。

如果在重新应用补丁时发生冲突,Rebase 过程会暂停,让你解决冲突。

解决 Rebase 冲突
  1. 手动解决:编辑标记为冲突的文件。
  2. 标记为已解决:使用 git add <file> 将解决好的文件加入暂存区。
  3. 继续 Rebase:使用 git rebase --continue
  4. 跳过当前提交(如果这个冲突的提交你不想要了):git rebase --skip
  5. 中止 Rebase(如果一团糟,想回到 rebase 前的状态):git rebase --abort
场景二:交互式 Rebase - 整理提交历史

这是 rebase 最强大的功能,允许你修改、合并、删除之前的提交。注意:只能操作你本地的,尚未推送到远程的提交。

git rebase -i <commit_hash>

这里的 <commit_hash> 是你想重写的提交的父提交的哈希值。一个更常用的方法是:

# 重写最近3次提交
git rebase -i HEAD~3

执行后会打开一个编辑器,列出你要操作的提交,例如:

pick d1fc4a5 Add feature A
pick a1b2c3d Fix typo in A
pick e4f5g6h Add feature B

你可以通过修改前面的命令来改变提交:

  • pick:保留该提交(不做改动)。
  • reword:保留提交,但修改提交信息。
  • edit:保留提交,但暂停以便你修改文件内容(例如添加漏掉的文件)。
  • squash:将该提交与前一个提交合并,并允许你合并提交信息。
  • fixup:类似于 squash,但会丢弃当前提交的提交信息。
  • drop:删除该提交。

示例: 将后两次提交合并到第一次提交中:

pick d1fc4a5 Add feature A
squash a1b2c3d Fix typo in A
squash e4f5g6h Add feature B

保存退出后,Git 会让你为这次合并后的新提交编写一条新的提交信息。

4. 黄金法则:什么时候不能用 Rebase?

永远不要对已经推送到公共仓库(如 GitHub, GitLab)的分支进行 Rebase。

为什么?
因为 Rebase 会重写历史,它会创建新的提交来替代旧的。如果你的同事已经基于你旧的提交 (D, E, F) 进行了工作,当你强制推送新的提交 (D', E', F') 后,他们的历史会和你的历史产生分歧,需要非常复杂的操作才能同步,这会给团队协作带来巨大的混乱。

对于公共分支,使用 git merge

5. 工作流总结

一个典型的使用 Rebase 的协作工作流如下:

  1. 本地开发:在 feature 分支上进行开发,可以随意使用 rebase -i 来整理你的本地提交历史。
  2. 准备集成:当你准备将分支推送到远程并发起 Pull Request 时:
    # 1. 获取远程最新代码
    git fetch origin
    
    # 2. 将你的特性分支变基到 origin/main 上,解决可能出现的冲突
    git rebase origin/main
    
    # 3. (可选) 最后整理一下你的提交,使其逻辑清晰
    git rebase -i HEAD~5 # 例如整理最近5次提交
    
    # 4. 推送到远程。由于历史被重写,需要强制推送。
    #    **确保这个分支是你个人使用的,没有别人基于它工作!**
    git push origin feature --force-with-lease
    
  3. 发起合并请求:在 Git 托管平台发起 Pull Request,此时由于你已经解决了冲突,合并会非常顺畅。

命令速查表

命令 作用
git rebase <base_branch> 将当前分支变基到 <base_branch>
git rebase -i <commit> 交互式变基,从 <commit> 之后开始修改
git rebase --continue 解决冲突后,继续变基过程
git rebase --abort 中止变基过程,回到操作前的状态
git rebase --skip 跳过当前正在应用的提交
git push --force-with-lease (比 --force 更安全) 强制推送,用于推送 rebase 后的分支

希望这个详细的解释能帮助你更好地理解和使用 git rebase!记住它的力量和责任,它会让你的 Git 历史变得非常优雅。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐