Git
Git 的数据模型
Section titled “Git 的数据模型”Git 的核心就是一个内容寻址的对象数据库(通过内容的哈希值来唯一识别和访问数据,而不是用名字或位置),有 3 种对象类型:
| 类型 | 用途 |
|---|---|
| blob | 代表文件的内容 |
| tree | 代表目录(以及其中的文件/子目录) |
| commit | 代表一次提交 |
每个对象都有一个唯一的 SHA-1 哈希作为 ID
type object = blob | tree | commit1. blob(Binary Large Object)
- blob 用于保存文件内容的本身
- 每个唯一内容的文件只会生成一个 blob
- Git 不管文件名或路径,只看内容,内容变了就是新 blob
// blob 是一组数据type blob = array<byte>2. tree
- tree 表示一个目录,记录目录下的所有文件(blob)和子目录(tree)的哈希值,定义了快照的结构和内容
- 包含文件名、权限、对象类型、对象哈希
// tree 是一个包含文件和目录的目录type tree = map<string, tree | blob>3. commit
- commit 是在提交时创建的对象
- 包含 tree(项目快照) 的哈希值以及父提交(前一个提交)、作者信息、提交说明、提交时间等元数据
// commit 包含一个父提交,顶层树和元数据type commit = struct { parents: array<commit> author: string message: string snapshot: tree}blob、tree、commit 层次关系

