Git学习总结

Git介绍

git其实是一个版本控制系统Version Control System(VCS),目前主流的VCS主要分为两种CVCS和DVCS,他们的代表分别是SVN和Git,下面看下这两者的比较

CVCS(Centralized Version Control Systems)

  • 代表:SVN
  • 特点:中心化的服务器保存着文件的所有版本,其他服务器从中央服务器获取最新的版本文件,但是如果中央服务器发生故障,那么这段时间所有人都无法提交更新或者拉取最新的版本。由于中心化的保存文件会出现如果中央服务器磁盘发生故障,并且在没有备份的情况下会丢失掉所有的文件以及历史版本,每个人的机器上只是保存了当时拉取到的最新的文件,及时使用这种方式进行数据复原也无法恢复历史版本。
  • 文件版本存储方式:保留源文件,对于版本变更保存每次的变更信息。可以将这种存储看成是一组基本文件和每个文件随着时间逐步积累的差异
    CVCS文件存储方式

DVCS(Distributed Version Control System)

  • 代表:Git
  • 特点:每个服务器都会拷贝所有文件以及所有的版本记录。并且在网络或者服务器不可用的时候可以在本地进行提交,待网络恢复之后再进行同步。相对于CVCS由于每个服务器都拷贝了仓库所有的信息,当中央仓库发生故障导致资料信息丢失时可以使用其他的服务器上进行数据恢复,虽然也可能丢失部分版本,但是大量历史版本还是能被保存。
  • 文件版本存储方式:Git每次提交都会对全部文件创建一个快照,并且保存这个快照的索引(为了提高效率如果文件没有发生修改的情况下只会新建一个文件指向源文件的链接)

DVCS文件存储方式

Git的三棵树

本地仓库由 git 维护的三棵“树”组成。

  • 工作区(workspace):实际就是我们计算机中的文件夹
  • 暂存区(index):缓存区域,保存临时改动
  • HEAD:指向最后一次提交的结果
    Git三棵树

简单理解文件在三棵树中的变化关系:

  • 在我们本地直接操作,比如说新增、删除、修改文件,这些都是我们的工作区。当我们执行执行git checkout这样的命令时,git会对我们的工作区进行修改,将工作区文件变成所切换分支的文件快照。
  • 当我们执行git add 将文件交给git管理的时候,文件就被放入到暂存区,在git中学名是索引。
  • 当我们执行git commit时,git会创建新的提交对象,并将我们的HEAD指针前移指向最新的提交对象

Git的本地命令基本都是围绕这三棵树进行的,下面介绍命令的时候也会提到

Git 分支

git 分支的原理

刚才提到了CVCS每次保存的是文件的改动,而像Git每次保存的是文件的快照,每次提交的都是一个commit对象。

举个🌰来描述下在我们进行文件提交时,Git内部的数据变化

  我们当前有个有一个工作目录,里面包含了三个将要被暂存和提交的文件。暂存操作会为每一个文件计算hash,然后会把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 对象来保存它们),最终将校验和加入到暂存区域等待提交。Git 仓库中有五个对象:三个blob对象(保存着文件快照)、一个树对象(记录着目录结构和 blob 对象索引)以及一个提交对象包含着指向前述树对象的指针和所有提交信息)
文件存储
后续我们的提交都会创建一个新的对象,并且有一个指向上一次提交对象的指针
多次文件提交

根据上面的分析我们了解到其实分支本质上仅仅是指向提交对象的可变指针。Git 的默认分支名字是 master。在多次提交操作之后,你其实已经有一个指向最后那个提交对象的 master 分支。master 分支会在每次提交时自动向前移动

分支本质上是指针,而我们可以创建多个分支,那怎么确定哪个分支是我们当前的分支呢?其实在Git内部还有一个特殊的指针叫HEAD,它就是代表了我们的当前分支
HEAD指针
上面图中,有两两个普通的指针分别代表着我们本地的两个不同的分支master和testing,而有一个HEAD指针他指向了testing分支,这就意味着我们的当前分支是testing分支,当我们执行切换分支的命令时,HEAD执行就会改变指向

git checkout master

分支切换

git branch命令

git branch是我们主要的操作分支的命令,主要有下面几种用法

# 展示本地所有的分支
git branch

# git branch <分支名> 创建分支
git branch demo-branch

# git branch -d <分支名> 删除分支
git branch -d demo-branch

# git branch -D <分支名> 删除分支(强制)
git branch -D demo-branch

