git merge的基本用法

**get merge:**将指定分支的修改合并到当前分支

其包含了两种合并模式:fast-forward、no-fast-forward

fast-forward

**fast-forward:**将目标分支的HEAD快速推进到待merge分支HEAD所指向的commit

这是一种执行快速、合并效果美观的合并方式,就如同待merge分支上的commits本就提交的是目标分支一样

注:达成条件比较苛刻,需要待merge分支从目标分支拉出后,目标分支无新commit(可尝试在待merge分支merge前执行git rebase,这种场景比较常见)

演示如下:master分支存有2个commits,a分支从master分支创建并新增3个新commits

通过以下命令可以观测到commit的节点树:

1
$ git log --graph --pretty=oneline --abbrev-commit
  • master分支
1
2
* ace4903 (HEAD -> master) master: 2
* 303ad04 master: 1

master HEAD -> ace4903

  • a分支
1
2
3
4
5
* 9923b85 (HEAD -> a) a: 3
* a0d70a7 a: 2
* 5029c65 a: 1
* ace4903 (master) master: 2
* 303ad04 master: 1

master HEAD -> ace4903

a HEAD -> 9923b85

  • master分支上执行get merge a
1
2
3
4
5
* 9923b85 (HEAD -> master, a) a: 3
* a0d70a7 a: 2
* 5029c65 a: 1
* ace4903 master: 2
* 303ad04 master: 1

master HEAD -> 9923b85

a HEAD -> 9923b85

合并后,目标分支依然是一条直线,没有分叉。

no-fast-forward

**no-fast-forward:**通用的合并方式,将产生一个独立的merge commit

演示如下:master分支存有2个commits,a分支从master分支创建并新增3个新commits,b分支从master分支创建并新增3个新commits,a、b分支的第2个commit修改冲突

  • master分支(已merge分支a)
1
2
3
4
5
* 9923b85 (HEAD -> master, a) a: 3
* a0d70a7 a: 2
* 5029c65 a: 1
* ace4903 master: 2
* 303ad04 master: 1

master HEAD -> 9923b85

  • b分支
1
2
3
4
5
* 6378c47 (HEAD -> b) b: 3
* 0eda1f7 b: 2
* af37583 b: 1
* ace4903 (master) master: 2
* 303ad04 master: 1

master HEAD -> ace4903

b HEAD -> 6378c47

  • master分支上执行get merge b
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
*   6153f56 (HEAD -> master) Merge branch 'b'
|\
| * 6378c47 (b) b: 3
| * 0eda1f7 b: 2
| * af37583 b: 1
* | 9923b85 (a) a: 3
* | a0d70a7 a: 2
* | 5029c65 a: 1
|/
* ace4903 master: 2
* 303ad04 master: 1

master HEAD -> 6153f56

b HEAD -> 6378c47

合并后结果:第一列是master分支,第二列是b分支,*代表有效commit,|/\是分支连线

合并后出现了明显分叉,分支关系变得复杂。

git merge b的git操作全过程

$ git merge b [master] Auto-merging README CONFLICT (content): Merge conflict in README Automatic merge failed; fix conflicts and then commit the result.

$ vim README

# resolve confilicits

$ git add README

$ git commit [master] [master df0e8f3] Merge branch ‘b’

可以分为三个阶段:1. merge,2,resolve confilicits,3. commit

merge新增的commit具有显示的Merge标记(如下第二行)

1
$ git log

commit 96caeda32150df24541e2d55d2ca6ec1dd861416 (HEAD -> master) Merge: ace4903 9923b85 Author: gumeng.xl gumeng.xl@bytedance.com Date: Thu Nov 4 14:44:19 2021 +0800

Merge branch 'a'

选项--no-ff

git merge默认采用的是--ff选项,即默认fast-forward。

若需要统一git merge时均要产生单独的commit记录,则需要--no-ff强制关闭fast-forward模式

1
$ git merge a --no-ff

弹出merge commit message的编辑界面

Merge made by the ‘recursive’ strategy. README | 2 +- a/README | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 a/README

最终git log graph

1
2
3
4
5
6
7
8
*   146ba8c (HEAD -> master) Merge branch 'a'
|\
| * 9923b85 (a) a: 3
| * a0d70a7 a: 2
| * 5029c65 a: 1
|/
* ace4903 master: 2
* 303ad04 master: 1

选项--no-ff --no-commit

git merge --no--ff内部过程分为两个步骤,merge + commit。

--no-commit将其中的commit过程剥离,增加了一次手动commit确认的机会

1
$ git merge a --no-ff --no-commit

Automatic merge went well; stopped before committing as requested

1
$ git status

On branch master All conflicts fixed but you are still merging. (use “git commit” to conclude merge)

Changes to be committed: modified: README new file: a/README

1
$ git commit

弹出merge commit message的编辑界面

[master 96caeda] Merge branch ‘a’

附录

文中用例的构建

  1. 创建测试用初始git仓
1
2
3
mkdir git
cd git
git init .
  1. 在master上新增2个commits
1
2
3
4
5
6
7
touch README
git add README
git commit -m "master: 1"

echo "master has README" > README
git add README
git commit -m "master: 2"
  1. 基于master创建分支a,新增3个commits
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
git checkout -b a
mkdir a; touch a/README
git add .
git commit -m "a: 1"

echo "master has README and a/README" > README
git add .
git commit -m "a: 2"

echo "a has README" > a/README
git add .
git commit -m "a: 3"
  1. 基于master创建分支b,新增3个commits,其中第2个commit与a的第2个commit冲突
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
git checkout master
git checkout -b b
mkdir b; touch b/README
git add .
git commit -m "b: 1"

echo "master has README and b/README" > README
git add .
git commit -m "b: 2"

echo "b has README" > b/README
git add .
git commit -m "b: 3"

参考资料

[1] git-merge - Join two or more development histories together.https://git-scm.com/docs/git-merge

[2] 创建与合并分支。https://www.liaoxuefeng.com/wiki/896043488029600/900003767775424

[3] 解决冲突。https://www.liaoxuefeng.com/wiki/896043488029600/900004111093344