分支合并
我们先在 master 分支的基础上新建一个 dev 分支, 并做一个 commit:
这时候另外一个开发人员发现 master 上的代码有一个问题,对 master 的代码做了一个 fix,使得 master 的 head 向前推进了一步:
如果我们想将 master 的 Fix 改动应用到 dev 分支上,要如何做呢?
$(dev) git merge master
dev 上 多出来的这个 commit(绿色的那个节点), 就是我们的 merge 信息。
有时候我们并不想要 git 记录这个 merge 信息,因为让 git 的历史记录变得很繁琐,要如何做呢?可以使用 rebase !
我们先回到 master 提交了 fix 之后的 git 状态:
$ (dev) git rebase master
比较下 merge 和 rebase 之后的状态图,我们可以发现 masste 的 fix 被接到 dev 的后面,并且没有多出一个 merge 信息。
什么时候用rebase和merge选择
在合并代码时,选择 git rebase master
还是 git merge --no-ff master
取决于团队的工作流程以及你对提交历史的需求。两者的主要区别在于提交历史的处理方式:
-
git rebase master
使用rebase
是将你的分支上的提交“平铺”在master
分支的最新提交之上,最终提交历史是线性的。
git checkout feature
git rebase master
特点:
- 线性历史:提交历史将是线性的,更加简洁。
-
重写历史:
rebase
会重新写入你分支上的提交,修改提交的父节点,因此它会改变提交 ID。 - 避免额外的合并提交:只包含分支上的提交,不会创建额外的合并提交。
适用场景:
- 在将 feature 分支合并到 master 分支之前,通过
rebase
来保持提交历史的整洁。 - 适用于代码库的风格要求提交历史线性、简洁的团队。
-
注意:如果其他开发者也在使用该分支,使用
rebase
可能会导致冲突,因为rebase
会重写提交历史。
-
git merge --no-ff master
merge --no-ff
命令进行的是普通合并,将master
分支的内容合并到当前分支,并创建一个新的合并提交来记录这次合并。
git checkout feature
git merge --no-ff master
特点:
- 保留分支历史:保留了分支点和合并点的提交信息,可以看到每个分支的开发过程。
-
不会重写历史:与
rebase
不同,merge
不会更改提交 ID。 -
创建合并提交:
--no-ff
强制创建一个合并提交,即使feature
分支可以直接快速前进(fast-forward)。
适用场景:
- 适用于需要保留分支信息的大型项目,通过合并提交可以清楚看到不同的功能或问题修复分支。
- 当项目团队希望使用“合并提交”来记录分支合并时。
- 团队工作流程鼓励保留所有分支信息,以便于回溯和查看分支变更记录。
什么时候使用哪个?
-
使用
git rebase master
:如果你想让提交历史看起来更简洁,并且你是唯一使用该分支的开发人员。 -
使用
git merge --no-ff master
:如果你希望保留分支的分叉和合并点,以更清晰地展示开发过程,尤其是团队开发或代码审核中,可以更容易看到分支的来源和合并点。
总结
-
rebase
:适用于想要平滑、简洁的提交历史。通常在个人开发分支上使用,并最终rebase
到主分支后合并。 -
merge --no-ff
:适用于希望保留分支合并信息,提供清晰的合并记录,适合团队合作的大项目。
git rebase -i
是 Git 中的交互式变基(interactive rebase)命令,允许你在重写提交历史时进行细粒度的控制。它常用于合并提交、修改提交信息、删除提交、重新排序提交等。以下是 git rebase -i
的详细用法和步骤:
合并多个commit为一个commit
当我们在本地仓库中提交了多次,在我们把本地提交push到公共仓库中之前,为了让提交记录更简洁明了,我们希望把如下分支B、C、D三个提交记录合并为一个完整的提交,然后再push到公共仓库。
现在我们在测试分支上添加了四次提交,我们的目标是把最后三个提交合并为一个提交:
这里我们使用命令:
git rebase -i [startpoint] [endpoint]
其中-i的意思是–interactive,即弹出交互式的界面让用户编辑完成合并操作,[startpoint] [endpoint]则指定了一个编辑区间,如果不指定[endpoint],则该区间的终点默认是当前分支HEAD所指向的commit(注:该区间指定的是一个前开后闭的区间)。
默认参数:
-
git rebase -i
后面没有参数时,相当于隐式使用git rebase -i <upstream>
,其中<upstream>
是当前分支与其父分支的分叉点。 - 这个行为通常用于整理或修改当前分支上的所有提交,而不影响父分支上的提交历史。
在查看到了log日志后,我们运行以下命令:
git rebase -i 36224db
或
git rebase -i HEAD~3
然后我们会看到如下界面:
上面未被注释的部分列出的是我们本次rebase操作包含的所有提交,下面注释部分是git为我们提供的命令说明。每一个commit id 前面的pick表示指令类型,git 为我们提供了以下几个命令:
pick:保留该commit(缩写:p) reword:保留该commit,但我需要修改该commit的注释(缩写:r)
edit:保留该commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e)
squash:将该commit和前一个commit合并(缩写:s)
fixup:将该commit和前一个commit合并,但我不要保留该提交的注释信息(缩写:f) exec:执行shell命令(缩写:x)
drop:我要丢弃该commit(缩写:d)
根据我们的需求,我们将commit内容编辑如下:
然后是注释修改界面:
编辑完保存即可完成commit的合并了:
解决冲突
在变基过程中,你可能会遇到合并冲突。Git 会提示你哪些文件有冲突,你需要手动解决冲突,解决完成后执行以下命令继续变基:
git add <resolved-files>
git rebase --continue
如果想中止变基操作,可以使用:
git rebase --abort
官网:https://git-scm.com/docs/git-rebase