0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Git命令解析-merge、rebase

张康康 2019-07-29 18:21 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

作者 | Video++极链科技后端Team

整理 | 包包

Git分支和工作流

分支本质是一个指向提交对象的可变指针。Git 保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照。在进行提交操作时,会保存一个提交对象(commit object),在多次提交后,commit对象形成连续的快照链,分支指针自动指向最新一次提交。Git 的默认分支名字是 master。如下图:

Git命令解析-merge、rebase


branch命令可以轻松创建一个新分支,就像这样:

$gitbranchnew_branch

这一命令实际是为当前提交对象添加了一个新的指针。这种分支形式比大多数版本控制系统更为轻量,无论是创建还是切换都几乎可以在瞬间完成。Git 鼓励在工作流程中频繁地使用分支与合并,这完全不会增加仓库负担,并且可以基于这一特性创建更自由和更可靠的合作开发流程。

许多使用Git 的开发者都喜欢使用这种方式来工作:仅在master分支上保留完全稳定的代码,这些代码通常处于已发布或等待发布的状态。此外使用一些短期分支,比如用develop分支开发新特性,使用test分支修复bug,测试稳定性,直到代码质量达到发布要求,再合并到master分支,完成一个版本的开发。

不同的开发者团队可以自由创造适合自己组织形式的分支策略。社区中也存在许多深受欢迎的流程范例,比如经典的gitflow工作流、PR工作流、集中式工作流等等,它们通常适用于不同的合作方式,并不是某种强制规范。有兴趣的读者可以继续深入探索,此处不再过多介绍。

merge

假设我们基于master分支创建了feature分支用来开发新功能,经过一段时间开发之后,需要把feature的分支代码合并回到master,通常执行的操作是先检出master分支,然后执行git merge feature。

Git命令解析-merge、rebase


一般来说,在单人开发的情况下,merge通常会产生快进(fast-forward)方式的合并。如果在子分支(feature)被创建之后,父分支(master)未产生新的修改和提交,此时把feature合并回master,Git会在提交链上把master指针简单的前移,使两个分支进度同步,并形成无分支记录的提交链。执行时在控制台输出Fast-forward标识。这种merge方式下不会产生冲突,git log命令会看到如下记录:

Git命令解析-merge、rebase


但在团队合作开发时,通常会多人修改同一远程分支。其中使用的pull和push命令实际包含了merge操作。这时git使用另外一种方式来进行分支合并。目前只有一方修改的情况下,也可以使用 —no-ff 参数来模拟这种方式。

Git命令解析-merge、rebase


这里使用了git最基础的三路递归合并(recursive three-way merge),输出Merge made by the 'recursive' strategy.标明合并方式。这种合并会形成带分支历史的提交链:

Git命令解析-merge、rebase


从图中可以看出,这种merge方式实际在发起合并的分支生成了一个带有Merge 标识的新提交。如果合并时存在冲突,解决冲突后的最终内容也会包含在这个新的提交中。

看到这里,可能有人会有疑问,工作空间中自始至终只出现了两个分支,为什么会是三路合并。从git 源码中可以找到merge执行的入口,它有这样的方法签名:

Git命令解析-merge、rebase


可以看出,除了含义明显的ours和theirs,还有一个待合并的文件叫做ancestor。根据文档和源码注释,这个版本实际是两个待合并分支的公共部分。在我们的例子中就是创建新分支的那个提交对象。

大体的流程是这样的,git merge会找出两个分支指向的最新commit,找到他们最近的公共祖先,然后对每个待合并的文件调用ll_merge,这个方法会比较各分支和祖先节点的差异。然后把这些差异整合成一个Merge提交,应用到当前分支上,生成最终的合并结果。

如果两个分支之间有多个公共祖先,git会选出最合适的祖先节点依照同样规则进行递归合并。可以使用git merge-base —all命令列出所有的备选祖先节点。

Git还可以一次性合并多个分支,只需要简单的把分支名当做merge的参数依次列出:

Git命令解析-merge、rebase


这种策略被称为octopus,其中核心逻辑与three-way merge相同,不再详述,可以通过阅读github上的源码和文档继续深入了解。

