把人生一分为二,前半生不犹豫,后半生不后悔。——溥仪
大家好,我是柒八九。
前言
作为一个新时代的开发者,想必大家在工作中,有一样东西是和大家形影不离的。那就是git
。(当然,这里也有个例,如果大家项目还停留在svn
阶段,就算我刚才的话唐突了)。
无论大家平时是喜欢在命令行中手搓git
命令,还是利用git
可视化工具(SourceTree)进行代码管理。终究都逃不过,add/commit/merge/push
等命令的支配。所以,今天我们来聊聊,在进行这些命令的时候,在最底层到底发生了啥。
还有一点,也算是一个认知提升吧。需要和大家唠叨一下,以后遇到比较棘手的问题,可以往这方面来靠拢
所有软件的底层实现都是操作和管理数据。
无论是我们平时用到的桌面程序,亦或是在命令行中进行敲敲打打处理一些特定的操作,还有就是我们熟悉的编程开发
中,无论是前端的开发过程中,使用原生也好,各种框架也罢,最后的根结都是数据的罗列和排布
;还是后端就更明显了,有SQL
的操作,那就更是再玩弄数据。 之所以我们看到的现象有些不同,无非就是数据的表现形式和处理方式的不同。可以说,在编程界,--万物皆数据。
这里简单举一个例子,日历大家都见过哇。
如果,给我们一个需求,要让我们实现一个飞书日历
或者google 日历
的开发任务,我们是不是一时感觉到无法下手。
那我们往万物皆数据这个定论上靠,那是不是每一个日程(无论是简单日程还是重复日程),它们本质上就是在每个小格子上展示。无非就是有的日程在单个格子上,有的日程是跨格子。 而针对这种情况,是不是就是在当前视图中,我们需要维护一个数组,而这个数组中的项就是每个格子的示例。(针对月视图/周视图/日视图的数据,其实都是一套,只不过在框架内部为我们提供了各自的展示逻辑)
这是一个开源的日历库(FullCalendar)。
而下面的events
就是我们在日历上显示的日程信息。
好了,天不早了,干点正事哇。
我们能所学到的知识点
- 前置知识点
- git init
- 新增一个文件
- git commit
- 新增修改
- 创建分支
- 分支切换
- 分支合并
- 远程提交
1. 前置知识点
前置知识点,只是做一个概念的介绍,不会做深度解释。因为,这些概念在下面文章中会有出现,为了让行文更加的顺畅,所以将本该在文内的概念解释放到前面来。如果大家对这些概念熟悉,可以直接忽略
同时,由于阅读我文章的群体有很多,所以有些知识点可能我视之若珍宝,尔视只如草芥,弃之如敝履。以下知识点,请酌情使用。
什么是git
Git
是一种用于源代码管理的工具。它是一个免费且开源的版本控制系统
,用于高效地处理从小型到非常大型的项目。Git
用于跟踪源代码的更改,使多个开发人员能够共同在非线性开发
中合作。Git
是由Linus Torvalds
于2005
年为Linux
内核的开发而创建的。
集中式管理
在使用Git
之前在维护代码之前,团队合作的模式如下:
- 开发人员过去会将他们的代码提交到中央服务器,而没有自己的副本。
- 对源代码所做的任何更改对其他开发人员来说都是未知的。
- 没有任何开发人员之间的沟通。
它的典型代表为SVN
分布式管理
- 每个开发人员都在其本地系统上拥有完整的代码副本。
- 对源代码所做的任何更改都可以被其他人跟踪。
- 开发人员之间有定期的沟通。
毋庸置疑,git
是这方面的王者。
git基础概念
workspace
:是本地项目的工作目录,属于本地代码发生更新但尚未执行git add
命令时的状态,working tree
的状态也随之更新index
:是索引文件,它是连接working tree
和commit
的桥梁,每当我们使用git add
命令来登记后,index file
的内容就会改变,此时index file
就和working tree
同步了。
- 它还有一个家喻户晓的名字 -暂存区
local repository
:是本地仓库,当我们使用git commit
命令提交最新代码时,代码才真正进入git
仓库。
git commit -m “xxx”
就是将index
里的内容提交到本地仓库中
remote repository
:是远程仓库,当我们使用git push
命令时就会将本地仓库的代码上传至远程仓库,完成整个代码的上传工作
git init --bare
VS git init
git init --bare
和 git init
是两种不同的Git初始化命令
,它们用于创建不同类型的Git仓库。
下面是它们之间的主要区别:
- 仓库类型:
git init
: 这个命令用于创建一个标准的Git工作目录仓库。它会在当前目录下创建一个.git
子目录,其中包含Git的版本控制文件和历史记录(这是我们这篇文章的重点)。这种类型的仓库通常用于开发和维护代码。git init --bare
: 这个命令用于创建一个"裸"(bare)仓库,它不包含工作目录。这意味着它只包含Git版本控制的文件和历史记录,没有实际的项目文件。"裸"仓库通常用作中央版本库,用于协作和共享代码。
- 默认分支:
git init
默认创建一个带有master
分支的工作目录仓库。git init --bare
默认不创建分支,因为裸仓库不包含工作目录。我们需要手动创建和设置分支。
一般情况下,如果我们需要创建一个新的Git仓库用于开发和维护代码,我们应该使用 git init
。如果我们需要创建一个中央版本库用于团队协作和共享代码,我们可以考虑使用 git init --bare
。
Hook
钩子(Hooks
)是一种通用概念,通常用于在特定事件发生时触发自定义代码。虽然不是编程语言本身的一部分,但编程语言和开发工具通常提供一些机制来支持编写和使用钩子。
下面我们简单介绍几种大家比较常见的利用Hook
概念的技术。
名称 | 描述 | 示例语法 |
Git Hooks | Git 允许在代码仓库的特定事件上运行自定义脚本。事件包括提交、推送、合并等。 | 使用 Bash 脚本编写,如 pre-commit 、post-commit 等。 |
JavaScript Hooks | JavaScript 用于前端和后端开发,事件处理程序在特定事件发生时执行自定义 JavaScript 代码。 | 前端中,事件处理程序如事件监听器。后端中,使用 EventEmitter 模块。 |
React Lifecycle Hooks | React 用于构建用户界面,包括生命周期方法,允许在组件的不同生命周期阶段运行自定义代码。 | 生命周期方法如 componentDidMount 和 componentWillUnmount 。当然,还有甚嚣尘上的针对函数组件的 React Hook |
GitHub Webhooks | GitHub 提供 Webhooks,是 HTTP 回调,用于在存储库的特定事件上触发自定义操作。 | 开发者编写 Webhook 处理程序响应事件,配置 Webhook URL。 |
Jenkins Pipeline Hooks | Jenkins 是一个持续集成工具,允许创建自定义流水线脚本。使用钩子定义流水线的阶段和操作。 | 钩子嵌入到 Jenkinsfile 中以定义流水线。 |
Git Hook
Git Hook
是一种非常强大的Git
自定义脚本系统,它允许我们在Git版本控制过程的不同阶段执行自定义操作。这些操作可以是自动化测试、代码格式化、验证提交消息格式、预防性错误检查等等。Git hooks
是一种强大的自定义工具,可以提高代码质量和协作效率。
- Git Hook的种类: Git提供了多种不同类型的
Hook
,每种类型对应着Git操作的不同阶段。以下是一些常见的Git挂钩类型:
- pre-commit:在执行实际提交之前运行,用于执行预提交检查。
- pre-push:在执行实际推送之前运行,用于验证推送到远程仓库的内容。
- pre-receive:在
接收端
执行,通常用于验证推送到远程仓库的提交。 - post-receive:在
接收端
执行,通常用于通知或自动化部署。
- Hook的位置: 每个Git存储库都有一个
.git/hooks
目录,其中包含用于存储各种Hook
脚本的文件。当我们在存储库中运行git init
时,Git会为我们创建示例Hook
文件,我们可以根据需要编辑或替换它们。这些示例文件以.sample
为扩展名。 - 编写Git Hook: 要编写
Git Hook
,我们只需创建一个可执行的脚本文件并将其放入.git/hooks
目录中。脚本的名称必须与hook
类型相匹配(例如,pre-commit
)。在脚本中,我们可以执行任何自定义操作,例如检查代码、验证提交消息、运行测试等。
git diff
git diff
命令后通常需要跟两个参数,参数1是要比较的旧代码,参数2是要比较的新代码。如果只写一个参数,表示默认跟 workspace
中的代码作比较。
git diff
显示的结果为第二个参数所指的代码在第一个参数所指代码基础上的修改
git diff
:查看workspace
与index
的差别git diff --cached
:查看index
与local repositorty
的差别git diff HEAD
:查看workspace
和local repository
的差别
HEAD
指向的是 local repository
中的代码最新提交版本
git diff HEAD^
是比较workspace
与最新commit
的前一次commit
的差异,与git diff HEAD
的是不同的git diff HEAD~2
是比较workspace
与上2次commit
的差异,相当于git diff HEAD~2 HEAD~0
,注意两个HEAD
的位置,diff
显示的结果表示 参数2(HEAD0) 相对于参数1(HEAD2)的修改
git 别名
在Git中,别名(Git Aliases
)是一种机制,允许我们为常用的Git
命令或命令序列创建简短的自定义命令。别名使我们可以用更短、更易记的名称来执行常用的Git操作,提高工作效率。
1. 创建别名: 我们可以使用git config
命令来创建Git别名。
git config --global alias.<alias-name> <git-command-or-sequence>
<alias-name>
:自定义别名的名称,我们可以选择任何喜欢的名称。<git-command-or-sequence>
:要与别名关联的Git命令或命令序列。
2. 例子: 以下是一些Git别名的例子:
git config --global alias.co checkout # 创建 'co' 别名来代替 'checkout' git config --global alias.br branch # 创建 'br' 别名来代替 'branch' git config --global alias.ci commit # 创建 'ci' 别名来代替 'commit' git config --global alias.st status # 创建 'st' 别名来代替 'status' git config --global alias.unstage 'reset HEAD --' # 创建 'unstage' 别名来取消暂存
3. 使用别名: 创建别名后,我们可以在命令行中使用它们。例如,使用上面的例子,我们可以这样执行命令:
git co my-branch # 等同于 'git checkout my-branch' git br -a # 等同于 'git branch -a' git ci -m "Message" # 等同于 'git commit -m "Message"' git st # 等同于 'git status' git unstage file.txt # 等同于 'git reset HEAD -- file.txt'
从基本层面上说,
Git
只是一堆通过文件名相互关联的文本文件。还有一点需要提前声明,如果大家也在自己的电脑中进行实验,下面文章中出现的各种
hash
值,都是和内容有关系。所以,大家要和自己的内容对号入座,不要和本文中的hash
值比较。
2. git init
为了演示方便,我们在本地的合适的文件夹中新建了一个dot_git
的项目。
mkdir dot_git
与此同时通过git init
来初始化项目。
现在让我们来看看.git
文件夹中有什么内容。
我们使用erd
来查看文件结构。
erd -y inverted .git
文档结构如下
看起来它创建了一堆文件和文件夹。让我们挑几个重要的来解释一下:
hooks
包含了在Git
执行任何操作之前/之后
可以运行的脚本。HEAD
指向的是local repository
中的代码最新提交版本
- 根据我们设置的“默认”分支是什么(
git config --global init.defaultBranch <分支名称>
),它将是refs/heads/master
(默认),refs/heads/main
,或者我们设置的其他分支名称。 - 它指向了
refs/heads
文件夹,并指向一个叫做master
的文件,这个文件在我们进行第一次提交之前是不存在的。 - 这个
master
文件只会在我们进行第一次提交后出现。
config
是一个文本文件,它包含了当前仓库的Git配置。
- 如果我们查看它,我们会看到一些关于我们的仓库的基本设置,比如是否
bare
、文件模式等。
objects
包含了Git对象
,也就是关于仓库中文件、提交等的数据。(这个狠最重要,狠重要)refs
,存储引用(指针)的地方。
refs/heads
包含分支的指针refs/tags
包含标签的指针