内容纲要

🗂 | 查看 Git 专题可浏览更多内容


概念

使用分支可以让我们从主线上分离开来,避免影响主线。

什么意思呢?想一想当都在主线上同时进行多个方向的工作,会有哪些问题:

  • 假设你是一位小说家,对于接下来的故事走向有两个方向,那么应该如何管理呢?
  • 假设你是一位工程师,领导先是要求加入购物车功能,但后来又改主意了,如何保留其他有用功能的代码的同时,只清除掉购物车功能的代码呢?
  • 在团队多人协作时,如何不影响到其他成员而进行自己的工作呢?

你可能会发现,尝试着在一个单一上下文背景环境中同时进行多个主题的工作时,事情只会变得越来越糟糕。

这些问题可以通过常见的 Git 分支的使用场景,或者说工作流解决:

  • 长期分支(Long-Running Branches)
    长期分支存于在项目的整个生命周期,代表了在整个项目生命周期的一种状态。以开发项目为例:将主分支定位为最高等级的分支,仅用于保存产品代码。如此一来主分支可以直接用来上线于生产环境,然后新建的开发分支(它衍生于主分支)专门用于开发、测试等等,待稳定后合并于主分支。
  • 主题分支(Topic Branches)
    主题分支就是基于某种主题的分支,例如着手开发一个新的功能的或是修复错误时,都应该对应不同的主题建立一个新的分支。这也是最常见的做法,特别是对于团队协作时你在本地的分支远程仓库并不可见,就可以尝试更多的方案择优选择,当多人协作时也可以避免提交冲突。

实践

查看分支

在创建一个 Git 项目时,Git 就已经默认为我们创建了一条名为「master」的分支,还记得在使用 git status 命令时,返回结果第一行就在告诉我们当前位于什么分支吗?

位于分支 master
无文件要提交,干净的工作区

一般来说我们可以把默认创建的「master」当作主分支,也就是上面提到的「长期分支」。另外,也不要觉得这个「master」相比其他分支有什么特别之处,它跟后续创建的其他分支都一样,只是名字叫做「master」而已。

💡 因为 🥱 无聊的政治正确,国外开始兴起用「main」来替代「master」的运动,所以你可能在一些平台或项目看到「main」而不是「master」…

除了使用 git status 还可以使用 git branch 命令查看 Git 的分支:

$ git branch

# 显示每个分支最新的 Commit ID 和提交信息
$ git branch -v

# 显示所有远程和本地的分支:
$ git branch -a

创建分支

假设当前只有一个分支 master,里面只有一个文稿文件 README.md,它的内容是这样的:

Git 简史

同生活中的许多伟大事物一样,Git 诞生于一个极富纷争大举创新的年代.

你接到一个「编号为 3」 的反馈说该项目的缺乏格式以及标点符号是英文符号,需要修改。

那开始着手进行修改吧,首先创建一个名为 iss3 的分支:

# git branch <branch-name>
$ git branch iss3

如果想要创建并立即切换到所创建的分支可以这样:

# 创建名为 iss3 的分支并切换过去
$ git switch -c iss3

# 在旧版本 Git 中普遍使用签出(checkout)命令:
$ git checkout -b iss3

切换分支

切换到指定分支可以使用 git switch 命令:

# 切换至 iss3 分支
$ git switch iss3
# 如果想快速从哪来回哪去,如从 master 切换到 iss3,现在想快速回 master,可以使用 -
$ git switch -

# 在旧版本 Git 中普遍使用签出 checkout 命令,注意这里没有 -b 选项
$ git checkout master

合并分支

切换到 iss3 后提交了两次提交,将 README.md 进行修改成了这样:

## Git 简史

同生活中的许多伟大事物一样,Git 诞生于一个极富纷争大举创新的年代。

两次提交为:

  1. 修正格式 – 第一行的修改,加上了标题
  2. 修正标点符号 – 第二杭的修改,使用中文标点;

修改完成后,将修改内容合并到 master 分支:

# 首先切换回 master 分支
$ git switch master

此时 master 分支下的 README.md 文件还是未修改的,现在合并指定分支 iss3 到当前分支 master

$ git merge iss3

删除分支

合并后 iss3 分支就可以删除了

# 删除已合并分支
# git branch -d <branch-name>
$ git branch -d iss3

合并的几种方式

fast-forward

一般情况下 git merge 是使用「快速合并(fast-forward)」,以刚才的例子为例,使用命令:

