【git教程】git add 命令讲解
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 一、基本使用方法
- `git add` 命令的常见用法:
- `-u`参数说明
- 对比说明
- 二、底层原理
- `git add` 的核心作用
- `SHA-1`哈希值生成
- 在线SHA-1工具与Git哈希值差异的原因
git add
是Git中用于将文件修改添加到暂存区(也称为索引)的命令。下面从使用方法和底层原理两方面进行详细讲解。
一、基本使用方法
git add
命令的常见用法:
# 添加单个文件
git add filename.txt# 添加多个文件
git add file1.txt file2.txt# 添加当前目录下的所有修改
git add .# 添加某个目录下的所有修改
git add directory/# 添加所有被修改和已删除文件,但不包括新文件
git add -u 或 git add --update# 添加所有修改(包括新文件、已修改和已删除文件)
git add -A 或 git add --all //*注意不能用 git add -a
-u
参数说明
git add -u
中的 -u
是 --update
的缩写,它源自英文单词 update(更新)。这个选项的作用是:只更新已经被Git跟踪的文件,即只添加那些已经存在于版本库中且被修改或删除的文件,但不包括新创建的文件(未被Git跟踪的文件)。
底层原理角度
从Git底层来看,-u
选项只更新暂存区中已存在的条目。当你执行 git add -u
时:
- Git会遍历暂存区中的所有文件路径。
- 对于每个路径,检查工作区中对应的文件是否被修改或删除。
- 如果有修改,则生成新的blob对象并更新暂存区记录;如果已删除,则从暂存区中移除该文件记录。
- 完全忽略那些不在暂存区中的新文件。
常用场景
git add -u
常用于以下场景:
- 你只需要提交已跟踪文件的修改,不希望意外提交新创建的测试文件或临时文件。
- 你在清理项目时删除了一些文件,希望将这些删除操作纳入提交。
- 配合
git add <path>
单独添加新文件,实现更精细的提交控制。
例如,假设你有以下文件状态:
已修改: a.txt
已删除: b.txt
新文件: c.txt
执行 git add -u
后,只有 a.txt
的修改和 b.txt
的删除会被添加到暂存区,而 c.txt
不会被处理。
对比说明
-
git add -u
或git add --update
:
只处理已跟踪文件的修改和删除,不处理新文件。 -
git add .
:
处理所有修改,包括新文件、已修改文件和已删除文件。 -
git add -A
或git add --all
:
处理所有修改,效果等同于git add .
,但在某些Git版本中可能有细微差异(例如在子目录中执行时)。
二、底层原理
Git的底层数据结构主要由三种对象组成:blob对象、tree对象和commit对象。理解git add
的底层原理,需要先了解这三种对象:
- blob对象(Binary Large Object):存储文件的内容,每个blob对应一个文件的特定版本。
- tree对象:类似于文件系统中的目录,记录文件名、目录结构和指向blob对象或其他tree对象的引用。
- commit对象:指向一个tree对象,并记录提交的元数据(作者、时间、提交信息等)。
git add
的核心作用
git add
命令的核心是将文件的当前状态保存为blob对象,并更新暂存区(索引)的内容。具体步骤如下:
-
计算文件哈希值:对文件内容使用SHA-1算法计算哈希值(例如:
1f7a7a472abf3dd9643fd615f6da379c4acb3e3
)。 -
存储blob对象:将文件内容压缩后存储在
.git/objects
目录中,文件路径为哈希值的前两位作为目录名,后38位作为文件名。例如:.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3
-
更新暂存区:在暂存区(
.git/index
文件)中记录文件路径、哈希值、文件模式等信息。暂存区本质上是一个二进制文件,记录了下次提交时要包含的内容。
示例说明
假设你有一个项目,包含两个文件 a.txt
和 b.txt
,执行以下操作:
-
修改
a.txt
后执行git add a.txt
:- Git会计算
a.txt
的新哈希值,创建对应的blob对象并存储在.git/objects
目录中。 - 暂存区会更新
a.txt
的记录,指向新的blob对象。
- Git会计算
-
此时如果再次修改
a.txt
但不执行git add
:- 工作区的
a.txt
与暂存区中的a.txt
指向不同的blob对象。 - 下次提交时,只有第一次
git add
时记录的版本会被提交。
- 工作区的
暂存区的作用
暂存区是Git区别于其他版本控制系统的一个重要特性,它的主要作用是:
- 选择性提交:允许你将不同的修改分批提交,而不必一次提交所有修改。
- 多阶段提交:可以将一个大的修改拆分成多个小的提交,使提交历史更加清晰。
- 提交准备:作为工作区和版本库之间的缓冲区,方便组织提交内容。
git add
命令的本质是:将工作区中的文件修改转换为Git内部的blob对象,并更新暂存区的记录。理解这一点有助于更好地使用Git,特别是在处理复杂的提交场景时。
SHA-1
哈希值生成
在Git中,当执行git add
命令时,Git会为文件内容计算SHA-1哈希值,这个哈希值是通过特定的组合字符串生成的。下面详细介绍这个组合字符串的构成。
组合字符串的基本结构
Git生成SHA-1哈希值的组合字符串由以下几部分构成:
blob [文件大小]\0[文件内容]
其中:
blob
是固定的前缀字符串,表示这是一个blob对象。[文件大小]
是文件内容的字节数,以十进制表示。\0
是ASCII码为0的空字节(null byte),用于分隔元数据和文件内容。[文件内容]
是文件的实际字节数据。
计算示例
假设我们有一个文件test.txt
,内容为hello world
(不包含换行符),其组合字符串的构成为:
- 前缀字符串:
blob
(注意后面有一个空格)。 - 文件大小:
11
(因为hello world
包含11个字符)。 - 空字节:
\0
。 - 文件内容:
hello world
。
组合后的完整字符串为:
blob 11\0hello world
对应的ASCII表示为:
b l o b 1 1 \0 h e l l o w o r l d
对这个组合字符串计算SHA-1哈希值,得到的结果是:
sha1("blob 11\0hello world") = 95d09f2b10159347eece71399a7e2e907ea3df4f
验证方法
可以通过以下命令在Git中验证这个哈希值:
# 创建测试文件
echo -n "hello world" > test.txt# 计算文件的SHA-1哈希值(组合字符串的哈希值)
git hash-object test.txt
# 输出:95d09f2b10159347eece71399a7e2e907ea3df4f# 或者使用以下命令直接计算组合字符串的哈希值
echo -n -e "blob 11\0hello world" | sha1sum
# 输出:95d09f2b10159347eece71399a7e2e907ea3df4f -
或者
import hashlibcontent = b"hello world" # 注意:是字节形式的 "hello world"
obj_type = "blob"header = f"{obj_type} {len(content)}\0"
store = header.encode() + contentsha1 = hashlib.sha1()
sha1.update(store)
git_hash = sha1.hexdigest()print(f"组合字符串: {header.encode() + content}")
print(f"Git哈希值: {git_hash}")
其他注意事项
- 内容相关 :如果两个文件内容相同,即使文件名不同或其他元数据不同,它们的blob 哈希值也会相同。
- 换行符敏感:文件中的换行符(LF或CRLF)会影响哈希值的计算。
- 空文件:对于空文件,组合字符串为
blob 0\0
,对应的哈希值是e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
。 - 二进制文件:Git对二进制文件和文本文件的处理方式相同,都是直接计算字节数据的哈希值。
- 完整性检查:哈希值包含文件大小信息,可用于验证文件内容在传输过程中是否被篡改。
理解这种组合字符串的构成,有助于深入理解Git的底层原理和数据存储方式。
在线SHA-1工具与Git哈希值差异的原因
网上有很多在线计算SHA-1哈希值工具,输入上述组合字符串之后发现得到的哈希值与Git哈希值并不一样,下面是搜索得到的答案:
问题本质在于 空字节(\0
)的处理方式不同。Git 中的 \0
是二进制空字节(ASCII 0),而在线工具通常将其作为普通文本字符处理。
核心差异:空字节的表示方式
-
Git 的处理:
blob 1\01
中的\0
是 二进制空字节(字节值为0x00
),组合字符串的实际字节序列为:
62 6c 6f 62 20 31 00 31
(对应blob 1\01
)。 -
在线工具的处理:
当您在网站输入blob 1\01
时,工具会将\0
视为两个普通字符(\
和0
),字节序列为:
62 6c 6f 62 20 31 5c 30 31
(对应blob 1\01
,其中\
是0x5C
,0
是0x30
)。
两种情况的输入字节完全不同,因此 SHA-1 哈希值必然不同。
为什么在线工具无法正确计算?
-
空字节无法直接输入:
大多数文本输入框会过滤或无法识别空字节(\0
在编程中通常表示字符串结束),因此无法在网页中直接输入真实的空字节。 -
工具设计定位:
这类在线工具主要用于文本内容的哈希计算,而非 Git 底层对象的哈希计算,未考虑 Git 的二进制格式要求。
总结:差异的本质
场景 | 输入内容的字节表示 | 哈希值(示例) |
---|---|---|
Git 实际计算 | blob 1\01 (\0 是0x00空字节) | 56a6051ca2b02b04ef92d5150c9ef600403cb1de |
在线工具文本输入 | blob 1\01 (\0 是"\0"字符串) | 37c720f73d5b7aa9f212e266aeccafb6f038d315 |
关键结论:Git 的哈希计算包含二进制空字节,而在线工具无法处理这种格式,因此必须通过 Git 命令或编程方式验证真实的 Git 哈希值。