Git 的分支管理
一、分支介绍
1、分支是什么
Git作为一个分布式版本控制系统,提供了强大而灵活的分支管理功能,使得开发团队能够高效地协作开发、管理不同的功能和版本。
2、为什么有分支
一般情况下主分支(master/main)应始终保持可部署的状态,避免在主分支上进行开发工作,所以需要从主分支上面新建分支,然后在这个新建分支上面进行开发,以后如果需要,可以将这个新建分支与主分支合并。
也就是说通过创建分支,开发者可以在不影响主线(通常是master
或main
分支)的情况下进行新功能的开发、bug修复或实验性改动。完成后,可以将分支上的更改合并回主线。
比如说有一个仓库有 master 分支和 develop 分支,我们在 develop 分支上的操作不会影响到 master 分支。
二、分支管理
1、分支相关操作指令
1)查看分支
git branch # 查看本地分支
git branch -r # 查看远程分支
git branch -a # 查看所有分支(包括本地和远程分支)
以下是这些指令的使用示例:
本地的分支 master 前面有一个 *,指的是当前的工作分支是这个分支。
2)创建分支
新建一个分支并依然停留在当前分支:
git branch [new-branch] # 新建一个分支并依然停留在当前分支
使用这个指令创建一个新的分支后,在列出本地分支,发现出现了这个新建的分支,而且当前的工作分支依旧是 master。
新建一个分支并切换到这个新分支(方法一)
git checkout -b [new-branch] # 创建一个分支并切换过去
-b
是 branch
的缩写,它告诉 Git 你想要创建一个新的分支,checkout 指令作用就是切换分支。
使用这个指令后,会出现一个提示,显示已经换到了新分支上了。可以看到 * 已经在新分支上了,也就是说当前的工作分支是在这个新分支上的。
新建一个分支并切换到这个新分支(方法二)
因为 checkout 作为单个命令的功能有些超载(承担的功能有些多),所以引入了 git switch 这个指令。
git switch -c [new-branch] # 创建并切换到新分支,适用于 Git 2.23 版本及以上
在 Git 2.23 及以上版本,可以使用这个指令进行创建并切换到新分支。
-c 是 create 的缩写,表示创建一个新的分支,switch 指令作用就是切换分支。
可以看到跟上面的指令作用一致。
基于一个远程分支创建一个本地分支:
git checkout -b local-new-branch origin/remote-branch # 通过远程分支 remote-branch 分支创建一个本地新分支 local-new-branch
3)切换分支
使用 checkout 指令:
git checkout [branch] # 使用 checkout 指令切换分支
可以看到使用 checkout 指令后,当前的工作分支就改变了。
使用 switch 指令:
git switch [branch] # 使用 switch 指令切换分支
可以看到使用 switch 指令后,当前的工作分支就改变了。
4)删除分支
删除已经合并的分支:
git branch -d [branch] # 删除已经合并的分支
在删除某个分支时,需要先切换到要删除的分支以外的分支。
可以发现使用此指令可以将指定的分支删除。如果尝试删除当前工作分支 develop 的话,就会出现以下提示:
强制删除未合并的分支:
git branch -D [branch] # 强制删除未合并的分支
使用此指令也要将当前的工作分支切换到要删除分支以外的其他分支。
可以发现使用此指令可以将指定的分支强制删除。如果尝试删除当前工作分支 develop 的话,就会出现以下提示:
5)重命名当前分支
git branch -m [branch] # 重命名当前分支
使用此指令之前,你需要先切换到你想要改名的分支。
6)推送分支到远程仓库
推送分支到远程仓库(不设置跟踪):
git push origin local-branch:remote-branch # 将 local-branch 推送到 remote-branch 分支
使用此指令可以将本地分支 local-branch 分支推送到远程 remote-branch 分支,这个指令不会设置本地分支跟踪远程分支。
可以看到本地的 feature 分支已经推送到远程的 feature 分支了。
这里也可以看到本地的 feature 分支没有跟踪到远程的 feature 分支,这里的指令下面会介绍。
推送分支到远程仓库并设置跟踪:
git push -u origin [branch] # 将 branch 推送到远程并设置追踪
此指令可以将分支推送到远程仓库,并将此本地分支设置跟踪到远程分支。设置了跟踪关系后,后续的操作就可以有一些简化了,直接使用 git push 自动推送当前分支到其跟踪的远程分支,直接使用 git pull 自动从跟踪的远程分支拉取更改。
这里提示 bugfix 分支已经追踪到远程的 bugfix 分支了。
然后我们可以使用以下指令来查看每个分支的跟踪情况:
git branch -vv # 查看所有分支以及其跟踪状态
可以看到 bugfix 分支已经设置追踪到了远程的 bugfix 分支了。这里的 feature 分支上面一种方式推送的,并没有设置跟踪到远程分支。
我们也可以手动设置跟踪到远程分支,使用以下指令:
git branch -u origin/branch-name branch-name # 设置 branch-name 分支跟踪远程分支
这里的两个 branch-name 可以不同。这里是将本地的 branch-name 分支设置跟踪远程 branch-name 分支。
如果你想要设置当前所在分支设置跟踪到远程的某个分支,可以使用以下指令:
git branch -u origin/branch-name # 设置当前分支追踪远程指定分支
可以看到这里的本地的 feature 分支已经跟踪到远程的 feature 分支了。
7)删除远程分支
$ git push origin --delete [remote-branch] # 删除指定远程分支
此指令可以删除指定远程分支。
可以看到这里的远程 feature 分支成功被删除。
2、使用 IDEA 界面进行分支操作
1)查看分支
这里有 Local 本地分支,也有 Remote 远程分支,这里有 标志的是当前工作分支。
2)创建分支
这样操作可以直接从当前工作分支创建新分支,如果要从指定的分支创建新分支,则可以直接右键指定的分支就,然后就可以出现以下选项:
然后就可以选择从选择的分支创建新分支。
可以右键远程分支,选择 Checkout 就可以基于远程分支创建一个新的本地分支:
3)切换分支
右键想要切换的分支,就会出现选项列表,然后使用 checkout 功能就可以切换分支了。
4)删除分支
右键想要删除的分支,就会出现选项列表,然后使用删除功能就可以删除分支了。
5)重命名当前分支
右键需要改名的分支,然后就会出现重命名的选项,就可以进行重命名了。
6)推送分支到远程仓库
右键需要推送的分支就可以出现推送选项。
7)删除远程分支
右键需要删除的远程分支,就会出现删除选项。
三、分支合并
可以使用指令:
git checkout [目标分支]
切换到目标分支上,然后使用指令:
git merge [要合并到目标分支的分支]
就可以将要合并的分支合并到目标分支。
下面我们的示例将使用 IDEA 的界面操作。
1、快进合并(Fast-Forward Merge)
1)介绍
快进合并发生想要合并到的分支在分支后没有提交记录。这种情况下,Git只需将 master 分支指针向前移动到 feature 分支的位置,无需创建新的合并提交。
A---B---C (master)
\
D---E (feature)
在合并后:
A---B---C---D---E (master, feature)
注意:在合并后 feature 分支并不会消失。如果你不再需要 feature 分支,你可以手动将其删除。
2)示例
首先 master 分支的文件状况是这样的:
然后我们从 master 分支创建一个 feature 分支:
然后我们在 feature 分支提交一些文件:
然后我们可以发现 master 分支和 feature 分支的提交记录只有这一次不同:
这时,我们将 feature 分支合并到 master 分支:
这里我们当前分支为 master 分支,然后我们右键想要合并的分支,就会出现将 feature 分支合并入 master 分支。
合并好之后,我们可以发现主分支的提交记录与 feature 分支的提交记录是一致的了,同时在主分支中也能看到 feature 分支创建的新文件了。
2、三方合并(Three-way Merge)
1)介绍
三方合并适用于目标分支和被合并分支都有各自独立的提交,且存在一个共同的祖先节点。在这种情况下,Git 会将两个分支的最新提交与二者的最近祖先节点三者进行合并并提交。
A---B---C---D (master)
\
E---F (feature)
合并后:
A---B---C---D---G (master)
\ /
E---F---/
(feature)
2)示例
最开始 master 分支的文件是这样的:
我们首先从 master 分支创建一个 feature 分支:
然后我们在 feature 上创建并提交一些文件:
然后我们切换到 master 分支,也在 master 分支提交一些文件:
然后我们分别查看 master 分支和 feature 分支的提交记录:
可以看到他们都有一个共同的祖先节点 commit 1,后面的提交是不同的,所以结构图应当是:
然后我们将 feature 分支合并到 master 分支上。
这时,我们将工作分支设为 master 分支,可以看到:
说明 feature 分支被合并到了 master 分支。
然后我们可以将工作分支设为 feature 分支,可以看到:
没有 master 分支的 Master1 的提交,因为 master 没有合并到 feature 分支。
这时的结构图应当是:
然后我们可以在 master 分支的提交记录上看到:
与我们画的结构一致。
3)补充
这里我们再看 feature 分支的提交记录是这样的:
这是因为我们是将 feature 分支合并到 master 分支上,而并没有将 master 分支合并到 feature 分支上。
而且在合并后,feature 分支依旧作为一个独立的分支存在,它不会消失,上面我们也有提到过。
3、分支合并出现冲突
1、介绍
上面我们介绍的合并都是没有冲突的,对于快进合并是不会出现冲突的,冲突会发生在三方合并中。
在合并过程中,如果同一文件的同一部分在两个分支都有不同的修改,Git无法自动决定最终的内容,此时会出现合并冲突。
2、示例
最开始只有一个 master 分支,文件情况为:
然后我们创建一个 feature 分支,然后在这个分支上对 Hello.java 文件进行改动:
然后将这个文件提交:
然后我们切换到 master 分支上,也对 Hello.java 文件进行改动,但是改动与上面的改动不同:
然后将这个文件提交:
3、冲突解决
然后我们将 feature 分支合并到 master 分支,这时就会出现冲突提示:
1)手动编辑冲突文件
如果我们点击 Merge 按钮,则需要对冲突文件进行编辑,以解决冲突:
这里我们将两个类都保留:
最终解决好冲突后的合并文件就是:
保留了两个类。
补充
如果不直接通过 IDEA 提供的冲突解决编辑器,Git 也给出了冲突文件的标记:
Git 标记的规则:
<<<<<<< HEAD
当前分支(目标分支)的内容
=======
被合并分支(源分支)的内容
>>>>>>> feature
然后我们可以在这个文件中进行编辑,然后我们确定了最终要保留的文件内容后,可以直接将这个文件 add 到暂存区,然后再进行提交就完成了这次合并。
如果不解决这个冲突文件,是没法完成这次合并的,没有完成合并,则没办法切换分支。实际上也可以通过中止合并,这样这次合并就会中止,分支也能正常切换,下面会介绍中止合并。
2)使用某一分支的文件
现在是在 master 分支,如果我们选择 Accept Yours,就会以当前分支 master 的修改为最终方案进行合并,如果选择 Accept Theirs 就会以要合并的分支 feature 的修改为最终方案进行合并。
4、放弃合并
在分支合并时遇到冲突,我们可以向上面那样编辑冲突文件,解决冲突,也可以放弃合并。
可以使用指令:
git merge --abort # 中止合并
在 IDEA 中可以点击下面这里来中止合并:
5、补充
实际开发中要尽量避免出现冲突。