官方参考指南:

Pro Git Book v2, § rebasing. English

Pro Git Book v2, § rebase:衍合. 中文版

(建议还是看一下英文原版,就当熟练英语。)

一、回顾merger

常用的整合多个分支的命令就是:git merger <branch>。

假设现如下:

当在branch:experiment执行>>>> git merge master后,会把两个分支的最新快照(C3 和 C4)以及二者最近的共同祖先(C2)进行三方合并,合并的结果是生成一个新的快照C5(并提交)。

log记录如下:

 Administrator@VergiLyn  /d/Adobe/WorkSpace Git/gitdemo (master) (这是分支master的log,未merger前) $ git log commit 0b9a92f26977d89edcb6293ed65cf2ae38da89de Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 22:57:49     C3   commit 501f4477210614af18b75cd9a1a24ce0c23e72d1 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 22:56:55     C2   commit eaef12481e848225ba3aca0b0b2e55bcd06c8725 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-02-28 17:45:34     add problem
 Administrator@VergiLyn  /d/Adobe/WorkSpace Git/gitdemo (experiment)(这是分支experiment的log,未merger前) $ git log commit 0df249cbc7081fe4d82fb1b52e6cc55628e03afa Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 23:00:10     C4   commit 501f4477210614af18b75cd9a1a24ce0c23e72d1 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 22:56:55     C2   commit eaef12481e848225ba3aca0b0b2e55bcd06c8725 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-02-28 17:45:34     add problem
 Administrator@VergiLyn  /d/Adobe/WorkSpace Git/gitdemo (experiment) $ git merge master Merge made by the 'recursive' strategy.  c3.txt | 1 +  1 file changed, 1 insertion(+)  create mode 100644 c3.txt   Administrator@VergiLyn  /d/Adobe/WorkSpace Git/gitdemo (experiment) (执行merger后) $ git log commit efabd4644acbe2a5993f30d6965416f4334d15d4 Merge: 0df249c 0b9a92f Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 23:01:12     Merge branch 'master' into experiment   commit 0df249cbc7081fe4d82fb1b52e6cc55628e03afa Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 23:00:10     C4   commit 0b9a92f26977d89edcb6293ed65cf2ae38da89de Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 22:57:49     C3   commit 501f4477210614af18b75cd9a1a24ce0c23e72d1 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 22:56:55     C2   commit eaef12481e848225ba3aca0b0b2e55bcd06c8725 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-02-28 17:45:34     add problem

明显可以看到有次merge的commit。

二、rebase实现上面例子

(上面是在experiment中执行git merge master。而下面相当于是在master执行git merge experiment)

rebase的执行过程是:

提取在 C4 中引入的补丁和修改,然后在 C3 的基础上应用一次。类似将某一分支上的所有修改都移至另一分支上,就好像在C3中“重新执行”一次在C4中的所有操作。

最后生成的快照C4'结果与merge的C5是一样的。

回到 master 分支,进行一次快进合并。

$ git checkout master

$ git merge experiment

从图例可以看出,rebase是呈线型的。而merge则不是。所以,rebase在某些时候能让commit log更加清晰明了。

摘自官方指南原文: (merge与rebase/变基 的区别)

此时,C4' 指向的快照就和上面使用 merge 命令的例子中 C5 指向的快照一模一样了。这两种整合方法的最终结果没有任何区别,但是变基使得提交历史更加整洁。你在查看一个经过变基的分支的历史记录时会发现,尽管实际的开发工作是并行的,但它们看上去就像是串行的一样,提交历史是一条直线没有分叉。

一般我们这样做的目的是为了确保在向远程分支推送时能保持提交历史的整洁——例如向某个其他人维护的项目贡献代码时。在这种情况下,你首先在自己的分支里进行开发,当开发完成时你需要先将你的代码变基到 origin/master 上,然后再向主项目提交修改。这样的话,该项目的维护者就不再需要进行整合工作,只需要快进合并便可。

请注意,无论是通过变基,还是通过三方合并,整合的最终结果所指向的快照始终是一样的,只不过提交历史不同罢了。变基是将一系列提交按照原有次序依次应用到另一分支上,而合并是把最终结果合在一起。

