git rebase VS git merge

写在前面

如果你不能很好的应用 Git,那么这里为你提供一个非常棒的 Git 在线练习工具 Git Online(回复公众号「工具」,获取更多内容) ,你可以更直观的看到你所使用的命令会产生什么效果

另外,你在使用 Git 合并分支时只会使用 git merge 吗?有时使用 git rebase 可以比 git merge 做出更优雅的操作
- - - - -

Merge 与 Rebase

不知怎么,git rebase 命令被赋予了一个神奇的污毒声誉,初学者应该远离它,但它实际上可以让开发团队在使用时更加轻松。

你可以将它理解成「七伤拳」,「七伤拳」并不是不能练,只是练「七伤拳」有一个先诀条件,那就是内功境界一定要非常高,即你要充分理解 git rebase 命令

在本文中,我们将 git rebase 与 git merge 命令进行比较。在 Git 工作流中,说明所有可以使用 rebase 的场景

概念概述

关于 git rebase ,首先要理解的是它解决了和 git merge 同样的问题。这两个命令都旨在将更改从一个分支合并到另一个分支,但二者的合并方式却有很大的不同。

当你在专用分支上开发新 feature 时,然后另一个团队成员在 master 分支提交了新的 commits,这会发生什么?这会导致分叉的历史记录,对于这个问题,使用 Git 作为协作工具的任何人来说都应该很熟悉。

现在,假设在 master 分支上的新提交与你正在开发的 feature 相关。需要将新提交合并到你的 feature 分支中,你可以有两个选择:merge 或者 rebase。

Merge 方式

最简单的方式是通过以下命令将 master 分支合并到 feature 分支中:

git checkout feature
git merge master

或者,你可以将其浓缩为一行命令:

git merge feature master

这会在 feature 分支中创建一个新的 merge commit,它将两个分支的历史联系在一起,请看如下所示的分支结构:

使用 merge 是很好的方式,因为它是一种 非破坏性的 操作。现有分支不会以任何方式被更改。这避免了 rebase 操作所产生的潜在缺陷(下面讨论)。

另一方面,这也意味着 feature 分支每次需要合并上游更改时,它都将产生一个额外的合并提交。如果master 提交非常活跃,这可能会严重污染你的 feature 分支历史记录。尽管可以使用高级选项 git log 缓解此问题,但它可能使其他开发人员难以理解项目的历史记录

Rebase 方式

作为 merge 的替代方法,你可以使用以下命令将 master 分支合并到 feature分支上:

git checkout feature
git rebase master

这会将整个 feature 分支移动到 master 分支的顶端,从而有效地整合了所有 master 分支上的提交。但是,与 merge 提交方式不同,rebase 通过为原始分支中的每个提交创建全新的 commits 来 重写 项目历史记录。

rebase 的主要好处是可以获得更清晰的项目历史。首先,它消除了 git merge 所需的不必要的合并提交;其次,正如你在上图中所看到的,rebase 会产生完美线性的项目历史记录,你可以在 feature分支上没有任何分叉的情况下一直追寻到项目的初始提交。这样可以通过命令 git loggit bisect 和 gitk 更容易导航查看项目。

但是,针对这样的提交历史我们需要权衡其「安全性」和「可追溯性」。如果你不遵循 Rebase 的黄金法则,重写项目历史记录可能会对你的协作工作流程造成灾难性后果。而且,rebase 会丢失合并提交的上下文, 你也就无法看到上游更改是何时合并到 feature 中的。

交互式 Rebase

交互式 rebase 使你有机会在将 commits 移动到新分支时更改这些 commits。这比自动 rebase 更强大,因为它提供了对分支提交历史的完全控制。通常,这用于在合并 feature 分支到 master 之前清理其杂乱的历史记录。

要使用交互式 rebase,需要使用 git rebase 和 -i 选项:

git checkout feature
git rebase -i master

这将打开一个文本编辑器,列出即将移动的所有提交:

pick 33d5b7a Message for commit #1
pick 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3

此列表准确定义了执行 rebase 后分支的外观。通过更改 pick 命令或重新排序条目,你可以使分支的历史记录看起来像你想要的任何内容。例如,如果第二次提交 fix 了第一次提交中的一个小问题,您可以使用以下 fixup 命令将它们浓缩为一个提交:

