git 变基:git rebase

发布于:2025-06-29 ⋅ 阅读:(20) ⋅ 点赞:(0)

Rebase(变基)

Rebase 是将一个分支的所有更改重新应用到另一个分支的最新状态上。使你的分支看起来像是在目标分支的最新提交之后创建的。
在这里插入图片描述


[应用举例] (heads/dev) git rebase master 详细工作原理
1、Git 会检查是否有未提交的更改,如果你有未提交的修改,Git 会尝试保留它们,如果这些修改与即将 rebase 的内容冲突,则会提示错误并中止。
2、Git 首先会找到devmaster分支的最近共同祖先提交。把共同祖先提交dev最新提交的每个提交都换为一个补丁,并将这些补丁依次应用到master最新提交上。如果某个补丁应用失败(如冲突),Git 会暂停 rebase 并提示你解决冲突。解决冲突后,使用 git add . 标记解决,然后运行 git rebase --continue 继续;也可以选择跳过某次提交(–skip)或中止整个 rebase(–abort)。
3、所有补丁成功应用后,Git 会更新dev的指针(HEAD),使其指向新的提交。
4、由于提交历史已改变,远程仓库上的 dev 分支与本地不一致,必须使用强制推送。推荐使用 git push --force-with-lease,而不是 git push --force

(heads/dev) git rebase origin/release          将某分支的提交移到新基础上
● (heads/dev) git rebase -i origin/release       交互式地rebase
● (heads/dev) git rebase -i HEAD~3                  交互式整理最近3个提交


git rebase -i 详解
git rebase发生冲突时 ☞ 撤销rebase,git rebase --abort
git rebase发生冲突时 ☞ 解决冲突,git add . && git rebase --continue
github上基于rebase的一次PR提交
rebase导致源分支修改丢失


参数 说明 Git 引入年份 / 最低版本
--exec "echo hahah" 在交互式变基过程中,对每个提交应用后执行指定命令(如测试、lint),确保提交质量 2005 / Git v1.0
--ignore-date 用当前时间覆盖原提交时间 2005 / Git v1.0
--onto <newbase> 自定义变基的目标基准(指定新基底) 2005 / Git v1.0
--abort 中断变基,回退到变基前状态 2005 / Git v1.0
--continue 解决冲突后继续变基流程 2005 / Git v1.0
--skip 跳过当前冲突提交,继续后续变基 2005 / Git v1.0
--keep-empty 保留空提交(默认跳过空提交) 2007 / Git v1.5.0
--signoff 给每个提交添加 Signed-off-by: 签名行 2007 / Git v1.5.0
--interactive / -i 进入交互模式,手动编排提交 2007 / Git v1.5.4
--committer-date-is-author-date 让提交者时间与作者时间一致,统一时间戳 2008 / Git v1.6.0
--no-ff 变基时禁用快进合并(保留合并提交) 无独立版本,继承merge --no-ff逻辑,Git v1.0 + 支持
--autosquash / -a 自动识别、合并标记过的 fixup 提交 2010 / Git v1.7.0
--rebase-merges / -r 保留原分支合并结构,重放合并提交 2012 / Git v1.8.5
--preserve-merges 在变基时尝试保留分支合并结构(通过重新创建合并提交),但不保留原合并提交的元数据(如提交信息、时间戳) ⚠️ 注意:此参数在 Git v2.0 后已被标记为 deprecated(不推荐使用),推荐使用 --rebase-merges 替代,后者能更完整地保留合并历史 2010 / Git v1.7.0(引入) 2012 / Git v1.8.5(被 --rebase-merges 替代) 2014 / Git v2.0(标记为 deprecated)
--force-rebase 强制对已有推送的分支执行变基(需谨慎) 无独立版本,依赖push --force-with-lease逻辑,Git v1.8.5+ 后更安全

–exec

git rebase -i HEAD~3 --exec "echo hahah"

pick be04f1b update
exec echo hahah
pick ddd538d update
exec echo hahah
pick a1c019c update
exec echo hahah

# $ git rebase -i HEAD~3 --exec "echo hahah"
# hint: Waiting for your editor to close the file... Can't find filter element
# Can't find filter element
# Executing: echo hahah
 hahah
# Executing: echo hahah
 hahah
# Executing: echo hahah
 hahah
# Successfully rebased and updated refs/heads/dev.

–ignore-date

git rebase 会更新 committer date,不会更新 author date(作者时间)。 而 --ignore-date 不仅更新 committer date,还会覆盖 author date,让整个提交的时间看起来就像“现在写的”。

# $ git log --pretty=format:"%<(10)%h | %<(20)%D | %<(20)%ai | %<(20)%ci | %s"
# 6ed3bac    | HEAD -> dev          | 2025-06-28 14:43:54 +0800 | 2025-06-28 14:43:54 +0800 | update 5

