作为一个工作两年的开发同学,git是每天都要接触的工具。但IDEA对git的封装已经满足了日常的代码提交需求,所以一直是以点点点的形式进行代码提交与更新,几乎没用命令行提交过(现在想来也是有些惭愧),对于git架构与底层也了解甚少。近期刚好研究代码相关,就趁此机会把git完整的学习一下。
1. 入门介绍
1.1 版本控制系统
git的本质是版本控制系统。版本控制是一种记录文件内容变化,以便将来查阅特定版本修订情况的系统。版本控制系统主要分为集中式、分布式,分别以svn、git为代表。
1.1.1 集中式版本控制系统 - CVCS
CVCS
有一个集中管理的服务器用来保存所有的修订版本,开发同学们通过客户端连接到服务器,获取最新的文件或更新文件。从图例中就可以看出CVCS的特点:单点、网络强依赖。
![](https://i-blog.csdnimg.cn/blog_migrate/b33ef8273802f003932ab11b4806db0c.jpeg)
常见的subversion,现归属Apache,是代表性的集中式版本控制系统,除此之外还有TortoiseSVN。通常都是可爱的小乌龟图案。
![](https://i-blog.csdnimg.cn/blog_migrate/e3d557c29b9b6cf7da1079702e2cd57f.png)
1.1.2 分布式版本控制系统(DVCS)
DVCS
不再使用中央仓库, 取而代之的是本地仓库与远端仓库。本地仓库里包含了所有的版本历史,与本地仓库交互并不需要网络。
![](https://i-blog.csdnimg.cn/blog_migrate/4669cd92539e9370b3ac39e136c0dd1e.jpeg)
Git
大部分版本控制系统存储的是每个文件与初始版本的差异。但git与常见的版本控制系统并不同。git以快照的形式来进行不同版本的保存。提交更新或保存项目状态时,对当时的全部文件创建一个快照并保存这个快照的索引。如果文件没有修改只保留一个链接指向之前存储的文件。常见的有gitlab、github。
1.1.3 对比
集中式与分布式版本控制系统的最大区别在于,版本控制的位置在中央服务器还是本地电脑。
集中式 | 分布式 | |
服务器故障时是否可提交 | ❌ | ✅ |
无网络也可工作 | ❌ | ✅ |
1.2 Git
1.2.1 结构
分支(Branch):一条独立的开发线,与与开发主线分离,可在不影响主线的同时工作。
工作区(Workspace):本地的代码区。
暂存区(Staging Area):临时存放代码的改动,即文件.git/index。
本地git仓库(Local Repository):存储于本地,含有所有版本的项目数据。
远程git仓库(Remote Repository):存储于远端,含有所有版本的项目数据。
![](https://i-blog.csdnimg.cn/blog_migrate/81bd1a4b664cfffd53e81bd9dd23e8dc.jpeg)
1.2.2 特点
git 更像是一个小型的文件系统,提供了许多以此为基础构建的超强工具,而不只是一个简单的 VCS。
- 三种状态:commited(已提交)、modified(已修改)、staged(已暂存)
- 三个工作区:工作区、暂存区域、Git 仓库(远程&本地)
- 保证完整性:丢失信息后及时发现。通过SHA-1的哈希计算校验和,判断信息完整性
- 一般只添加数据:commit后,数据很难丢失
- 近乎所有操作都是本地执行: 速度极快,无需网络也可提交
2. 基础使用
2.1 提交流程
如果是研发人员,对git的提交绝对不陌生。在日常中使用git最高频的操作是将自己最新的修改推到远端分支,具体的操作流程如下:
- 在工作区中修改文件。
- 暂存文件,将文件的快照放入暂存区域。
- 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。
2.2 使用方式
git主要两大使用方式:图形化页面、命令行。
2.2.1 图形化页面
在许多ide中都可以通过点击页面菜单的方式进行git commit、push等操作。如下图就是IDEA中的git操作菜单,可以看到已支持的命令可覆盖大部分的日常git使用需求,但依旧无法支持全部git指令。使用起来较为简便,只需要点点点即可。
![](https://i-blog.csdnimg.cn/blog_migrate/e6380a1ba246d1451c5120bf5296531d.png)
2.2.2 命令行
在Terminal中直接手敲命令完成git交互。可执行全部git指令,根据所需自定义参数等。需要手动输入命令,但灵活性较高。
![](https://i-blog.csdnimg.cn/blog_migrate/b3a45a2abb68cbe29c4dd5cfb0608cac.png)
2.3 常用命令
总结了日常使用中最常用的六条命令如下。文章末尾也总结了git的大部分命令列表。
git add | 增加git对指定文件的跟踪,将文件增加到暂存区 |
git pull | 将远程仓库的代码拉到本地 |
git push | 将代码从本地仓库上传到远程仓库 |
git clone | 将一个远程仓库中的项目拷贝到本地 |
git commit | 将所选暂存区的文件提交到本地仓库 |
git checkout | 切换分支 |
3. 关键组件
3.1 文件
3.1.1 文件状态
一个文件在本地的状态变化主要有未跟踪、未修改、已修改、已暂存四种,会根据用户的操作不断变化。
3.1.2 忽略文件
在项目中有些文件无需纳入git管理,比如运行过程数据等。可记录在项目根目录的.gitignore中,去除git对该文件的管理。
GitHub 有一个十分详细的针对数十种项目及语言的 .gitignore 文件列表,详见https://github.com/github/gitignore
3.2 标签
git支持打标签的方式来记录关键节点,可用于记录发布版本等。可以通过在特定的标签上创建一个新分支来复现打了标签的版本。
3.2.1 标签类型
轻量标签(lightweight):一个特定提交的引用。
附注标签(annotated):存储在 Git 数据库中的一个完整对象。通常创建的标签都是附注标签。
3.2.2 标签命令
标签列表 | git tag | 列出已有的标签 |
git show ${标签名} | 某个标签信息与对应的提交信息 | |
创建标签 | git tag -a ${标签名} -m ${备注} | 创建附注标签 |
git tag ${标签名} | 创建轻量标签 | |
git tag -a ${标签名} ${某个提交的校验和} | 基于已有的提交创建标签 | |
git push origin ${标签名} | 将标签推到服务器上 | |
恢复标签 | git checkout -b ${分支名} ${标签名} | 标签代码的恢复依赖于分支 |
3.3 分支
几乎所有的版本控制系统都以某种形式支持分支。分支可以把个人的开发工作从主线上分离,以免影响开发主线。 在很多版本控制系统中,这是一个略微低效的过程——常常需要完全创建一个源代码目录的副本。对于大项目来说,这样的过程会耗费很多时间。git 的分支,其实本质上仅仅是指向提交对象的可变指针。做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针。
3.3.1 类型
![](https://i-blog.csdnimg.cn/blog_migrate/836717c058559a13993fc8358bf62d07.png)
master | 主分支,代码的正式版本。原则上不能直接在master分支上修改,只能从其他分支合并。 其实master与其他分支并无不同,之所以用master做主分支,是因为git init默认创建的分支就是master,大家懒得改。 |
develop | 主开发分支,包含要发到下一个release分支的代码。 |
feature | 功能分支,主要用于开发新功能的分支。aone上新建变更后拉出的就是feature分支 |
release | 预发布分支,在发布正式版本前,将待发布的分支都合并到release分支,打出一个预期发布版本。Aone上多分支发布的集成分支就是release分支。 |
hotfix | bug修复分支,做短小精悍的bug修复。 |
3.3.2 常用指令
git branch | 创建新分支 ,相当于在当前所在的commit 上创建一个指针。Git 的分支实质上仅是包含所指对象校验和的文件,所以它的创建和销毁都异常高效。 |
git checkout | 切换分支 |
3.3.3 HEAD指针
概念
HEAD指针总是通过分支指针,间接的指向当前分支的最新提交。
样例
文字可能不好理解,我们直接看git中的head指向就明白了。在命令行中进入某个项目的文件夹,目前处于master分支。进入.git文件夹,里面包含一个HEAD文件。
查看head文件内容,发现指向另一个文件 refs/heads/master
那么我们继续查看这个文件,发现是一串commit id
head所指向的commit id就是master最新一次提交的commit id
3.4 Commit
3.4.1 Commit的组成
blob:提交的文件,一个文件的一个版本对应一个blob对象
tree:目录,一个tree对象内部聚合了另一些tree或者blob
commit:一次提交.一个commit可以理解为某一个版本下的仓库的各个文件以及文件夹的快照。相当于聚合了这个版本下的blob以及tree对象。commit同时也会包含提交信息以及parent的commit。
![](https://i-blog.csdnimg.cn/blog_migrate/be31cdf6775366c8c8cb5d34bd999ff6.jpeg)
3.5 协议
在clone项目时,有两种方式:SSH、HTTPS。
3.5.1 SSH
安全性较高,需要生成SSH密钥,并将本地公钥上传至gitlab。
3.5.2 HTTPS
安全性较弱,使用用户名与密码登录。
4. 展开说说
4.1 别名的使用
可以对一些常用指令设置别名,减少每次的打字数
比如git commit,我想自定义成git ci,可设置git别名
git config --global alias.ci commit
后续commit只需要输入git ci即可。
4.2 git到底怎么读
这可以说是一个很有趣的话题了,作为一个"交友网站",读法还是十分多样的,大概如下:
- [git]
- [dʒit]
- [geit]
具体怎么读,并没有标准答案,目前博主读的是[git],参照Linus的读法。原视频
5. 命令汇总
常用指令 | |
git clone | 将一个远程仓库中的项目拷贝到本地 |
git add | 增加git对指定文件的跟踪,将文件增加到暂存区 |
git commit | 将所选暂存区的文件提交到本地仓库 |
git push | 将代码从本地仓库上传到远程仓库 |
git pull | 将远程仓库的代码拉到本地 |
git checkout | 切换分支 |
分支 | |
git branch | 创建新分支 |
git log | 按提交时间列出分支上的所有的更新 |
git reset | 撤销操作,将head回退至指定状态 |
git merge | 分支合并 |
git diff | 工作目录中当前文件和暂存区域快照之间的差异 |
标签 | |
git tag | 列出已有的标签 |
git show ${标签名} | 某个标签信息与对应的提交信息 |
git tag -a ${标签名} -m ${备注} | 创建附注标签 |
git tag ${标签名} | 创建轻量标签 |
git tag -a ${标签名} ${校验和} | 基于已有的提交创建标签 |
git push origin ${标签名} | 将标签推到服务器上 |
git checkout -b ${分支} ${标签} | 将标签记录的阶段代码恢复到分支上 |
仓库 | |
git init | 初始化仓库 |
git config --list | 列出所有 Git 当时能找到的配置。 |
git status | 查看文件所处状态 |
git ls-remote (remote) | 远程引用的完整列表 |
git fetch | 拉取最新的远程仓库分支,本地仓库不变 |