【git使用四】git分支理解与操作(详解)

目录

(1)理解git分支

主分支(主线)

功能分支

主线和分支关系

将分支合并到主分支

快速合并

非快速合并

git代码管理流程

(2)理解git提交对象

提交对象与commitID

Git如何保存数据

示例讲解

(3)git提交对象与分支

git分支的本质

git的默认分支

如何创建分支

HEAD指针是什么

(4)分支创建与切换

[如何在指定的commit id点创建分支](#如何在指定的commit id点创建分支)

查看各个分支当前所指的提交对象

分支切换与head指针变化

分支切换的好处

注意分支切换会改变你工作目录中的文件

查看分支交叉历史

Git分支本质

(5)Git头指针分离状态

1.什么是分离头指针状态?

2.产生head分离状态

3.游离状态下修改了工作区的内容然后提交会怎样呢?

4.案例讲解

(6)分支操作常用命令

分支查看、创建、删除和重命名

分支切换

分支差异查看

分支合并

git远程分支相关操作

(1)上传本地分支到远程分支

(2)拉取远程分支到本地分支

(3)删除远程分支

(3)远程分支与本地分支关联

(7)git本地分支如何与远端分支关联

[(8)git pull的用法](#(8)git pull的用法)

[(9)git push的用法](#(9)git push的用法)


(1)理解git分支

主分支(主线)

在Git中新建一个项目后,默认有一个分支,即主分支。主分支一般表示项目的稳定版本,主分支应该包含稳定没有 Bug 的代码,并保持随时可以发布的状态,对于小型项目来说,只有一个主分支就够用了,每次我们提交都会创建一个commit节点。

bash 复制代码
$ git commit -m "c1"
$ git commit -m "c2"
$ git commit -m "c3"

上面的命令会创建三个commit节点,此时master分支如下图所示,代码经历了C1,C2,C3这三个版本,且master分支目前指向C3这个提交版本。

如果项目功能较复杂,且需要多次提交,不建议在主分支直接修改。主分支上应该只包合并提交,所有的迭代应该都在分支上进行。如果是简单的改动,直接在主分支修改也是可以的。

功能分支

当有新的功能要开发时,应该新建一个功能分支,比如创建一个名为a的分支,并切换到a分支,命令如下:

bash 复制代码
$ git checkout -b a

创建新分支时,新分支默认指向的代码提交版本为当前分支所指向的代码提交版本,比如这里新分支a指向的提交将为C3。此时当前分支为a,所指向的提交为C3。

接下来在a分支上创建两个提交,命令如下:

bash 复制代码
$ 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 模式进行合并,如果不符合条件(并非当前分支的直接后代),则会拒绝合并请求并且退出。

快速合并

当功能分支开发完成后,需要合并回主分支,合并回主分支有两种选择,快速合并和非快速合并,二者的区别在于是否创建提交节点,命令如下:

bash 复制代码
$ 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代码管理流程

以下是开发流程的一个详细的概述:

  1. 主线(main):这是项目的核心和稳定版本,比如版本1.1。所有的用户都从这个版本中获取功能,并且它代表了项目的当前状态。
  2. 创建分支:当需要开发新功能或修复bug时,你会从主线创建一个新的分支。这个新分支可以用来存放与版本1.2相关的所有更改。
  3. 在分支上工作:在新的分支上,你可以自由地添加新功能、修复bug或进行其他任何形式的代码更改。这些更改不会影响主线,因为主线和分支是两个独立的代码线。
  4. 测试和调试:在将更改合并到主线之前,你应该在分支上进行充分的测试和调试,以确保你的更改是稳定且按预期工作的。
  5. 合并分支:一旦你的更改在分支上通过了所有必要的测试和调试,你就可以将它们合并回主线了。这个过程会将这些更改从分支复制到主线,并创建一个新的提交,该提交包含了这些更改。
  6. 发布新版本:合并分支后,主线现在包含了版本1.2的所有更改。你可以将主线的代码发布为一个新的版本(即版本1.2),以便所有用户都可以访问和使用这些新功能或bug修复。
  7. 删除分支(可选):在将分支的更改合并到主线后,这个分支通常就没有用了,因此你可以将其删除以清理你的代码库。

(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命令:

bash 复制代码
git branch testing

这会在当前所在的提交对象上创建一个指针

HEAD指针是什么

那么,Git又是怎么知道当前在哪一个分支上呢?也很简单,它有一个名为HEAD的特殊指针。在Git中,它是一个指针,用来表明当前所在的本地分支是哪个分支。例如在上面使用了git branch命令创建了一个"testing"分支,但你仍然在master分支上,因为git branch命令仅仅创建 一个新分支,并不会自动切换到新分支中去。如下图所示:

使用"git branch -a"可以查看本地仓库和远程仓库的全部分支,以及当前所在分支,如下示例可以看到当前分支在bran_test,就是head所指向的分支。

bash 复制代码
D:\projects\test>git branch -a
* bran_test
  deal_data
  master
  remotes/origin/bran_test
  remotes/origin/deal_data
  remotes/origin/master
 
D:\projects\test>

(4)分支创建与切换

如何在指定的commit id点创建分支

方法1、创建命令:git checout -b <new_branch_name> <commid_id>

方法2:"git checkout <commid_id>" then "git switch -c <new_branch_name>"

查看各个分支当前所指的提交对象

你可以简单地使用git log命令查看各个分支当前所指的对象。提供这一功能的参数是--decorate。

bash 复制代码
git log --oneline --decorate

例如,下图显示当前"master"和"testing"分支均指向校验和以f30ab开头的提交对象。

分支切换与head指针变化

要切换到一个已存在的分支,你需要使用git checkout命令。

例如切换分支前是这样的

我们现在切换到新创建的testing分支去:

bash 复制代码
git checkout testing

执行上面的命令之后,HEAD就指向testing分支了,如下图所示

分支切换的好处

Git的分支切换有什么好处呢?下面进行一步一步的图解

第一步:在上面我们已经切换到testing分支下了,现在我们对一个文件进行修改并提交

bash 复制代码
vim test.rb
 
git commit -a -m 'made a change'

当执行完上面的命令之后,结果下图所示,你的testing分支向前移动了,但是master分支却没有,它仍然指向运行git checkout时所指的对象

第二步:假设现在我们再次切换回master分支,那么最终的结果会如下图所示,这一步做了两件事情:一是使HEAD指回master分支,二是将工作目录恢复成master分支所指向的快照内容

也就是说,你现在做修改的话,项目将始于一个较旧的版本。本质上来讲,这就是忽略testing分支所做的修改,以便于向另一个方向进行开发

bash 复制代码
git checkout master

第三步:现在我们在master分支上,现在我们再来做一些操作,结果会如下图所示,Git重新创建了一个提交对象指针

bash 复制代码
vim test.rb
 
git commit -a -m 'made other changes'

如上图所示,这个项目的提交历史已经产生了分叉。因为刚才你创建了一个新分支,并切换过去进行了一些工作,随后又切换回master分支进行了另外一些工作。上述两次改动针对的是不同分支:你可以在不同分支间不断地来回切换和工作,并在时机成熟时将它们合并起来。如何"git merge"参见下文。

注意分支切换会改变你工作目录中的文件

在切换分支时,一定要注意你工作目录里的文件会被改变。如果是切换到一个较旧的分支,你的工作目录会恢复到该分支最后一次提交时的样子。如果 Git 不能干净利落地完成这个任务,它将禁止切换分支。

查看分支交叉历史

你可以使用下面的命令查看分叉历史,它会输出你的提交历史、各个分支的指向以及项目的分支分叉情况

bash 复制代码
git log --oneline --decorate --graph --all 

示例

可以看到master先经过了三次commit,然后开启新分支testing,新分支testing有一次commit,master有一次commit,当前工作分支在master分支上。

Git分支本质

由于Git的分支实质上仅是包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件,所以它的创建和销毁都异常高效。创建一个新分支就像是往一个文件中写入 41 个字节(40 个字符和 1 个换行符),如此的简单能不快吗?

这与过去大多数版本控制系统形成了鲜明的对比,它们在创建分支时,将所有的项目文件都复制一遍,并保存到一个特定的目录。完成这样繁琐的过程通常需要好几秒钟,有时甚至需要好几分钟。所需时间的长短,完全取决于项目的规模。而在Git中,任何规模的项目都能在瞬间创建新分支。同时,由于每次提交都会记录父对象,所以寻找恰当的合并基础(备注:即共同祖先)也是同样的简单和高效。这些高效的特性使得Git鼓励开发人员频繁地创建和使用分支。

(5)Git头指针分离状态

1.什么是分离头指针状态?

我们知道在Git中分支是指向提交,而HEAD指针指向分支。所谓的分离头指针状态就是HEAD不再指向分支,而是直接指向某个commit。

2.产生head分离状态

对于下图,此时分支master分支指向c1(c1是某次提交的commit id),HEAD指向master(master 后面的*号表明了HEAD当前指向的分支是master)。

进行任意一次提交,master分支会重新指向新的提交

如果执行 git checkout commitId就会导致HEAD指向该次提交,而不在指向分支。

如果我们执行git checkout c2(c2是该次提交的commit id),结果如下:

可以看到此时HEAD指针不在指向master而是直接指向c2提交,此时头指针就处于分离状态。

3.游离状态下修改了工作区的内容然后提交会怎样呢?

游离状态时

如果这时候修改了工作区的内容然后提交会怎样呢?

可以看到此时这个提交不在任何分支上。如果此时切换回master分支,你会发现刚刚在分离头指针状态下对文件做了修改提交在master分支下是看不到的,并且由于c3这次提交不在任何分支上,在未来极有可能会被Git清理掉。我们知道如何合并分支,但是没办法将c3这个提交给合并过来,难道我们刚刚在c3上做的大量修改都要重写?聪明的git早就给我们提示了,可以给这个分离的提交创建一个分支,然后在将这个分支合并到master中,最后删除这个临时的分支。

通过git branch branchName commitId给这个提交创建一个临时的分支,这个分支是基于头指针分离下修改提交的commit id创建的。

将分支temp合并到当前分支(即master分支):git merge temp

最后删除临时分支:git branch -d temp

4.案例讲解

创建一个仓库,在工作区增加test.txt,然后基于这个文件做两次commit。

可以清楚的看到此时的HEAD是指向master的(HEAD -> msstaer),而master指向最近一次提交。

看一下test.txt文件:

现在开始分离HEAD指针,让HEAD直接指向最近一次提交。

注意观察,当我们执行git checkout 8fcba5e37c,git会提示我们当前处于分离头指针状态,并且HEAD指向8fcba5e37c这次提交。

执行git status看看,可以看到HEAD指针指向的是提交。

现在HEAD指针指向的是提交,这时我们再修改test.txt文件,然后提交。

git提示我们在分离HEAD指针状态下提交了修改。再打开test.txt文件看一眼:enmmm,没毛病。

重点来了!现在切换回master分支:

ok!成功切换了,但是git给我们警告了,有一个未被关联在任何分支上的提交,并且告诉我们可以使用git branch branchName 5e2e3f7 为5e2e3f7这次提交创建一个分支。先来瞅一眼此时的test.txt文件:

enmm,确实少了789。如果我们执行gitk --all命令会弹出一个界面如下:

从这个界面我们可以发现,只能看到两次提交记录,关于789的提交记录看不到,这是因为它没有关联在任何分支上,并且在未来git也极有可能会把它清理掉。

执行git branch temp 5e2e3f7 为该提交创建一个temp分支,然后合并分支,最后删除temp分支:

最后再瞅一眼test.txt。enmm,没毛病。

(6)分支操作常用命令

分支查看、创建、删除和重命名

  • git branch //查看本地所有分支和当前所在本地分支
  • git branch -r //查看远程所有分支
  • git branch -a //查看本地和远程所有分支和当前所在本地分支
  • git branch -v // 查看分支对应的提交ID以及提交注释
  • git branch 分支名称 // 创建本地分支,指向当前分支所指向commitID(当前分支指向的最新commitID可以通过"git log"命令查看)
  • git branch slave master // 在现有的master分支上创建slave分支
  • git branch 分支名称 commitID // 创建本地分支,且指向commitID提交
  • git branch -d 分支名称 //删除本地分支
  • git branch -D 分支名称 //强行删除本地分支
  • git branch -m oldName newName // 重命名本地分支

分支切换

  • git checkout 分支名称 //切换本地分支(如果不存在该本地分支,但存在该远程分支时,会自动创建该本地分支并自动追踪远程分支)在切换分支时,一定要注意你工作目录里的文件会被改变。如果是切换到一个较旧的分支,你的工作目录会恢复到该分支最后一次提交时的样子。如果 Git 不能干净利落地完成这个任务,它将禁止切换分支
  • git checkout -b 分支名称 //创建+切换分支(等价于先git branch,再git checkout)

分支差异查看

  • git diff 分支1 分支2 显示出所有有差异的文件的详细差异
  • git diff 分支1 分支2 --stat 显示出所有有差异的文件列表

分支合并

  • git merge 分支名称 //将该分支合并到当前分支
  • git merge 原分支 目标分支 // 合并分支
  • git merge --ff/--no-ff/--ff-only三个合并参数模式:自动合并模式(默认值)、非 Fast-forward 模式、Fast-forward 模式

git远程分支相关操作

(1)上传本地分支到远程分支

当我们在本地分支中进行了一些修改,想要将这些修改推送到远程分支中,需要使用git push命令:

bash 复制代码
git push origin local_branch:remote_branch

其中,local_branch是本地分支的名称,remote_branch是远程分支的名称。如果这个远程分支不存在,会自动创建一个新的分支。如果省略remote_branch参数,Git会自动将本地分支推送到与之同名的远程分支。

bash 复制代码
git push origin new_branch

git push命令将本地的new_branch分支与远程的origin仓库中的new_branch分支关联起来。如果这个分支在远程仓库中不存在,会自动创建一个新的分支。

(2)拉取远程分支到本地分支

如果我们想要从远程仓库中拉取最新的代码到本地分支中,并且使得本地分支与远程分支保持同步,可以使用git pull命令。例如,我们想要拉取origin仓库中的master分支:

bash 复制代码
# 拉取origin仓库中的master分支代码到本地分支中
git pull origin master


# 拉取origin仓库中的new_branch分支代码到本地分支中
git pull origin new_branch

Git会自动将远程代码合并到本地代码中,并且创建一个新的提交。如果有代码冲突,需要手动解决冲突。

(3)删除远程分支

如果我们不再需要某个远程分支,需要将其从远程仓库中删除,可以使用git push命令。

bash 复制代码
git push --delete origin 远程分支名称   // 删除远程分支
git push origin --delete remote_branch

其中,remote_branch是需要删除的远程分支的名称。请注意,删除远程分支会永久删除所有已经提交到这个分支中的代码,所以请谨慎操作。

(3)远程分支与本地分支关联

  • git branch --set-upstream-to=origin/远端分支名 // 把本地当前分支与指定的远程分支关联
  • git branch --set-upstream-to=origin/远程分支名称 本地分支名称 //将本地分支与远程分支建立关联

(7)git本地分支如何与远端分支关联

1、本地和远端都没有分支的情况

第一步:在本地创建分支test

bash 复制代码
git checkout -b test

第二步:在远端创建分支test,并将本地分支与之相关联

bash 复制代码
git push --set-upstream origin test

2、本地有分支,远端没有分支

参考情况1的第二步

3、本地无分支,远端有分支

第一步:使用git pull命令获取远端代码并与本地代码合并

bash 复制代码
git pull

第二步:创建本地分支并与远端分支相关联

bash 复制代码
git checkout -b test origin/test

4、本地和远端都有分支但是未关联

bash 复制代码
git branch --set-upstream-to=origin/远端分支名 本地分支名

(8)git pull的用法

git pull作用

取回远程主机某个分支的更新,再与本地的指定分支合并

使用语法

bash 复制代码
git pull <远程主机名> <远程分支名>:<本地分支名>

比如,要取回origin主机的next分支,与本地的master分支合并,需要写成下面这样

bash 复制代码
git pull origin next:master

如果远程分支(next)要与当前分支合并,则冒号后面的部分可以省略。上面命令可以简写为:

bash 复制代码
git pull origin next

上面命令表示,取回origin/next分支,再与当前分支合并。

实质上,这等同于先做git fetch,再执行git merge

bash 复制代码
git fetch origin
git merge origin/next

如果当前分支与远程分支存在追踪关系,git pull就可以省略远程分支名,命令如下。下面这个命令表示本地的当前分支自动与对应的origin主机"追踪分支"(remote-tracking branch)进行合并

bash 复制代码
git pull origin

如果当前分支只有一个追踪分支,连远程主机名都可以省略。下面这个命令表示当前分支自动与唯一一个追踪分支进行合并。

bash 复制代码
git pull

(9)git push的用法

作用:

git push是GIT中的一个命令,是将本地仓库某个分支的代码提交到远程仓库某个分支的命令

基本语法:

bash 复制代码
git push [远程库名] [本地分支名]  [远程分支名]

例如 git push origin master:refs/for/master ,即是将本地的master分支推送到远程主机origin上的对应master分支, origin 是远程主机名,第一个master是本地分支名,第二个master是远程分支名。

例如,我们将本地分支master推送到远程库origin的test分支上,可以使用以下命令

bash 复制代码
git push origin master:test

如果要推送的远程分支与本地分支名称一样,则还可以简写

bash 复制代码
git push origin master

这条命令的意思是将本地分支master推送到远程库origin的master分支上

如果当前分支与远程分支存在追踪关系,则本地分支和远程分支都可以省略,将当前分支推送到origin主机的对应分支

bash 复制代码
git push origin

如果当前分支只有一个远程分支,那么主机名都可以省略

bash 复制代码
git push

有时候,本地分支的内容和远程分支的内容存在冲突,我们需要强制覆盖远程分支,可以使用以下命令

bash 复制代码
git push -f [远程库名] [本地分支名]  [远程分支名]

end

相关推荐
aPurpleBerry37 分钟前
【问题解决】Github上手动Delete file之后, git remote add+git push出错
git·github
M_emory_2 小时前
解决 git clone 出现:Failed to connect to 127.0.0.1 port 1080: Connection refused 错误
前端·vue.js·git
Make_magic2 小时前
Git学习教程(更新中)
大数据·人工智能·git·elasticsearch·计算机视觉
不穿铠甲的穿山甲2 小时前
git-.git目录解析
git
唔知小罗12 小时前
git config是做什么的?
git
不是鱼17 小时前
新人程序猿必备的git技能(超实用基础版)
git·github
Exclusive_Cat21 小时前
Git的使用(基础语句)
git
江上清风山间明月21 小时前
git撤销、回退某个commit的修改
git·commit·版本·撤销·回退·特定
cui_win21 小时前
Redis高可用-主从复制
redis·git·github·主从复制·哨兵
Anlici1 天前
大厂怎么用Git命令
git