rebase — Git中一个强大的忍者

rebase实操:https://www.jianshu.com/p/5c9c6383aa36

rebase实操1:https://juejin.im/post/5cb20c45e51d456e2e656d11

1 what is rebase



rebase,直接翻译过来就是变基,而这个命令就是这么人如其名。通过rebase命令,我们可以改变一串commits的基点(父commit)。首先我们先来操作一遍这个命令,看看效果就知道这个命令是干啥的了。

假设我们的git结构是这样子的。我们在commit3这个地方开出了一个分支branch1,然后在branch1分支上开发并提交了两个commit(6和7)。同时其他分支合并到master分支导致master分支多了两个提交(4和5)。

这时我们执行以下命令: (注: 此时的活动分支是branch1)

(branch1) git rebase master

执行完命令后的git结构如下:

我们可以看到,在使用rebase命令之前,branch1和master的交叉点是commit3,这也是branch1的"基点"。通过执行git rebase命令后,branch1的"基点"就变成了5。从而改变了branch与master的交叉点,现在就是commit5。

rebase的工作原理是:Git会让我们想要移动的提交序列(commit6和commit7)在目标分支(master)上按照相同的顺序重新再现一遍。这就相当于我们为各个原提交做了个副本,它们拥有相同的修改集、同一个作者、日期以及注释信息。所以执行变基的相关commit是被复制了一份,上图也做了标识,而原先在branch1的commit6和commit7还是在历史版本中,我们可以通过commit的sha-1序列号来找到它们,只有在用gc命令执行垃圾回收之后(这个命令git会自动执行,以清理版本库),它们才会真正从版本库中消失。

rebase的执行步骤如下:

  1. 确定涉及到哪些提交:确认活动分支(branch1)哪些提交不在目标分支上(master),这些提交(commit6和commit7)就是要进行变基的提交。
  2. 确认新的基点:新的基点就是目标分支的最新提交,这里是commit5。
  3. 复制提交:将需要变基的提交一次复制一份并创建新的提交(commit复制6和commit复制7),这里是按顺序来的
  4. 将活动分支重置: 活动分支被移动到上述复制提交的顶部,在这里是commit复制7。

通过上面的演示已经原理讲解,大家应该对rebase有了一个基本的了解了。下面说说什么时候使用它。

2 什么时候使用rebase


  1. 净化历史,这也是使用rebase的最重要的原因,使用rebase变基后在合并代码,就会使得我们的主干分支(master)不会出现交叉情况,就是一条线往下走,这样就易于维护和管理。
  2. 移植分支,可以把一个分支上的一些提交移植到另一个分支上
  3. 通过rebase的高级用法,交互式变基,你可以对需要移动的分支(比如上面的commit6和commit7)进行修改、合并等等。

3 如何用rebase


  • 合并开发分支到master,净化历史

当我们在功能分支上开发完一个功能时,我们需要将其合并到主分支上(master),继续拿上面的那个例子:

之前我们的做法是使用merge命令:

(branch1) git checkout master

(master) git merge branch1

执行完上述命令后,git结构如下:

合并以后会产生一个新的提交节点(commit7)。这样就产生了交叉,出现了"钻石链"。让master分支看起来很乱,也不易维护和管理。

下面看看使用rebase命令的效果。执行以下命令:

(branch1) git rebase master

注:rebase命令是在活动分支执行,而merge命令需要切换到你要合并的分支。所以rebase命令在branch1分支下执行,而merge命令在master分支下执行。这也是两者不同点之一

执行完上述命令,效果如下

这是branch1还没有合并到master。还需切换回master分支,执行merge命令。执行以下命令:

(branch1) git checkout master

(master) git merge branch1

至此,合并操作正式完成,看下效果:

上面的merge是快进合并(fast-faward),不会产生新的提交。如果需要产生新的提交(为了更好的记录追踪),可以使用--no-ff选项,在新版git中支持,旧版不一定支持:git merge --no-ff branch1

commit6和commit7后续会被清理掉。这样master分支就是一条线,清晰也易于维护和管理。

4 rebase的高级用法



rebase命令还有一些可选项,利用这些可选项,我们进行更复杂的操作。下面主要介绍两个--onto和--interactive。

使用--onto移植提交

在如下的git结构中,我们希望能将branch1分支和branch2分支同时合并到master。这是我们可以先将branch1分支中master没有的提交(commit6和commit7)合并到branch2,再通过上面的方法将branch2合并到master。这就是移植提交。可以将commit6和commit7合并到branch2中。

  1. 首先将活动分支切为branch1

git checkout branch1

  1. 执行移植操作

git rebase master --onto branch2 上面命令的意思是:将branch1分支中master分支没有的提交拷贝到branch2,执行后的结果如下:

这样就可以将branch1中的提交移植到branch2中。

使用--interactive进行更多操作

给rebase加上--interactive选项,就变成了交互式变基,就是在变基(复制提交)过程中,会中途停止,然后给我们一些选择命令,让我们进行不同的操作。

