代码合并在日常开发中是较为常见的场景,采用合适的合并方式,可以起到事半功倍的效果。对应在 Git 中合并的方式主要有三个,Merge,Rebase,Cherry-Pick. 开始部分会首先介绍一下这三个命令,并录制了一些动画,用于演示三个命令的不同合并过程。之后会实操这三个命令,演示如何解决冲突。

Git Merge

Merge 会将两个分支合并到一起,并生成一个新的 commit 记录。新生成的 commit 节点会有两个父节点。一般在开发某个新功能时,会选择新的分支,之后在合并回主线。

在 master 分支上,使用 git merge bugFix 将 bugFix 分支合并到 master 分支。

Git rebase

rebase 也能合并分支,它会取出一系列 commit 记录,复制它们。然后在目标分支逐个放下去。rebase 的好处是能保持线性的提交历史,从而使历史更加清晰。

在 bugFix 分支上,使用 git rebase master 分支,会将 bugFix 分支上的 commit 记录复制到 master 分支,并且保证了提交历史是线性的。

rebase 除了可以在合并时使用,还可用于整理 commit 记录。

使用 rebase 来整理记录

使用 git rebase -i 命令打开交互式的 rebase UI. 在打开的界面中会显示每个 commit 的哈希值以及提交说明,可以在其中调整 commit 的顺序、删除不想要的提交、以及合并提交。

通过 git rebase -i HEAD~4 后,我们看到了实际的交互式界面,但实际界面一般由 vim 打开并进行相应的操作。在这里,我们删除 c2,调整了 c5 和 c4 的位置。

这里需要注意的是:在真实的环境中,打开后的交互页面,commit 记录方向和 log 记录的 commit 是相反的。

cherry-pick

cherry-pick 而是将一些 commit 复制到当前的分支的 HEAD 上,和 rebase 相比,更加灵活,可以随意的选择 commit 进行复制。

通过 git cherry-pick c3 c4 c7 将其他分支上的 3 个 commit 复制到当前的 master.

三种情况下的冲突解决

场景模拟:团队开发时,不同的开发者需要经常会对公共类进行修改。当修改的位置重合或者删除公共的代码片段后,便会产生冲突。现在模拟这一场景。

首先,创建一个仓库用于演示,其中包含一个名叫 origin.txt 的文件,并在其中写入 "line1",在 master 分支提交。

git 仓库初始化:

mkdir merge-conflict-things
cd merge-conflict-things
git init
vim origin.txt
echo line1 > origin.txt

创建初始 commit 及三种情况 branch 用于演示:

git add .
git commit -m "add origin.txt"
git branch merge_branch
git branch rebase_branch
git branch cherrypick_branch

查看初始化提交信息

git log --oneline --all  -n4 --graph

准备工作:在 master 分支上,创建两个 commit.

第一个 commit : 对 origin.txt 增加一些内容,相当于增加一个 feature.

origin.txt 中添加 "line2" 后:

line1
line2

第二个 commit 用于对过去提交的内容进行修改,相当于 Bug Fix.

origin.txt 的内容如下:

line1 - fixed
line2

接下来,会在三个分支进行相同的操作,并演示如何解决冲突。

git merge

merge_branch 分支,模拟和 master 分支相同的操作,这里提交的过程就不演示了,只要和 master 分支提交的内容不同即可。

第一个 commit:用于增加 feature "line 2 in merge branch"。

第二个 commit:同于修改之前的 bug "line 1 fixed in merge branch"。

提交后的内容如下,merge 分支多了两个 commit :

下面将 merge 和 master 分支进行合并,通常的规范是在次分支去主动合并主分支的代码,然后再推送给主分支,这样做是由于所有分支都是基于主分支,万一在主分支发生错误,影响较为广泛。

进行 merge 操作

git merge master

由于 master 和 merge 分支修改了相同的内容,这时 git 并不知道哪个是需要的,所以提示 origin.txt 发生冲突,让我们手动解决。

<<< HEAD ===== 中间的内容是当前所在的分支的内容,下面的是 master 分支上的内容。

这里你需要注意的是,这里提示冲突的行是两个 commit 的内容。这就意味着,**merge 解决冲突时内容是基于两个分支的所有的 commit **

选择 merge_branch 的方案进行提交。

git add origin.txt
git commit -m "fixed conflict with master"

可以看到,merge 操作将分开的分支进行合并,并**形成一个新的 merge commit. **

git rebase

rebase_branch 分支上模拟 master 的提交操作。

第一个 commit 添加了新行:“line2 - rebase branch”

第二个 commit 用于修改bug: “line1 - rebase branch”

结果如下:

使用 rebase 合并操作