three-way merge机制有一定的隐患。如果其中一个待合并分支,比如ours,和ancestor版本的某一部分代码相同,但另一个待合并分支theirs中有不同的修改,合并的结果就会采用theirs分支不同的那部分,并不会依照修改的时间顺序来决定最终内容。在实际项目中可能会反复修改同一段代码来响应需求变更,就有几率发生这种合并结果与预计不符的情况,需要特别留意。

rebase

Git rebase,通常被称作变基或衍合, 可以理解为另外一种合并的方式,与merge 会保留分支结构和原始提交记录不同,rebase 是在公共祖先的基础上,把新的提交链截取下来,在目标分支上进行重放,逐个应用选中的提交来完成合并。

为了形象理解rebase的过程,可以看下面例子:

使用 merge 合并后:

Git命令解析-merge、rebase


下面使用rebase方式达到同样效果:

Git命令解析-merge、rebase


除了原本的多分支记录变为了直线提交链,还可以注意到,其中原本在feature分支上的提交,rebase后的SHA编码发生了变化。rebase消除了真实历史,重新生成了新的提交。

和merge类似,rebase在遇到冲突时也会暂停,需要手动修复后才可以继续。但是rebase的处理要相对繁琐一些,merge 如果发生 conflict,只需要在最终的Merge 提交上解决一次。而 rebase 的 conflict 可能发生在每一次提交的重新应用上,所以需要依次解决。

为了避免这种情况,可以在与另一分支合并之前,提前把所有需要提交合并为一个提交。同样需要用到rebase命令。

执行这样一个命令来合并当前最新的3个提交:

Git命令解析-merge、rebase


这条命令将打开一个编辑页面,我们可以修改前面的命令来合并或丢弃单个提交。

pick 表示将会应用这个提交。

squash 表示把当前提交合并到前一个提交,它的前面必须至少有一个被pick的提交存在。

把某条提交注释或删除表示丢弃这条记录。

这里选择合并第一个和第三个,丢弃第二个提交。

Git命令解析-merge、rebase


保存退出后进入新的编辑页面,提示编辑提交信息,这里选择不做改动。

Git命令解析-merge、rebase


再次保存退出后成功合并完成,形成这样的log:

Git命令解析-merge、rebase


git还有一个可爱的命令cherry-pick,通常译作拣选。它的参数是提交对象的SHA编码,可以视为针对单个提交的rebase操作。示例如下:

Git命令解析-merge、rebase


Git命令解析-merge、rebase


总结

merge 和 rebase 的差异在于最终的历史记录,可以发现 merge 保持了所有分支的原始修改记录,可能会包含很多不必要的信息;而 rebase相当于对历史记录做出修剪,可以维持一条简单清晰的提交路线。

通常我们会在基于一个过时的版本进行了本地修改的情况下使用rebase,在实际开发中经常会出现这种情况,当你在本地分支上工作了几天,突然想起应该push到远程仓库时,远程分支已经被别人更新过了。此时你会得到一个reject信息。

有些人会选择用pull命令合并远程和本地的同名分支,但pull实际执行了fetch和merge两个操作,会生成复杂的分支历史和一个多余的merge提交。你也可以选择用fetch和rebase代替pull,始终生成一个美观的提交链。

rebase的另一个重要应用是合并过多的本地提交。因为防止修改内容丢失,经常commit到本地仓库是一个很好的开发习惯。但是当需要提交到公共分支时,大量无明确意义的提交信息对历史记录造成不必要的干扰。此时你可以用rebase命令把本地记录规范化,再进行推送。

使用rebase的时候需要遵循一条重要原则:不要对在你的本地仓库外有副本的提交记录进行变基。rebase的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。 如果其他人已经在这些提交上做出过大量修改、冲突合并等工作,那么你的rebase将成为他们的恶梦。

