本机测试环境:macOS 11
01 | 课程综述#
VSC:版本控制系统
- 集中式 —— 客户端必须时刻和服务器相连
- SVM
- 分布式 —— 服务端和客户端都有完整的版本库
- Git,Linus 开发
- 基于 Git 的开源社区 —— 背景:DevOps 时代,开发运维
- GitHub—— 全球最大
- GitLab—— 公司常用;免费、提供了持续集成 - CI;如阿里云、点评
02 | 安装 Git#
- 起步 - 安装 Git—— 官网:Linux、macOS、Windows、源码
03 | 使用 Git 之前需要做的最小配置#
## 添加name和email配置
git config [--local | --global | --system] user.name 'Your name'
git config [--local | --global | --system] user.email 'Your email'
## 查看现有配置
git config --list [--local | --global | --system]
## 区别
## --local:本仓库
## --global: 当前用户的所有仓库
## --system: 本系统的所有用户
- 作用:记录人员信息,CR(Code Review,代码审查)时也可以对应发邮件提醒
04 | 创建第一个仓库并配置 local 用户信息#
- 创建仓库的两种方式,均会生成.git 文件夹
## 在已有项目文件夹里创建git仓库
git init
## 新建一个名为[Project Name]的git仓库
git init [Project Name]
- 如果需要单独配置 local 用户信息,使用 --local 配置即可,该种配置优先级最高,见上一节
- 进行一次简单的 commit❗️
## 添加文件到该git项目中,略
## 添加要commit的文件到
git add [File Name]
### PS:添加当前路径下的所有文件—— git add .
## 附带信息进行commit操作
git commit -m'Some Message'
### 查看git状态,如:哪些文件待commit,哪些未被跟踪
git status
### 查看git记录,如:commit的记录
git log
05 | 通过几次 commit 来认识工作区和暂存区#
- 暂存区❗️——Git 的精髓之一
- 使用场景:开发两套方案,第一套方案做好后先暂存,然后做第二套方案,如果第二套方案没有第一套好,可以再回到第一套
- PS:
- 如果要把工作区的某个 file 替换为暂存区,则
git checkout -- file
- 如果要把工作区变更的所有文件都恢复成和暂存区的一样,则
git checkout *
- 如果要把工作区的某个 file 替换为暂存区,则
git add -u
与git add .
- 前者:只操作已经被跟踪的文件,即将文件的修改和删除,添加到暂存区
- 后者:还会操作文件的新建,即将文件的创建、修改和删除,添加到暂存区
- PS:
git add all/-A
针对整个仓库,不只是当前路径
- 具体操作类似上一节 —— 一次简单的 commit
06 | 给文件重命名的简便方法#
- 细化步骤
## 重命名
mv readme readme.md
## 添加新文件readme.md
git add readme.md
## 删除被跟踪的旧文件readme
git rm readme
## 提交
git commit -m"rename"
- 使用
git reset --hard
回退到最新的一次提交,即清空暂存区 - 再展示精简步骤
## 一条命令顶上面三条
git mv readme readme.md
## 提交
git commit -m"rename"
- 实操展示
通过 git status 实时观察暂存区状态。
PS:第一种方式 git status 也能检测出是 renamed
07 | 通过 git log 查看版本演变历史#
## 查看简洁的单行历史
git log --oneline
## 查看最近的4条历史
git log -n4
git log -4
## 查看所有分支的历史
git log --all
## 查看指定分支(名为BRANCH_NAME)的历史
git log BRANCH_NAME
## 查看图形化的版本历史
git log --graph
## 组合使用:查看所有分支最近4条图形化的单行历史
git log --all -n4 --graph --oneline
## 跳转到网页版的git log帮助文档
git help --web log
git log --all -n4 --graph --oneline
效果图
附加命令
git branch
查看本地有哪些分支,-a
会同时查看本地和远程的分支git checkout -b
<new_branch> []- 创建名为 new_brach(以 commit hash 值为 start point 的 commit 为基)的新分支
- 为了简便,start point 可以选取 hash 值的一个片段,只要能够唯一标识 commit 即可
git commit -am'msg'
- 直接将工作区和暂存区中的文件添加到版本库中,相当于使用了 add
- ⚠️:只能提交已经被跟踪的文件,新建的文件还是要先通过
git add .
添加到暂存区
PS
- 可以在~/.zshrc 里,通过别名设置更友好的 log 显示方式,设置好后使用
lg ...
即可
alias lg="git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
- 一般地,单字母的选项对应
-
,非单字母的选项是--
08 | gitk:通过图形界面工具来查看版本历史#
- Mac 下安装:
brew install git-gui
- 使用:
gitk
- 界面简单展示
- 选择 View-New View 后,选中 All refs 后,即可展示所有分支、Tag 信息
- 右键某一个 commit 有很多操作,如 Create tag
PS
- 为什么一次 commit 既有作者 Author,又有提交人 Committer
- 尊重版权
- 比如
git cherry-pick
命令:将指定的 commit 应用于其他分支 - 具体使用参考git cherry-pick 教程—— 阮一峰
- 另推荐一款 Git 图形界面工具:Sourcetree,其实工具之间大同小异,看个人习惯
09 | 探密.git 目录#
Git 具有优秀的存储能力,不需要网络,本地也能做版本管理
- 🌟4 个关键文件:HEAD、config、refs、objects,下面一一剖析
- ❗️3 个重要对象:commit、tree、blob,PS:tag
- 关系图如上,参考GIT 对象模型——Git Community Book 中文版
- blob:全称 Binary Large Object,即二进制大型对象,可理解为文件对象
- 下一节还会深入讲解,先来看看 4 个关键文件~
- HEAD:指向当前正在工作的分支
手动修改 HEAD 里的分支,效果同 git checkout
- config:本地仓库(local)配置信息
主要关注 user 信息
手动修改 config 文件,效果同 git config --local
- refs:包含 heads 和 tags 文件夹
- heads 里每个分支存放的是一个 commit 的 hash 值
- tags 里每个里程碑存放的也是一个 commit 的 hash 值
- 也可能是一个 tag 的 hash 值,其又指向 commit
git cat-file
:显示版本库对象的信息-t
:类型-p
:内容(直接用 cat 命令可能会显示乱码,因为这些对象需要解析)- 后者接唯一的 hash 值或文件名即可
PS:细心的你会发现一个 commit 里存储了一个 tree 的 hash 值,是不是和一开始的关系图串起来了,再试试查看 tree 的内容
- objects:存放所有对象,包括 tree /blob/commit
- 1)大部分文件夹📁都是以 hash 值的前 2 位字符命名,里面存放 38 位字符命名的文件
- 由这 40 位字符组成唯一的 hash 值,指向一个对象
- 2)tree 对象里存储了 blob 对象信息,blob 对象的内容就是某个文件的内容,如 css.x
- tree 对象里还可以存储另一个 tree 对象信息
- 只要文件的内容相同,在 Git 眼里它就是唯一的一个 blob
- 3)blob 对象内容与其指向的文件内容一致
- 一定会一致吗?不一定,和版本有关
- PS:还有 pack 文件夹和 info 文件夹
- 前者用于存放打包的松散的文件夹,后者与前者相配合
10 | commit、tree 和 blob 三个对象之间的关系#
上一节提到了它们仨,这一节再具体看一个 commit,加深理解
- 这是它们三者的关系图,可以关注箭头的走向,参考GIT 对象模型——Git Community Book 中文版
- 解析
- 一个 commit 里的内容由一棵 tree 来管理,有且只有一棵
- 存放当前 commit 里所有文件夹和文件的一个快照
- 一棵 tree 里可以有多个子 tree 和多个 blob
- 一个 blob 里存储的就是文件对象的具体信息
- 文件内容一样则唯一,与文件名完全无关
- 一个 commit 里的内容由一棵 tree 来管理,有且只有一棵
- 示例
commit👉tree👉blob + tree
PS: lg
是在第 7 节配置的 git log
友好显示版命令
11 | 小练习:数一数 tree 的个数#
新建的 Git 仓库,有且仅有 1 个 commit,仅仅包含 /doc/readme
请问内含多少个 tree、多少个 blob?
下面开始试验🧪
## 首先新建Git仓库
git init watch_git_object
## 进入仓库,创建doc文件夹,并添加readme
cd watch_git_object
mkdir doc
echo "hello, world" > doc/readme
## 此时观察.git/objects里有没有文件类型的文件:空
find .git/objects -type f
## 把doc添加到暂存区
git add doc/
## 再次观察:Git生成了一个blob对象,里面记录了readme的内容
find .git/objects -type f
- 添加到暂存区时,就会产生 blob 对象
- ❗️此时还没有 commit、tree 对象,下面进行 commit
## 进行commit
git commit -m'Add readme'
## 观察.git/objects里的文件:已经有4个了
find .git/objects -type f
## 逐个检查它们的类型和内容,如下图所示
git cat-file -t ***
git cat-file -p ***
- 可以看出新生成了:1 个 commit、2 个 tree
- 它们的关系是:
- commit👉tree(commit 对应一个 tree)👉tree(doc 文件夹)👉blob(readme 文件)
12 | 分离头指针情况下的注意事项#
detached HEAD
- 产生:切换 HEAD 到某一个 commit 上(即使是某分支最新的 commit)
git checkout <HASH_OF_COMMIT>
,而非某一个分支
此时就会出现 detached HEAD 情况,你可以做一些试验性操作,并进行 commit
1)如果不想保留这些 commit:可以直接切换回分支,不做其它额外的操作
2)如果想保留这些 commit:则需要在此创建一个新的分支,从而与分支挂钩
- 接着对 readme 做一些修改,并进行提交,当切换回某个分支时
Git 友好提示:如果想要保留刚才的提交,可以通过 commit 的 hash 值创建一个新的分支
借助 gitk 查看此时分支的情况,可以看到,看不到刚才 commit 的影子
执行上述友好提示中的 git branch fix_readme 34976f8
前面做的提交已经与 fix_readme 分支联系起来了
- 小结
- 使用场景:做一些试验性尝试
- ⚠️:当想做保留所做的提交时,要与某个分支挂钩
13 | 进一步理解 HEAD 和 branch#
- 带着 2 个问题学习:
- 新建分支的时候,HEAD 会发生什么变化?(HEAD 指向)
- HEAD 相关的使用技巧是什么?(指代 commit,特殊标记
^
、~
)
下面一一进行探讨:
- 1
HEAD 可以指向具体的分支,也可以指向具体的 commit
通过 git checkout -b <NEW_BRANCH_NAME> <BASE_BRANCH_NAME/HASH_OF_COMMIT>
命令,基于某个分支或提交,创建新的分支,并切换到新分支上
可以发现:HEAD 从指向分离头指针,转变到,指向 fix_new 分支上
再观察 HEAD 文件的信息
其实,HEAD指向某个分支,而该分支指向某个特定的 commit
- 2
既然 HEAD 最终指向一个 commit,那也可以用它来指代 commit 的 hash 值啦~
比如在使用 git diff <A> <B>
比较两个 commit 时
上面 3 条命令是等价的,即:
git diff 34976f8 a60a3d5
、 git diff HEAD HEAD~
、 git diff HEAD 'HEAD^'
⚠️:
HEAD 的 ^
和 ~
标记都可以获取 HEAD 的祖先 commit,它们的区别可参考下图:
这个分支图的方向和 git log --graph
生成的图方向相反,即 A 是最小的;且结合了有 merge 分支的情况
参考What's the difference between HEAD^ and HEAD~ in Git?——StackOverflow
PS:在 zsh 下, ^
有特殊含义,所以需要用引号包起来
学到这里,是不是更新了自己对 Git 的认识呢~是否期待 Next Part 呢?!