上面删除分支的参数如果是-d,git会判断删除的分支是否已经合并到当前分支,如果没有合并则会删除失败,-D是强制删除,没有合并也可以删除

在这里插入图片描述

git checkout命令

git checkout 通常都是用来切换分支的,对于三棵树的操作是,现将我们的HEAD中选中的分支的文件快照复制到暂存区,再用暂存区的文件覆盖我们的工作区。当我们直接修改我们的工作区的文件,如果在没有进行提交的情况下进行checkout切换分支会把这次的改动带到新切换的分支上去,但是如果修改的文件上有冲突在分支切换的时候就会提示有冲突切换失败,这个时候可以加上-f 强制切换,但是要注意执行这个操作我们的改动就会丢失
checkout切换分支
git checkout除了切换分支之外还可以放弃工作区的修改

# git checkout -- <fileName> 放弃fileName的修改
git checkout -- readme.md

上面的撤销操作只会对工作区的文件有效,如果这个文件已经被暂存那么这个撤销是无效的,我们可以使用git reset命令来进行处理,具体命令我们后面会介绍

git log

git log命令只要是来看我们的提交日志
git查看提交日志
上面的图片中我们可以看到我们的HEAD是future,表示这是我们的当前分支,并且可以看到这个分支的各个提交记录。并且不同的分支指针所指向的commit也可以看到

分支的合并

假设目前我们有一个master分支,我们现在要基于master分支进行业务开发
分支
我们先从master分支上切出来一个分支

git checkout -b iss53

切出分支
但是当我们开发业务到一半的时候突然出现了一个紧急的bug,我们不得不放下手里的开发工作,先进行bug的修复,我们要再次从master分支上切出来一个新的分支,并进行代码的修改,目前分支的情况如下:

切出来fix分支
当bug解决之后我们要将hotfix分支合并回master分支,由于hotfix是直接从master上切出来的,并且中间没有其他的提交,git合并直接将master分支前移到hotfix所指向的提交,这种合并叫fast forward
fast-forward
当我们合并结束之后我们的分支提交情况如下
合并结束
现在我们继续开发功能之后需要将开发好的iss53合并到master分支,注意由于master分支相对于之前iss53切出来的时候已经发生了改变,无法使用fast-forward进行合并,git需要进行文件的比较如下图
比较合并过程
这时候git的操作就是将iss53和master这两个所处的提交C4、C5以及他们共同的父提交C2进行合并,并且生成一个新的提交
合并提交
但是在我们的合并中并不是顺利的,有可能会存在冲突的情况,这时候Git 做了合并,但是没有自动地创建一个新的合并提交。Git 会暂停下来,等待你去解决合并产生的冲突。

git stash命令

在工作中我们经常会碰到这样的问题,我正在一个分支上开发新的功能,突然遇到一个紧急的需求或者bug,需要切换到其他分支去处理,这时候很多人会先将代码进行临时提交,然后切换到其他分支,这样的话我们的代码的提交历史会变得很长,有些改动很难准确的找到哪次提交提上去的,这时候可以使用git stash命令来帮助我们解决这个问题,git stash命令会将我们的暂存区的数据暂时存储起来,之后会清空我们的暂存区,但是不会马上提交,这样我们就可以切换到其他分支进行工作,当我们需要的时候可以重新将我们存储的文件进行恢复,使用git stash apply即可

git rebase命令

除了merge使用git rebase也可以实现分支上的合并,不过rebase有一个更重要的作用那就是在不同分支中部分改动的转移。

在我们的实际开发中,可能存在因为一些特殊的业务需求对于不同的环境使用了一些特殊的逻辑,而因为这些逻辑我们无法将这些代码合并到我们的主分支中,从而不得不维护多个主分支。当开发的业务越多,两个分支的差异会越来越大,当我们某一天需要再两个分支中开发相同的代码中,就会出现一个麻烦,如果需求的改动比较小还好,可以手动在另一个分支上重写一遍代码,但是如果改动特别大,涉及很多的文件,那么手动再次修改就会很费时费力,但是由于特殊的代码逻辑存在又不能进行merge操作,这时候rebase可以帮我们轻松的解决这个问题,下面就是一个示例

git rebase
这个图片展示了当我们同时存在两个主分支的情况,同时我们在两个主分支下都进行不同的开发,后面需要将其中一个分支的某次提交合并到另一个中,使用merge进行处理的时候会将我们不想合并的提交被合并到主分支中,这并不是我们想要的结果,而使用git rebase则可以轻松解决这个问题。

