乐趣区

接触Git这么久谈一谈对Git以及相关事物的理解

1.Git 的诞生以及相关历史

1.1Git 的定义

​ Git 为 分布式 版本控制系统,是目前最先进的 版本控制 系统。

思考:

  1. 版本控制是啥意思?

    维基百科:

    版本控制(英语:Version control)是维护工程蓝图的标准作法,能追踪工程蓝图从诞生一直到定案的过程。此外,版本控制也是一种软件工程技巧,借此能在软件开发的过程中,确保由不同人所编辑的同一程序文件都得到同步。

    百度百科:

    版本控制是指对软件开发过程中各种程序代码、配置文件及说明文档等文件变更的管理,是软件配置管理的核心思想之一。

    我的理解:版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。

    通俗地说就是对一个或若干个文件的内容改动情况按照特定的版本号进行保存,以便将来浏览者快速清晰了解文件的改动信息(内容变化信息,内容改动时间,作者等)

1.2. Git 的创造者(爸爸)

​ 名称:Linus Torvalds 林纳斯 托瓦兹) 芬兰人 现受聘于 开放源代码开发实验(OSDL:Open Source Development Labs, Inc)

​ 自传:《乐者为王》just for fun

1.3. 相关历史
  • 1991 年,Linus 创建可谓统治服务器的操作系统 -Linux 系统,由于它的快速发展 Linux 的代码管理成为一个大难题
  • 2002 年以前,Linus 其实都是通过手工方式合并世界各地开发者提交的源代码
  • 2002 年,名为 BitMover 的这家公司出于人道主义精神免费提供 BitKeeper 商业版的版本控制系统的使用权
  • 2005 年,但由于 Linux 秀儿众多,开发 Samba 的 Andrew 试图破解 BitKeeper 的协议,被 BitMover 公司发现了,于是 BitMover 公司怒了,要收回 Linux 社区的免费使用权
  • 不让用便自己搞一个,Linus 便花费 10 天的时间用 C 语言开发了 Git,往后时间,Linux 的源码都是用 Git 管理。
  • 2008 年 GitHub 网站上线,为开源的项目免费提供 Git 存储。

2. 版本控制系统的分类与特征

2.1 版本控制系统
  • 本地版本控制系统

    • 第一代版本控制系统被称为本地版本控制系统。通过 加锁将并发执行转换成顺序执行。一次只能有一个人处理文件。具体流程如下:首先,应该把文件放在一个服务器上,方便使用者上传或下载文件;其次,任何人想对文件修改时,需要先把这个文件加锁,通过 checkout 指令,使得其他人无法修改;最后,当修改完成之后,需要释放锁,通过 checkin 指令,形成一个新的版本,存放到服务器端。第一代版本控制系统主要有 RCSRevision Control System)、SCCS。
    • 硬盘上 (本地,local computer) 保存补丁集(文件修订前后的变化),通过所有的补丁,可以计算出各个版本的文件内容,大多都是采用某种简单的数据库来记录文件的历次更新差异。

  • 集中式版本控制系统

    • 第二代版本控制系统被称为集中式版本控制系统(Centralized Version Control Systems,CVCS),其对同步修改更加宽容,但有一个明显的限制,用户必须在允许提交之前将当前修订合并到他们的工作中。不便之处就是要联网,若中央服务器发生单机故障,宕机了,那么在这宕机期间谁都无法提交更新,也就无法协同工作,还有中央服务器丢失数据的可能等。
    • 由下图可看到,在集中式版本控制系统中,如果服务器嗝屁了,那么所有的开发者就只能干瞪眼了!因为,SVN 对于项目的管理是依赖于服务器中的中心仓库的!我们的更改必须要提交到服务器中的中心仓库。第二代版本控制系统主要有 CVSSubversion、SourceSafe、Team Foundation Server、SVK。

  • 分布式版本控制系统

    • 第三代版本控制系统被称为 分布式式版本控制系统(Distributed Version Control Systems,DVCS),其允许合并和提交分开。在每个使用者电脑上就有一个完整的数据仓库,没有网络依然可以使用。
    • 由下图可看到,分布式版本控制系统也可以有个服务器端的仓库,用来同步各开发者的私有仓库。在分布式版本控制系统中,每个参与者的本地也会有一个完整的仓库。及时服务器端崩溃,我们仍然可以使用 Git(仅在本地仓库管理我们的代码),在网络具备时,再和服务器进行同步即可!第三代版本控制系统主要有 Bazaar、Git、Mercurial、BitKeeper、Monotone。

思考:

  1. 怎么理解分布式与集中式?(廖雪峰老师给出的理解)

    • 集中式:版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。
    • 分布式:分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。
