git rebase 的两种用法


# git rebase 的两种用法

# 前言

rebase 在 Git 中是一个非常有魅力的命令,使用得当会极大提高自己的工作效率。相反,如果乱用会给团队中其他人带来麻烦。

它的作用简要概括为:可以对某一段线性提交历史进行编辑、删除、复制、粘贴。因此,合理使用 rebase 命令可以使我们的提交历史干净、简洁。

# 用法一: 合并当前分支的多个 commit 记录

有时候会遇到对同一处代码进行多次处理的场景。这会导致如下提交记录:

$ git log --pretty=format:'%h: %s'
ad3593c: feat: modify c
21511a4: feat: modify b
a7b8f93: feat: modify b
89ab26f: feat: modify b
5f5d89a: feat: modify a
7092a92: Initial commit
1
2
3
4
5
6
7

其实,中间的对 b 的 3 次提交 完全可以合并成一次 commit,这个时候 rebase 就很有用了。

# 1. 执行 rebase -i 命令

找到想要合并的 commit,使用 rebase -i:

git rebase -i 5f5d89a
1

注意

  • git rebase -i [startPoint] [endPoint]
  • 前开后闭区间,这里的 [startPoint] 指目标 commit 的前一个 commit(即上述示例中的 5f5d89a: feat: modify a)。 因为三个 commit 肯定要基于上一个 commit 来合并成新的 commit。
  • 谨慎使用 [endPoint],省略即默认表示从起始 commit 一直到最后一个,但是一旦填写了,则表示 [endPoint] 后面的 commit 全部不要了。

# 2. 进入 Interact 交互界面

终端会进入选择交互界面,让你进行变基选择操作:

git rebase 交互界面

(git rebase 交互界面)

说明

  • 最上面三行,就是刚刚选中的三个 commit,按时间先后顺序依次往下排序(和 git log 的展示顺序是反的,查看的时候需要注意)
  • 前面的三个 pick 就是下面 Commands 展示的一系列命令中的第一个 p,也就是使用 commit。
    • pick:保留该 commit(缩写:p)
    • reword:保留该 commit,但我需要修改该 commit 的注释(缩写:r)
    • edit:保留该 commit,但我要停下来修改该提交(不仅仅修改注释)(缩写:e)
    • squash:将该 commit 和前一个 commit 合并(缩写:s)
    • fixup:将该 commit 和前一个 commit 合并,但我不要保留该提交的注释信息(缩写:f)
    • exec:执行 shell 命令(缩写:x)
    • drop:丢弃该 commit(缩写:d)

# 3. 使用 s 命令合并 commit

使用 s 命令,合并到上一个commit(编辑时就跟操作 vim 一样):

  • i 进入操作,将第二、三个 commit 的 pick 改成 s
  • Esc 退出操作。
  • 输入 :wq 保存并退出。
使用 s 命令合并 commit

(使用 s 命令合并 commit)

# 4. 修改 commit 记录

接下来会弹出第二个页面,分别展示三个 commit 的提交信息:

相关的 commit 信息列表

(相关的 commit 信息列表)

在这次的示例中,三个 commit 信息都是一样的。选用第一个的提交信息(也可以编辑下),将其余的全部注释掉,重复上述步骤,保存退出即可。

编辑提交信息

(编辑提交信息)

# 5. 查看最新合并情况

查看最新合并情况,会发现原来三个一样的提交现在合并成了一个新的 commit。

commit 合并前后对比

(commit 合并前后对比)

# 用法二: 避免出现分叉合并

接下来,将通过实际示例和场景,来分析 rebase 是如何解决分叉合并的。在此之前,我先做如下规定:

  • 有两个分支: develop(主分支),feature(feature 分支)
  • 新需求按时间顺序叫 ab……等(a 需求最早,b 其次,以此类推)
  • 原 commit a 变基之后(hashId 改变)叫 a'

# 场景 1:合并时,最舒服的情况

这种场景是基于 develop 分支没有新的提交,feature 分支有新的提交后,把 feature 分支的改动合并到 develop 分支。

developfeatureaa合并

(最理想的合并情况)

此时的合并有两点好处:

  • 没有冲突
  • 没有多余的 commit 提交

其实这种情况下,rebase 和 merge 的效果是一样的。而后面所有的 rebase 都是奔着这个目标来的。

# 场景 2:各分支都有自己新的 commit

develop 新增需求 a: "feat: a"

develop 分支的 log

(develop 分支的 log)

feature 新增需求 b: "feat: b"

feature 分支的 log

(feature 分支的 log)

# 1)develop merge feature

develop 直接 merge feature:切换到 develop 分支执行命令 git merge feature

develop 直接 merge feature

(develop 直接 merge feature)