pick 33d5b7a Message for commit #1
fixup 9480b3d Message for commit #2
pick 5c67e61 Message for commit #3

保存并关闭文件时,Git将根据您的指示执行 rebase,从而产生如下所示的项目历史记录:

消除这种无意义的提交使你的功能历史更容易理解。这是 git merge 根本无法做到的事情。至于 commits 条目前的 pickfixupsquash 等命令,在 git 目录执行 git rebase -i 即可查看到,大家按需重排或合并提交即可,注释说明非常清晰,在此不做过多说明:

Rebase 的黄金法则

一旦你理解了什么是 rebase,最重要的是要学习什么时候不能使用它。git rebase 的黄金法则是永远不要在公共分支上使用它。

例如,想想如果你 rebase master 分支到 feature 分支之上会发生什么:

rebase 将所有 master 分支上的提交移动 feature 分支的顶端。问题是这只发生在 你自己 的存储库中。所有其他开发人员仍在使用原始版本的 master。由于 rebase 导致全新 commit,Git 会认为你的 master 分支历史与其他人的历史不同。

此时,同步两个 master 分支的唯一方法是将它们合并在一起,但是这样会产生额外的合并提交和两组包含相同更改的提交(原始提交和通过 rebase 更改的分支提交)。不用说,这是一个令人非常困惑的情况。

因此,在你运行 git rebase 命令之前,总是问自己,还有其他人在用这个分支吗? 如果答案是肯定的,那就把你的手从键盘上移开,开始考虑采用非破坏性的方式进行改变(例如,git revert 命令)。否则,你可以随心所欲地重写历史记录。

Force Push

如果你尝试将 rebase 了的 master 分支推送回 remote repository,Git 将阻止你这样做,因为它会与远程master 分支冲突。但是,你可以通过传递 --force 标志来强制推送,如下所示:

# Be very careful with this command!
git push --force

这样你自己 repository 的内容将覆盖远程 master分支的内容,但这会使团队的其他成员感到困惑。因此,只有在确切知道自己在做什么时才要非常小心地使用此命令。

如果没有人在 feature branch 上作出更改,你可以使用 force push 将本地内容推送到 remote repository 做清理工作

工作流程演练

rebase 可以根据你所在团队的需要方便的整合到现有的 Git 工作流程中。在本节中,我们将了解 rebase 在功能开发的各个阶段可以提供的好处。

在任何工作流程中,利用 git rebase 是为每个功能创建专用分支。这为你提供了必要的分支,以安全地利用 rebase:

本地清理

将 rebase 纳入工作流程的最佳方法之一是清理本地正在进行的功能。通过定期执行交互式 rebase,你可以确保功能中的每个提交都具有针对性和意义。这可以使你在编写代码时无需担心将其分解为隔离的提交(多个提交),你可以在事后修复整合它。

使用 git rebase 时,有两种情况:feature 父分支(例如 master )的提交,或在 feature 中的早期提交。我们在 交互式 Rebase 部分已经介绍了第一种情况的示例。我们来看后一种情况,当你只需要修复最后几次提交时,以下命令仅做最后 3 次提交的交互式 rebase。

git checkout feature
git rebase -i HEAD~3

通过指定 HEAD~3 ,你实际上并没有移动分支,你只是交互式地重写其后的3个提交。请注意,这不会将上游更改合并到 feature 分支中。

如果要使用此方法重写整个功能,git merge-base 命令可用于查找 feature 分支的原始 base。以下内容返回原始 base 的提交ID,然后你可以将其传递给 git rebase

git merge-base feature master

交互式 rebase 的使用是引入git rebase 工作流的好方法,因为它只影响本地分支。其他开发人员唯一能看到的就是你提交的最终版,这应该是一个简洁易懂易跟踪的分支历史记录。

但同样,这仅适用于 私有 feature分支。如果你通过相同的功能分支(公共分支)与其他开发人员协作,那么你是 不被允许 重写其历史记录的。

将上游更改合并到功能分支中

在 概念概述 部分中,我们了解了 feature 分支可以使用 git merge 或 git rebase 合并 master 分支的上游更改 。merge 是一个安全的方式,可以保留存 git repository 的整个历史记录,而 rebase 则是通过将 feature 分支移动到 master 顶端来创建线性历史记录。