2.2 SVN 与 Git 的对比

SVN 的优缺点:

  • [优] 采用集中式,易于管理,保证安全性
  • [优] 管理方便,逻辑明确,理念符合常规思维
  • [优] 代码的一致性高,适合人数不多的项目开发
  • [优] 支持二进制文件,更容易处理大文件,支持空目录
  • [优] 允许一个文件有任意多的可命名属性,会关注所有的文件类型
  • [缺] 服务器压力太大,数据库容量暴增
  • [缺] 必须连接在服务器上,否则基本不能工作、提交、对比、还原等
  • [缺] 不适合开源开发

Git 的优缺点:

  • [优] 适合分布式开发,强调个体,公共的服务器压力和数量都不会太大。
  • [优] 速度快,成熟的架构,开发灵活;任意两个开发者之间可以很容易的解决冲突。
  • [优] 离线工作,管理代码成本低,不需要依赖服务器。
  • [优] 部署方便。基本上下个命令就可以用。
  • [优] 良好的分支机制,可以让主干代码保持干净。
  • [缺] 不符合常规思维;资料少,学习成本比较大,学习周期比较长,要求人员素质比较高。
  • [缺] 代码保密性差,一旦开发者把整个库克隆下来就可以完全公开所有代码和版本信息。

    SVN 与 Git 的最主要的区别:

  • SVN 的存储需要依赖一个服务器,而 git 所有的东西是放在线上的。节约成本,省时省力。
  • git 是分布式的,svn 不是。
  • git 按照源数据的方式存储内容,svn 是按照文件的形式存储。
  • git 和 svn 中的分支不同。
  • git 没有全局版本号,svn 有。
  • git 内容的完整性优于 svn。

SVN 与 GIT 的特性对比:

特性 SVN GIT
架构模式 集中式 分布式
安全性 较差,定期备份 高,开发者本地电脑就是一个完整的版本库
适用性 文档管理, 代码管理,
易用性 简单上手,对新手友好 上手困难,学习成本高但效率搞
灵活性 较低,易发生单点故障,拉取分支 高,单机本地操作,多个备份,本地新建分支
权限管理 拥有严格的权限管理 尚无严格权限管理,有账号角色划分
3. 代码托管平台
  • GitHub

    • GitHub 是什么?

      • 百度百科上的定义是:面向开源以及私有软件项目的代码托管平台,只支持 git 作为唯一的版本库格式进行托管,故名 GitHub。
      • GitHub 是一个代码托管云服务网站,帮助开发者存储和管理其项目源代码,且能够追踪、记录并控制用户对其代码的修改。甚至可以把它当作存储代码等的网盘,用来存储任何东西。
    • Github 与 Git 的关系?

      • GitHub 不等同于 Git,二者完全是不同物,不能搞混,类似地,捋一捋 java 与 javascript、周杰与周杰伦的关系,或许你能从中领悟到一些真谛。
      • Git 只是一个命令行工具,一个分布式版本控制系统。正是它在背后管理和跟踪你的代码历史版本,好比一个时光机,让你在代码出错时不至于手忙脚乱,能快速回退之前的历史版本。
      • GitHub 是一个代码托管网站,背后使用 Git 作为版本管理工具(而非 svn)。主要服务是将你的项目代码托管到云服务器上,而非存储在自己本地硬盘上。
    • GitHub 能做什么?

      • 托管代码,管理项目的历史版本
      • 查找查看开源项目的介绍及源码等
      • 使用 GitHub Pages 更能,能够搭建属于自己的个人博客
      • 分享技术心得、项目等,在线交流,提升自己的影响力
  • Gitlab

    • Gitlab 是什么?

      + GitLab 是一个用于仓库管理系统的开源项目,使用 Git 作为代码管理工具,并在此基础上搭建起来的 web 服务(在线代码仓库管理软件)。
    • Gitlab 与 GitHub 的区别?查看对比

      • GitHub 上存放的项目是面向世界开源的,若想存放私人仓库,得交钱,交钱使你的项目变得更隐私
      • GitHub 是在线代码仓库,全世界只有 GitHub 一家,大家把代码存储在人家的服务器上
      • GitLab 比较私密, 用于企业、学校或者个人的代码托管库
      • Gitlab 相当于小型的 GitHub,你可以在本地搭建一个属于你自己的类似 GitHub 仓库,让小伙伴把代码存储在上面,这样代码只有你们几个人能看见,但是你要存在 GitHub 上,全世界都能看见
    • Gitlab 能为我们做什么?

      • Git 仓库管理、代码审查、问题跟踪、动态订阅、wiki 等功能,GitHUb 能做的 Gitlab 也能做(99%)。
  • 国内代码托管平台

    • Gitee
    • Coding
    • 阿里云代码托管
