Git|03 Git的分支与合并

一、分支

(一)创建分支

1
2
3
4
5
6
7
8
9
10
11
git branch          # 查看本地分支
git branch <分支名> # 创建本地分支
git switch - # 切换回上一个分支

# 切换分支的两种写法
git checkou <分支名>
git switch <分支名>

# 创建不存在的分支,并切换过去的两种写法
git checkout -b <分支名>
git switch -c <分支名>
  • 创建 dev01 分支并设置 git push -u origin dev01 与远端链接,远端将出现新分支。

0a6f8d8c0c8aad90c161812a29a1dc8f.png
5fd681f8-9a1f-4019-b60f-9ce93bb7aa9c.png
9b675c3b-2467-4989-9997-e8cf71f5fc8e.png


(二)删除分支

1
2
git branch -d <分支名>  # 检查式删除
git branch -D <分支名> # 强制式删除


二、合并

(一)实际经验

  • 项目开发一般不会直接在主分支(main/master)上进行,而是检出新分支(dev/develop),在新分支上开发,最后合并到主分支,保证了主分支的稳定与安全。
  • 大型项目可能还会在 dev 分支上继续检出新的细小分支,并以如下特定的马甲来命名:
    • feat/xxx:新功能/新特性分支
    • fix/xxx:bug修复分支
    • hotfix/xxx:紧急修复分支
    • chore/xxx:杂项分支
  • 各细小分支对应到个人/部门负责,最后小分支合并到开发分支,开发分支合并到主分支。这仅是习惯,有些会省掉 dev,直接在 main 检出小分支,如 clash-verge-rev 的分支安排:
    f973d1dd-8d22-4f00-a954-d65bde75f3b2.png
  • 没有仓库权限的人通过 PR 向仓库推送合并请求,由管理员/所有者检查是否合并

(二)合并

1. 快进模式

  • 命令:git merge <分支名>

  • 分支 B 合并到分支 A,则需要切回分支 A ,执行 git merge B

  • 若 B 分支大于 A,则会触发快进模式(Fast-forward);分支和master做了不同操作,合并

  • 触发开进模式的条件:

    • 当前分支是目标分支的祖先
    • 两个分支没有发生分叉
  • 这种情况下如果 main 合并 dev01,会进行快进模式,把 main 分支指针快进到 dev01 当前指向的提交。


因为 HEAD 是当前检出的分支指针,如果当前在 main:

  1. 初始状态
1
2
3
A --- B --- C
↑ ↑
main dev01
  1. 执行 git merge dev01

  2. 快进之后

  3. 同时 HEAD --> main

1
2
3
4
5
6
A --- B --- C
↑
dev01
main
↑
HEAD

简单演示
  1. 在 dev01 分支的 file04.txt 文件写一句话,并提交
    87690c8a-0ae5-4668-8c68-f3889ad00d09.png

  2. 切回 main 分支,file04.txt 无内容,即“一种干净无提交”的状态,dev01 包含 main 的内容,符合快进
    0973bb55-b3fb-4e27-8241-da9ff4b3656f.png

  3. 快进后,dev01 在 file04.txt 上的改动被合并进 main 的空白 file04.txt 文件,并且快照信息(commit-message)也合并
    181e3c73-232f-489e-b5b1-c741716c26d8.png


2. 普通合并

两个分支有发生分叉,即有不同的新提交,不能使用快进模式,例如如下情况:

1
2
3
4
5
        C --- D   dev
/
A --- B
\
E --- F main

这时候在 main 上执行 git merge dev,Git 不能只移动 main 指针,因为 main 自己也有新的提交 E、F。

于是 Git 会创建一个新的合并提交:

1
2
3
4
5
        C --- D
/ \
A --- B M
\ /
E --- F

M 就是合并后的快照(merge-commit)


3. 合并 & 绝决合并冲突

若两个分支对同一部分内容做了彼此不兼容的修改,会产生合并冲突,需手动解决合并冲突

简单演示
  1. 在 dev01 分支的 file03.txt 文件写上一点东西并 commit:
1
2
熊大爱吃蜂蜜
熊二爱熊大

53cce0bd-c072-4860-aa07-f77898dd94fc.png

  1. 回到 main 分支,也在 file03.txt 写上一点东西并commit:
1
蜂蜜爱吃熊大
  1. 在 main 合并 dev01:
  • 发现会有冲突,冲突部分用特殊符号标记,冲突部分以等号相隔
1
2
3
4
5
<<<<<<< HEAD

=======

>>>>>> <分支名>

31e3666b-2f03-4875-955e-ecacce65cf0a.png

  • 需要手动删除这些符号并解决冲突,可以保留当前更改也就是 main 的修改;也可以保留传入修改,也就是 dev01 的改动;也可以自己融合两个分支的修改;也可以什么都不保留,写其他
  • 最后 commit 就完成了一次冲突的解决过程,左侧的 git 树会有一条合并线
    53a9d5c2-21b5-493f-936b-3dd0e5352835.png

4. 协作中的合并冲突

情景:A、B用户修改了同一个文件,且修改了同一行位置的代码,此时会发生合并冲突。

A 用户在本地修改代码后优先推送到远程仓库,此时 B 用户在本地修订代码,提交到本地仓库后,也需要推送到远程仓库,但此时 B 用户晚于 A 用户,故 B 需要先拉取远程仓库的提交(即 A 的提交),经过合并后才能推送到远端分支。总的来说就是先 pull 再 push。

远程分支也是分支,所以合并时冲突的解决方式也和解决本地分支冲突相同相同。