这种使用 git rebase 类似于本地清理,但在此过程中它包含了那些来自 master 上游提交。

请记住,将当前提交 rebase 到远程 branch(非 master 分支)一样是合法的。当与另一个开发人员协作使用相同的功能并且你需要将他们的更改合并到你的 repository 时,就会发生这种情况。

例如,如果你和另一个名为 John 的开发人员添加了对 feature 分支的提交,在你 fetch (注意 fetch 并不会自动 merge )来自 John 的远程 feature分支后,你的 repository 可能如下所示:

你可以整合上来自上游的分叉:要么用 john/feature merge 本地 feature ,要么 rebase 本地feature 到john/feature 的顶部。

请注意,此 rebase 不违反 Rebase 黄金规则,因为只有你的本地 feature 提交被移动, 之前的所有内容都不会受到影响。这就像是说 "将我的更改添加到 John 已经完成的工作中"。在大多数情况下,这比通过合并提交与远程分支同步更直观。

默认情况下,使用 git pull 命令执行合并,但你可以通过向其传递 --rebase 选项来强制它将远程分支 以 rebase 方式集成。

git pull --rebase

使用 Pull 请求 Review Feature

如果你在代码审查过程中使用 pull 请求,在使用了 pull 请求之后你应该避免使用 git rebase 。一旦你发出 pull 请求,其他开发人员就会查看你的提交,这意味着它是一个 公共 分支。重写其历史记录将使 Git 和你的队友无法跟踪添加到该功能的任何后续提交。

其他开发人员的任何更改都需要合并 git merge 而不是 git rebase

因此,在提交拉取请求之前,通常使用交互式 rebase 清理代码通常是个好的办法。注意使用顺序

集成已批准的功能

在你的团队批准某项 feature 后,你可以选择将该功能 rebase 到 master 分支的顶端,然后git merge再将该功能集成到主代码库中。

这与将上游更改合并到 feature 分支中的情况类似,但由于你不允许在 master 分支中重写提交,因此你必须最终使用 git merge 该功能进行集成。但是,通过在 merge 之前执行 rebase,你可以确保会以 fast-forward 方式 merge,从而产生完美的线性历史记录。

如果您不熟悉 git rebase,可以随时在临时分支中执行 rebase。这样,如果你不小心弄乱了功能的历史记录,可以查看原始分支,然后重试。例如:

git checkout feature
git checkout -b temporary-branch
git rebase -i master
# [Clean up the history]
git checkout master
git merge temporary-branch

总结

如果你更喜欢没有不必要的干净的合并提交,线性历史记录,你就需要开始了解使用 rebase 功能。同时你应该会使用 git rebase 而不是 git merge集成来自另一个分支的更改。

另一方面,如果你想保留项目的完整历史记录并避免重写公共提交的风险,你可以坚持下去git merge。这两种选择都是完全有效的,但至少现在你可以选择利用 git rebase 的好处 。

灵魂追问

  1. 你有使用过 git rebase 吗?这样清晰的线形历史是不是可以尝试一下呢?
  2. 交互式 rebase 提交条目前的命令 fixup 等你能灵活使用吗
  3. 在 feature 分支上开发时,试试 git pull -rebase?

带着疑问去思考,然后串联,进而归纳总结,不断追问自己,进行自我辩证,像侦查嫌疑案件一样看待技术问题,漆黑的街道,你我一起寻找线索,你就是技术界大侦探福尔摩斯


提高效率工具


推荐阅读