举个例子,我们在feature分支开发一个功能,这其中进行了多次提交,产生多个commit。但我们希望当该分支合并到master时,只产生一个commit,这时就可以使用--interactive选项了,下面我们模拟操作下:

注: 在本地进行rebase之前,建议先pull一下,让远端和本地代码保持一致,因为rebase之后,会改变本地的commit结构,那时如果要同步远端的话,需要强制推,先pull,再rebase,然后再使用git push --force-with-lease进行强制本地覆盖远端,就会比较安全且没问题。如果这里看不懂,先往下看。

  1. 首先从master分支开出一个新的功能分支feature

git checkout -b feature

  1. 模拟开发,进行多次提交,这里模拟了三次提交

我们看看feature分支日志情况

提交了三个分支。再看下master分支情况:

master最新提交是"Merge branch ‘test1‘"

接下来,我们开始进行合并操作:

  1. 切换活动分支为feature

git checkout feature

  1. 执行交互式变基 -i 是--interactive的缩写

git rebase master -i

执行该命令后,变基操作会进入交互状态:

我们可以看到,上面列出了需要变基的所有提交,顺序从上之下,也是提交从前到后,先提交的commit显示在上面。接下来,就是交互式变基提供的7个命令:

  • pick也是默认,就是使用该commit,将其复制过去,正常变基
  • reword使用该提交,但是可以修改该提交的提交信息,当时用该命令是,接下来就会进入一个页面,让我们写信的提交信息。如果我们想修改那个commit的提交信息时,可以使用这个方法
  • edit使用该提交,并且变基中断,这时我们可以修改工作区文件,修改完以后git add . => git commit --amend进行重新提交。提交完后使用git rebase --continue继续变基。如果你想修改某个提交,可以使用该方法
  • squash合并提交,它会合并到前面的提交中,然后让我们重新编辑提交信息,这也是我们要实现的。
  • fixup也是合并提交,但是没有然我们重新编辑提交信息,而是丢弃这些提交的提交信息
  • drop丢弃该提交,也就是该提交不会复制过去。
  • exec执行shell命令,貌似不知道有什么用

在了解了这些命令后,继续我们刚刚的任务,上面已经提到过,我们使用sqash命令合并提交,并重新编辑提交信息。

按字母i键进入编辑,因为是合并到前面的提交,我们把后面两个提交的命令改为squash,让这三个提交合并成一个,按ESC进入命令模式,再按ZZ保存。变基继续进入编辑提交信息页面

它会列表每个提交的之前的提交信息,我们重新编辑保存退出,变基就会继续完成。

没有冲突就会变基完成,如果有冲突,解决完冲突后,执行git add . => git rebase --continue继续变基即可。

变基完成并没有合并到master。

注:变基后,feature分支的本地仓库就发生了改变,三个commit合并成了一个。如果你之前将本地分支提交到远端过,就是远端的feature分支还是有三个提交。这时如果你将本地分支提交到远端,再通过open merge request 合并到master分支的话,你就要注意了。不能执行git pull命令。要使用git push --force-with-lease。强制将本地覆盖远端,这样,本地和远端的分支就同步了。

这里的强制提交有两个参数,--force和--force-with-lease。这两个具体使用和介绍,建议大家Google下,详细了解后再使用。比较功能比较强大。

传送门(为什么不用--force而用--force-with-lease)

在实际开发中,一般都是同个Create Merge Request 的方式来合并到master,这样可以进行代码审核。这样,我们就需要将本地分支push到远端再发起合并。到这步变基完成之后,正如上面所说,远端仓库和本地是不同的了,这就需要你执行上面的操作,将远端覆盖掉,然后再发起合并。

git push --force-with-lease

如果你可以在本地合并到master的话,那操作就更简单了。

  1. 切换到master

git checkout master

  1. 进行合并操作

git merge feature

此时,任务完成,但是还是要提醒一下,如果你之前有提交到这几个commit到远端,那边远端feature分支和本地的就不同了,如何想同步,就必须强制push了。接下来我们看看master日志

git log --graph

只有一个提交。如果在feature开发过程中master有新的提交。也不会出现交叉,就不演示了。

5 总结



rebase还是一个比较强大的命令,有些人因为太强大而害怕使用,生怕出现什么问题,当你真正了解了这些命令的原理以后,你就会觉得没有什么可以害怕的,因为它都在你的掌握之中。掌握rebase -i 这个命令,我相信对git的了解和使用会上一个阶梯,真的比较强大和好用,掌握它,用好它,会帮助我们提高工作效率。

作者:millan
链接:https://juejin.im/post/5c052717f265da61506438c7

原文地址:https://www.cnblogs.com/justlove/p/12656269.html

时间: 2024-10-17 16:28:48

rebase — Git中一个强大的忍者的相关文章

使用Git中的Merge与Rebase与开源项目同步代码

