前言
什么是 Git
Git 是一个版本管理系统,和其他版本控制系统最大的区别是,其他系统是基于差异的版本控制(即一组基础文件和每个文件随着时间变化积累的差异)。而 Git 是直接记录文件快照(相当于每次是将文件做了一次完整复制,每一次的记录都是一个全新的副本),在每一次存储文件之前会计算文件的校验和(Git 计算校验和使用的是 SHA-1 散列即哈希),Git 数据库中保存的信息都是以文件内容的哈希值来作为索引。
Git 中文件的三种状态
- 已修改(modified):表示文件已经被修改,但还未保存到数据库中
- 已暂存(staged):表示已将已修改的文件添加到暂存区,但还未提交
- 已提交(committed):表示已将文件提交保存到本地数据库中
Tips:此外在工作区中新建但还未提交的文件会被标记为未跟踪状态(untracked)
Git 中的三个区域
- 工作区(Working Directory):当前使用和修改的文件,即你当前磁盘上显示的文件,在工作区中新建或修改文件会将该文件标识为未跟踪或已修改状态
- 暂存区(Staging Area):暂存区只是记录了哪些文件会在下次提交时保存到 Git 仓库中,它并没有保存任何文件内容,仅记录了一份文件列表信息
- Git 仓库(Repository):用来保存项目元数据和对象数据库的地方,存储了该项目各个版本的快照并使用文件内容的哈希值进行索引,
Git 中的三个区域可以看作三棵不同的树,而 Git 则是用于专门管理这三棵树的工具
树 | 用途 | 区域 |
---|---|---|
HEAD | 上一次提交的快照,下一次提交的父节点 | Git 仓库 |
Index | 预期的下一次提交的快照 | 暂存区 |
Working Directory | 沙盒 | 工作区 |
Tips:HEAD 是指当前分支引用的指针,它总是指向该分支上的最后一次提交,它属于 Git 仓库的一部分
其中 Git 仓库和暂存区包括一些当前仓库的 Git 设置都在项目根目录下的.git
文件夹中,项目中其他位置则为工作区
Git 工作流程
一般情况下,Git 工作流程如下:
- 从 Git 仓库由 HEAD 指向的分支版本检出文件到工作区
- 工作区对文件进行增删改后将文件加入到暂存区
- 暂存区文件提交到 Git 仓库中永久保留,HEAD 移动到最新的分支版本处,如果是第一次初始化仓库则有略微差别。
首次初始化仓库时(即运行git init
),只有工作区中存在文件,此时暂存区为空,仓库为空,HEAD 指向的是还未创建的默认的master分支。其工作流程如下:
- 使用
git add *
将所有文件添加到暂存区中 - 使用
git commit
将索引(暂存区)中的内容保存为永久快照(提交到 Git 仓库中),然后创建一个指向该快照的提交对象,最后更新master来指向本次提交。此时 HEAD 还是指向的master,但master不为空
Tips:当三棵树中的内容都一致时,使用git status
没有输出,当其中任意一个与另外两个不同时使用git status
则会获得提示
Git 中的配置
配置文件
- 系统配置文件:包含每一个用户及仓库的通用配置
- Windows:是 Git 安装目录下的 etc 文件夹中的 gitconfig
- Linux:是 /etc/gitconfig 文件
- 用户的全局配置文件:包含该用户下所有仓库的通用配置
- 所有系统都是用户家目录下的
.gitconfig
文件,Linux 系统下也有可能是.config/git/config
文件
- 所有系统都是用户家目录下的
- 仓库配置文件:仅对当前仓库生效
- 项目文件夹下的
.git/config
文件
- 项目文件夹下的
配置方式
使用git config
命令可以对 Git 进行配置
--system
选项表示进行系统配置,读写的是系统配置文件,对系统上所有用户及其下仓库生效,此命令需要管理员权限--globle
选项表示进行用户配置,读写的是用户配置文件,对该用户下所有仓库生效--local
选项表示进行仓库配置,读写的是当前仓库的配置文件,仅对当前仓库生效(此命令只能在项目文件夹下使用)--list
选项表示列出 Git 找到的所有有配置,可能会有多个重复的选项,因为此命令会读取上面三个文件并全部显示
常用配置
user.name "xxx"
设置提交的用户名,必须设置user.email "xxx@xxx.xxx"
设置提交的邮件地址,必须设置core.editor vim
设置默认的文本编辑器,当 Git 需要你输入信息时会调用,如提交说明,合并冲突等,如果是在 Windows 下需要指定编辑器的绝对路径
Tips:当后面不跟设置的值时,默认是显示该选项的值,因此可以通过此命令查看已经设置的值
本地仓库
初始化
命令:git init
用处:将当前文件夹初始化为一个 git 仓库。所有的 git 命令都只在 git 仓库下才有意义,因此要使用 git 相关命令第一步是确保你在一个 git 仓库下
git 仓库下会有一个.git
的文件夹,在 Linux 和 MacOS 下这是一个隐藏文件夹,要使用ls -a
才能看到,这里面存放了关于仓库的所有信息,以及仓库中文件的所有信息,如果删除此文件夹,则 git 仓库就不再是一个仓库了
添加文件
命令:git add dir/filename
用处:当一个仓库初始化后,在该仓库下新增的文件还需要手动添加到该仓库中,才能实现对文件进行追踪和版本管理。使用git add
表示将文件添加到暂存区,下次提交时会将暂存区中所有文件一并提交到本地仓库中
文件名应当是一个完整的路径,可以是相对路径也可以是绝对路径,但所有文件一定是在仓库根目录下面或仓库根目录下的各个子目录中,可以一次添加多个文件,也可以使用*
表示所有文件,如果指定的是一个目录则会递归添加该目录下所有未跟踪或未暂存的文件
此命令不仅用于新建文件,文件被修改后也需要使用此命令将文件添加到暂存区,也能用于合并时将有冲突的文件标记为已解决状态
提交文件
命令:git commit -m "xxx"
用处:将添加的文件提交到仓库中,-m
后指定的是此次提交的说明文字,一般用于简短描述此次编辑的作用,方便以后查找改动记录。如果有大段文字说明可以不使用-m
,会默认启动你设置的文本编辑器来让你输入提交说明。提交时是提交的存放在暂存区的快照,如果在工作区修改了文件但并未加入到暂存区中则不会提交,提交是将当前暂存区的快照进行永久保存,以后可以随时回退到此状态或是进行比较
使用-a
选项会将当前工作区中已跟踪的所有做过修改的文件直接提交,此参数实际上相当于先将工作区已修改文件全部添加到暂存区再进行提交,即跳过git add
步骤
当提交时提交信息写错了或有几个文件忘记添加到暂存区导致没有提交,如果再按照正常流程就会在历史记录中多一次重复的提交记录,此时使用--amend
选项可以覆盖前一次提交记录,此选项会强制提交当前暂存区的快照到仓库中,无论暂存区的文件是否有发生变化,没有变化则只会覆盖提交信息,有变化则完全覆盖上一次提交。这样可以尽可能的保持仓库历史
查看文件状态
命令:git status
用处:查看当前仓库中的文件都处于什么状态,一般为以下一种或多种类型,-v
选项会输出更详细的提交说明
Untracked files
下面的表示未跟踪的文件Changes to be committed
下面的表示文件已暂存但还未提交Changes not staged for commit
下面的表示文件已跟踪,但内容发生了变化还未暂存
添加-s
或--short
选项表示简略显示:
??
表示未跟踪文件A
表示添加到暂存区的文件M
表示修改过的文件MM
两栏的左边表示暂存区状态,右边表示工作区状态,所以此处的意思是:文件的修改中既有已暂存的也有未暂存的(即修改了最少两次,在暂存后还未推送又产生了修改)
忽略文件
工作目录下有时会产生一些无需纳入版本管理的文件,如编译的临时文件,编辑的缓存文件,程序运行时产生的日志或其他临时文件,因此可以通过创建名为.gitignore
的文件来指定仓库中忽略哪些文件不进行版本管理。一般来说只需要在仓库根目录下创建一个.gitignore
即可,整个仓库都会递归应用该文件中指定的忽略的规则,也可以在子目录下单独创建,其作用返回只在该子目录下所有文件和文件夹
.gitignore
文件中的格式规范如下:
- 所有空行和以
#
开头的行都会被忽略 - 可以使用标准 glob 模式匹配(即简化版正则),会递归应用到整个工作区
*
表示零个或多个任意字符[]
表示匹配中括号中任意一个字符或范围(以短横线连接的表示范围)?
表示单个任意字符**
表示匹配任意中间目录
- 匹配模式可以以
/
开头防止递归 - 匹配模式可以以
/
结尾指定目录 - 以
!
开头表示取反,会忽略指定模式以外的文件或目录 - 每个规则一行
- 下面的规则优先级高于上面的规则
Tips:Github 上有一个十分详细的针对数十种项目及语言的.gitignore
文件列表
查看修改细节
命令:git diff
作用:默认不带任何参数是用于比较当前工作区和暂存区中文件的具体变化内容,即当前工作区修改后但还没有添加到暂存区的文件修改了哪些内容,会以补丁形式显示出具体的修改内容。当添加--staged
或--cached
(两个为同义词)参数时则表示比较暂存区和最后一次提交的文件的具体变化内容,即当前已添加到暂存区但还没有提交到本地仓库的文件具体修改了哪些内容,也是以补丁文件的形式显示具体修改内容
Tips:diff 可以调用外部图形化工具来比较差异如 vimdiff 等,使用git difftool --tool-help
来查看当前系统支持哪些 Git Diff 插件
移除文件
命令:git rm
作用:在 Git 仓库中要删除某个文件,需要从暂存区和工作区都删除该文件,如果仅从磁盘上删除则文件还保留在暂存区中还需要手动提交一遍(否则会出现在未跟踪文件清单中),使用此命令则直接从工作区删除文件后并从暂存区移除并提交,不会出现在未暂存清单中
使用-f
参数除了删除当前工作区中的文件,还会强制删除已添加到暂存区的文件
使用--cached
参数可以仅删除已添加到暂存区的文件,仍保留当前工作区的文件(比如将编译生成文件不小心添加到暂存区就可以使用此命令删除)
此命令还支持.gitignore
文件中的 glob 模式
移动文件
命令:git mv
作用:用于移动文件或重命名文件,相当于运行了三条命令mv
、git rm
、git add
查看历史
命令:git log
作用:默认情况下会显示所有提交最近的更新在最上面,此命令会列出每个提交的 SHA-1 校验值、作者名和电子邮件、提交具体时间以及提交说明
使用-num
可以指定显示最近的指定数目的提交
使用-p
或--patch
会将每次提交时的差异(即修改内容)以补丁格式输出
使用--stat
会输出简略的统计信息,如几个文件产生了修改,新增了多少,删除了多少
使用--pretty=format
可以自定义显示格式,其内建的有四种格式
oneline
:每次提交信息输出在一行short
:简单输出,只显示 SHA-1 校验值前几位full
:完整输出fuller
:最全面输出format:"常用选项"
:根据常用选项自定义输出
选项 | 说明 |
---|---|
%H | 提交的完整的哈希值 |
%h | 提交的简写的哈希值 |
%T | 树的完整哈希值 |
%t | 树的简写哈希值 |
%P | 父提交的完整哈希值 |
%p | 父提交的简写哈希值 |
%an | 作者名字 |
%ae | 作者的电子邮件地址 |
%ad | 作者的修订日期(可以使用--date= 选项来定制格式) |
%ar | 作者修订日期(按照多久以前来显示) |
%cn | 提交者的名字 |
%ce | 提交者的电子邮件地址 |
%cd | 提交日期 |
%cr | 提交日期(按照多久以前来显示) |
%s | 提交说明 |
使用--graph
可以在日志旁以 ASCII 图形显示分支和合并历史
使用--abbrev-commit
会仅显示 SHA-1 校验值的前几个字符
使用--since=2.weeks
或--after
列出最近两周的所有提交,也可以使用类似 2020-01-01 这种具体某一天的,或是 1 years 1 day 1 minutes ago 这种
使用--until
或--before
和--since
一样的用法
使用--author
列出指定作者的提交记录
使用--grep
在提交说明中进行搜索
可以指定多个作者和说明搜索,这样会输出任意匹配到的提交,使用--all-match
则只输出所有匹配到--grep
模式的提交
使用-S
指定一个字符串,只显示添加或删除了该字符串的提交
使用--no-merges
在结果中排除掉合并提交的记录
重置文件
命令:git reset
作用:对 Git 中三棵树中的文件进行操作,将一个或多个区域的文件重置到指定的分支版本
检出文件
命令:git checkout