git rebase VS git merge的更多相关文章

  1. git rebase和git merge的用法

    http://softlab.sdut.edu.cn/blog/subaochen/2016/01/git-rebase%E5%92%8Cgit-merge%E7%9A%84%E7%94%A8%E6% ...

  2. git rebase vs git merge详解

    https://medium.com/@porteneuve/getting-solid-at-git-rebase-vs-merge-4fa1a48c53aa#.std3ddz0g 请参考另外一篇文 ...

  3. git rebase VS git merge? 更优雅的 git 合并方式值得拥有

    写在前面 如果你不能很好的应用 Git,那么这里为你提供一个非常棒的 Git 在线练习工具 Git Online ,你可以更直观的看到你所使用的命令会产生什么效果 另外,你在使用 Git 合并分支时只 ...

  4. git rebase 与git merge 小结

    git merge是用来合并两个分支的. $ git merge b   将b分支合并到当前分支 同样  $ git rebase b ,也是把 b分支合并到当前分支 ---------------- ...

  5. git rebase 和 git merge 总结

    git merge 和 git rebase 都是用于合并分支,但二者是存在区别的. 在使用时,记住以下两点: 当你从 remote 去 pull 的时候,永远使用 rebase(除了一个例外) 当你 ...

  6. git rebase、git merge、git cherry-pick 使用详解

    1.git cherry-pick 是合入其他分支的某一次或者几次提交(cherry-pick是挑选的意思):把其他分支的某些功能合入当前分支 2.git merge 把其他分支合入当前分支,一般用作 ...

  7. 你在开发过程中使用Git Rebase还是Git Merge?

    摘要:在git里面经常的一个争论是到底用rebase还是用merge? 1. 痛苦吗?代码历史中的迷失羔羊 我们先来看一个真实的代码提交历史图形化截图: 图片源自 https://storage.kr ...

  8. git rebase和git merge的区别

    前言:    平时工作中发现一般同事在同步远程代码的时候都是用git pull,其实git pull包含有两个操作,一个是fetch远程的代码,一个是将本地当前的代码和远程代码进行merge,即git ...

  9. git rebase与 git合并(error: failed to push some refs to)解决方法

    1.遇到的问题 本地有一个git仓库,在github上新建了一个空的仓库,但是更新了REWADME.md的信息,即在github上多了一个提交. 关联远程仓库,操作顺序如下: git remote a ...

随机推荐

  1. AtCoder Grand Contest 014题解

    传送门 \(A\) 首先大力猜测一下答案不会很大,所以次数大于\(10^6\)输出\(-1\)就行了 不过我并不会证上界,据说是因为如果\(a=b=c\)且都是偶数肯定\(-1\),否则设\(a\le ...

  2. Codeforces 1172E Nauuo and ODT [LCT]

    Codeforces ZROI那题是这题删掉修改的弱化版--ZROI还我培训费/px 思路 按照套路,我们考虑每种颜色的贡献,然后发现不包含某种颜色的路径条数更容易数,就是删掉该颜色的点后每个连通块大 ...

  3. pve 导入 ova

    匆忙记录 Proxmox includes qm importdisk as command. Extract your ova: tar -xvf *.ova Create a new VM wit ...

  4. 小程序input组件失焦的使用

    失去焦点就开始做数据请求判断电话号码是正确 <view class='register-input-box'> <input class='register-input' place ...

  5. 使用sqlyog连接 Mysql 出现1251错误

    错误如图所示: 错误详情信息: client does not support authentication protocol requested by server;consider upgradi ...

  6. windows10 命令行修复系统引导

    文章介绍的方法适用于拥有一个刻录有原版win10安装镜像的用户 下载地址:https://www.microsoft.com/zh-cn/software-download/windows10 首先进 ...

  7. 前端 MV*框架的意义

    早期前端都是比较简单,基本以页面为工作单元,内容以浏览型为主,也偶尔有简单的表单操作,基本不太需要框架. 随着 AJAX 的出现,Web2.0的兴起,人们可以在页面上可以做比较复杂的事情了,然后前端框 ...

  8. 为什么要监控sql语句?如何监控?

    01 为什么要监控sql语句? ① 因为程序大了以后,sql语句有可能被多个地方调用 .你不能确认当前时间是不是只执行了你需要的那条语句 . ② 有的持久层框架采用linq的语法来写sql , 程序中 ...

  9. darknet 的python接口使用

    首先,python接口文件在安装好的darknet目录下的python文件夹,打开就可以看到 这里的darknet.py文件就是python接口 用编辑器打开查看最后部分代码: 使用十分简单,先将网络 ...

  10. shell编程系列9--文本处理三剑客之sed概述及常见用法总结

    shell编程系列9--文本处理三剑客之sed概述及常见用法总结 sed的工作模式:对文本的行数据一行行处理,如下图 sed(stream editor),是流编辑器,依据特定的匹配模式,对文本逐行匹 ...