log记录如下,会发现在分支experiment的C4会在master的log,正如上面说的"把C4的操作在当前分支中重新执行一遍":

 Administrator@VergiLyn  /d/Adobe/WorkSpace Git/gitdemo (master) $ git rebase experiment First, rewinding head to replay your work on top of it... Applying: C3   Administrator@VergiLyn  /d/Adobe/WorkSpace Git/gitdemo (master) $ git log commit c6bebe97d0570e16339c53c3bc8958930b3cb587 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 22:57:49     C3   commit 0df249cbc7081fe4d82fb1b52e6cc55628e03afa Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 23:00:10     C4   commit 501f4477210614af18b75cd9a1a24ce0c23e72d1 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 22:56:55     C2   commit eaef12481e848225ba3aca0b0b2e55bcd06c8725 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-02-28 17:45:34     add problem

三、 利用rebase合并多余的commit log

核心命令:git rebase -i <other>

(更好的可以参考官方指南的:  更有趣的变基例子/More Interesting Rebases)

官方指南给出的例子用的是:

$ git rebase --onto master server client

以上命令的意思是:“取出 client 分支,找出处于 client 分支和 server 分支的共同祖先之后的修改,然后把它们在 master 分支上重放一遍”。

或者

$ git rebase master server

将 server 分支中的修改整合到master中。

使用 git rebase [basebranch] [topicbranch] 命令可以直接将特性分支(server)rebase到目标分支( master)上。

以下是用 git rebase -i 举例:

假设有以上commit  log。当master执行push后,那么origin中的log也是如图。

但是,在图中的C1、C2、C3其实可以只有一次log存在。

ex:

C1是只把功能做了50%,中午吃饭前commit到本地记录。下午接着做了25%,发现临时有事,又commit C2到了本地。后面回来完成了剩余的25%,commit C3并想push到remote。

针对这3次commit,即使对自己而言,最终也没必要有3次commit log。因为,如果以后要比对/还原代码,也是以整个功能来做操作。此时,C1\C2\C3就在log中显得多余。

(对别人而言更是多余,个人认为的理想情况是,一个commit最好只包含一个完整的功能。这样有助于后面做代码还原。)

所以针对以上情景,就会用到git的rebase命令(rebase:衍合,上面指南中翻译的是"变基"。但个人喜欢叫"衍合")。

远程仓库的commit log(或者说是push log):

 Administrator@VergiLyn  /d/Adobe/WorkSpace Git/gitdemo (master) $ git log origin/master commit eaef12481e848225ba3aca0b0b2e55bcd06c8725 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-02-28 17:45:34     add problem   commit 4002b861bff4a0553267e77539a9e21659adf417 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-02-28 17:33:50     2017-02-28 

本地的commit log::

 Administrator@VergiLyn  /d/Adobe/WorkSpace Git/gitdemo (master) $ git log commit d4d1049144097a10939cad4d3126725a69c574e3 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 23:51:31     C3   commit 16cfad2a5da48fafa1b9b22e1c8f1cc6cfa90e07 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 23:51:16     C2   commit 90adaa5af3e22393ce3cc0fac03a252f2e306f33 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 23:50:59     C1   commit eaef12481e848225ba3aca0b0b2e55bcd06c8725 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-02-28 17:45:34     add problem

C1\C2\C3是本地的多次commit,且都没有push到remote。

如果此时git push,那么远程仓库中的log:会有本地3次commit的log信息。

 Administrator@VergiLyn  /d/Adobe/WorkSpace Git/gitdemo (master) $ git log origin/master commit d4d1049144097a10939cad4d3126725a69c574e3 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 23:51:31     C3   commit 16cfad2a5da48fafa1b9b22e1c8f1cc6cfa90e07 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 23:51:16     C2   commit 90adaa5af3e22393ce3cc0fac03a252f2e306f33 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 23:50:59     C1   commit eaef12481e848225ba3aca0b0b2e55bcd06c8725 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-02-28 17:45:34     add problem

