1. 前言
Branches in Git are incredibly lightweight as well. They are simply pointers to a specific commit – nothing more. This is why many Git enthusiasts chant the mantra:
branch early, and branch often.
Because there is no storage / memory overhead with making many branches, it’s easier to logically divide up your work than have big beefy branches.
When we start mixing branches and commits, we will see how these two features combine. For now though, just remember that a branch essentially says “I want to include the work of this commit and all parent commits.”
参考文档:
2. git分支规范
参考文档:
3. 分支基本操作
3.1. 查看分支
1 | git branch -a |
3.2. 本地仓库中创建分支
本地仓库中创建与当前分支相同的bugfix分支,并切换到bugfix分支
1 | git checkout -b bugfix |
创建与本地仓库main分支相同的分支
1 | git checkout main -b bugfix |
创建与远程仓库main分支相同的分支,追踪远程分支
1 | git checkout origin/main -b bugfix |
3.3. 切换分支
下载仓库后,切换到bugfix分支,bugfix分支追踪远程 origin/bugfix 分支
1 | git pull |
3.4. 远程仓库中创建分支
创建远程仓库bugfix分支和bugfix2分支
1 | git push origin HEAD:bugfix |
3.5. 上传分支
在bugfix分支下进行了修改,然后提交修改
1 | git add . |
3.6. 删除分支
1、删除本地分支
1 | git branch -D bugfix |
2、删除远程分支
1 | git push --delete origin bugfix |
4. 恢复已删除分支
1、查看全局日志
1 | git reflog |
或者
1 | git log -g |
2、新建分支
1 | git branch bugfix 3eac14d |
3、上传分支
1 | git checkout bugfix |
5. 合并分支
5.1. 合并分支概述
合并分支,可以使用merge,也可以使用rebase。它们俩有什么区别呢?主要是适用场景不同:
场景一:基于 master 拉出来一个开发分支 dev,在 dev 分支开发完成了之后,要合并到 master 上。
操作:切换到 master 分支,使用 git merge dev
PS:对于git服务器端的merge合并,往往要发起MR,也就是Merge Request
为什么不使用git rebase dev
?因为master是主干,rebase的话,主干就长歪了。为了保证master提交记录是笔直的主干,是一条直线,所以使用merge。
场景二:基于 master 拉出来一个开发分支 dev,在 dev 上开发了一段时间后,master分支也有了新的变更。为了保证我们在最新的版本基础上进行开发,这时需要把 master 分支提交的新内容更新到 dev 分支。
操作:切换到 dev 分支,使用 git rebase master
为什么不使用git merge master
?因为dev最终是要merge到master的,如果master先merge到dev,开发一段时间后dev最后又merge到master,整个提交树会很混乱。为了保证提交记录的清晰明了,所以使用rebase。
查看提交树命令:git log --graph --pretty=oneline
5.2. 模拟分支变更
1、初始化仓库和分支
1 | mkdir test |
2、bugfix分支上,新建test.txt文件,提交
1 | echo "hello bugfix" > test.txt |
3、切到master分支,新建test.txt文件,提交
1 | git checkout master |
5.3. merge
1、通过merge命令合并bugfix分支到master
1 | git merge bugfix |
提示冲突:
1 | Auto-merging test.txt |
2、查看冲突git status
,提示test.txt文件冲突。
打开test.txt,可以看到如下冲突:
1 | <<<<<<< HEAD |
其中 ======= 隔开的上半部分,是 HEAD(即 master 分支,在运行 merge 命令时检出的分支)中的内容,下半部分是在bugfix分支中的内容。解决冲突的办法无非是二者选其一或者由你亲自整合到一起。
3、解决冲突
修改test.txt如下:
1 | hello master |
4、提交
1 | git add test.txt |
5、查看提交记录
1 | git log |
5.4. rebase
5.4.1. rebase合并内容
需求:text.txt 打算合并master分支和bugfix分支中的内容。
text.txt最终内容为:
1 | hello master bugfix |
1、通过rebase命令合并master分支到bugfix
1 | git checkout bugfix |
提示冲突:
1 | First, rewinding head to replay your work on top of it... |
此时我们进入了一个临时分支。
2、查看冲突git status
,提示test.txt文件冲突。
打开test.txt,可以看到如下冲突:
1 | <<<<<<< HEAD |
3、解决冲突
修改test.txt如下:
1 | hello master bugfix |
4、提交
1 | git add test.txt |
5、查看提交记录
1 | git log |
可以看到三条记录:
1 | update file in bugfix branch |
5.4.2. rebase保留master
需求:text.txt 打算保留master分支中的内容,舍弃bugfix分支中的内容。
text.txt最终内容为:
1 | hello master |
1、通过rebase命令合并master分支到bugfix
1 | git checkout bugfix |
提示冲突:
1 | First, rewinding head to replay your work on top of it... |
此时我们进入了一个临时分支。
2、查看冲突git status
,提示test.txt文件冲突。
打开test.txt,可以看到如下冲突:
1 | <<<<<<< HEAD |
3、解决冲突
修改test.txt如下:
1 | hello master |
或者直接使用命令行解决冲突:
1 | git checkout --ours test.txt |
4、提交
1 | git add test.txt |
5、查看提交记录
1 | git log |
可以看到两条记录:
1 | update file in master branch |
5.4.3. 放弃rebase
rebase过程中,可以随时放弃rebase
1 | git rebase --abort |
5.5. 解决冲突小结
合并时难免代码冲突,git会将冲突的代码用 <<<<<<< HEAD ======= >>>>>>> xxx
标识出来,其中=======之前表示的是ours分支,之后表示theirs分支。
解决冲突办法:首先确定保留哪一部分代码,然后手动删除标志 <<<<<<< HEAD ======= >>>>>>> xxx
,最后commit。
如果想要保留两个分支中的某一个,可以使用 git chekout --ours <fileName>
或者 git checkout --theirs <fileName>
。
对于merge和rebase来说,这两个选项对应的分支正好是相反的。
在使用 merge 时,ours指的是当前分支(master),theirs指的是要被合并的分支(dev)。
而在 rebase 时,theirs指的是当前分支(dev),ours指向(master)。
6. HEAD分支告警
6.1. 问题描述
执行 git status
,出现了warning
1 | warning: refname 'HEAD' is ambiguous. |
6.2. 解决办法
解决办法:删除 HEAD 分支
1 | git branch -D HEAD |
原因分析:
这个问题是因为我们有一个叫做 HEAD 或者 head 的分支造成的,这个名字是一个保留字,它用于标示哪个分支是当前分支 ,而不应该直接拿来做分支的名字。
但是 remote 的 HEAD 分支是 OK 的,比如 origin/HEAD,经常有人把这个 checkout 下来,结果就变成了本地的 HEAD 分支,这就是此问题产生的源头。
如果用 HEAD 创建了一个 tag,这也会引起相同的问题。