说说merge 、rebase和revert

date
Nov 18, 2023
slug
how-to-use-git-merge-and-git-rebase-and-git-revert
status
Published
tags
编程开发
summary
git merge应该是大部分人合并分支的方式,可能很多人也只知道这种。但除了merge的方式之外还有rebase的方式,你知道这两种的区别么?是否知道这两者的应用场景呢? 而当分支合并之后,或者你提交的代码被发现存在问题之后,你知道如何回滚么,知道git revert如何使用么? 本文想通过几个简单的例子来帮助理解几个常用的分支合并和回滚指令,即git merge、git rebase和git revert的使用。
type
Post
git merge应该是大部分人合并分支的方式,可能很多人也只知道这种。但除了merge的方式之外还有rebase的方式,你知道这两种的区别么?是否知道这两者的应用场景呢?
而当分支合并之后,或者你提交的代码被发现存在问题之后,你知道如何回滚么,知道git revert如何使用么?
本文想通过几个简单的例子来帮助理解几个常用的分支合并和回滚指令,即git merge、git rebase和git revert的使用。

git merge

用法:git merge [branch]
工作流程: 当执行 git merge 时,Git 会创建一个新的“合并提交”,该提交包含两个分支的更改。这保留了项目历史的完整性,因为可以看到所有分支的历史。
 
简单来说,就是将目标分支合并到当前分支(如果没有指定的话)上去。接下来用几个例子来直观的感受下。
 
示例1
  1. 初始状态:
    1. master分支上当前有三个提交A和B。
    2. feature分支基于 master 的提交 B 创建,并有额外的提交 C 和 D。
  1. 合并分支,将feature合并(merge)到master
    1. 合并后分支状态。
     
    这个例子很简单,master在切出feature分支后,也没有新的提交,因此feaure分支合并后master分支状态也很干净。
     
    示例2
    1. 初始状态:
      1. master分支上当前有三个提交A和B和C
      2. feature分支基于 master 的提交 B 创建,并有额外的提交 D 和 E。
    1. 合并分支,将feature合并(merge)到master
      1. 合并后分支状态。
          • 合并后会多出一个新的提交(Merge branch 'feature’)
          • 根据提交的时间,分支状态会有些许顺序上的差异,这里假设提交C的时间晚于D和E
       
      这个场景日常开发比较常见,合并入目标分支(这里为master)后,因为两个分支的进度不同,因此会多出一个新的提交出来。
      可以看到,采用git log --graph查看提交历史,分支的演进会显得有些乱。
      当合并存在冲突的时候就需要解决冲突,解决完冲突后再进行git addgit commit即可完成分支合并。

      git rebase

      用法: git rebase [basebranch]
      目的: 将一个分支上的提交移动到另一个分支的顶部。
      工作流程: 通过 rebase,Git 会取出一个分支上的一系列提交,暂时将它们存放起来,然后一次一个地将它们重新应用到另一个分支的顶部。这通常用于保持一个线性的提交历史,可以使历史更干净、更直观。
       
      示例1
      1. 初始状态:
        1. master分支上当前有三个提交A和B和C。
        2. feature分支基于 master 的提交 B 创建,并有额外的提交 D 和 E。
      1. 执行变基操作,以master为基,将feature上的提交挪动(变基)master
        1. 变基后feature分支的状态
          1. 合并分支,将feature合并(merge)到master
            1. 合并后分支状态。
              1. 不会多出一个新的提交
              2. 保持住了线性提交
             
            注意到当master有新的提交时,为了让这些提交能够应用到feature分支上,我们没有使用合并(merge)的方式,而是在feature分支上采用的git rebase,即将master分支作为变基的基本分支,feature分支上的提交挪动到基本分支之后。最终分支保证了线性的演进,查看提交历史时也很直观。
            最后将feature分支合并入master分支时,我们依旧采用的合并(merge)的方式,得益于之前已经完成了rebase,此时合并后并不会产生一个多余的合并提交,分支也维持线性和干净。
             
            总结下适合使用 git rebase 的情况:
            1. 整理本地提交历史:如果你在本地分支上有一系列尚未推送到远程仓库的提交,并且想要整理这些提交(比如修改提交信息、压缩或重排提交),git rebase 是一个很好的工具。
            1. 更新特性分支:在一个长时间运行的特性分支上工作时,定期使用 git rebase 将主分支(如 main 或 master)上的最新更改整合到特性分支上,可以避免将来合并时的复杂冲突。
            1. 维护线性历史:在某些开发流程中,维护一个干净、线性的提交历史是很重要的。在这种情况下,git rebase 可以用来确保特性分支的提交在合并到主分支之前是基于主分支的最新状态。
            1. 准备合并:在将特性分支合并到主分支之前,使用 git rebase可以简化合并过程,因为它可以预先解决可能在合并时出现的冲突。
            1. 代码审查:在某些代码审查过程中,保持提交历史的清晰和简洁被视为良好的实践。使用git rebase可以帮助创建更易于审查的提交序列。

            git pull --rebase

            当我们要同步远程仓库的提交到本地时,会采用git pull的方式。但默认情况下,它采用的是merge的方式,此时如果远程和本地提交差别很大,将会出现很杂乱的提价历史。
            此时如果执行git pull,则会形成如下提交历史:
             
            此时如果我们采用 git pull --rebase,则会形成如下的提交历史
            可见提交历史显得更加简洁优雅了。

            git revert

            假设你有一个提交(我们称之为提交 A),之后你意识到这个提交引入了一个错误,你希望撤销它。你可以使用 git revert 命令来撤销这个提交。这会创建一个新的提交(称之为提交 B),它包含了撤销提交 A 所有更改的内容。这也意味着它会取消之前提交的所有更改,但原来的提交仍然保留在历史记录中。
            命令格式如下:
            其中[commit-hash]是你想要撤销的提交的哈希值。
             
            示例
            1. 初始状态,feature分支有A、B、C三个提交
            1. C提交存在问题,需要回滚,假设C提交的commit id为5a7256
            1. revert回滚后,feature分支状态
             
            来看下它的使用场景
            1. 撤销公共提交:当错误的提交已经被推送到远程仓库,并且其他人可能已经基于这些提交做了进一步的工作时,使用 git revert 是更安全的选择。因为它不会重写历史记录。
            1. 撤销老旧提交:如果需要撤销的提交不是最近的一个,git revert 可以生成一个新的提交来反转指定提交的更改,而不影响之间的其他提交。
            1. 避免直接修改公共历史:在多人协作的项目中,直接修改公共历史(如用 git rebasegit reset)可能会导致混乱。相比之下,git revert 保持历史的完整性,对其他协作者更为友好。
             
            注意事项
            • git revert 撤销的是指定提交的更改,而不是回退到该提交。它不会移除提交之后的任何工作,只是简单地应用一个相反的更改。
            • 如果要撤销的提交包含复杂的更改或与后续提交有交集,可能会出现冲突。这种情况下,你需要在完成 git revert 操作之前手动解决这些冲突。
            • 使用 git revert 时,最好清楚地了解你要撤销的提交究竟做了哪些更改,以确保新生成的提交不会引入意外的问题。

            总结

            以上便是git merge、git rebase和git revert的讲解和简单使用,当然在这些指令后面还可以加很多参数,例如git rebase -i开启一个交互式的变基过程。这些就自己后续使用到的时候再去探索吧,掌握了它们的使用方法,再去理解进一步的使用方式就不难了。