在push前,利用rebase合并3次提交记录(特别提醒:不能乱用rebase来整合commit log,后面在细讲原因。)

 Administrator@VergiLyn  /d/Adobe/WorkSpace Git/gitdemo (master) $ git rebase -i HEAD~3 [detached HEAD 8b092c3] rebase C1\C2\C3.  Date: Sat Apr 15 23:59:46 2017 +0800  1 file changed, 4 insertions(+), 1 deletion(-) Successfully rebased and updated refs/heads/master.   (rebase -i 提示修改,个人是把git的编辑改成了notepad++了。注意看P/R/E/S的意思) pick 74c9765 C1 s 428ca30 C2 s 39d90f5 C3  # Rebase eaef124..39d90f5 onto eaef124 (3 commands) # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # d, drop = remove commit # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out  (合并后指定本次的commit log信息)  # This is a combination of 3 commits.# The first commit's message is:  rebase C1\C2\C3.  # Please enter the commit message for your changes. Lines starting# with '#' will be ignored, and an empty message aborts the commit.## Date:      Sat Apr 15 23:59:46 2017 +0800## interactive rebase in progress; onto eaef124# Last commands done (3 commands done):#    s 428ca30 C2#    s 39d90f5 C3# No commands remaining.# You are currently rebasing branch 'master' on 'eaef124'.## Changes to be committed:#    modified:   test.txt## Untracked files:#    .idea/#    .project#    GitDemo.iml#  (rebase -i 后可以发现已经不存在C1\C2\C3的记录了,随之变成了rebase合并的一条记录"rebase C1\C2\C2.")  Administrator@VergiLyn  /d/Adobe/WorkSpace Git/gitdemo (master)  $ git log commit 8b092c3236c3d2dc760e301da9e144fd324b00f5 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 23:59:46     rebase C1\C2\C3.   commit eaef12481e848225ba3aca0b0b2e55bcd06c8725 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-02-28 17:45:34     add problem   (如果发现rebase有问题,也不用担心。可以通过reflog找到所有的操作,然后再用对应hash做想做的操作。  前提是,这些reflog没有被git gc回收。一般会保存30天。 ) Administrator@VergiLyn  /d/Adobe/WorkSpace Git/gitdemo (master) $ git reflog 8b092c3 HEAD@{0}: rebase -i (finish): returning to refs/heads/master 8b092c3 HEAD@{1}: rebase -i (squash): rebase C1\C2\C3. 53778fd HEAD@{2}: rebase -i (squash): # This is a combination of 2 commits. 74c9765 HEAD@{3}: rebase -i (start): checkout HEAD~3 39d90f5 HEAD@{4}: commit: C3 428ca30 HEAD@{5}: commit: C2 74c9765 HEAD@{6}: commit: C1 (push后remote的log) Administrator@VergiLyn  /d/Adobe/WorkSpace Git/gitdemo (master) $ git push Counting objects: 3, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 302 bytes | 0 bytes/s, done. Total 3 (delta 1), reused 0 (delta 0) remote: Resolving deltas: 100% (1/1), completed with 1 local object. To https://github.com/vergilyn/GitDemo.git    eaef124..8b092c3  master -> master   Administrator@VergiLyn  /d/Adobe/WorkSpace Git/gitdemo (master) $ git log origin/master commit 8b092c3236c3d2dc760e301da9e144fd324b00f5 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-04-15 23:59:46     rebase C1\C2\C3.   commit eaef12481e848225ba3aca0b0b2e55bcd06c8725 Author: VergiLyn <vergilyn@vip.qq.com> Date:   2017-02-28 17:45:34     add problem

这样在remote中的log要更加整洁,有用。当要还原或对比时,不用对比到一个错误的版本C2。

四、rebase整合commit带来的风险

怎么避免rebase带来的风险?

简单来说,只能rebase合并自己本地的commit。如果这些commit之前已被push到remote,那么就不允许rebase。

(虽然可能最后不影响代码完整性与正确性)

如果把push后的commit进行rebase后并push。那么,你的伙伴不得不重新pull进行整合。

又可能你的伙伴A不知道为什么要rebase哪些commit,所以伙伴A又用git push --force把你rebase的commit重新push到remote。

伙伴A做了多余的整合工作,并又错误的把rebase的commit push到remote中。

于是你有抱怨伙伴A为什么要这么做,并自己可能又要重新rebase这些commit后push。伙伴A又要重新整合,只是已知你rebase的目的。