git rebase master

查看冲突的文件:

rebase 的合并过程不同于 merge,在合并时会选取 master 的所有 commit,和当前分支中一个出现冲突的 commit 进行合并。

如上图中所示,HEAD 指针停留于master 分支上最新的 commit 节点,而 rebase 指针指向添加 feature 的节点。

由于 rebase 和 merge 相比较为特殊,这里详细演示下,选取 master 分支内容,选取 rebase 分支内容 ,选择 master 和 rebase 分支的共同内容的处理方法。

1. 选择 rebase 分支内容作为合并后的结果

保存当前的修改:

git add origin.txt
git rebase --continue

修改后的内容直接就提交成功了,因为第二次需要合并的 commit 本就是基于当前内容的 commit,并不会产生冲突。

2. 选择 master 分支内容作为合并后的结果

保存当前的修改:

git add origin.txt
# 由于我们选择了 master 分支内容进行合并,而当前又基于 master,和 master 本身分支上的内容没有任何区别。
# 所以执行 skip 跳过这次冲突。 也就是意味着放弃当前分支提交的 commit,这个也能理解,既然要 master 的 commit,
# 当前分支的 commit 自然就不需要了,直接忽略掉,同时当前的 commit 也不会出现在 commit 的历史中。
git rebase --skip

接着会出现第二次冲突:

再次产生的冲突的原因是,之前解决冲突时,选择了 master 的内容,也就意味着在 rebase_branch 提交的 commit add feature for origin.txt in rebase 被放弃了,而当前的 commit fixed before commit in rebase_branch 是基于丢弃的那个 commit 。

了解到产生冲突的原因,任意选择内容,保存就可以了,这里我们选择了 rebase_merge 分支上的内容。

git add .
git rebase --continue

3. 选择将 master 和 rebase_branch 的内容合并后的结果

这里我们选择将 master 和 rebase_branch 的分支都保留下来:

保存当前的修改:

git add origin.txt
git rebase --continue

可以看到,第二次冲突产生,原因是 rebase_branch 分支中过去的 commit 出现变更,和 rebase_branch 原有 commit 的记录不一致。

同样的,我们也会遇到三种情况:

A 以第一次解决冲突后的内容为准:

git add .
# 这就意味着 rebase_branch 最新节点的添加的内容被舍弃,所以没有任何改变,可以直接跳过
git rebase --skip

B 以 rebase_branch 分支上的结果为准

C 保留 rebase_branch 和 之前修改后的内容:

B C 处理方法相同:

git add .
# 保存我们的更改就可以了
git rebase --continue

注意有时 git rebase --continute 操作无法生效时,原因在于 rebase 会逐一应用当前分支的 commit 到目标分支(一般是 master)。像例子中演示的那样,第一次解决冲突后的内容,和第二次解决冲突后的内容一样,这时用 git rebase --skip 跳过就好了。

git cheery-pick

cherrypick_branch 分支上模拟 master 的提交操作。

使用 cherry-pick 进行合并操作

git cherry-pick  eb1a8fb

出现了冲突,可以看到和 rebase 操作的合并操作刚好相反,这里是以 cherrypick_branch 的最新 commit 为准,去合并 master 的 commit.

选择 master 内容,进行合并:

git add origin.txt
git commit -m "fixed add feature conflict with master"

合并 master 分支上提交的第二个 commit:

git cherry-pick 23e106b

再次出现了冲突:

修改后,提交就可以了:

git add origin.txt
git commit -m "fixed add feature conflict with master"

注意有时 git commit 操作无法生效,提示你是否提交的一个空的 commit. 原因在于修改后的冲突内容和当前分支的原有内容一样。但这样有些矛盾,取过来别人的 commit,最后却不用。解决办法是,取消 cherry-pick 这个 commit 或者提交一个空的 commit 信息。

总结

git merge 是基于两个分支上的最新内容到有冲突的内容之间的所有 commit 进行合并,之后会形成一个新的 commit 记录,将两个分支重新关联起来。

git rebase 正如它的中文名字“变基操作”一样,会将所在分支新添加的内容,增加到目的分支,并保证了 commit 提交记录的串行性。简单来说就是,会以目的分支(一般是 master)为基础,逐一的将当前分支的 commit 记录应用。需要注意的是,在应用时并不是直接应用在 master 分支,而是将 master 分支整体拷贝,然后将当前 commit 应用在拷贝后的分支上。

git cherry-pick 和 rebase 操作正好相反,会以当前的分支为基础,然后将 commit 一个个的拿过来应用。形成的 commit 记录也是串行的。

最后附上一个 Git 练习网站,网站上设置了各种任务,可以用于检验自己的操作水平。