Git 在储存数据时,所有的对象都会基于它们的 SHA-1 哈希进行寻址
objects = map<string, object> // 哈希值 -> 对象
def store(object): id = sha1(object) objects[id] = object
def load(id) return objects[id]blob、tree 和 commit 都是对象,当它们引用其他对象时,它们并没有真正的在硬盘上保存这些对象,而是仅仅保存了它们的哈希值作为指针
所有的快照可以通过它们的 SHA-1 哈希值来标记,但这很不方便,毕竟谁也记不住一串 40 位的十六进制字符
-
人能看懂名字:
main、master、HEAD等 -
Git 能看懂哈希:
d8329fc1cc938780ffdd9f94e0d364e0ea74f579
引用(reference)是一个人类可读的名字,用来指向 Git 对象(通常是 commit 对象)的哈希,于是引用就可以把上述两者联系起来
references = map<string, string> // 名字 -> 哈希值
def update_reference(name, id): references[name] = id
def read_reference(name): return references[name]
def load_reference(name_or_id): // 传入 name,先通过 references 去除 id 再加载 if name_or_id in references: return load(references[name_or_id]) // 传入 id,直接加载 else: return load(name_or_id)1. 引用的作用
-
管理分支:每个 branch 其实就是一个
ref分支是 ref 的一种,位于
refs/heads/目录下,它是一个可移动的指针,指向某个 commit,当提交新的 commit,分支指针会自动向前移动,指向最新提交 -
管理标签:每个 tag 也是一个
ref标签也是 ref 的一种,位于
refs/tags/目录下,它是一个固定的指针,通常指向某个特定的 commit,标签一般用于给重要版本打标记,比如v1.0、release-2023-08,标签一般不随新提交移动,是静态的 -
管理当前状态:HEAD 是一个特殊的 ref,用来指示“当前在哪个分支”或“当前在哪个 commit 上”
2. 引用的位置
Git 把所有的引用保存在 .git/refs 目录下:
.git/├── HEAD # 当前引用├── refs/│ ├── heads/ # 分支引用│ │ ├── main│ │ └── dev│ └── tags/ # 标签引用│ | └── v1.0| ├── remotes/ # 远程分支引用│ │ └── origin/│ │ ├── main│ │ └── dev└── packed-refs # 被压缩的引用(很多时 Git 会打包)Git 把工作状态分成了三个分区:
- 工作区(Working Directory)
- 暂存区(Staging Area)
- 仓库区(Repository)
1. 工作区
工作区就是所有源文件目录,增加文件、修改文件首先发生在这个区域,文件在这个区的状态是红色的已修改(modified),通过 git status 可以看到哪些文件被修改了
2. 暂存区
使用 git add <file> 可以把文件从“工作区”放进“暂存区”,暂存区包含了当前想提交的文件内容的快照,但这个快照并不是直接拷贝整个文件,而是:
-
记录了文件路径
-
文件的执行权限
-
文件对应的 blob 对象的哈希值
当被修改的文件加入暂存区后,状态会变成绿色的已修改(modified),它存储在 .git/index 文件中
3. 仓库区
使用 git commit 命令时,Git 会把“暂存区”的内容保存到本地仓库,仓库中记录的是 Git 对象(提交 commit、树 tree、文件 blob),这些对象组成了 Git 的历史,位于 .git/objects/ 中,是 Git 的核心数据
三大分区工作流程
基本的 Git 工作流程如下:
- 在工作区中修改文件
- 将你想要下次提交的更改选择性地暂存,这样只会将更改的部分添加到暂存区
- 提交更新,找到暂存区的文件,将快照永久性存储到 Git 目录
编辑文件 ↓ 工作区 ↓ git add 暂存区 ↓ git commit 仓库区远端仓库是托管在网络服务器(如互联网、局域网)上的 Git 仓库,与本地仓库(开发者电脑上的仓库)相对应。它本质上是一个裸仓库(Bare Repository),即不包含工作区,仅存储版本控制所需的元数据(如提交历史、分支信息等),专门用于接收和分发代码,通常位于第三方托管平台。
这里的远端不一定表示仓库在网络上的其他位置,而只是表示仓库在别处,所以也可以在自己的本地主机上创建一个远端仓库, 在这样的远端仓库上工作,仍然需要和其它远端仓库上一样的标准推送、拉取和抓取操作。
1. 远程仓库作用
-
代码备份:本地代码推送到远程仓库后,即使本地设备故障,也能从远端恢复代码。
-
多人协作:团队成员通过远端仓库共享代码,避免直接传输文件的混乱(如邮件、U 盘传递)。
-
版本集中管理:远端仓库保存完整的提交历史,所有成员可基于同一版本库协作,减少冲突。
-
支持分支协作:远端仓库可托管多个分支(如
main、develop),方便多人并行开发。
2. 常见的托管平台
远端仓库通常需要借助第三方平台托管,以下是主流选择:
| 平台 | 特点 | 适用场景 |
|---|---|---|
| GitHub | 全球最大的开源社区,支持公开 / 私有仓库,集成 CI/CD、Issue 管理等 | 开源项目、个人学习、团队协作 |
| GitLab | 支持自托管(可部署在企业内网),功能全面(代码审查、容器 registry 等) | 企业级私有项目、对数据安全要求高的场景 |
| Gitee(码云) | 国内平台,访问速度快,支持中文,提供免费私有仓库 | 国内团队协作、对访问速度敏感的项目 |
| Bitbucket | 与 Atlassian 工具(Jira、Confluence)集成紧密,适合小型团队 | 与 Atlassian 生态结合的团队 |
Git 配置
Section titled “Git 配置”Git 自带一个 git config 的工具来帮助设置控制 Git 外观和行为的配置变量,Git 配置有三个层级,配置的优先级从低到高依次是:
| 层级 | 作用范围 | 配置文件位置 |
|---|---|---|
| 系统级(system) | 适用于系统上所有用户 | /etc/gitconfig(Linux/macOS) |
| 全局级(global) | 当前用户所有仓库 | ~/.gitconfig 或 ~/.config/git/config |
| 仓库级(local) | 当前 Git 仓库 | .git/config |
- 执行
git config配置时可以指定配置层级 - 每一个级别会覆盖上一级别的配置,所以
.git/config的配置变量会覆盖/etc/gitconfig中的配置变量
常用命令:
查看所有配置:
git config --list查看某个配置项:
git config user.name设置用户名和邮箱:
git config --global user.name "name"git config --global user.email "name@example.com"设置默认编辑器:
git config --global core.editor "vim"配置合并工具:
git config --global merge.tool meld设置别名:
git config --global alias.co checkoutgit config --global alias.br branchgit config --global alias.ci commitGit 的命令接口
Section titled “Git 的命令接口”用于查看 Git 命令的帮助信息
常见用法:
git help <verb>:获取<verb>命令的帮助git <verb> --help:与git help等效man git-<verb>:使用man手册查看命令帮助
用于将当前目录初始化为 Git 仓库,生成 .git 子目录,包含 Git 所需的必要文件
常见用法:
git init:初始化当前目录为仓库
status
Section titled “status”用于显示当前工作目录和暂存区的状态,比如
- 被修改但还未提交的文件
- 已经准备好提交的文件
- 新建但未被跟踪的文件
- 当前所在的分支
- 当前分支与远程分支的同步状态(是否落后或领先)
常见用法:
git status:显示完整状态git status -s:显示简洁状态(简洁格式)
用于将指定文件的变更添加到暂存区,常用于:
- 把未被跟踪的文件变为已跟踪状态
- 更新已跟踪文件的快照
常见用法:
git add <filename>:将单个filename文件添加到暂存区git add <directory>:将整个directory目录添加到暂存区git add .:将该目录下所有文件添加到暂存区git add -u:添加所有被跟踪文件的修改(不包括新文件,因为新文件还未被跟踪)git add -p:交互式添加文件的部分修改
commit
Section titled “commit”用于将暂存区的内容(即通过 git add 添加的变更)提交到 Git 仓库,形成一次新的提交记录
常见用法:
git commit:创建一次提交,打开编辑器编辑提交信息git commit -m "<message>":直接附带提交信息,跳过编辑器git commit -a -m "<message>":跳过暂存区,直接提交所有已跟踪文件的修改
参考如何编写 良好的提交信息
用于显示当前分支的提交历史,按时间从新到旧列出每一次提交,包括提交哈希、作者、时间、提交信息等
常见用法:
git log:显示完整的提交历史git log <filename>:查看指定文件的提交历史git log -<n>:显示最近n条提交git log --oneline:显示提交历史,精简格式,单行显示git log --oneline --graph --all --decorate:图形化查看提交历史
显示两个版本之间的内容差异
常见用法:
-
git diff:查看工作区(未暂存)和暂存区的差异 -
git diff --staged:查看暂存区和最新提交(HEAD)的差异 -
git diff HEAD:查看整个工作区(包括未暂存和暂存的)与最新提交的差异 -
git diff <branch1> <branch2>:查看两个分支之间的差异 -
git diff <commit1> <commit2>:查看两个提交之间的差异 -
git diff <tag1> <tag2>:查看两个标签之间的差异
branch
Section titled “branch”用于 Git 中的分支管理,包括创建、查看、删除、重命名本地或远程分支等
常见用法:
-
git branch:查看所有本地分支 -
git branch -vv:查看所有分支及其对应的远程分支、最新提交、同步状态(是否 ahead/behind) -
git branch <new-branch>:创建新分支(不会自动切换) -
git branch -d <branch>:删除已合并的本地分支,如果分支未合并 Git 会阻止删除(安全删除) -
git branch -D <branch>:会强制删除分支(不管是否已合并) -
git branch -m <old-name> <new-name>:重命名分支 -
git branch -m <new-name>:重命名当前分支 -
git branch --set-upstream-to=<remote/branch> <branch>:将本地branch分支设置为跟踪远程remote/branch分支,方便后续使用git pull或git push时自动匹配远程目标
Section titled “checkout”checkout
用于切换分支,或者将工作区的文件还原到某个提交或分支的状态
常见用法:
-
git checkout <branch>:切换到指定分支 -
git checkout -b <new-branch>:创建并切换到新分支(相当于git branch+git checkout) -
git checkout <commit>:将工作区还原到某个提交( HEAD 会处于游离状态) -
git checkout -- <file>:丢弃工作区中对指定文件的修改,恢复为上一次提交的状态 -
git checkout <commit-hash> -- file.txt:丢弃工作区中对指定文件的修改,恢复为commit-hash指定的提交状态
用于将另一个分支的修改合并到当前分支,生成一次新的合并提交
常见用法:
git merge <branch>:将指定分支合并到当前分支git merge --no-ff <branch>:强制创建一次合并提交,即使可以 fast-forward(便于保留分支历史)git merge --abort:合并冲突未解决时,取消合并并回退到合并前的状态git mergetool:在遇到合并冲突时,调用合并工具(如 VS Code、meld、vimdiff 等)来手动解决冲突
rebase
Section titled “rebase”把前分支上从公共祖先之后的提交一个个取出来,重新应用(复制)到目标分支的最新提交上
常见用法:
git rebase <branch>:将当前分支的提交,变基到<branch>的最新提交上git rebase -i <commit>:交互式变基,从指定提交之后开始,支持编辑、压缩、删除等操作(常用于整理提交历史)
remote
Section titled “remote”用于管理远端仓库的引用,包括查看、添加、删除和修改远端仓库
常见用法:
git remote:列出所有远端仓库的简短名称(如origin)git remote -v:显示远端仓库的详细 URL 信息git remote add <name> <url>:添加一个新的远端仓库,并命名为<name>git remote remove <name>:删除指定的远端仓库git remote rename <old> <new>:重命名远端仓库git remote show <name>:显示指定远端仓库的详细信息,包括跟踪的分支和推送权限
用于从远端仓库复制整个仓库到本地,包含所有分支和提交历史
常见用法:
git clone <url>:克隆远端仓库到当前目录,默认目录名为仓库名git clone <url> <directory>:克隆远端仓库到指定目录git clone --branch <branch> <url>:克隆指定分支git clone --depth <n> <url>:浅克隆,只获取最近的 n 次提交,减少下载量
从远端仓库获取最新的更新,但不会自动合并到当前分支,只是把更新下载到本地仓库
常见用法:
git fetch:从默认远端仓库(如origin)获取所有分支的更新git fetch <remote>:从指定远端仓库获取更新git fetch <remote> <branch>:只获取指定远端仓库的指定分支的更新
从远端仓库拉取最新的更改并自动合并到当前分支,等同于执行 git fetch + git merge
常见用法:
git pull:从默认远端仓库拉取当前分支的更新并合并git pull <remote> <branch>:从指定远端仓库和分支拉取更新并合并git pull --rebase:拉取更新后使用 rebase 代替 merge,保持提交历史整洁
将本地分支的提交推送到远端仓库,更新远端分支
常见用法:
git push:将当前分支推送到默认远端仓库对应的分支git push <remote> <branch>:将本地分支推送到指定远端仓库的分支git push -u <remote> <branch>:推送并设置本地分支跟踪指定远端分支(方便后续使用git pull或git push)git push <remote> <tagname>:将本地标签推送到远端仓库
用于给提交打标签,方便标记重要的版本
常见用法:
git tag:列出所有标签git tag <tagname>:给当前提交创建一个轻量标签git tag -a <tagname> -m "<message>":创建带注释的标签,并添加说明信息git tag -d <tagname>:删除本地标签
撤销 & 停止追踪
git commit --amend:主要用途有两个- 修改最近一次提交的提交信息(比如改错字、补充说明)
- 将暂存区的新修改合并进最近一次提交(不产生新的提交记录)
git reset HEAD <file>:将文件从暂存区移除,恢复为未暂存状态git checkout -- <file>:丢弃工作区对指定文件的修改(恢复到暂存区或最新提交状态)git rm --cached <filename>:停止 Git 跟踪,但保留本地文件(仅取消追踪文件)git rm <filename>:删除文件并从版本控制中移除(删除文件并取消追踪)
.gitignore
.gitignore 文件用于告诉 Git 哪些文件或目录应该被忽略,不纳入版本控制,能帮助避免提交临时文件、编译产物、日志文件等不需要跟踪的内容
.gitignore 文件格式与规则:
- 每一行写一个模式
- 空行或以
#开头的行表示注释 - 支持通配符:
*匹配任意字符?匹配单个字符**匹配任意目录层级
- 以
/开头表示相对仓库根目录匹配 - 以
/结尾表示目录
示例:
# 忽略所有 .log 文件*.log
# 忽略 node_modules 目录node_modules/
# 忽略所有 .DS_Store 文件(macOS系统文件).DS_Store
# 忽略所有临时文件*.tmp
# 忽略 dist 目录/dist/
# 忽略所有以 ~ 结尾的文件(编辑器临时文件)*~
# 忽略 config.json 文件(通常是包含配置信息的文件)config.json