摘自官方指南原文:

Ahh, but the bliss of rebasing isn’t without its drawbacks, which can be summed up in a single line:

Do not rebase commits that exist outside your repository.

不要对在你的仓库外有副本的分支执行变基。

如果你遵循这条金科玉律,就不会出差错。否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。

变基操作的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。如果你已经将提交推送至某个仓库,而其他人也已经从该仓库拉取提交并进行了后续工作,此时,如果你用 git rebase 命令重新整理了提交并再次推送,你的同伴因此将不得不再次将他们手头的工作与你的提交进行整合,如果接下来你还要拉取并整合他们修改过的提交,事情就会变得一团糟。

让我们来看一个在公开的仓库上执行变基操作所带来的问题。假设你从一个中央服务器克隆然后在它的基础上进行了一些开发。你的提交历史如图所示:

Figure 44. 克隆一个仓库,然后在它的基础上进行了一些开发

然后,某人又向中央服务器提交了一些修改,其中还包括一次合并。你抓取了这些在远程分支上的修改,并将其合并到你本地的开发分支,然后你的提交历史就会变成这样:

Figure 45. 抓取别人的提交,合并到自己的开发分支

接下来,这个人又决定把合并操作回滚,改用变基;继而又用 git push --force 命令覆盖了服务器上的提交历史。之后你从服务器抓取更新,会发现多出来一些新的提交。

Figure 46. 有人推送了经过变基的提交,并丢弃了你的本地开发所基于的一些提交

结果就是你们两人的处境都十分尴尬。如果你执行 git pull 命令,你将合并来自两条提交历史的内容,生成一个新的合并提交,最终仓库会如图所示:

Figure 47. 你将相同的内容又合并了一次,生成了一个新的提交

此时如果你执行 git log 命令,你会发现有两个提交的作者、日期、日志居然是一样的,这会令人感到混乱。此外,如果你将这一堆又推送到服务器上,你实际上是将那些已经被变基抛弃的提交又找了回来,这会令人感到更加混乱。很明显对方并不想在提交历史中看到 C4C6,因为之前就是他把这两个提交通过变基丢弃的。

五、rebase vs. merge (全部摘自官方指南)

变基 vs. 合并

至此,你已在实战中学习了变基和合并的用法,你一定会想问,到底哪种方式更好。在回答这个问题之前,让我们退后一步,想讨论一下提交历史到底意味着什么。

有一种观点认为,仓库的提交历史即是 记录实际发生过什么。它是针对历史的文档,本身就有价值,不能乱改。从这个角度看来,改变提交历史是一种亵渎,你使用_谎言_掩盖了实际发生过的事情。如果由合并产生的提交历史是一团糟怎么办?既然事实就是如此,那么这些痕迹就应该被保留下来,让后人能够查阅。

另一种观点则正好相反,他们认为提交历史是 项目过程中发生的事。没人会出版一本书的第一版草稿,软件维护手册也是需要反复修订才能方便使用。持这一观点的人会使用 rebase 及 filter-branch 等工具来编写故事,怎么方便后来的读者就怎么写。

现在,让我们回到之前的问题上来,到底合并还是变基好?希望你能明白,这并没有一个简单的答案。 Git 是一个非常强大的工具,它允许你对提交历史做许多事情,但每个团队、每个项目对此的需求并不相同。既然你已经分别学习了两者的用法,相信你能够根据实际情况作出明智的选择。

总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利