# 这个命令是将存在于dev-master1分支中的所有提交但是不存在master1上的提交在dev-master分支上进行复现
git rebase --onto dev-master master1 dev-master1

在使用了rebase后我们的分支就可以直接使用fast-forward进行快速合并

git reset命令

git reset主要就是用来回滚我们的提交操作,主要就是操作我们git仓库的三棵树,使用不同的参数来控制操作的范围

git reset 后面主要参数是 soft、mixed、hard,其实他们分别影响到git中的三个区域

reset原始情况
举个例子,假设我们现在提交一个文件:file.txt,并进行了三次修改,目前最新的一次是V3,我们的三个区域中都已经是最新版本,这个时候我们如果要回滚操作,回滚到上个版本

# HEAD~是指向当前的前一个版本,HEAD~2则是回滚两个版本,这里可以指定某次提交的hash值,直接进行回滚
git reset --soft HEAD~ 

当执行完上面的命令后,git中的变化如下:

-- soft
我们的HEAD指针进行了一次后移(这个指针的移动和checkout不同,理解为指针的指针发生了移动),相当于丢弃了我们之前的一次提交,但是暂存区和工作区没有发生变化。我们看到效果就好像是执行了git add但是没有执行git commit

当我们更换参数重新执行的话

# 如果不加参数直接是git reset HEAD~也是一样的效果,mixed是缺省参数
git reset --mixed HEAD~

-- mixed
根据上面图片我们可以看到我们更换了参数之后,这次不仅仅是HEAD指针发生了移动,同时我们的暂存区也被覆盖,只有我们的工作区文件没有受到影响,就好像我们修改了文件没有执行git add一样

最后一个参数则是会将所有的三棵树都进行重置,这个命令很危险,一旦执行所有的更改都会丢失,除非已经提交将commit push到了远程分支,否则就彻底找不回这些改动了(其实这些提交在我们git上还存在,只是没有指针可以帮助我们找到这些提交,如果你记得hash,也可以利用hash新建一个分支)

git reset --hard HEAD~

--hard
注意我们这些操作都是在我们本地的git上操作的,如果同时要回滚远程的分支我们直接push会被拒绝,因为我们的分支落后于远程分支,git拒绝这样的推送,这时候我们只能使用-f的参数进行强制推送
push -f

Git标签

标签分类

git可以对分支进行打标签,表示重要的节点,比如某个重大版本的发布。我们可以简单的理解为是标签是某一次commit的别名

git标签分为两种,分别是注释标签和轻量标签

  • 轻量标签:对某次commit的引用
  • 注释标签:存储在git仓库中的一个完整对象,包含打标签者的名字、电子邮件地址、日期时间 以及其他的标签信息。可以使用hash进行校验

两种标签的创建

轻量标签使用git tag 标签名 来创建

轻量标签
注释标签使用 git tag -a 标签名 -m 注释内容 创建

注释标签

tag与分支

我们可以通过checkout标签名来指向某个指定的tag版本,但是我们这个时候会处于一个 分离头指针的状态

游离指针
看上图的实例中我们从master分支切换到tag1的标签,这时候我们的分支这边的信息展示的一串hash值,这是tag对应的commit的hash。
在“分离头指针”状态下,如果你做了某些更改然后提交它们,标签不会发生变化,但你的新提交将不属于任何分支,并且将无法访问,除非通过确切的提交哈希才能访问。因此,如果你需要进行更改,比如你要修复旧版本中的错误,那么通常需要创建一个新分支

git checkout -b <分支名> <标签名>

从tag checkout分支

分支和标签重名情况

我们新建的分支名有可能会出现和分支名相同的情况

分支与标签重名
如上图,新建一个分支branch-tag同时推送到远程,再次新建一个tag与branch同名叫branch-tag,但是在我们正常推送的时候无法推送,使用推送全部tag的命令git push origin --tags可以正常推送

# 这个命令会推送我们所有分支到远程
git push origin --tags

远程分支
远程标签
执行完刚才的推送操作之后,在远程服务器中可以看到同名的tag和branch

我们正常使用checkout命令来切换分支的时候,在tag和branch同名的情况下正常是切换到分支,如果希望切换到tag则需要使用下面的命令

同名切换

git checkout refs/tags/branch-tag

为了避免不必要的麻烦,一定要杜绝分支和tag同名的情况

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值