对于使用rebase还是merge来合并代码,实际并没有什么固定的模式,取决于开发者如何看待仓库的历史记录。一些人认为历史记录应该反映全部真实变更细节,而另一些人认为历史记录应该是精心维护的变更目录。具体如何使用取决于项目合作者的一致共识。无论是merge还是rebase,都应该了解其中原理,避免危险操作,才能享受到Git诸多特性带来的便利。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    Mmc命令导致 TF 卡不可读,IO 错误,怎么解决?

    mmc-utils 软件包 http://git.kernel.org/cgit/linux/kernel/git/cjb/mmc-utils.git 里面的 mmc 命令,在星光板子
    发表于 03-30 07:40

    嵌入式开发绕不开的版本管理工具——git

    Git是一个开源的分布式版本控制系统,由Linux之父Linus Torvalds于2005年用C语言开发,主要是为了帮助管理Linux内核开发而创建的一个开放源码的版本控制软件。Git与常用的版本
    的头像 发表于 01-30 16:47 1453次阅读

    是德示波器自动化测试SCPI命令控制指南

    是德示波器通过标准命令接口SCPI(Standard Commands for Programmable Instruments)实现自动化控制,为测试系统集成与高效调试提供强大支持。本文结合其核心
    的头像 发表于 09-23 17:54 1950次阅读
    是德示波器自动化测试SCPI<b class='flag-5'>命令</b>控制指南

    mqtt dns解析失败是为什么?

    解析域名的ip地址就能正常连上,而直接解析域名就不行,为什么呢
    发表于 09-16 06:38

    测试工程师都在用的Linux命令清单(建议收藏)

    的Linux命令,并结合实际场景解析其应用方法。一、文件和目录操作:基础中的基础1.1文件查看命令#查看文件内容cat/etc/passwd#显示完整文件内容more/va
    的头像 发表于 08-08 10:06 821次阅读
    测试工程师都在用的Linux<b class='flag-5'>命令</b>清单(建议收藏)

    Linux基础命令which详解

    在Linux系统中,which命令用于查找并显示指定命令的可执行文件路径。这对于系统管理员和开发人员来说是一个非常有用的工具,可以帮助定位命令所在的位置,确认命令是否已正确安装,并且能
    的头像 发表于 07-29 17:58 1051次阅读

    解析K8S实用命令

    前言: 作为运维工程师,掌握 Kubernetes 命令行工具是日常工作的核心技能。本文将深入解析 K8S 最实用的命令,从基础操作到高级技巧,助你成为容器化集群管理专家。
    的头像 发表于 07-24 14:07 990次阅读

    一文掌握Linux命令

    作为一名运维工程师,熟练掌握Linux命令是基本功中的基本功。无论是日常工作中的系统维护,还是面试时的技术考核,Linux命令都是绕不开的核心技能。本文将从实战角度出发,系统梳理运维工程师必须掌握的Linux命令,并结合实际场景
    的头像 发表于 07-22 15:23 711次阅读

    详解Linux网络管理中的关键命令

    本文档概述了网络管理中的关键命令,如ifconfig配置网络接口,ip管理路由,ping测试连通性,以及nmap进行安全扫描。还介绍了nslookup和dig用于域名解析,tcpdump抓包分析,以及arp操作和nmap的深入应用。
    的头像 发表于 07-04 11:37 1459次阅读
    详解Linux网络管理中的关键<b class='flag-5'>命令</b>

    SSH常用命令详解

    SSH常用命令详解
    的头像 发表于 06-04 11:30 2173次阅读

    如何使用Traceroute命令

    以下是如何使用 traceroute 命令的详细指南,包括其语法、选项和实际示例。
    的头像 发表于 06-04 11:28 2494次阅读

    Bluetooth LE L2CAP Signaling Channel支持的PDU命令只有三个

    : ​ 编辑 Attribute Protocol(ATT) ,就是我们经常用到的应用层,应用数据就跟在ATT命令后面,ATT支持如下命令列表: ​ 编辑 至此Bluetooth LE空中包解析就告一段落了
    发表于 06-03 11:24

    Docker Volume管理命令大全

    Docker Volume管理命令大全
    的头像 发表于 05-28 17:14 1068次阅读

    Linux常用命令大全

    Linux常用命令是指在Linux操作系统中广泛使用的命令工具,这些命令工具可以完成各种不同的任务,如管理文件和目录、操作进程、网络通信、软件安装等。
    的头像 发表于 05-03 18:08 2022次阅读

    NVMe控制器之完成信息解析模块

    完成信息解析模块用于解析NVMe命令执行完成后返回的信息。该模块首先提取完成信息中的Status Field字段和ID号。通过检查Status Field字段,判断NVMe命令是否成功
    的头像 发表于 05-03 15:58 709次阅读