$ git reflog
a50e9f5 (HEAD -> master, iss3) [email protected]{0}: merge iss3: Fast-forward
e8f2bbc [email protected]{1}: checkout: moving from iss3 to master
a50e9f5 (HEAD -> master, iss3) [email protected]{2}: commit: 修正标点符号
0aadb9c [email protected]{3}: commit: 修正格式
e8f2bbc [email protected]{4}: checkout: moving from master to iss3
e8f2bbc [email protected]{5}: commit: 添加内容初稿
160218c [email protected]{6}: commit (initial): 添加说明文件
$ git log --graph --pretty=short
* commit a50e9f525347c53317a2b5386c4f1a8ed81ce786 (HEAD -> master, iss3)
| Author: Conners Hua <[email protected]>
|
|     修正标点符号
|
* commit 0aadb9cc5cd0a4286b19efc9c6506c54b9658967
| Author: Conners Hua <[email protected]>
|
|     修正格式
|
* commit e8f2bbc62fd170cbf7c574d681643d717fd5a053
| Author: Conners Hua <[email protected]>
|
|     添加内容初稿
|
* commit 160218c2425705eff8d2d5de173629f1c33ee423
  Author: Conners Hua <[email protected]>

      添加说明文件

可以看到 master 分支的提交历史中也有 iss3 的提交历史。

除了 fast-forward 还有另外几种方式。

–no-ff

如果合并时使用的是

git merge --no-ff <branch-name>

那么使用命令:

$ git log --graph --pretty=short
*   commit f4c4ec661856d02e306c035a10f28ad3b055e11a (HEAD -> master)
|\  Merge: e8f2bbc a50e9f5
| | Author: Conners Hua <[email protected]>
| |
| |     合并 'iss3' 分支,修正文稿内容
| |
| * commit a50e9f525347c53317a2b5386c4f1a8ed81ce786 (iss3)
| | Author: Conners Hua <[email protected]>
| |
| |     修正标点符号
| |
| * commit 0aadb9cc5cd0a4286b19efc9c6506c54b9658967
|/  Author: Conners Hua <[email protected]>
|
|       修正格式
|
* commit e8f2bbc62fd170cbf7c574d681643d717fd5a053
| Author: Conners Hua <[email protected]>
|
|     添加内容初稿
|
* commit 160218c2425705eff8d2d5de173629f1c33ee423
  Author: Conners Hua <[email protected]>

      添加说明文件

可以看到,使用 --no-ffmaster 的提交历史虽然可以看到 iss3 的提交历史,但是注意最左侧的线,它清晰的显示这部分的提交是来自于另一条分支。

–squash

如果合并时使用的是

$ git merge --squash <branch-name>

那么使用命令 git log --graph --pretty=short 时你会发现没有新的提交,因为此时来自 iss3 分支的修改内容直接存到 master 分支的工作区和暂存区。

这时候需要手动的提交:

$ git commit -m '合并 iss3 分支,修正文稿内容'

然后再查看下历史

git log --graph --pretty=short
* commit a1c313d85cbc517aa37b5ada8a553ce65eb8e91f (HEAD -> master)
| Author: Conners Hua <[email protected]>
|
|     合并 iss3 分支,修正文稿内容
|
* commit e8f2bbc62fd170cbf7c574d681643d717fd5a053
| Author: Conners Hua <[email protected]>
|
|     添加内容初稿
|
* commit 160218c2425705eff8d2d5de173629f1c33ee423
  Author: Conners Hua <[email protected]>

      添加说明文件

使用 --squash 可以让其他分支的最新改动直接存到当前分支的工作区和暂存区,然后再进行提交,并且不会显示被合并分支的提交历史。

小结

分支命令

关于分支:

  • 查看分支使用命令 git branch,其 -a 选项可查看所有分支、-v 选项显示更多信息;
  • 创建分支使用命令 git branch <分支名>,创建分支切立即切换使用命令 git switch -c <分支名>
  • 切换分支可以使用命令 git switch <分支名>,切换后快速返回切换前的分支可以使用命令 git switch -
  • 合并分支先切换至要合并到的分支,然后使用命令 git merge <分支名>
  • 合并后删除已合并的分支使用命令 git branch -d <分支名>,若想删除未合并的分支使用命令 git branch -D <分支名>

关于合并:

  • fast-forward:会在当前分支添加被合并分支的提交历史;
  • --no-ff:清晰的显示哪些提交来自被合并分支的提交历史;
  • --squash:将被合并分支的修改直接存到工作区及暂存区,然后手动的进行提交,这样当前分支的提交历史不会参杂被合并分支的提交历史;

分支策略

对于分支的使用管理,有一种简单通用的流程:

  1. 仅使用一个「长期分支」
    「长期分支」可以表示为主分支,它代表了在整个项目生命周期的一种状态,所以不应该直接在这个分支上工作,而是整合其他分支的内容到主分支上来。
  2. 积极使用「主题分支」
    Git 鼓励使用分支,这也是 Git 的特色之一。在着手开发一个新的功能或错误修复时,应该针对单一的主题创建一个新的分支,也就是每个新的「主题分支」应该只对应一件事。