目录
(8)git代码分支合并(pull->merge->push)
(1)理解git分支
主分支(主线)
在Git中新建一个项目后,默认有一个分支,即主分支。主分支一般表示项目的稳定版本,主分支应该包含稳定没有 Bug 的代码,并保持随时可以发布的状态,对于小型项目来说,只有一个主分支就够用了,每次我们提交都会创建一个commit节点。
$ git commit -m "c1"
$ git commit -m "c2"
$ git commit -m "c3"
上面的命令会创建三个commit节点,此时master分支如下图所示,代码经历了C1,C2,C3这三个版本,且master分支目前指向C3这个提交版本。
如果项目功能较复杂,且需要多次提交,不建议在主分支直接修改。主分支上应该只包合并提交,所有的迭代应该都在分支上进行。如果是简单的改动,直接在主分支修改也是可以的。
功能分支
当有新的功能要开发时,应该新建一个功能分支,比如创建一个名为a的分支,并切换到a分支,命令如下:
$ git checkout -b a
创建新分支时,新分支默认指向的代码提交版本为当前分支所指向的代码提交版本,比如这里新分支a指向的提交将为C3。此时当前分支为a,所指向的提交为C3。
接下来在a分支上创建两个提交,命令如下:
$ git commit -m "c4"
$ git commit -m "c5"
每次有新的提交后,分支都会指向最新的提交,两次提交后的提交树情况如下,此时主分支的代码版本为C3,a分支的代码版本为C5。
主线和分支关系
在Git中,分支(Branch)是一个非常核心的概念,它允许开发者在主线(通常是master或main分支)之外进行工作,而不影响主线上的代码。
因为主线和分支是两个独立的代码线,这样,你可以在一个分支上尝试新功能、修复bug或进行其他任何类型的开发,而不必担心破坏主线的稳定性。
通常主线就是当前项目正在运行的版本,假设为版本1.1,分支的作用就是尝试新开发一个功能或者修复某个bug,版本为1.2,当分支通过了所有必要的测试和调试,就可以将分支合并到主线,这样主线就从1.1版本升级到了1.2版本。
将分支合并到主分支
git merge有三种模式:git merge --ff/--no-ff/--ff-only。
先简单介绍一下 git merge 的三个合并参数模式:
- -ff 自动合并模式:当合并的分支为当前分支的后代的,那么会自动执行 --ff (Fast-forward) 模式,如果不匹配则执行 --no-ff(non-Fast-forward) 合并模式
- --no-ff 非 Fast-forward 模式:在任何情况下都会创建新的 commit 进行多方合并(即使被合并的分支为自己的直接后代)
- --ff-only Fast-forward 模式:只会按照 Fast-forward 模式进行合并,如果不符合条件(并非当前分支的直接后代),则会拒绝合并请求并且退出。
快速合并
当功能分支开发完成后,需要合并回主分支,合并回主分支有两种选择,快速合并和非快速合并,二者的区别在于是否创建提交节点,命令如下:
$ git checkout master # 切换到master分支
$ git merge a # 将a分支的内容合并到当前分支即master分支 # 快速合并
$ git merge --no-ff a # 非快速合并
快速合并的结果,会直接将 master 指向了a所指向的提交即C5,如下图所示:
非快速合并
非快速合并的结果,会在 master 创建合并提交节点,如下图所示:
使用git merge --no-ff来合并,使用该命令合并时会创建一个新的commit,所以加上-m参数,把commit描述写进去:git merge --no-ff -m "merge with no ff" dev。
两种合并方式都可以。当合并的分支跟 master 不存在共同祖先节点的时候,这时候在 merge 的时候 git 默认无法使用 Fast-forward 模式。推荐使用非快速合并。
git代码管理流程
以下是开发流程的一个详细的概述:
- 主线(main):这是项目的核心和稳定版本,比如版本1.1。所有的用户都从这个版本中获取功能,并且它代表了项目的当前状态。
- 创建分支:当需要开发新功能或修复bug时,你会从主线创建一个新的分支。这个新分支可以用来存放与版本1.2相关的所有更改。
- 在分支上工作:在新的分支上,你可以自由地添加新功能、修复bug或进行其他任何形式的代码更改。这些更改不会影响主线,因为主线和分支是两个独立的代码线。
- 测试和调试:在将更改合并到主线之前,你应该在分支上进行充分的测试和调试,以确保你的更改是稳定且按预期工作的。
- 合并分支:一旦你的更改在分支上通过了所有必要的测试和调试,你就可以将它们合并回主线了。这个过程会将这些更改从分支复制到主线,并创建一个新的提交,该提交包含了这些更改。
- 发布新版本:合并分支后,主线现在包含了版本1.2的所有更改。你可以将主线的代码发布为一个新的版本(即版本1.2),以便所有用户都可以访问和使用这些新功能或bug修复。
- 删除分支(可选):在将分支的更改合并到主线后,这个分支通常就没有用了,因此你可以将其删除以清理你的代码库。
(2)理解git提交对象
提交对象与commitID
- 在进行提交操作(git commit)时,Git会保存一个提交对象(commit object)。知道了 Git保存数据的方式,我们可以很自然的想到——该提交对象会包含一个指向暂存内容快照的指针
- 但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针
- 首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象
- 每个提交对象都对应一个 commitID
Git如何保存数据
Git保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照
示例讲解
我们假设现在有一个工作目录,里面包含了三个将要被暂存和提交的文件(README test.rb LICENSE):
- 暂存操作:会为每一个文件计算校验和(SHA-1 哈希算法),然后会把当前版本的文件快照保存到Git仓库中(Git使用blob对象来保存它们),最终将校验和加入到暂存区域等待提交:git add README test.rb LICENSE,git commit -m 'The initial commit of my project'
- 当使用git commit进行提交操作时:Git 会先计算每一个子目录(本例中只有项目根目录)的校验和,然后在Git仓库中这些校验和保存为树对象。随后,Git便会创建一个提交对象,它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。如此一来,Git就可以在需要的时候重现此次保存的快照。
- 现在,Git 仓库中有五个对象:三个blob对象(保存着文件快照),一个树对象(记录着目录结构和blob对象索引),一个提交对象(包含着指向前述树对象的指针和所有提交信息)。
由于提交对象包含了全部信息,我们只需要关注提交对象即可,每个提交对象都对应一个 commitID。
提交对象及其树结构内容如下:
如果此时做些修改后再次提交(git commit),那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针,如下图所示从左到右三次提交,最上方显示了其commitID。
(3)git提交对象与分支
git分支的本质
Git 的分支,其实本质上仅仅是指向提交对象的可变指针 。
git的默认分支
- 备注:Github在2020.10.1将仓库的默认分支从master更改为main,下面我们仍以master作为介绍,但是你需要了解现在的默认分支已经是main了
- Git的默认分支名字是master。在多次提交操作之后,你其实已经有一个指向最后那个提交对象的master分支。它会在每次的提交操作中自动向前移动
- Git的“master”分支并不是一个特殊分支。它就跟其它分支完全没有区别。之所以几乎每一个仓库都有master分支,是因为git init命令默认创建它,并且大多数人都懒得去改动它
如何创建分支
Git是怎么创建新分支的呢?很简单,它只是为你创建了一个可以移动的新的指针
比如,创建一个testing分支,你需要使用git branch命令:
git branch testing
这会在当前所在的提交对象上创建一个指针