4. Git 的工作原理

​ 我们使用 Git 来记录每一次文件内容的变更,版本的更新,清晰地比较出不同版本的内容差异;可以使用 Git 在项目的历史版本自如地进行切换;还可以使用 Git 从当前项目的更改中撤销一些操作,可以新建分支,合并分支甚至关联远程服务器仓库等,这一切的背后都是怎么实现的?了解 Git 的思想以及基本原理这些操作也就略知一二了。

4.1Git 的分区
  • workspace: 工作区 直接编辑的地方,开发者可见具体的项目,可以直接操作项目文件
  • index(stage): 暂存区 数据暂存的地方
  • repository: 本地仓库 存放已提交的数据
  • remote: 远程仓库 在远程服务器上存放本地仓库的数据

4.2Git 数据库
  • Git 本质上是一个 内容寻址文件系统 (就是根据文件内容的 hash 码来定位文件。这就意味着同样内容的文件,在这个文件系统中会指向同一个位置,不会重复存储。)Git 的核心部分是一个简单的 键值对数据库,可以向该数据库插入任意类型的内容,它会返回一个键值,通过该键值可以在任意时刻再次检索(retrieve)该内容
  • Git 保存的不是文件的变化或者差异,而是一系列不同时刻的 文件快照。在进行提交操作时,Git 会保存一个提交对象(commit object)。该提交对象会包含一个指向暂存内容快照的指针。但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。
  • git 通过一种安全散列算法 1(SHA-1)可以得到任意文件的 SHA- 1 哈希值(40 位的字符),也就是 commit ID, 然后通过文件哈希值存取数据,存取的数据都位于 objects 目录,SHA- 1 哈希值的前两个字符作为子目录名称,后 38 个字符则作为子目录内文件的名称
  • Gi 的数据存储原理

    • Git 对象

      • 数据对象(blob object) 存储的是一个文件的具体内容
      • 树对象(tree object) 存储的文件的目录,一大坨指针,指向子级 tree,或者 blob
      • 提交对象(commit object) 存储作者信息,提交者信息,注释,指向一个 big tree 的指针
      • 标签对象(tag object)它包含一个标签创建者信息、一个日期、一段注释信息,以及一个指针。
  • Git 的底层命令、高层命令

    • Git 常用命令共有 30 多个,可运行 git help 查看;但 Git 总共有 130 多个命令,可以通过 git help -a 查看,这些命令可以分为高层命令和底层命令,底层命令被设计成 unix 风格,不常用

      • 往 Git 数据库存入数据
      • 往 Git 数据库取出数据
      • 首次提交 testA.txt 文件
      • 第二次提交,修改了 test.txt 文件内容
      • 第三次提交,增加一个新文件 testB.txt,一个新目录 lib,lib 里增加一个文件 testC.txt
      • 第四次提交,新建一个分支 branchB,并且在新分支中做了一次 commit
      • 第五次提交, 合并一分支
  • Git 的引用(reference 或 refs)

    浏览完整的提交历史, 但为了能遍历那段历史从而找到所有相关对象, 需要记住最后提交的SHA- 1 值。我们需要一个文件来保存 SHA-1 值,并给文件起一个简单的名字,然后用这个名字指针来替代原始的 SHA-1 值。文件被称为“引用(references,或缩写为 refs)”,使用git branch (branchname) 这样的命令时,Git 实际上会运行 update-ref 命令,取得当前所在分支最新提交对应的 SHA-1 值,并将其加入你想要创建的任何新引用中。

    • HEAD 引用

      • 分支和标签都是指向提交对象的指针, 所有的本地分支都存储在 git/refs/heads 目录下,每一个分支对应一个文件
      • Git 分支的本质:一个指向某一系列提交之首的指针或引用
    • 远程引用

      • 如果你添加了一个远程版本库并对其执行过推送操作,Git 会记录下最近一次推送操作时每一个分支所对应的值,并保存在 refs/remotes 目录下
    • 标签引用

Git 的对象模型

