赞
踩
本文让你熟练使用 rebase,学会以下两种操作,从此拒绝杂乱无章的 git 提交。
你可能出现过对同一处代码进行多次处理的场景。这会导致如下提交记录:
$ git log --pretty=format:'%h: %s'
d2399da: feat: modify c
0134695: feat: modify b
eb63848: feat: modify b
51c0bca: feat: modify b
4cb600e: feat: modify a
d29f331: Initial commit
其实,中间的对 b 的3次提交完全可以合并成一次 commit,这个时候 rebase 就很有用了。
git rebase -i 4cb600e
注意
git rebase -i [startPonit] [endPoint]
前开后闭 区间
这里的 [startPonit] 是指需要合并的 commit 的前一个 commit (即当前示例中的 “4cb600e: feat: modify a”)。 因为, 三个 commit 肯定要基于上一个 commit 合并成了新的 commit。谨慎使用[endPoint]
省略, 即默认表示从起始 commit 一直到最后一个,但是一旦你填写了, 则表示 [endPoint] 后面的 commit 全部不要了!进入 Shell 终端选择交互界面,让你进行变基选择操作:
说明
接下来会弹出第二个页面, 分别展示三个 commit 的提交信息:
这里三个信息都是一样的,我们选用第一个的提交信息,将其余的全部注释掉,重复上述步骤, 保存退出即可。
会发现原三个一样的提交现在合并成了一个新的 commit 。
命令 | 缩写 | 含义 |
---|---|---|
pick | p | 保留该 commit |
reword | r | 保留该 commit,但需要修改该 commit 的注释 |
edit | e | 保留该 commit, 但我要停下来修改该提交(不仅仅修改注释) |
squash | s | 将该 commit合并到前一个 commit |
fixup | f | 将该 commit合并到前一个 commit,但不要保留该提交的注释信息 |
exec | x | 执行 Shell 命令 |
drop | d | 丢弃该 commit |
接下来,我将用实际示例和场景,来分析 rebase 是如何解决分叉合并的,在此之前, 我先做如下规定:
a
、b
、…等(a 需求最早, b 其次, 以此类推);a
变基之后(hashId 改变) 叫 a'
。
此时的合并有两点好处:
请大家记住这个场景, 后面所有的 rebase 都是奔着这个目标来的。
develop
新增需求 a: “feat: a”
feature
新增需求 b: “feat: b”
会出现以下结果:
会出现以下结果:
上述两种操作虽然都能完成合并,但是结果却不是我们想要的。直接 merge
会产生多余提交,而直接 rebase
虽不会产生多余提交,但是会改变自身原有的 commit,这对上游的主分支来说是灾难性的。试想一下,如果上游的 master
主分支被你悄悄修改了 commit,而其他所有人还是基于原来的 commit 进行的开发,可想而知,到时候合代码就会如同车祸现场一样,遍地冲突…
所以,正确的操作是:先提前将 master 主分支的新代码 rebase 到自己的分支,再在主分支上 merge 自己的分支。
这样有两点好处:
我们来看下完整示例:
# feature分支
git fetch origin
git rebase develop
# 或者 直接一个命令
git pull develop --rebase
会出现以下结果:
到这里, 你应该有所察觉了!
: 没错! 这一步相当于 回到了场景1的模式, 我当前的 feature 相当于先把需求b 拎出来, 同步完最新的 develop, 再把需求b放在了最后面。
所以, 接下来 merge 的时候 就很舒服了~!
# develop分支
git merge rebase_new
而这, 也是 rebase 为什么不会产生多余的 commit 记录的原因了。
注意!
不是commit ! 不是commit ! 不是commit !先引用官网上的一段话:
如果提交存在于你的仓库之外,而别人可能基于这些提交进行开发,那么不要执行变基。
如果你遵循这条金科玉律,就不会出差错。 否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。
因为变基最强大也是最危险之处, 就在于, 它能改变原 commit 的 hashId, 而一旦你对历史提交做出改变, 那么 从变基那个节点开始往后的所有节点的 commit id 都会发生变化。
这对线上环境来说, 是不可控的!
如果 feature 在写完新需求 b 后,切了新分支 feature_B,然后又 rebase 了 develop,那么新分支feature_B 无论是合进 develop 还是 合进 feature 都会有冲突, 出现重复的 b(其实是 b 和 b’)
除非 你能百分百确保 你的分支已经完成新需求, rebase 操作结束之后, 再切新分支, 这时 他们才是同步的。
首先, 自己的分支, 如果想对已经推送的 commit 进行修改, 可以在修改完后, 使用 git push -f
强行 push 并覆盖远程对应分支。
但是如果这些修改 已经被合到了其他分支(比如主分支),那又会出现冲突,因为其他分支保存的是你 rebase 之前 合进去的 commit。
从变基那个节点开始往后的所有节点的 commit id 都会发生变化。这个就不再赘述了。
所以可以想象一下, master上有100个 commit,你悄悄改了第50个 commit,那从 50—100 的所有 commit 全部改变了, 这时, 别人的分支合进来, 就会有51个冲突, 解决完冲突之后, 就会产生 51*2 个相同的提交记录, 恐怖如斯!
git rebase
是一条集增删改查于一体的强大命令,它既可以对自己的分支进行修修剪剪,也可以在合并其他分支的时候缝缝补补,让我们的 commit 提交看上去干净清爽。
记住一条原则:只对尚未推送或未分享给别人的本地修改执行变基操作,清理历史, 从不对已推送至别处的提交执行变基操作。
这样,你才能享受到两种方式(rebase 和 merge)带来的便利。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。