# zhang@zhangziwa MINGW64 /d/gitPrac/ckck (dev)
$ git rebase release --ignore-date
# Current branch dev is up to date, rebase forced.
# Successfully rebased and updated refs/heads/dev.

# $ git log --pretty=format:"%<(10)%h | %<(20)%D | %<(20)%ai | %<(20)%ci | %s"
# e3f5262    | HEAD -> dev          | 2025-06-28 14:53:19 +0800 | 2025-06-28 14:53:19 +0800 | update 5

–onto

git rebase --onto main develop feature,将 feature 分支中从 develop 到 feature 的所有提交,重新应用到 main 上

–skip,–continue,–abort

git rebase 过程中用于处理冲突或中断操作时,有如下3个操作

git rebase --skip      跳过当前正在处理的提交,不将其应用到新的基础上
git rebase --continue  解决冲突并继续
git rebase --abort     中止整个变基

–keep-empty

在进行以下操作时,Git 默认会忽略所有空提交,加上 --keep-empty 参数后,Git 在重写历史时会 保留这些空提交,不会自动跳过它们。

git rebase
git rebase -i

–signoff

给每个提交添加 Signed-off-by: 签名行

# $ git log --pretty=format:"%<(10)%h | %<(20)%D | %<(20)%ai | %<(20)%ci | %s"
# 6ed3bac    | HEAD -> dev  | 2025-06-28 14:43:54 +0800 | 2025-06-28 14:43:54 +0800 | update 5
$ git rebase release --signoff
# Current branch dev is up to date, rebase forced.
# Successfully rebased and updated refs/heads/dev.

# $ git log --pretty=format:"%<(10)%h | %<(20)%D | %<(20)%ai | %<(20)%ci | %s - %b"
# 405bb6c    | HEAD -> dev  | 2025-06-28 14:43:54 +0800 | 2025-06-28 15:03:48 +0800 | update 5 - Signed-off-by: zhangziwa <zhangziwa@qq.com>

–committer-date-is-author-date

# $ git log --pretty=format:"%<(10)%h | %<(20)%D | %<(20)%ai | %<(20)%ci | %s"
# 6ed3bac    | HEAD -> dev          | 2025-06-28 14:43:54 +0800 | 2025-06-28 14:43:54 +0800 | update 5

$ git rebase release --signoff  # git rebase 会更新 committer date,不会更新 author date(作者时间)
# $ git log --pretty=format:"%<(10)%h | %<(20)%D | %<(20)%ai | %<(20)%ci | %s"
# 815ac13    | HEAD -> dev          | 2025-06-28 14:43:54 +0800 | 2025-06-28 15:15:58 +0800 | update 5

$ git rebase release --committer-date-is-author-date  # 让提交者时间与作者时间一致,统一时间戳
# $ git log --pretty=format:"%<(10)%h | %<(20)%D | %<(20)%ai | %<(20)%ci | %s"
# 6ed3bac    | HEAD -> dev          | 2025-06-28 14:43:54 +0800 | 2025-06-28 14:43:54 +0800 | update 5

–no-ff

变基时禁用快进合并(保留合并提交)

–autosquash

当你使用 git commit --fixupgit commit --squash <commit> 创建了“修复提交”后,使用 git rebase -i --autosquash 自动识别并合并 fixup/squash 提交。

–preserve-merges(已废弃), --rebase-merges

–rebase-merges 的作用是:让 Git 在变基时记住你以前是怎么合并分支的,避免历史变得混乱或丢失重要信息。–preserve-merges 也是相同思路,但是已弃用。

原始结构如下:
A---B---C---D <-- main
     \
      E---F---M <-- feature

git rebase main
A---B---C---D <-- main
             \
              E'--F'--M' <-- feature   E'相对于E表示sha变了

git rebase --preserve-merges main(已废弃)
A---B---C---D <-- main
     \        \
      E'---F'---M' <-- feature         M'是对原始M的“复制”,可能无法准确反映合并逻辑,已废弃

git rebase --rebase-merges main
A---B---C---D <-- main
     \        \
      E'---F'---M' <-- feature         M'是通过重新执行合并生成的新提交

git merge main
A---B-----C----D <-- main
     \           \
      D---E---M---N <-- feature

git merge feature
A---B---C---D---N <-- main
     \         /
      E---F---M <-- feature

–force-rebase

–force-rebase 的作用是:即使提交已经存在于目标分支中,也强制重新 apply 所有提交。

原始结构:
A---B---C <-- main
         \
          D---E <-- feature
          
git rebase main
A---B---C <-- main
         \
          D---E <-- feature (无变化)
          
git rebase --force-rebase main
A---B---C <-- main
           \
            D'---E' <-- feature

网站公告

今日签到

点亮在社区的每一天
去签到