文章转自:Git 合并代码的差别方式 – Merge Commit、Squash and merge、Cherry-pick、Rebase and merge – 夜空中最亮的星
媒介
我们在日常开辟中经常使用 Git 管理代码, 每个人在各自的分支开辟代码, 开辟完毕后在 Gitlab/Github 上提交 MR/PR, 最后点击 merge 按钮即将代码合并至主分支…
在稍微学习 Git 相干的知识后, 我们会发现 Git 代码合并的方法绝不但此一种, 差别的代码合并方式之间有什么差别?各自又实用于什么样的场景?我将在这篇文章为各人展开聊聊这些话题。
差别的代码合并方式
1. Merge
在常见的 Git 工作流中, 我们会有 2 个长期存在而且不会被删除的分支: master 和 develop。而在日常开辟流程中, 我们使用的是特性分支,也叫功能分支。当需要开辟一个新的功能的时候,可以新建一个 feature-xxx 的分支,在里边开辟新功能,开辟完成后,将之并入 develop 分支中,如下图:
- H---I---J feature-xxx
- / \
- E---F---G---K----L develop
- /
- A---B---C---D master
复制代码 其中 L 这个提交是由 Git 自动天生的合并提交节点。
注意, 如果 git 可以通过移动指针完成合并, 那么默认情况下将不会创建提交节点, 这个优化又被称之为 fast-forward(ff) , 如需关闭该优化项, 可添加参数 --no-ff 要求 git 创建提交节点。
2. Squash Merge
在日常的 MR/PR 过程中, 我们会发现合并时有个选项叫 squash commits 。 顾名思义, Squash 意味着会将多个 commit(提交) 合并到一个。与 Merge 雷同的是, 使用 Squash Merge 将会在该分支末尾追加一个提交记录, 如下拓扑结构:
- H---I---J feature-xxx
- /
- E---F---G---K----L' develop (where L' == (H + I +J)
- /
- A---B---C---D master
复制代码 但是, 与普通的 Merge 差别的是, Squash Merge 会抛弃原来分支 (feature-xxx) 上的所有提交记录, 并天生一个包罗原来提交的所有内容的提交节点。
基于以上特性, 如果 Squash Merge 后继续在 feature-xxx 分支开辟, 那么下次合并后将大概率出现冲突,这时候就需要用到 cherry-pick 。
3. Cherry-pick
根据 git-book 中的介绍, cherry-pick 提供了从另一分支中 挑选(pick) 单个或数个提交并应用到当前的开辟分支中的能力。 我们以 Squash Merge 后不测地在原分支中继续开辟为例, 介绍 cherry-pick 的操作流程, 如下拓扑结构:
- H---I---J---M---N feature-xxx
- / ` `
- E---F---G---K----L'---M'---N' develop (where M', N' is chery pick from M, N)
- /
- A---B---C---D master
复制代码 除了修复 Sqaush Merge 引来的不测冲突以外, cherry-pick 还常用于从不稳定的开辟分支(不具备合并到主分支的条件)挑选个别需要告急发布的安全修复到稳定分支中, 这种场景合并没有意义, 由于合并反而会引入更多不需要的变更。
4. Rebase
最后一种常用的, 也是最强大(复杂)的合并方式是 Rebase。顾名思义, Rebase(变基) 即变更当前分支的根节点, 我们以如下拓扑结构为例介绍 Rebase 的流程:
- E---F---G feature-xxx
- /
- A---B---C---D develop
复制代码 当我们开辟的基础分支已经落后于原分支时, 我们在提交接码前就应该使用 rebase :
- ➜ git rebase develop feature-xxx
复制代码 执行以上操作后, 拓扑结构将调整为如下所示:
- E'---F'---G' feature-xxx
- /
- A---B---C---D develop
复制代码 其中, E’, F’, G’ 与原来的 E, F, G 内容完全一致, 本质上是在另一个根节点后重新应用原来的提交。
值得注意的是, rebase 后的分支是一定符合 fast-forward 的优化条件的, 这意味着 rebase merge 可以不创建无意义的合并节点, 有利于保持代码分支的可读性。
交互式 Rebase
Rebase 本质上是在另一个根节点上 重放 你的代码提交记录, 因此 rebase 不但仅具备变更根节点的能力, 还能压缩代码提交记录(squash), 修改代码提交信息(edit) 乃至可删除部门提交(drop)。我们可以通过启动一个交互式的 Rebase 会话来做到上述功能:
执行上述指令后, Git 将打开一个编辑器, 依据指引操作即可:
- pick 6b2e82f 2
- pick a95710b 4
- ## 变基 7244a00..a95710b 到 7244a00(2 个提交)
- #
- ## 命令:
- ## p, pick <提交> = 使用提交
- ## r, reword <提交> = 使用提交,但修改提交说明
- ## e, edit <提交> = 使用提交,进入 shell 以便进行提交修补
- ## s, squash <提交> = 使用提交,但融合到前一个提交
- ## f, fixup <提交> = 类似于 "squash",但丢弃提交说明日志
- ## x, exec <命令> = 使用 shell 运行命令(此行剩余部分)
- ## b, break = 在此处停止(使用 'git rebase --continue' 继续变基)
- ## d, drop <提交> = 删除提交
- ## l, label <label> = 为当前 HEAD 打上标记
- ## t, reset <label> = 重置 HEAD 到该标记
- ## m, merge [-C <commit> | -c <commit>] <label> [## <oneline>]
- ## . 创建一个合并提交,并使用原始的合并提交说明(如果没有指定
- ## . 原始提交,使用注释部分的 oneline 作为提交说明)。使用
- ## . -c <提交> 可以编辑提交说明。
- #
- ## 可以对这些行重新排序,将从上至下执行。
- #
- ## 如果您在这里删除一行,对应的提交将会丢失。
- #
- ## 然而,如果您删除全部内容,变基操作将会终止。
- #
- ## 注意空提交已被注释掉
复制代码 如何区分差别的合并方式?
一般情况下, 我们选择差别的合并方式应该基于同一个准则: 维护一份干净且可用的代码提交汗青。为此, 我们需要区分差别的场景使用以上差别的合并方式。
1. Merge
毋庸置疑, 合并是最通用的代码合并方式。当你需要将来自一个分支的整个功能完全合并到另一个分支时, 使用 merge 可以将代码提交汗青完整地保存下来, 为代码溯源(git blame)提供最有价值的技术指导。
以 Git 工作流为例, 当需要发布 develop 至稳定的情况时, 就应当将 develop 分支 merge 到 master 分支。
2. Squash Merge
如前所述, Squash Merge 会将代码提交记录压缩合并为 1个, 而且操作不当容易引发代码冲突。不过仍然有些情况是建议将提交记录举行压缩的:
以功能开辟为例, 当我们开辟一个功能分支时, 可能会产生许多意义不大的提交记录(例如可能 commit 后才发现有 typo, 于是又多了个修复 typo 的 commit)。
一般情况下, 是否使用 Squash Merge 是一个团队偏好问题:
- 如果你觉得意义不大的提交记录污染了主分支的代码汗青, 那么你将代码合并到主分支前就应当合并你的代码提交汗青, 而 Squash Merge 则是其中一种合并提交记录的方式。
- 如果你觉得所有提交都应该被追踪(例如某些团队以提交记录作为工作根据?), 那么你的所有提交就不应该被任何人”窜改”!
一些团队可能以为使用 Squash Merge 有助于保持主分支的整洁, 但是并不能说这就是绝对正确的事变,以是这主要还是一个偏好问题。
而且, 为什么不使用 rebase 调整代码记录后再举行代码合并呢!
3. Cherry-pick
Cherry-pick 用于从某个分支挑选个别提交记录合并至指定分支, 因此 cherry-pick 常用的场景即是从开辟分支中 挑选(pick) 安全修复至稳定分支(如, master)。除此之外, 在日常开辟中如需从其他开辟分支中摘取部门代码时, 亦可使用 cherry-pick 。
4. Rebase
Rebase 是 Git 常用下令中最强大的下令之一, 使用场景亦是最广泛的, 包罗:
这是团队开辟中最为常见的场景: 当其他人将代码合并至远程的 develop 分支后, 你的开辟分支将落后于 develop 分支。
为了保证开辟的功能不被其他人粉碎, 当地测试时应当保证当地代码是最新的。在这种情况下, 我们可以将 develop 分支逆向合并至当地开辟分支, 但是这会产生不必要的代码提交记录。使用 Rebase 即可更优雅地解决这个 “噪音” 问题。
正如媒介, rebase 后的分支是一定符合 fast-forward 的优化条件的, 这意味着 rebase merge 可以不创建无意义的合并节点, 有利于保持代码分支的可读性。
这种情况在实际开辟中也是经常发生的: 例如当你在代码提交后不测发今世码中(或者 commit message 中)存在错别字, 但是这份代码又并未合并到主分支时。
我们期望维护一份干净而可用的代码提交汗青,不希望某些意义不大或存在歧义的提交记录污染主分支的代码提交汗青, 此时我们就应该使用可交互式的 Rebase 压缩或调整代码提交记录。
总结
Git 提供了多种合并代码的方式, 日常开辟使用普通的 Merge 即可。如非团队开辟约定, 尽量少用 Squash Merge 。如需压缩代码提交记录, 可于当地使用 Rebase 调整代码提交汗青后, 再合并至主分支。而对于安全修复等告急发布, 可使用 cherry-pick 摘取提交记录合并至主分支。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |