1. 痛苦吗?代码历史中的迷失羔羊

我们先来看一个真实的代码提交历史图形化截图:

图片源自 https://storage.kraken.io/kk8yWPxzXVfBD3654oMN/c8b97f4dbb5f7d49fc3eb3624eafff79/london-tube-map-commit.png

https://dev.to/neshaz/git-merge-vs-git-rebase-5134

不知道大家看到这张图以后有什么感受?是不是很无语呢?我是无语凝噎的感受。代码历史到了这个地步,基本上是废了的!!!

通过本文我们就来谈一下代码历史线的问题。这里涉及到一个代码历史的管理问题,如果你希望拥有比较清晰的代码历史,从而可以很快速地追溯你以前的代码记录的话,你一定要关注这个话题。

如果你作为一个程序员用过mercurial,你可能对这个rebase概念已经有所了解。目前程序员用的最多的source control工具是git。在git里面经常的一个争论是到底用rebase还是用merge?

说争论实际上是不太准确的。因为在实际工作中,这主要有两种情况,一是根本就不知道rebase,另外一种是根本就不知道怎么用rebase。

那我们就说一下rebase和merge的区别,在功能上rebase把你当前的修改工作提升到最前沿,
你自己独有的修改记录顺序不会改变,但是时间主要会基于当前目标分支的时间戳进行调整。

Merge不会改变时间戳。这样就会导致不同的人在合并自己修改代码记录的时候,会出现相互交错的情形。本文开篇是一张代码提交历史的截屏,里面的连线就像电路板一样复杂。

出现这样的情况,主要是因为程序员不做rebase直接merge造成的。

而类似这样电路板状的代码提交历史是没有意义的,因为太过复杂了。几乎没有可读性。如果你想去查看某些历史记录的时候,很容易被迅速淹没在这些信息海洋里面无法自拔,最后失去自我,迷失了。

2. Git Merge vs. Rebase

小注:虽然已经参考链接中标注,但是有必要再强调一下此节翻译参考了如下链接的英文内容:

https://dev.to/neshaz/git-merge-vs-git-rebase-5134

Git merge 和rebase的目的是一样的,它们都是将多个分支合并成一个。虽然他们最终的目标是一样的,但这两种方法实现的方式是不同的。那么我们应该用哪个呢?

这里我们有一个示例仓库,它有两个不同的分支:主分支和特性分支。我们想把它们融合在一起。让我们来看看如何使用这些方法来解决这个问题。

图片源自 https://storage.kraken.io/kk8yWPxzXVfBD3654oMN/fc73a41ce658a6a566e2a54d60534ade/git-flow.png

https://dev.to/neshaz/git-merge-vs-git-rebase-5134

Merge

当你运行 git merge 时,你的 HEAD 分支会生成一个新的提交,并保留每个提交历史的祖先。

图片源自 https://storage.kraken.io/kk8yWPxzXVfBD3654oMN/673b91456bdc6fd454c5ad203f825568/git-merge-2.png
https://dev.to/neshaz/git-merge-vs-git-rebase-5134
Fast forward merge是一种不创建提交的合并类型,会更新分支指针到上一次提交。

Rebase

Rebase是将一个分支的修改重写到另一个分支上,而不需要创建新的提交。

你在特性分支上的每一个提交,都会在主分支上创建一个新的提交。这看起来就像这些提交一直是写在主分支之上的一样。

图片源自 https://storage.kraken.io/kk8yWPxzXVfBD3654oMN/5ade4f7276bc6ad18dad4b6078950ac9/git-rebase.png
https://dev.to/neshaz/git-merge-vs-git-rebase-5134

Merge的优点和缺点

优点

  • 使用简单,易于理解。
  • 保持源分支的原始上下文。
  • 源分支上的提交与其他分支的提交是分开的。
  • 可以保留提交历史。

缺点

图片源自 https://storage.kraken.io/kk8yWPxzXVfBD3654oMN/c8b97f4dbb5f7d49fc3eb3624eafff79/london-tube-map-commit.png
https://dev.to/neshaz/git-merge-vs-git-rebase-5134

Rebase的优点和缺点

优点

  • 代码历史是简化的、线性的、可读的。
  • 与许多独立的特性分支的提交历史相比,操作单个提交历史更容易。
  • 干净、清晰的提交信息可以更好地跟踪一个bug或何时引入的某个功能。可以避免众多的单行提交污染历史。

缺点

  • 会更改历史提交时间,可能会丢失上下文。

比起Merge,你需要更加小心的使用Rebase。

应该用Merge还是Rebase?

当你的团队对于rebase不熟悉时,那么git merge就是你的正确选择。

  • Merge允许保存任何给定功能的提交历史,而不必担心覆盖提交和改变历史。
  • 它可以避免不必要的 git revert或reset。

另一方面,如果你更看重干净、线性的代码历史,那么git rebase是最合适的。这种方式可以避免不必要的提交,并保持更集中和线性的变化!

这里要注意的是,如果你不正确地重写了历史,可能会导致严重的问题,所以在使用Rebase时请确保知道你在做什么。

3. 一杯水与一桶水

先说一下一杯水和一桶水的关系,这个关系可以用来描述老师向学生传授知识的情形。主要意思是说老师要是给学生一杯水的话,这个老师必须要有一桶水才行。

其实对于我们软件行业来说,这个道理也是适用的。

关于本文的聚焦点代码历史,我们想给外部呈现的是清晰干净的代码历史记录。要到的这个目标,我们需要做很多的工作,这项工作相较于干净的代码历史就是一桶水与一杯水的关系。

这也是我们经常说的,对自己狠一点,让别人舒服一点。对自己狠一点不是一句空话,实际上是让自己绞尽脑汁的去想,如何把这个事情做好?把自己放在对方的角度上去看我们的输出结果。我们问自己,我作为这些代码的维护者和接收者,是不是可以接受这样混乱的代码历史记录,答案当然是否定的。

明白了这一点,在我们做代码历史管理的时候,再苦再累也是值得的。

一些有多年工作经验的程序员,他们可能用过很多代码管理的工具,这些代码管理工具在使用的时候经常用的一个操作就是merge。进入到git时代以后,这些程序员也保留了这样的习惯,直接就merge。

我跟一些程序员聊过,他们甚至都没有意识到有rebase这个操作。有的则觉得rebase太麻烦了,用了几次就放弃了。

这绝对是人之常情。

当我们习惯了用一种方式做事的时候,我们做得越久感觉越安全,因为它是行之有效的,能够解决问题的。当有另外一种更好的方式,但是却与以前的方式有很大差异的时候,我们就会有本能的排斥心理。这是源于我们对未知领域的一种恐惧感。

离开舒适区以后,我们大多数人都会有一种不适应,有的甚至会产生很强烈的失落感。

但是当我们回归本心的时候,我们会发现我们所付出的一切,经受的痛苦都是值得的。因为我们的初心就是让用户满意。代码历史的用户就是我们这些程序员。

4. 如何正确做rebase?

要点备注:

Rebase相关的要点有这几个:

  1. 在自己的分支上做Rebase;
  2. 要Rebase正确的目标分支,注意这个目标分支不是你的远程分支,这个地方经常有人犯错;
  3. Rebase要常做,以避免出现冲突,或者冲突的难度增加;
  4. 在合入代码之前,一定要最后做一次Rebase再Merge;
  5. 最好用Squash Merge, 添加正确的提交消息;
  6. 不要在主分支上做Rebase;
  7. 尽量不要force push;

在主分支上应该rebase吗? 怎么去做rebase?

首先,我要强调一下,在主分支上不要做rebase。这是因为如果你在主分支上做了rebase,如果你是第1次push可能没有问题,但是如果别的人也做了一个rebase,这个时候就导致你的主分支上有两个不同的head,这个时候如果想再push的话就存在问题了。

如果一切可控的话,你可以非常粗暴的使用强制push。但是如果团队成员比较多,会导致这样的操作会冲掉其他已经进来的提交。

除非万不得已,我们坚决不能在主分支上去做rebase。

接下来说一个正确使用rebase的场景。在接到一个任务以后,我们会在主分支上最新的节点处提交上创建一个新的分支。在新的分支上,我们会不断的进行新的提交,直到完成这个分支任务。

到这个时候,我们想创建一个merge request或者pull request,在此之前,我们首先要做的就是rebase,rebase选的目标分支是我们的主分支。

完成这次rebase操作以后,我们就可以创建mr或者pr了。在审查过程中可能有很多修改意见,修改完成以后需要先
rebase再push。如此反复几次,mr或者pr审批通过。此时要做一次squash merge把数次提交打包合成一个到主分支上。

以上是我对在正常工作流程中如何使用rebase进行了一个现实案例的描述讲解,希望大家去体验一下,告诉我体会,请留言说说你的想法。


PS:
其他知友的答案都说到冲突的问题:

1. 用merge确实只需要解决一遍冲突,比较简单粗暴
2. 用rebase有时候会需要多次fix冲突(原因在于本地分支已经提交了非常多的commit,而且很久都没有和上游合并过)

还有一点说明的是,在项目中经常使用git pull来拉取代码,git pull相当于是git fetch + git merge,如果此时运行git pull -r,也就是git pull --rebase,相当于git fetch + git rebase

最后推荐一些git可视化工具,我用的是gitkraken,这些工具功能基本一样,看个人喜欢好使用


延伸:

git rebase和git merge的区别

1、前言

如果你作为一名开发人员,许多时候必须在 Merge和 Rebase 之间进行选择。 我们从互联网上获得的所有参考文档文章,几乎都告诉我们“不要使用 Rebase,它可能会导致严重的问题。”在这里,我将解释什么是 merge 和 rebase,为什么你应该(或者不应该)使用它们,以及如何做。Git Merge 和 Git Rebase 其实是都是为了完成同样的目的。 它们旨在将来自多个分支的更改集成到一个分支中。 虽然最终目标是相同的,但这两种方法以不同的方式实现。这个问题在 Git 社区中引起了分歧。有些人认为应该总是使用 rebase ,而另一些人则认为应该总是使用 merge 。双方都有一些令人信服的好处。

2、git merge

  1. merge 是一种常见的做法,不管创建分支是为了测试、修复 bug 还是其他原因,将提交更改合并到另一个位置。 更具体地说,merge 获取源分支的内容并将它们与目标分支集成。 在此过程中,仅更改目标分支。 源分支历史保持不变。
  2. 优点:
  1. 缺点:
  1. 如何操作(假设A、B两个分支,需要将A分支合并到B分支上):
     git checkout B    切换到B分支上git merge A        将A分支合并到B分支上

或是通过:

     git merge A B      将A分支合并到B分支上

3、git rebase

  1. Rebase 是将更改从一个分支集成到另一个分支的另一种方法。 Rebase 将所有更改压缩为单个“补丁”。然后它将补丁集成到目标分支上。与 merge 不同,重定位使历史变得扁平,因为它将完成的工作从一个分支转移到另一个分支。在这个过程中,不需要的历史记录被消除。Rebases 是更改应从层次结构顶部向下传递的方式,并且 Merge 是它们向上流回的方式
  2. 优点:
  1. 缺点:
  1. 如何操作(将 feature 分支 Rebase 到主分支上):
     git checkout featuregit rebase master

4、交互式的 rebase

这允许在将提交移动到新分支时更改提交。 这比自动 rebase 更强大,因为它提供了对分支的提交历史的完全控制。 通常,这用于在将 feature 分支合并到主分支之前,清理杂乱的历史记录。
bash 代码:

     $ git checkout feature$ git rebase -i master

这将通过打开编辑器,列出即将移动的所有提交。
bash 代码:

     pick 22d6d7c Commit message#1pick 44e8a9b Commit message#2pick 79f1d2h Commit message#3

这精确定义了 rebase 执行后分支的确切内容。通过重新排序实体,可以使历史记录看起来像您想要的任何内容。例如,可以使用 fixup ,squash ,edit 等命令代替 pick。

5、我们应选择哪种方式

团队在设置 Git rebase 与 merge 策略时,需要考虑几个问题:

6、个人意见

随着团队的增长,使用始终 merge 策略管理或跟踪开发更改将变得非常困难。要有一个清晰易懂的提交历史记录,使用 Rebase 是合理和有效的。

通过考虑以下情况和指南,您可以充分利用 Rebase :

  1. 你在本地开发:如果您还没有与其他人合作。此时,你应该更喜欢 rebase 而不是 merge 以保持历史的整洁。如果您拥有存储库的个人分支并且未与其他开发人员共享,那么即使您已经推送到分支之后,也可以安全地进行 rebase 。
  2. 您的代码已经准备好接受评审:您创建了一个 pull 请求。其他人正在评审您的工作,并可能将其提取到他们的分支中进行本地评审。此时,您不应该 rebase 你的工作。您应该创建 rework 提交并更新您的 feature 分支。这有助于拉取请求中的可追溯性,并防止意外的历史记录破坏。
  3. 评审已经完成,准备集成到目标分支中:恭喜你!您将要删除您的 feature 分支。考虑到其他开发人员从现在起不会在这些更改中进行获取合并,这是您清理历史记录的机会。此时,您可以重写历史记录并折叠原始提交,并将那些讨厌的’pr rework’和’merge’提交到一小组重点提交中。为这些提交创建显式合并是可选的,但有价值。它记录了该功能何时升级为 master 。

1、什么是 rebase?

  1. git rebase 你其实可以把它理解成是“重新设置基线”,将你的当前分支重新设置开始点。这个时候才能知道你当前分支于你需要比较的分支之间的差异。
    原理很简单:rebase需要基于一个分支来设置你当前的分支的基线,这基线就是当前分支的开始时间轴向后移动到最新的跟踪分支的最后面,这样你的当前分支就是最新的跟踪分支。这里的操作是基于文件事务处理的,所以你不用怕中间失败会影响文件的一致性。在中间的过程中你可以随时取消rebase 事务。
    官方解释: https://git-scm.com/book/zh/v2/Git-分支-变基

2、git rebase 和 git merge 有啥区别?

  1. rebase会把你当前分支的 commit 放到公共分支的最后面,所以叫变基。就好像你从公共分支又重新拉出来这个分支一样。
    举例:如果你从 master 拉了个feature分支出来,然后你提交了几个 commit,这个时候刚好有人把他开发的东西合并到 master 了,这个时候 master 就比你拉分支的时候多了几个 commit,如果这个时候你 rebase master 的话,就会把你当前的几个 commit,放到那个人 commit 的后面。
  2. merge 会把公共分支和你当前的commit 合并在一起,形成一个新的 commit 提交。

3、注意:

4、抛出问题:

  1. 为什么不要再公共分支使用rebase?
    因为往后放的这些 commit 都是新的,这样其他从这个公共分支拉出去的人,都需要再 rebase,相当于你 rebase 东西进来,就都是新的 commit 了
    1-2-3 是现在的分支状态
    这个时候从原来的master ,checkout出来一个prod分支
    然后master提交了4.5,prod提交了6.7
    这个时候master分支状态就是1-2-3-4-5,prod状态变成1-2-3-6-7
    如果在prod上用rebase master ,prod分支状态就成了1-2-3-4-5-6-7
    如果是merge
    1-2-3-6-7-8
    … |4-5|
    会出来一个8,这个8的提交就是把4-5合进来的提交

  2. merge和rebase实际上只是用的场景不一样
    更通俗的解释一波,比如rebase,你自己开发分支一直在做,然后某一天,你想把主线的修改合到你的分支上,做一次集成,这种情况就用rebase比较好.把你的提交都放在主线修改的头上如果用merge,脑袋上顶着一笔merge的8,你如果想回退你分支上的某个提交就很麻烦,还有一个重要的问题,rebase的话,本来我的分支是从3拉出来的,rebase完了之后,就不知道我当时是从哪儿拉出来的我的开发分支同样的,如果你在主分支上用rebase, rebase其他分支的修改,是不是要是别人想看主分支上有什么历史,他看到的就不是完整的历史课,这个历史已经被你篡改了

5、常用指令

  • git rebase -I dev 可以将dev分支合并到当前分支
    这里的”-i“是指交互模式。就是说你可以干预rebase这个事务的过程,包括设置commit message,暂停commit等等。

  • git rebase –abort 放弃一次合并

  • 合并多次commit操作:
    1 git rebase -i dev
    2 修改最后几次commit记录中的pick 为squash
    3 保存退出,弹出修改文件,修改commit记录再次保存退出(删除多余的change-id 只保留一个)
    4 git add .
    5 git rebase --continue

你在开发过程中使用Git Rebase还是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 和 Git merge 的区别,你知道吗?

    编辑搜图 请点击输入图片描述 Git是大多数程序必备的工具之一,Git常用那么几个命令:pull.push.status.merge.rebase.Git rebase 和 Git merge都是合并 ...

  3. git rebase和git merge使用方法详解

    看了网上很多讲git rebase和git merge的文章,这里做下总结,其中主要参考了这两位大佬的博客,后面附上链接 https://blog.csdn.net/weixin_42310154/a ...

  4. git rebase 与git merge 小结

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

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

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

  6. 浅谈git rebase和git checkout --ours(theirs)

    先描述下场景. A在master基础上创建了个新的branch fix_bug, 并在fix_bug上进行了1次对foo.py的修改并提交78d4c5. B在master上直接进行了1次对foo.py ...

  7. git rebase 和 git merger

    & git merge 在上图中,每一个绿框均代表一个commit.除了c1,每一个commit都有一条有向边指向它在当前branch当中的上一个commit. 图中的项目,在c2之后就开了另 ...

  8. 浅谈git rebase命令 -- git修改历史提交信息

    浅谈git rebase命令 如果你只想修改git log命令打印到终端上的最后一个提交信息,那么使用git commit --amend命令直接修改就好. 如果你还想合并最近的某几个提交为一个提交, ...

  9. [Git Rebase] Git Rebase 合并提交(命令操作 SourceTree 操作)

    前言 在开发过程中, 我们有的时候会有代码提交失误的情况. 在前一章, 我们介绍了git push - - force命令. 本章我们介绍git rebase命令及 SourceTree内的相关操作. ...

最新文章

  1. Linux-LNMP(静态元素不记录日志和过期时间,防盗链,解析php,代理,支持ssl)
  2. python 删除代码中的注释,正则匹配
  3. rpm卸载mysql和php_Linux系统中自带Mysql、Apache、Php卸载
  4. 多个asp.net程序共用Session(sso程序的外设接口)
  5. 获取本机IP地址[JavaScript / Node.js]
  6. 基于easyui开发Web版Activiti流程定制器详解(六)——Draw2d的扩展(一)
  7. input限制输入字符
  8. Android Binder机制:编写自己的本地服务
  9. 用C#语言实现记事本
  10. C#报错Newtonsoft.Json.JsonSerializationException: “A member with the name ‘phone‘ already
  11. OSG学习:OSG组成(二)——渲染状态和纹理映射
  12. 23种设计模式彩图-设计模式之禅
  13. 从零开始写一个框架的详细步骤
  14. Unity 一键给所有按钮添加按钮音效
  15. Kafka的offset自定义存储实现
  16. BCB6.0 Import Type Library Excel2003时报错:因为我安装了Excel2007兼容包 的缘故
  17. python安装教程(Win8环境)
  18. 爆肝一周,完成了一款第一人称3D射击游戏,现在把源代码分享给大家,适合新手跟着学习
  19. leetcode844,难度easy
  20. 速卖通平台的未来在于走品牌化道路

热门文章

  1. 移动、联通低头了?取消达量限速推出新优惠政策,网友:怕电信了
  2. 马超计算机博士,厦门工学院走出的中国科学院博士校友马超
  3. (附源码)计算机毕业设计SSM绝味鸭脖连锁店信息系统
  4. 3.28关于天保基建的因缘际会
  5. linux读取sqlite数据库,linux 下sqlite数据库数据的备份和导入表格
  6. MySql列的插入,修改和删除
  7. 【校招VIP】计算机网络之HTTP和HTTPS
  8. 2022-2028中国财务会计咨询服务市场现状研究分析与发展前景预测报告
  9. 如何进入公司的公共盘
  10. 第一讲:var的使用