5. .git文件夹
  • .git 下的文件夹

    • hooks 文件夹则存放项目的客户端或服务端钩子脚本
    • info 文件夹下的 exclude 文件包含项目全局忽略匹配模式,与.gitignore 文件互补

      • exculd 文件
    • logs 保存所有更新的引用记录

      • refs
      • HEAD # 最后一次的提交信息
    • objects 文件夹存储着 Git 数据库的所有内容,存储所有 Git 的对象

      • info 记录对象存储的附加信息
      • pack 以压缩形式(.pack)存储许多对象的文件,附带索引文件(.idx)以允许它们被随机访问
    • refs 文件夹存储着所有分支指向各自提交对象的指针;本地分支,远端分支,标签等

      • heads 记录 commit 分支的树根

        • master 标识了本地项目中的 master 分支指向的当前 commit 的哈希值
      • remotes 记录从远程仓库 copy 来的 commit 分支的树根(只读)

        • origin

          • HEAD
          • master 标识了远端项目中的 master 分支指向的当前 commit 的哈希值。
      • tags 记录任何对象名称(不一定是提交对象或指向提交对象的标签对象)
  • .git 下的文件

    • HEAD 文件指向当前分支,包含了一个分支的引用,通过这个文件 Git 可以得到下一次 commit 的 parent,可以理解为指针
    • index 文件存储着暂存区的内容信息
    • config 文件包含项目的配置信息
    • description 存储着仓库的描述信息,主要给 gitweb 等 git 托管系统使用
    • packed-refs 打包标头和标签以便高效的存储库访问
    • FETCH_HEAD 是一个版本链接,指向着目前已经从远程仓库取下来的分支的末端版本
    • ORIG_HEAD 记录的是在进行危险(drastic)操作(如合并 merge,回退 reset 等)时,此操作之前 HEAD 所指向的位置,便于我们在发生毁灭性失误时进行回退
    • COMMIT_EDITMSG 保存最新的 commit message,Git 系统不会用到这个文件,只是给用户一个参考

6. Git 的常用命令
  • 简单命令

  • 高级命令

    • HEAD

      • 总是指向当前分支最新的一次提交 commit
      • git diff HEAD 显示工作区与当前最新 commit 之间的差异
    • commit

      • git commit –amend -m [message] 修改上一次提交
    • branch

      • git branch –track remote-branch 新建一个分支,与指定的远程分支建立追踪关系
      • git branch –set-upstream-to=origin/[remote branch] 将 remote 设置为当前分支的上游分支
    • merge 合并指定分支

      • git merge branch 合并其他分支到当前分支

    • rebase 衍合指定分支
    • reset 重置
    • revert 撤销,回滚到指定的特定版本
    • cherry-pick 选择合并某次提交的 commit 到当前分支
    • reflog 查看 HEAD 的所有移动轨迹

思考:

git merge 与 git rebase 的区别是?

  • rebase 会合并该分支与其他分支的 commit history,可能会得到一个新的 commit history
  • rebase 得到更简洁的项目历史,去掉了 merge commi,如果合并出现代码问题不容易定位,因为 re-write 了 commit history
  • merge 会创建新的 commit,包括每个分支的 commit 详情
  • 每次 merge 会自动产生一个 merge commit,特别是 commit 比较频繁时,看到分支很杂乱。
  • 想要得到一个干净的,没有 merge commit 的线性 commit 历史记录,选择 git rebase
  • 想要得到一个完整的 commit 历史记录,且想避重写 commit 历史记录的风险,选择 git merge

git reset 与 git rebase 的区别?

  • git revert 会生成一个新的提交来撤销某次提交,此次提交之前的 commit 都会被保留,也就是说对于项目的版本历史来说是往前走的。
  • git reset 则是回到某次提交,类似于穿越时空。
7. Git Flow
  • gitflow 工作流约定使用的分支简介

    • master分支为项目的 核心 分支,也是最终对外发布的分支,唯一且稳定。仅提供可读,不可在该分支上直接修改代
    • develop分支是项目的 开发主干 分支,唯一。仅提供可读,不可在该分支上直接修改代码。新功能的开发需从该分支拉取新的分支展开。develop 分支应该包含项目完整的全部历史记录。
    • featrue分支项是目的需求开发分支,可多个,从 develop 分支或其他 featrue 分支拉取。程序员的多人分工协作即通过 featrue 来实现,是代码具体实现的一线程序员接触最多的分支。需求开发完成后,要合并回 develop 分支。
    • release分支为预发布分支,通常被叫做 测试分支 ,主要用于 开发阶段的测试及 bug 修复。当 feature 分支开发完毕后会合并回 develop 分支,然后再从 develop 分支拉取 release 分支提测。测试并修复后的 release 分支要合并回 develop 分支以及 master 分支,并打上合适的 tag 标记(包含必要的 releaseNote)。
    • hotfix分支为 紧急线上修复分支,即当对外发布的 master 分支出现重大 bug,影响线上使用时,从 master 分支拉取 hotfix 分支进行紧急修复。修复后的 hotfix 分支要合并回 master 分支和 develop 分支。

  • GitFlow 工作流程