Merge,Rebase,Cherry-Pick 一文解惑的更多相关文章

  1. git之rebase、merge和cherry pick的区别(面试常问)

    git flow图例镇楼 merge 这个简单,初学者常用.比如主分支是Dev,最新版本是01.然后小明基于此,搞了个feature 分支A,业务:打酱油.然后在上面多次提交,完成功能迭代开发,如A1 ...

  2. git merge rebase的区别及应用场景

    前两天和同事交流发现他在日常开发中跟上游保持同步每次都是用git pull操作,而我一直习惯git fetch然后rebase,发现这两种操作后的log是有些区别的.他每次pull操作之后都会自动生成 ...

  3. git 开发merge rebase 记录

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

  4. Git merge rebase cherry-pick 以及 游离commit 的测试

    Microsoft Windows [版本 10.0.17134.345] (c) Microsoft Corporation.保留所有权利. C:\Users\zhangyang\Desktop\b ...

  5. git merge / rebase 分支的新建与合并

    merge https://git-scm.com/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%9A%84%E6%96%B0%E5% ...

  6. git原理及如何选择分支模式

    一.git 原理介绍 1.git的四个工作区域 Git有四个工作区域:工作目录(Working Directory).暂存区(Stage/Index).资源库(Repository或Git Direc ...

  7. git merge 与 rebase 的区别

    http://gitbook.liuhui998.com/4_2.html merge rebase

  8. Git:pull --rebase 和 merge --no-ff

    首先是吐嘈 如果你正在 code review,看到上图(下文将称之为:提交线图)之后,特别是像我这样有某种洁癖的人,是否感觉特别难受?如果是的话,请看下文吧 :) 为什么 Git 作为分布式版本控制 ...

  9. git merge & git rebase

    git merge & git rebase bug error: You have not concluded your merge (MERGE_HEAD exists). hint: P ...

随机推荐

  1. Java多线程--synchronized的用法

    全面介绍Synchronized:https://blog.csdn.net/luoweifu/article/details/46613015 synchronized class/this/obj ...

  2. win服务器管理软件巧利用——如何让服务器管理事半功倍

    那些服务器管理大牛估计看到这个标题会笑了,服务器怎么管理,靠自带的远程桌面肯定是远远不够的,要实现上千台服务器同时登陆,没有一个好程序管理,估计得三餐不食为其颠倒. 那么,有什么好的服务器推荐呢?站长 ...

  3. Rocket - diplomacy - LazyModule的实例化(补)

    https://mp.weixin.qq.com/s/rgCRorjPYyyD6i7moIzbZg   介绍LazyModule和Node构造方法的执行过程,即实例化过程.     1. NullIn ...

  4. Rocket - diplomacy - DUEB参数模型分析

    https://mp.weixin.qq.com/s/533bJxcPRgO4W2gf_OEhEw   分析DUEB参数模型中各种参数类型的可能性.     1. 节点类型   根据参数的传播方向,可 ...

  5. 带你学够浪:Go语言基础系列-环境配置和 Hello world

    文章每周持续更新,原创不易,「三连」让更多人看到是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) 前面几周陆陆续续写了一些后端技术的文章,包括数据库.微 ...

  6. Physic Design:Floorplan算法概览

    仅用于学习交流,转载请联系本人. 1 floorplan是什么 floorplan常被翻译成布图规划,是指在芯片级别上对模块进行布局,也就是哪个单元放在什么地方,但是单元内部的具体布局并不关心.该步骤 ...

  7. Java 第十一届 蓝桥杯 省模拟赛 合法括号序列

    合法括号序列 题目 问题描述 由1对括号,可以组成一种合法括号序列:(). 由2对括号,可以组成两种合法括号序列:()().(()). 由4对括号组成的合法括号序列一共有多少种? 答案提交 这是一道结 ...

  8. Java实现蓝桥杯VIP算法训练 纪念品分组

    试题 算法训练 纪念品分组 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作.为使得参加晚会的同学所获得的纪念品价值 相对均衡, ...

  9. Mybatis多表操作

    一:引言 在学习完前面的mybatis基本语法后,大家都有个认知,这个Mybatis太强大了,比之前使用JDBC写方便多了,但是你们当初在使用原生JDBC写SQL查询的时候有没有遇到过多表查询呢?肯定 ...

  10. Python 爬虫之request+beautifulsoup+mysql

    一.什么是爬虫?它是指向网站发起请求,获取资源后分析并提取有用数据的程序:爬虫的步骤: 1.发起请求使用http库向目标站点发起请求,即发送一个RequestRequest包含:请求头.请求体等 2. ...