.git目录是 Git 用来存储项目版本控制信息的核心目录,下面是.git目录中重要文件和文件夹的作用:

HEAD文件指向当前处于的分支。其内容通常为:

1
ref: refs/heads/main

这表明当前分支为main

如果内容为ref: refs/heads/feat,表明当前分支为feat

Git 的设计是基于引用(refs)体系的,所有引用都存储在 .git/refs 目录下,其中refs/heads/ 用于存储分支

config

存储对应项目的 Git 配置信息,例如远程仓库地址、体验化选项等。文件内容大致为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
[remote "origin"]
url = https://github.com/xxx/xxx.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
[branch "feat1"]
remote = origin
merge = refs/heads/feat1
vscode-merge-base = origin/main

description

GitWeb 服务中用于显示项目描述,通常在本地使用时没有实际作用。

objects/

Git 中最重要的文件夹,存储所有对象(如 commit 、tree 、blob,以及annotated tag)。不同对象根据对象的标识码和数据内容保存。

Git 中的所有对象(commit、tree、blob 和 annotated tag)在存储时都会经过以下两步处理:

  • 添加头部信息:包括对象类型和内容长度;
  • 使用 zlib 压缩:将对象压缩为二进制格式存储。

想要查看对象存储的信息可以使用命令git cat-file -p <hash-id>

Commit 对象

记录一次提交操作,包含提交描述信息和对应根目录的 tree 对象。文件内容

1
2
3
4
5
6
tree <tree-hash>
parent <parent-hash> # 可能有多个
author <author-name> <email> <timestamp>
committer <committer-name> <email> <timestamp>

<commit-message>
  • tree-hash:指向根目录的 tree 对象。
  • parent-hash:指向上一个提交的哈希值(如果有多个父提交,则记录多个)。
  • author:提交作者的信息,包括名字、邮箱和时间戳。
  • committer:实际提交人信息(可能与作者不同)。
  • <commit-message>:提交的描述信息。

Tree 对象

存储目录中文件和子目录的结构信息,相当于一个目录的实时展示。文件内容

1
<file-mode> <type> <hash> <filename>

  • file-mode:文件的权限信息(如普通文件、可执行文件、子目录等)。
  • type:对象类型(blob 或 tree)。
  • hash:对应 blob 或子 tree 对象的标识码。
  • filename:文件或目录的名称。
    示例:
    1
    2
    100644 blob 789abcd file.txt
    040000 tree 123efgh subdir

Blob 对象

存储文件的内容,没有文件名称或目录结构信息,是 Git 存储原始文件数据的核心实体。

文件内容:直接存储文件内容(存储时经过添加头部zlib压缩,故不是明文),不包含元数据。

  • 例如,一个文本文件 hello.txt,内容为 Hello, world!,其 blob 对象使用git cat-file -p查看后显示:
    1
    Hello, world!

Annotated Tag 对象

标记一个实体对象,通常用于永久标记,包含创建者、时间和描述信息。文件内容

1
2
3
4
5
6
object <object-hash>
type <type>
tag <tag-name>
tagger <tagger-name> <email> <timestamp>

<tag-message>

  • object-hash:指向被标记对象(如 commit)的哈希值。
  • type:对象的类型(如 commit、tree、blob)。
  • tag-name:标签的名称。
  • tagger:标签创建者的信息。
  • <tag-message>:标签的描述信息。

示例:

1
2
3
4
5
6
object f3a23bc4f1e8e5a1d07e8f5f0f7c12ef8f7d1234
type commit
tag v1.0
tagger John Doe <johndoe@example.com> 1672531200 +0000

Initial release

这些对象是 Git 存储和追踪文件历史的核心,采用哈希值(SHA-1 或其他算法)唯一标识,确保了数据完整性和高效性。

refs/

存放分支和标签信息,这些参照指向对应的 commit。包括三个个重要存储目录:

  • refs/heads/:存放分支信息;
    • refs/heads/xxxxxx为本地分支名,其文件中存储该分支最新提交的hash id
  • refs/remote/origin/:存放远程分支信息
    • refs/heads/xxxxxx为远程分支名,其文件中存储该分支最新提交的hash id
  • refs/tags/:存放标签信息。
    • refs/tags/xxxxxx为附注标签名,其文件中存储该Annotated Tag的hash id

hooks/

存放各种钩子脚本,用于在 Git 操作时执行自定义操作。其中.sample后缀的文件只是样例,需要将后缀去除并赋予执行权限后才能作为真正的脚本。脚本编写可以使用任何支持的脚本语言,如 Bash、Python 等

1
2
3
4
5
ls hooks
applypatch-msg.sample pre-applypatch.sample pre-rebase.sample sendemail-validate.sample
commit-msg.sample pre-commit.sample pre-receive.sample update.sample
fsmonitor-watchman.sample pre-merge-commit.sample prepare-commit-msg.sample
post-update.sample pre-push.sample push-to-checkout.sample

例子:检查提交信息格式(commit-msg),确保提交信息以特定前缀开头

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
echo "Validating commit message..."

COMMIT_MSG=$(cat "$1")
if [[ ! $COMMIT_MSG =~ ^(feat|fix|docs|style|refactor|test|chore): ]]; then
echo "Invalid commit message. Must start with one of: feat|fix|docs|style|refactor|test|chore."
exit 1
fi

echo "Commit message is valid."
exit 0

Hooks 不会随仓库一起推送到远程服务器。推荐将 Hooks 脚本存储在项目目录(如 .githooks/)中,并通过文档指导团队成员启用。

info/

包含额外的配置信息,例如exclude文件,设置本地排除要求。

logs/

记录分支和HEAD的历史变化记录,便于优化和借助调试。

index

Git 的 暂存区(staging area)的核心数据结构,用于管理文件的快照。它记录了工作区中文件的状态信息,是 Git 的暂存操作、差异计算以及提交生成的关键组件,是一个二进制文件。

packed-refs

将亚气式存储的参照(如分支)压缩为单个文件,减少文件总数,提高性能。

COMMIT_EDITMSG

存储最近一次 commit 的消息,便于检查和修改。

ORIG_HEAD

记录最近一次重覆或结合之前的 HEAD 位置,通常用于撤销操作。

理解.git目录中各文件和文件夹的作用,能让我们更深入了解 Git 运作机制,并能在工作中更好地排查问题和优化流程。对于初学者,探索.git目录中的内容是学习 Git 的重要一步;对于高级用户,理解该目录能让你更好地自定义 Git 操作和优化项目管理。