会出现以下结果:

  • 会保留所有的 commit(hashId 不变)
  • 按提交顺序排序
  • 产生新的 commit 点(Merge branch 'xxx' into develop
developfeatureabbMerge branch 'xxx' into develop

(develop 直接 merge feature)

# 2)develop rebase feature

如果在 develop 分支没有直接执行 merge 命令,而是执行的 rebase 命令 git rebase -i feature

develop rebase feature

(develop rebase feature)

会出现以下结果:

  • develop 分支的 a 会被排在合进来的 feature 分支 b 的上面(尽管 a 是先完成的)
  • develop 的原 commit a 被移除,产生了新的 commit a'(hashId 已变)
  • 从 feature 合进来的 b 不变(不会对合进来的 commit 进行变基)
adevelopfeaturebba'

(develop rebase feature)

# 3)rebase 两步走 - 正确姿势

  • Step 1:切换到 feature 分支,去 rebase develop,即 git rebase -i develop
    • feature 的 commit b 会重新生成一个 b',排在 develop 的新需求 a 的后面。
    • 这一步相当于回到场景 1 的模式,我在当前 feature 先把自己的 b 拎出来,同步完最新的 develop,再把需求 b 放在最后面。
    • 接下来回到 develop 进行 merge 的时候就很舒服了。
  • Step 2:切换到 develop 分支,去 merge feature,即 git merge feature
    • 合并后查看 log,可以发现 develop 分支作为主分支,本次合并没有影响自己原来的 commit 历史,只在后面新增了 feature 的新内容,并没有多余的 commit。
×badevelopfeatureb'b'afeature rebase developdevelop merge feature 

(rebase 两步走)

以上就是 rebase 为什么不会产生多余 commit 记录的原因了。

# rebase 时如何解决冲突

如果两个分支修改了同一文件的同一位置,在 rebase 的时候会提示发生冲突,较为优雅的解决方案:

  • 先解决冲突,再保存。
  • git add .
  • git rebase --continue

# 使用 rebase 的注意点

注意

如果提交存在于你的仓库之外,而别人可能基于这些提交进行开发,那么不要执行变基。
如果你遵循这条金科玉律,就不会出差错。否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。 —— 引用自 官网 (opens new window)

# 不要基于即将 rebase 的分支去切新分支

badevelopfeatureb'afeature_brebase develop此时 feature_b 还在 b 节点然而 feature rebase 完,已经没有 b 了

(不要基于 rebase 的分支,切新分支)

如上图所示:

  • feature 通过场景 2 中的 rebase 两步走之后,与 develop 实现了同步。
  • 但是原先基于 feature 切出去的新分支 feature_b 还是原来的 commit b。
  • 此时无论 develop 还是 feature,再合并 feature_b 的时候,都会产生冲突,形成两个相同的提交记录(其实就是 b 和 b')。

所以,首先要确保你的分支已经完成新需求,然后在 rebase 操作结束之后,再去切新分支,这时他们才是同步的。

# 不要对已经合并到主分支的本地修改进行变基

首先,自己的分支,如果想对已经推送的 commit 进行修改,可以在修改完后,使用 git push -f 强行 push 并覆盖远程对应分支。

但是如果这些修改已经被合到了其他分支(比如主分支),那又会出现冲突,因为其他分支保存的是你 rebase 之前合进去的 commit。

典型操作(应避免):

  • feature 分支有三个新需求(a,b,c)。
  • develop 合并 feature 后,保持同步完成。
  • 这个时候 feature 分支使用 rebase -i,对中间 b 进行修改,导致 b 和 c 的 commit-id 都发生了改变。
  • develop 再合并 feature 时,就会产生冲突,解决后,会多出两次重复的提交记录(以前就有 b,c,现在又多了 b',c')。

# 不要在预发布/正式分支上使用 rebase -i

从变基的那个节点开始往后的所有节点的 commit-id 都会发生变化,这个就不再赘述了。

所以可以想象一下,master 上有 100 个 commit,你悄悄改了第 50 个 commit,那从 50—100 的所有 commit 全部改变了。这时别人的分支合进来,就会有 51 个冲突,解决完冲突之后,就会产生 51*2 个相同的提交记录,恐怖如斯!

# 总结

总的原则是,只对尚未推送或未分享给别人的本地修改执行变基操作清理历史,永远不要对已推送至别处的提交执行变基操作。这样,你才能享受到两种方式(rebase 和 merge)带来的便利。

(完)

上次编辑于: 2022年11月17日 17:25

玻璃钢生产厂家幼儿园户外玻璃钢雕塑好看的商场美陈装置买玻璃钢雕塑安徽玻璃钢仿真水果雕塑厂家玻璃钢红军雕塑生产厂玻璃钢雕塑制作技术流程河西圣诞商场美陈哈尔滨玻璃钢公园雕塑宁德玻璃钢卡通雕塑厂家合肥园林玻璃钢雕塑福建水果玻璃钢雕塑批发辽宁城市玻璃钢雕塑铭星商场美陈灯光北京大型主题商场美陈厂家直销潮州玻璃钢熊猫雕塑万宁动漫玻璃钢卡通雕塑福建仿铜玻璃钢雕塑优势火龙果树脂玻璃钢雕塑红星商场马桶美陈创意云南景观玻璃钢雕塑供应商儿童玻璃钢雕塑设计一般多少钱上海抽象玻璃钢雕塑东莞玻璃钢雕塑批量定做上海组合式玻璃钢花盆湖北商场美陈报价玻璃钢雕塑好还是泡沫好郑州铸铜玻璃钢彩绘雕塑厂嘉峪关动物玻璃钢雕塑公司昭通玻璃钢雕塑零售商场七夕美陈布置香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化