Git是一个非常强大的代码管理工具,它不仅可以让你方便地提交、推送、拉取代码,还可以让你灵活地处理各种复杂的分支、合并、冲突等问题。说到使用,相信大家都会,无非就是commit、push、pull 。但是如果要说熟练掌握Git的使用,对代码管理做到得心应手却不是那么容易。现在我们就先从基本的概念开始,系统地学习Git的原理和操作。
前言
📌首先要知道Git 是如何保存数据的?
Git 保存的不是文件的变化或者差异,而是一系列不同时刻的 快照,在进行提交操作时,Git 会保存一个提交对象(commit object)。下面会说到这个object,它被保存在.git隐藏文件中。
三种状态
Git 有三种状态,后面经常会用到这些概念,所以需要理解并记住。一般来说,你的文件会处于处于三种状态其中之一: 已提交(committed)、已修改(modified) 和 已暂存(staged)
- 已修改表示修改了文件,但还没保存到数据库中。
- 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
- 已提交表示数据已经安全地保存在本地数据库中。
这会让我们的 Git 项目拥有三个阶段:工作区、暂存区以及 Git 目录。
工作区
这是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。
暂存区
它是一个文件,保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。 按照 Git 的术语叫做“索引”,不过一般说法还是叫“暂存区”。
Git 仓库目录
这是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,复制的就是这里的数据。
基本的 Git 工作流程
✂git的工作流程一般为:
- 在工作区中修改文件。
- 将你想要下次提交的更改选择性地暂存,这样只会将更改的部分添加到暂存区。
- 提交更新,找到暂存区的文件,将快照永久性存储到 Git 目录。
如果 Git 目录中保存着特定版本的文件,就属于 已提交 状态。 如果文件已修改并放入暂存区,就属于 已暂存 状态。 如果自上次检出后,作了修改但还没有放到暂存区域,就是 已修改 状态。
commit
Git commit 是将暂存区里的改动提交到本地的版本库。每次使用 git commit 命令我们都会在本地版本库生成一个 40 位的哈希值,这个哈希值也叫 commit-id。
在Git中,commit可以看作是你对代码所做的一次保存。每一次提交都会记录你对代码所做的更改,包括添加、删除和修改的文件。这些提交被保存在Git的数据库中,构成了代码版本控制系统的历史记录。
这个历史记录📒允许你在代码中查看和恢复以前的更改,以及在代码版本之间进行比较。简单来说,commit就是记录你对代码所做的更改,并将这些更改保存在Git数据库中的一个过程。
branch分支
Git的分支是指代码版本的一种分支,允许你在不影响主代码版本的情况下进行更改和试验。
假设你在一个项目上工作,你可以使用分支来创建一个独立的代码版本,在这个版本中你可以进行试验和更改,而不会影响到主版本。
如果你的更改最终被证明是有用的,你可以将它们合并回主版本中。这就是分支的作用,它可以让你在不破坏主版本的情况下,进行试验和更改。
Git 创建新分支实际上是它为你创建了一个可以移动的新的指针。它指向当前所在的提交对象。
📌当有多个分支时,Git 又是怎么知道当前在哪一个分支上呢?
很简单,它有一个名为 HEAD
的特殊指针。 它会指向当前所在的本地分支
HEAD
HEAD 是一个指向你正在工作中的本地分支的指针,可以将 HEAD 想象为当前分支的别名。在 Git 中,每次提交,Git 都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在 Git 里,这个分支叫主分支,即 master 分支。HEAD 严格来说不是指向提交,而是指向 master,master 才是指向提交的,所以,HEAD 指向的就是当前分支。
如果你切换到了其他分支,HEAD 就会指向那个分支。如果你提交了新的修改,master 分支也会随之移动,而 HEAD 指向的分支也会随之移动。
我们可以把git看作是一辆车🚚,而HEAD就像是车头。车头总是指向车身的前方,告诉我们它们所在的方向。同样,HEAD也指向我们当前所在的代码版本。
三者的关系
📌commit,branch和HEAD之间到底是什么关系呢?
假设你正在写一篇文章,你可以把文章的每一个版本看作是一个提交(commit)。
当你需要进行修改时,你可以创建一个新的版本,类似于在文章上打草稿。你可以在这个新的版本上进行修改,而不会影响原先的版本。这个新的版本就像是一个分支,指向你正在编辑的版本。
当你完成修改时,你可以提交新的版本,就像是把你的修改写进正式的文章中。这个提交(commit)将成为一个新的版本,你的分支将指向这个新的版本。
HEAD的作用就像是你在写文章时的光标🏷。它指向你正在编辑的版本,告诉你哪一个版本是当前正在被修改的。当你创建新的版本时,HEAD也会跟随移动到新的版本上,指向新的版本,这样你就可以在这个新的版本上进行修改了。
master和origin
推送代码的命令是git push origin master
,那这里面的origin 和 master
代表了什么呢?
“master ”的含义?
Git 的默认 branch
(俗称主 branch
/ 主分支)。它和其它 branch
的区别在于:
- 新建的仓库中的第一个
commit
会被master
自动指向; - 在
git clone
时,会自动checkout
出master
。
Git 的
master
分支并不是一个特殊分支。 它就跟其它分支完全没有区别。 之所以几乎每一个仓库都有 master 分支,是因为git init
命令默认创建它,并且大多数人都懒得去改动它。
“origin”的含义?
“origin” 指远程仓库,指向的是当你运行 git clone 时默认的远程仓库名字
可以运行 git remote
命令,查看你已经配置的远程仓库服务器
git remote
# 会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL
git remote -v
他们没有特别的含义,只是使用的比较广泛的名字。查看.git/config可以看到它们的含义
.git隐藏文件夹
选取一个文件夹,打开后执行git init
,即创建一个新的git
仓库。可以在文件夹下看到一个.git文件(要打开隐藏文件才能看见)
.git文件说明
.git文件包含了几乎所有 Git 存储和操作的东西。 如若想备份或复制一个版本库,只需把这个目录拷贝至另一处即可。
新初始化的 .git
目录的典型结构如下
$ ls -F1
config description HEAD
hooks/ info/ objects/ refs/
文件跟踪
在git仓库中,你工作目录下的每一个文件都不外乎这两种状态:已跟踪 或 未跟踪。
-
已跟踪文件:就是 Git 已经知道的文件。
已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后, 它们的状态可能是未修改,已修改或已放入暂存区。
-
未跟踪文件:工作目录中除已跟踪文件外的其它所有文件
未跟踪文件,它们既不存在于上次快照的记录中,也没有被放入暂存区。
初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态,因为 Git 刚刚检出了它们, 而你尚未编辑过它们。
编辑过某些文件之后,由于自上次提交后你对它们做了修改,Git 将它们标记为已修改文件。 在工作时,你可以选择性地将这些修改过的文件放入暂存区,然后提交所有已暂存的修改,如此反复。
使用git status
可以查看文件状态
1)比较干净的工作目录
2)存在未跟踪的文件
可以看到Untracked files文件,即存在未跟踪的文件,意味着 Git 在之前的快照(提交)中没有这些文件。根据提示可以看到使用 "
git add
"能让文件变为被跟踪的状态
3)跟踪文件
现在根据提示操作git add README.md
现在README.md文件出现在
Changes to be committed
这行下面,说明是已暂存状态
4)暂存已修改文件
工作目录中有一个已被跟踪的文件demo1.txt,修改了这个文件后,会看到下面内容
可以看到demo1.txt文件出现在了
Changes not staged for commit
这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。
要暂存这次更新,需要运行 git add
命令。
这是个多功能命令:
- 可以用它开始跟踪新文件,
- 或者把已跟踪的文件放到暂存区,
- 还能用于合并时把有冲突的文件标记为已解决状态等。
将这个命令理解为“精确地将内容添加到下一次提交中”而不是“将一个文件添加到项目中”要更加合适。
现在运行这个命令 git add demo1.txt
可以看到现在两个文件都已暂存,下次提交时就会一并记录到仓库。
取消暂存
可以看到在 “Changes to be committed” 文字正下方,提示使用 git restore --staged <file>…
来取消暂存。 所以,我们可以这样来取消暂存某个文件,比如 demo1.txt
文件:
git restore --staged demo1.txt
5)暂存非暂存同时出现
现在我们准备提交了,但是突然发现漏写了东西,所以对demo1.txt文件又进行了一点改动,这个时候还可以直接提交吗?不妨在运行git status
看看。
可以看到demo1.txt文件同时出现在了暂存区和非暂存区,这是为什么呢?
因为Git 只不过暂存了你运行
git add
命令时的版本。 如果你现在提交,demo1.txt
的版本是你最后一次运行git add
命令时的那个版本,而不是你运行git commit
时,在工作目录中的当前版本。 所以,运行了git add
之后又作了修订的文件,需要重新运行git add
把最新版本重新暂存起来
如果觉得每次都要先git add
比较麻烦,可以在提交的时候给 git commit
加上 -a
选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add
步骤
# 这很方便,但是有时这个选项会将不需要的文件添加到提交中。
git commit -a
更简单的查看
从上面git status
命令输出到的结果是比较详细的,如果想简洁点查看可以使用命令
git status -s
输出中有两栏,左栏指明了暂存区的状态,右栏指明了工作区的状态。
??
:新添加的未跟踪文件
A
:新添加到暂存区中的文件
M
:修改过的文件例如,上面的状态报告显示:
README.md
是新添加到暂存区中的文件。demo1.txt
文件已修改,暂存后又作了修改,因此该文件的修改中既有已暂存的部分,又有未暂存的部分。demo3.txt
是新添加的未跟踪文件
总结
目前为止,git中的重要概念我们都了解,下篇我们会接着说git在实战中的基础用法和高级用法以及常见问题的解决方案。
参考资料
🎨【点赞】【关注】不迷路,更多前端干货等你解锁
往期推荐