【Git】git使用 - rebase的使用的更多相关文章

  1. git 开发merge rebase 记录

    git status git lg git add src/ git commit -m "restful api and portal" //先commit到自己的本地branc ...

  2. Git git rebase 使用

      原文:http://gitbook.liuhui998.com/4_2.html 一.基本 git rebase用于把一个分支的修改合并到当前分支. 假设你现在基于远程分支"origin ...

  3. git<git rebase 修改以前提交过的内容>

      git rebase 使用总结: 使用git rebase 修改以前已经提交的内容 比如要修改之前的commit的 hashcode为:187f869c9d54c9297d6b0b1b4ff47d ...

  4. Git x SVN rebase事故

    Git x SVN rebase事故 @author ixenos 2019-01-09 14:21:21 前言: 昨天在Git x SVN 中进行git svn dcommit的时候,提示需要再进行 ...

  5. [Git] Git 使用记录

    1. 配置git客户端 1.1 安装git bash https://git-scm.com/downloads 1.2 设置ssh Key 查看是否有ssh key ls -al ~/.ssh 没有 ...

  6. 8.3 Customizing Git - Git Hooks

    https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks https://github.com/git/git/blob/master/temp ...

  7. 8.3 Customizing Git - Git Hooks 钩子 自动拉取 自动部署 提交工作流钩子,电子邮件工作流钩子和其他钩子

    https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks https://github.com/git/git/blob/master/temp ...

  8. error setting certificate verify locations: CAfile: E:/git/Git/mingw64/ssl/certs/ca-bundle.crt

    一.问题: 当git clone项目时报 error setting certificate verify locations: CAfile: E:/git/Git/mingw64/ssl/cert ...

  9. [git]git 分支

    什么动作,关键看你想完成什么 1. 添加新的远程分支: git push origin current_local_branch:new_remote_branch 2. 删除远程分支(冒号前必须要有 ...

  10. [git] git 的基本认知

    版本管理 ( Version Control ) 版本管理系统是一个记录文件变更的系统,让你在一段时间后可以恢复指定版本的文件.版本管理系统大致可分为三类:独立的本地版本管理系统.中心化版本管理系统. ...

随机推荐

  1. 【2020-02-11】1346. Check If N and Its Double Exist

    更多LeetCode解题详解 Easy Given an array arr of integers, check if there exists two integers N and M such ...

  2. 《ASP.NET Core 高性能系列》环境(EnvironmentName)的设置

    一.概述 程序启动时Host捕获到环境相关数据,然后交由IEnvironment(传说要作废,但是觉得这个有设计点问题,因为.NET Core 非Web怎么处理?),然后交由IWebHostEnvir ...

  3. ATL的GUI程序设计(3)

    第三章 ATL的窗口类 CWindowImpl.CWindow.CWinTraits,ATL窗口类的奥秘尽在此三者之中.在本章里,李马将为你详细解说它们的使用方法.另外,本章的内容也可以算是本书的核心 ...

  4. supervisor管理tomcat

    操作目的:用supervisor工具管理tomcat服务 配置环境,安装服务,以及多实例 脚本编辑: 前提 机器的opt目录下必须有jdk-8u131-linux-x64_.rpm 以及apache- ...

  5. python学习记录(六)

    0903--https://www.cnblogs.com/fnng/archive/2013/04/21/3034442.html 基本语句的用法 使用逗号输出(想要同时输出文本和变量值,又不希望使 ...

  6. BZOJ 3339 Rmq Problem(离线+线段树+mex函数)

    题意: q次询问,问[l,r]子区间的mex值 思路: 对子区间[l,r],当l固定的时候,[l,r]的mex值对r单调不减 对询问按照l离线,对当前的l,都有维护一个线段树,每个叶节点保存[l,r] ...

  7. PHP第三方登录——QQ登录

    主要内容 简单回顾OAuth协议基本原理 接入QQ登录的前置条件以及开放平台账号申请 引入官方SDK SDK参数配置 SDK核心方法解读 整合QQ登录SDK到Web应用中 SDK优化 调用API的开发 ...

  8. postman之存储测试结果

    前言 在Jmeter的随笔中,我跟大家讲过利用Jmeter工具存储测试结果,那么,postman工具要该如何存储测试结果呢?下面一起来学习吧! 一:添加一个登录请求,填入接口参数点击send 二:点击 ...

  9. rabbitmq在kubernetes中持久化集群部署

    背景 Javashop电商系统的消息总线使用的事rabbitmq,在订单创建.静态页生成.索引生成等等业务中大量采用异步消息系统,这个对于mq高可用的要求有两个重要的考量: 1.集群化 2.可扩容 3 ...

  10. 浅谈CC攻击原理与防范

    概念         CC攻击的原理就是攻击者控制某些主机不停地发大量数据包给对方服务器造成服务器资源耗尽,一直到宕机崩溃.CC主要是用来攻击页面的,每个人都有这样的体验:当一个网页访问的人数特别多的 ...