利用分支,你就可以在同一个代码基础上同时处理多个完全没有关联、相互独立的工作。
考虑以下场景。
假设你正在改一个 Bug-A,此时已经产生了大量的代码修改,并且离修复完成还有很长一段时间(起码得明天)。此时,有一个着急但简单的 Bug-B 需要你立即完成,并在一个小时内同步给团队。你会如何管理你手头的代码?
- 先提交 Bub-A 未完成的代码,然后开始写 Bug-B 的代码。(如果此时团队要发布产品,那产品中将包含 Bug-A 中未完成的代码,即包含一个半成品。)
- 删除 Bug-A 所做的修改,然后开始写 Bug-B 的代码。(勇气可嘉,你不仅丢失了你的成果,而且有可能丢掉你的工作。)
- 找到所有关于 Bug-A 所做的修改,将代码复制到其它文件中进行备份,然后再开始写 Bug-B 的代码。(天知道你改了多少行代码,你确认你能掌控这种错综复杂的代码吗?真替你头疼。)
- 使用分支❗(明智之举👍)
本节 Git 命令概览
git branch branch-name # 创建分支。branch-name 参数是分支名称
git branch # 查看本地分支列表
git switch branch-name # 切换到 branch-name 分支
git checkout branch-name # 旧版本 Git 的切换分支命令
git swicth -c branch-name # 创建分支,并切换到该分支。 -c 标志是 -create 标志的简写
git merge branch-name # 将分支 branch-name 合并到当前所在的分支上去
git branch --help # 打开分支命令说明文档(本地html文件)
git help branch # 同上
git branch -h # 在命令行中显示分支说明文档
q # 命令行中有分页器时,按下q退出,分页器使用上下键翻页
git branch --verbose # 查看分支列表详情
git branch -v # 同上,简写
git config --global core.editor "code -w" # 将 Git 的默认编辑器设置为 VSCODE
git config --global core.editor # 查看 Git 设置的编辑器符号
git merge branch-c -m "message" # 合并时直接创建提交消息,这样不会弹出编辑器,而是直接提交
git commit # 缺少消息参数时,git 会弹出编辑器要求你输入消息
git branch -d branch-name # 删除已合并的分支
git branch branch-name branch-commit-id # 基于一个提交 ID 创建一个分支
git branch -D branch-name # 强制删除分支(包括未合并的分支)
在初始化的时候,Git 就默认创建了一个名为 master 的分支,通常,master 分支被叫做主分支。
使用 branch
命令创建分支
git branch branch-name # 参数 branch-name 是分支名称
📢 提示:在创建分支的时候,分支名称里不能有空格,否则会报错❗
查看当前所在分支
git branch
该命令会列出本地所有的分支名称,当前所在的分支前面会标注有 *
号。
使用 switch
命令切换到目标分支
git switch target-branch # 参数 target-branch 是要切换的目标分支名称
# 旧版的切换分支命令,👇 这个命令仍旧有效
# git checkout target-branch
此时使用 git branch
再次查看本地分支列表时,如果星号出现在目标分支前,表示切换成功。
合并指令:创建并切换到目标分支
git swicth -c branch-name # -c 标志是 -create 标志的简写
该命令会创建 branch-name
分支,然后再切换到该分支上。
💡 如何理解分支,分支到底什么?
可以将分支想象成一个便签,这个便签包含分支的名称,以及一个提交的引用。而引用的这个提交一定是该分支上最后一个提交。根据最后一个提交对象,按父级关系向前推导,就可以追溯到分支的第一个提交,这些提交的集合,就是一条分支。
切换分支时,发生了什么
分支像一个便签,也可以理解成一个指针,每个分支都指向该分支的最后一个提交。而提交是一个一组文件的快照。因此,当切换分支时,会将该分支指向的提交(快照)替换(重写)到工作目录下,使得工作目录与该分支最后一次提交的状态保持一致。
特性分支(主题分支)的概念
master 分支(有时候也叫 main 分支)是在创建存储库的时候自动生成的分支,一般会包含项目所有的文件或主要的文件,并且其它分支的工作内容最终都要合并到这条分支上来,因此这个分支被称作是集成分支
为了解除某个问题或完成某种任务而创建的分支,被称作特性分支或主题分支。在它们完成后,一般都会合并到设成分支上。
使用 merge
命令合并分支、
假设现在要将 barnch-b
分支合并到 master
分支上。则 master
分支是合并的申请者,也叫做目标分支;而 branch-b
分支是合并的被申请者,也叫做源分支。
在合并分支时,首选要做是切换到目标分支上去(此时的目标分支是 master
分支)。
git switch master # 切换到目标分支
git merge branch-b # 将 branch-b 分支合并到 master 分支上去
📌 题外话:如何查看命令的帮助手册
git <command> --help
可以激活一个页面来查看对应命令的使用说明。<command>
表示可选的命令项。比如 git branch --help
. (git <command> --help
也可以写成 git help <commnad>
)
git <command> --help
git help <command> # 另一种写法
# 👆 这两个命令会打开网页git <command> -h # -h 是 --help 的简写
# 👆 这个简写的命令不会打开网页,会直接输出在命令行中
键入 q
退出分页器
Git 在查看某些信息时,要输出信息内容比较多,会使用一个分页器进行分页显示。分页器可以使用上下箭进行翻页。当按下 q 键时会退出分页器。
查看分支的详细模式
git branch --verbose
git branch -v
# --verbose 或 -v 表示详细模式
# 具体说明可以通过 git branch --help 进行查看
💡 合并分支的时候发生了什么
快进式合并(fast-forward)
快进式合并发生的条件:
- 从 master 分支创建 branch-b 分支后,master 分支上没有新的提交记录
- 除了 master 主分支外,只有一个 branch-b 分支
- 将分支 branch-b 合并到 master 分支上,而不是将 master 分支合并到 branch-b 分支上。
当满足以上条件时,在合并时,master 分支的标签会直接快进到 branch-b 标签指向的引用位置。因为这种情况下,branch-b 分支就是整个项目的所有内容的最终版本。这是一种简体式的合并。命令行中会有 fast-forword 的提示。
如果在当前情况下,将 master 分支合并到 branch-b 分支上,则不会有任何变化。因为 master 相对 branch-b 分支来说,没有新的提交(此时,master 是 branch-b 的子集)。
设置 Git 默认编辑器
git config --global core.editor "code -w" # 参数 "code -w" 表示 vscode
git config --global core.editor # 在没有参数的情况下表示查看 git 配置的编辑器名称,此时会输出 code -w
code -w
参数表示 vscode,如果想设置其它编辑器,在探索引擎里查询对应的参数即可。
真正的合并
假设现在在主分支 master(或branch-b)的最后一个提交上,此时要将分支 branch-c 的最后一个提交合并到 master 分支,命令如下:
git swtich master
git merge branch-c
此时,Git 会使用默认编辑器弹出一个编辑框,要求你输入提交信息(其它就是 git commit 指令),当你输入信息,保存后关闭编辑器,Git 就自动执行了 git commit -m "meesage"
的指令。
所以,此时的合并是创建地了一个新的提交! 即由于两个分支的提交内容不一致,所以要将这两个分支的最后一个提交合并成一个新的提交,即 提交A4。A4 指向的父级分别是 B3 和 C2.因此这个提交数据中将有两个 parent id.
图例说明:
- 一个圆圈表示一个提交
- 便签表示分支(它是指向一个提交的引用,可以理解成指针)
由上图可以看出,将 bracnh-c 合并到 master 分支后,创建了一个新的提交 A4,并且 master 分支的引用移动到了 A4 上。
git merge branch-c -m "message" # 合并时直接创建提交消息,这样不会弹出编辑器,而是直接提交
git commit # 缺少消息参数时,git 会弹出编辑器要求你输入消息
解决合并时产生的冲突
假设提交 C2 中与 提交 B3 中都修改了同一个文件中的同一行代码,在合并时 Git 就不知道要该为这个文件使用哪个修改。因此 Git 会停下合并动作,在命令行中提示 CONFLICT以表示有冲突产生,并且会停下合并动作,在工作目录中生成冲突文件。
💡 此时使用 gti status
即可查看冲突描述哦。
使用编辑器打开冲突的文件,将会看到类似于以下格式的信息:
<<<<<<<<<<<<< HEAD
1111111111111111
=============
2222222222222222
>>>>>>>>>>>>> branch-c
说明:
<<<<<<<<<<<<<
标记冲突区开始HEAD
表示当前所在的分支,此时就是 master 分支=============
分隔冲突内容>>>>>>>>>>>>>
标记冲突区结束branch-c
表示要合并到 master 的分支
上述信息表示的是就是:master 分支对该文件的某行提交的内容是 1111111111111111
,而 branch-c 分支对该文件的某行提交的内容是 2222222222222222
,Git 要求你做出选择。
处理方式:
- 保留其中一项
- 删除这两项
- 保留这两项
假设保留两项内容,结果就是:
1111111111111111
2222222222222222
记得把冲突标记要删除掉❗
保存后,将这些变化添加到暂不区,然后提交即可。
git add file-name
git commit # 打开默认编辑器
# 或
git commit -m "Merge branch 'branch-c'"
删除分支
当特性分支成功合并到集成分支后,就要将该特性分支删除。这是个良好的习惯❗
git switch master # 如果你在 branch-c 分支时,是无法删除 branch-c 分支的💡
git branch -d branch-c # 删除分支
删除分支时,只是将指向提交的引用删除了,而不是删除提交记录❗
如果误删除了分支怎么办?
git branch <branch-name> <branch-commit-id> # 基于提交 id branch-commit-id 创建一个分支 branch-name
假如仓库有三去分支:master、branch-c 和 branch-d,当你想要删除 branch-b 时误删除了 branch-d,可以通过上面的命令恢复 branch-d 分支。
因为在删除一条分支时,Git 会输出删除掉的分支名及分支指向的提交的 ID。因此,通过这个 ID 可以重新创建一个被删除的分支具有相同引用的分支(变向恢复)。
❌ git branch -d branch-d
# Deleted branch branch-d (was 64dc8sa). 👈 这是删除一个分支时的输出内容
✔️ git branch -c branch-d 64dc8sa
删除未合并到的分支
如果要删除一条未被合并的分支时,Git 会报错。因为合并后的最后一个提交有两个父级引用,其中一条引用指向了被合并分支的最后一条提交。但如果分支没合并就被删除,那将无法对删除分支上的提交历史进行追溯,因此 Git 会发出提醒。
但假如你就想删除掉这个分支的话 👀
git branch -D branch-name # -D 大写的 D 表示强制删除分支,❗❗ 谨慎使用。
一般的分支工作流程
- 基于集成分支上的最新提交创建分支;
- 在特性分支上完成工作,然后将特性分支合并到集成分支;
- 尽量不要重用特性分支(因为特性分支可能落后集成分支太长时间太导致过时),必要时完全可以基于集成分支重新创建新分支。