Git
最后更新于
最后更新于
它是软件项目开发过程中用于储存我们所写的代码所有修订版本的软件。对软件开发进程中文件或目录的发展过程提供有效的追踪手段,保证在需要时可回到旧版本,避免文件丢失、修改的丢失和相互覆盖。
不同于SVN(集中式的版本控制,集中式存在单点故障的风险),Git是一款分布式的版本控制工具,每个用户相当于一个备份。
Git使用了一种类似快照存储的方式来存储项目的整体状态。大多数版本控制系统只关心内容变化了的文件。若文件没有变化,其他版本系统不会记录,Git 而会对未变化的文件的做一链接。
差异存储:会记录每个文件的更改内容,而不是整个文件的副本。
快照存储:它会存储整个文件或项目的副本,而不是仅存储更改的部分。
Git基于DAG(有向非环图)的设计比SVN的线性提交提供更好的合并追踪,避免不必要的冲突,提 高工作效率。Git基于对内容的追踪而非对文件名追踪,所以遇到一方或双方对文件名更改时, Git能够很好进行自动合并或提供工具辅助合并。而SVN遇到同样问题时会产生树冲突,解决起来很麻烦。
Git 三种状态:
已提交(committed),表示数据已经安全的保存在本地数据库中
已修改(modified),表示修改了文件,但还没保存到数据库中
已暂存(staged),表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中
四个区域:
工作区:就是你在电脑里能看到的目录
暂存区: 存放修改过的文件。一般存放在 .git 目录下的 index 文件(.git/index)中,英文叫 stage 或 index,所以我们把暂存区有时也叫作索引(index)
本地仓库: 存放确认修改的文件
远程仓库:比如Github网站的服务器
在工作目录中修改文件
暂存文件,将文件的快照放入暂存区域
提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录
Git 将顶级目录中的文件和文件夹称作集合,并通过一系列快照来管理历史记录。在 Git 的术语中,文件被称为 blob 对象(数据对象),也就是一组数据。目录则被称为 tree(树),目录中可以包含文件和子目录。
BLOB: 即二进制对象,这就是 Git 存储的文件,Git 不像某些 VCS (如 SVN)那样存储变更 delta 信息,而是存储文件在每一个版本的完全信息。Linus 在设计时,BLOB 中仅记录文件的内容,而不包含文件名、文件属性等元数据信息,这些信息被记录在第二种对象 TREE 里。
TREE: 目录树对象。在 Linus 的设计里,TREE 对象就是一个时间切片中的目录树信息抽象,包含了文件名、文件属性及 BLOB 对象的 SHA1 值信息,但没有历史信息。这样的设计好处是可以快速比较两个历史记录的 TREE 对象,不能读取内容,而根据 SHA1 值显示一致和差异的文件。
CHANGESET:即 Commit 对象。一个 CHANGESET 对象中记录了该次提交的 TREE 对象信息(SHA1),以及提交者(committer)、提交备注(commit message)等信息。
顶层的树(也就是 root) 包含了两个元素,一个名为 foo 的子树(包含了一个 blob 对象“bar.txt”),和一个 blob 对象“baz.txt”。
在 Git 中,历史记录是一个由快照组成的有向无环图。这些快照通常被称为“commit”,看起来好像是下面这样:
这个互动沙盒来自网页,这篇笔记不会聚焦于 Git 的具体命令,请在互动沙盒中闯关学习
Git 的数据模型:
Git 中的对象可以是 blob、tree 或者 commit:
Git 在存储数据的时候,所有的对象都会基于它们的安全散列算法进行寻址。
blob、tree 和 commit 一样,都是对象。当它们引用其他对象时,并没有真正在硬盘上保存这些对象,而是仅仅保存了它们的哈希值作为引用。例如:
所有的快照都可以通过它们的哈希值来标记,但 40 位的十六进制字符实在是太难记了,很不方便。针对这个问题,Git 的解决办法是给这些哈希值赋予一个可读的名字,也就是引用(reference),引用是指向 commit 的指针,与对象不同,它是可变的,可以被更新,指向新的 commit。通常,master 引用通常会指向主分支的最新一次 commit。
这样,Git 就可以使用“master”这样容易被记住的名称来表示历史记录中特定的 commit,而不需要再使用一长串的十六进制字符了。
在 Git 中,当前的位置有一个特殊的索引,它就是“HEAD”。
在硬盘上,Git 仅存储对象和引用,因为其数据模型仅包含这些东西。所有的 git 命令都对应着对 commit 树的操作。
代码库应该有一个、且仅有一个主分支。所有提供给用户使用的正式版本,都在这个主分支上发布。Git主分支的名字,默认叫做Master。
主分支只用来发布重大版本,日常开发应该在另一条分支上完成。我们把开发用的分支,叫做Develop。
这个分支可以用来生成代码的最新隔夜版本(nightly)。如果想正式对外发布,就在Master分支上,对Develop分支进行"合并"(merge)。为了保证版本演进的清晰,我们一般加上--no-ff
参数:
除了常设分支以外,还有一些临时性分支(使用完以后,应该删除,使得代码库的常设分支始终只有Master和Develop),用于应对一些特定目的的版本开发。临时性分支主要有三种:
功能(feature)分支:它是为了开发某种特定功能,从Develop分支上面分出来的。开发完成后,要再并入Develop。可以采用feature-*的形式命名
预发布(release)分支:发布正式版本之前(即合并到Master分支之前),我们可能需要有一个预发布的版本进行测试。是从Develop分支上面分出来的,预发布结束以后,必须合并进Develop和Master分支。它的命名,可以采用release-*的形式。
修补bug(hotfix)分支:软件正式发布以后,难免会出现bug。这时就需要创建一个分支,进行bug修补。是从Master分支上面分出来的。修补结束以后,再合并进Master和Develop分支。它的命名,可以采用hotfix-*的形式。
test分支:测试环境分支,外部用户无法访问,专门给测试人员使用,版本相对稳定。
DEV 环境(Development environment):用于开发者调试使用
FAT环境(Feature Acceptance Test environment):功能验收测试环境,用于测试环境下的软件测试者测试使用
UAT环境 (User Acceptance Test environment):用户验收测试环境,用于生产环境下的软件测试者测试使用
PRO 环境(Production environment):生产环境
对应关系:
master
主分支,稳定版本
PRO
是
develop
开发分支,最新版本
DEV
是
feature
开发分支,实现新特性
否
test
测试分支,功能测试
FAT
是
release
预上线分支,发布新版本
UAT
是
hotfix
紧急修复分支,修复线上bug
否
开发团队流程示例:
develop 分支和 hotfix 分支,必须从 master 分支检出
由 develop 分支合并到 test 分支
功能测试无误后,由 test 分支合并到 release 分支
UAT测试通过后,由 release 分支合并到 master分支
对于工作量小的功能开发(工时小于1天),可以直接在devolop 分支进行开发,否则由 develop 分支检出 feature 分支进行开发,开发完后合并到develop 分支
业界应用的比较广泛的是Angular Git Commit Guidelines:
type:提交类型
scope:可选项,本次 commit 波及的范围
subject:简明扼要的阐述下本次 commit 的主旨,在Angular Git Commit Guidelines
中强调了三点。使用祈使句,首字母不要大写,结尾无需添加标点
body: 同样使用祈使句,在主体内容中我们需要把本次 commit 详细的描述一下,比如此次变更的动机
footer: 描述下与之关联的 issue 或 break change
项目中实际可以采用简易版规范:
Angular Git Commit Guidelines
中推荐的type类型如下:
feat: 新增功能
fix: 修复bug
docs: 仅文档更改
style: 不影响代码含义的更改(空白、格式设置、缺失 分号等)
refactor: 既不修复bug也不添加特性的代码更改
perf: 改进性能的代码更改
test: 添加缺少的测试或更正现有测试
chore: 对构建过程或辅助工具和库(如文档)的更改
除此之外,还有一些常用的类型:
delete:删除功能或文件
modify:修改功能
build:改变构建流程,新增依赖库、工具等(例如webpack、gulp、npm修改)
test:测试用例的新增、修改
ci:自动化流程配置修改
revert:回滚到上一个版本
单次提交注意事项:
提交问题必须为同一类别
提交问题不要超过3个
提交的commit发现不符合规范,git commit --amend -m "新的提交信息"
或 git reset --hard HEAD
重新提交一次