首先,完成中央仓库的初始化,将新项目搭建起框架后的工程代码或要转 gitflow 的项目代码上传至 git 中央仓库。项目负责人克隆中央仓库到本地形成 master 分支,并拉取 develop 分支 (步骤①) 推送至服务器。一般的实际场景,开发团队中只有项目负责人有权限操作 master 分支,拉取 develop 分支,并将 develop 分支的代码合并到 master 分支中。

然后,开发团队中的其他人克隆中央仓库的 develop 分支到本地,形成全体成员统一的唯一的 develop 分支轨迹。之后,按照需求及成员各自的分工,各个成员可以从 develop 分支拉取出各自的 featrue 分支 (步骤②) 进行独立的开发;若涉及到多人合作开发同一分支,拉取的分支要及时推送至服务器,便于各成员共享。

当各成员完成各自的功能开发后,需将完成后的代码提交到 featrue 分支,然后合并到 develop 分支(步骤③)。代码合并后,featrue 分支可以不再保留。

功能累积足够且稳定或到达约定的提测周期时,项目负责人应当从 develop 分支拉取出 release 分支(步骤④),打包提交相应的版本给测试人员进行部署测试,测试中提交的 bug 全部在该 release 分支完成修改。

测试结束并完成 bug 修复后,release 分支应该合并回 develop 分支和 master 分支(步骤⑤),代码合并后,release 分支可以不再保留。合并后的 master 分支,应由项目负责人及时推送到中央仓库(步骤⑥)。同时全体成员要及时同步自己 develop 分支。

有上线需求时,直接从 master 分支打包提交应用版本进行部署。当线上版本出现重大 bug,项目负责人需从 master 分支拉取 hotfix 分支(步骤⑦),进行线上的紧急修复。

最后,修复后的 hotfix 分支要合并回 develop 分支和 master 分支(步骤⑧)。并推送到中央仓库(步骤⑨)。

8. 实践操作
  • 获取 Git 仓库

    • 现有文件目录使用 git init 初始化仓库
    • 从远程服务器上克隆 git clone 一个仓库
  • 合并其它分支的代码

    • git merge
    • git rebase
  • 撤销合并 merge/rebase

    • git reset --hard [commit] 工作区、暂存区撤回到制定的 commit 版本
    • git reset --merge [commit] 或者 git reset --merge HEAD^ 撤回到合并前的 commit
  • 修改某次的 commit

    • git commit -amend -m 'comment' 替换上一次的 commit
    • git rebase -i HEAD~[数字 n] 进入 vim 编辑模式,并显示最近 n 条最新的 commit 记录
  • 撤销某次的 commit

    • git revert HEAD 产生新一次 commit 来抵消上一次提交
    • git revert commit_id 撤销中间某次 commit
    • git revert -m commit_id 撤销其他分支合过来的 commit
    • git revert --no-commit commit1..commit5 撤销 commit1(不包括)至 commit5 之间的连续 commit

      git revert 的参数:

      –no-edit:执行时不打开默认编辑器,直接使用 Git 自动生成的提交信息。

      –no-commit:只抵消暂存区和工作区的文件变化,不产生新的提交。

  • 丢弃某次 commit

    • git reset [last good SHA] 丢弃掉某个提交之后的所有提交,在提交历史中彻底消失
    • git reset --hard [last good SHA] --hard参数可以让工作区里面的文件也回到以前的状态
  • 查看提交历史记录

    • git log
  • 查看文件的改动

    • git diff 对比工作区与暂存区的同文件, 未添加至暂存区的文件改动
    • git diff --staged/cached查看添加至暂存区所有文件的内容修改
    • git state 显示工作目录和暂存区的状态
  • 暂存文件的改动

    • git stash 临时保存和恢复工作区文件的改动
  • 撤销文件的改动

    • `
      git checkout — [filename]
      `
  • 撤销暂存区的文件

    • git rm --cache [filename]

Git 深入学习链接:
  • 网站链接

    • 猴子都能懂的 Git 入门
    • 图解 git 原理与日常实用指南
    • https://backlog.com/git-tutor…
    • https://juejin.im/post/599e14…
  • 视频网站

    • 哔哩哔哩
  • 相关书籍

    • 《GitHub 入门与实践》pdf 文档 提取码:cyvs
    • 《Git 权威指南》pdf 文档 提取码:x488
    • 《Git 权威指南》在线阅读
    • 《Pro Git》在线阅读 强烈推荐阅读两三遍
退出移动版