基于开源项目的开发有两种主要工作模式.模式1是在从开源项目中拉出一个分支,在这个分支中开发新feature,完成后合并到upstream中.适用于本身是开源项目的developer.模式2是从开源项目中拉出分支后独立发展,但定期从upstream拉更新(如重要版本升级时).无论是哪种,都会面临本地分支与upstream同步代码的问题.为此,git主要提供了两种方式:一种是merge, 一种是rebase.下面通过例子简单过一下它们的基本流程. 假设开源项目的git地址为git://xxx.org

关于Git中分支merge和rebase的适用场景及区别

最近刚接触Git,下面对一些基本的使用做一下总结. 本文是转载于CSDN:http://blog.csdn.net/rryqsh/article/details/8230560 几乎所有的版本控制工具都有branch功能,branch主要用于以下几个场景: 1,控制产品OEM. 基本上做产品,不同的客户都会提出多种不同特性需求,最简单的例子就是LOGO和标题完全不一样.但是可能产品自身的大部分功能和模块的代码一样的,这个时候如何管理多个客户定制的功能特性,并且不会干扰其他OEM版本的功能呢? 如

Git知识总览(五) Git中的merge、rebase、cherry-pick以及交互式rebase

上篇博客聊了<git分支管理之rebase 以及 cherry-pick相关操作>本篇博客我们就以Learning Git中的关卡进行展开.下方列举了LearningGit中的 merge.rebase.reset.revert.cherry-pick 以及交互式rebase相关关卡的操作以及对应的解析.后边在聊交互式rebase操作是,不单单给出了LearningGit中的内容,而且给出了真正的Git分支在交互式rebase操作时的具体案例. learngitbranching的地址为:ht

【转】如何在Git中撤销一切

翻译:李伟 审校:张帆译自:Github 任何一个版本控制系统中,最有用的特性之一莫过于 “撤销(undo)”操作.在Git中,“撤销”有很多种含义. 当你完成了一次新的提交(commit),Git会及时存储当前时刻仓库(repository)的快照(snapshot):你能够使用Git将项目回退到任何之前的版本. 下文中,我将列举几个常见的.需要“撤销”的场景,并且展示如何使用Git来完成这些操作. 一.撤销一个公共修改 Undo a "public" change 场景:你刚刚用g

Git中pull对比fetch和merge

本文参考于:http://www.zhanglian2010.cn/2014/07/git-pull-vs-fetch-and-merge/ 使用git fetch和git pull都可以更新远程仓库的代码到本地,但是它们之间还是有区别 git fetch git fetch origin master git log -p master..origin/master git merge origin/master 从远程的origin仓库的master主分支更新最新的版本到origin/mas

Git 系列(四):在 Git 中进行版本回退

在这篇文章中,你将学到如何查看项目中的历史版本,如何进行版本回退,以及如何创建 Git 分支以便你可以大胆尝试而不会出现问题. 在你的 Git 项目的历史中,你的位置就像是摇滚专辑中的一个片段,由一个被称为 HEAD 的 标记来确定(如磁带录音机或录音播放器的播放头).要在你的 Git 时间线上前后移动 HEAD ,需要使用 git checkout 命令. git checkout 命令的使用方式有两种.最常见的用途是从一个以前的提交中恢复文件,你也可以整个倒回磁带,切换到另一个分支. 恢复一

【Git项目管理】分布式 Git - 向一个项目贡献

分布式 Git - 向一个项目贡献 向一个项目贡献 描述如何向一个项目贡献的主要困难在于完成贡献有很多不同的方式. 因为 Git 非常灵活,人们可以通过不同的方式来一起工作,所以描述应该如何贡献并不是非常准确 - 每一个项目都有一点儿不同. 影响因素包括活跃贡献者的数量.选择的工作流程.提交权限与可能包含的外部贡献方法. 第一个影响因素是活跃贡献者的数量 - 积极地向这个项目贡献代码的用户数量以及他们的贡献频率. 在许多情况下,你可能会有两三个开发者一天提交几次,对于不活跃的项目可能更少. 对于

辛星浅析git中的submodule

有时候,我们需要将一些通用的部分抽取出来做成一个公共库,它可以给别的工程使用,而公共代码库的版本管理是比较麻烦的.我们可以使用git中的submodule来做到这一点. 1.添加 为当前工程添加submodule,命令格式是:git   submodule   add   仓库地址    路径 在添加完成后,在当前工程路径下会生成一个名为".gitmodules"的文件,它记录了子模块的信息,添加完成之后,将子模块所在的文件夹添加到工程中即可. 2.删除 删除submodule,我们首

git中手动删除的文件如何在git中删除

在日常开发中,我们可能或手动删除(delete键删除的)一些文件,然而我们本来应该是用git rm fileName命令删除的,但是现在我们手动删除了,那么要如何在git里面讲那些手动删除的文件删除呢? 我们这里有两种方法可以在git中删除那些手动删除(delete键删除的)的文件: 第一种就是用 git rm files 删除你手动删除的文件或文件夹. 当然,如果你删除的文件有很多,而且分布在不同的文件夹中,使用第一种 git rm files 的方法,显然不方便,效率也很低下,那么有没有更快