这一篇在干嘛?

这是全书最核心的一篇。搞懂「三种状态、三个区域」,Git 的一切行为都变得可预测;搞不懂,后面每个命令都像玄学。对应《Pro Git》第 2 章前半部分。

拿到一个仓库的两种方式

方式一:把现有目录变成仓库

cd myproject
git init

这会创建一个 .git 子目录——整个仓库的数据库都在里面,你的文件此时还没有被跟踪

方式二:克隆别人的仓库

git clone https://github.com/user/repo.git

克隆会把完整历史一并拉下来,且默认所有文件都已跟踪。注意 URL 末尾的名字可以自定义:git clone https://github.com/user/repo.git mydir

心脏模型:三种状态、三个区域

工作目录里的每个文件,永远处于以下三种状态之一:

工作区(Working Directory)        暂存区(Staging Area)          仓库(Repository)
  ┌─────────────────┐   git add   ┌──────────────┐   git commit  ┌──────────────┐
  │  已修改 Modified │ ──────────▶ │  已暂存 Staged│ ────────────▶ │  已提交 Committed│
  └─────────────────┘             └──────────────┘               └──────────────┘
        ▲  git restore(丢弃工作区修改)/ git restore --staged(取消暂存)
        └────────────────────────── 回退方向 ──────────────────────────┘
  • 工作区:你正在编辑的文件本身。
  • 暂存区:一个「下次提交购物车」——你把本次想提交的修改挑选、放进去,凑齐了再一次性提交。
  • 仓库(.git 目录):所有历史快照的永久存档。

另外文件分两大类:已跟踪(Git 知道的文件,可处于未修改/已修改/已暂存三种状态)和未跟踪(新出现、还没 git add 过的文件)。

新手最常见的困惑

为什么要有暂存区?为什么不直接 commit? 因为它让你把「我改了很多东西」拆成若干个干净的提交:这 3 个文件的改动属于「修 bug」,那 2 个属于「加功能」,可以分开提交。不想要这个能力时,git commit -a 可以跳过 add 直接提交所有已跟踪文件的修改。

全流程实操

1. 查看状态:git status

$ git status
On branch main
nothing to commit, working directory clean

工作区干净 = 所有已跟踪文件都没改。新建一个文件再看:

$ echo 'My Project' > README
$ git status
Untracked files:
        README

2. 跟踪新文件 / 暂存修改:git add

git add 是个多功能命令:跟踪新文件、暂存修改、合并冲突时标记已解决——一句话理解:「精确地把内容放进下次提交」

git add README          # 单个文件
git add .               # 当前目录下全部改动(最常用)

3. 对比差异:git diff

两个方向要分清:

git diff                # 工作区 vs 暂存区:改了但还没 add 的部分
git diff --staged       # 暂存区 vs 上次提交:已 add、将被提交的部分

建议

提交前先 git status(看哪些文件动了)再 git diff(看具体改了哪几行),养成习惯后几乎不会提交错东西。

4. 提交:git commit

git commit -m "修复登录页在移动端的布局错位"

输出会显示:分支名、提交的 SHA-1 短号、多少文件变了、增删了多少行。提交记录的是暂存区的快照——add 之后又改过的文件不会自动进这次提交,需要重新 git add

5. 删除与改名:git rm / git mv

git rm file.txt            # 从工作区和暂存区同时删除(下次提交后不再跟踪)
git rm --cached file.txt   # 只停止跟踪,文件保留在磁盘上(比如误提交的日志)
git mv old.txt new.txt     # 改名,等价于 mv + git rm + git add 三连

.gitignore:让 Git 睁一只眼闭一只眼

编译产物、日志、临时文件不该进仓库。在仓库根目录建一个 .gitignore

# 忽略所有 .a 文件
*.a
# 但跟踪 lib.a
!lib.a
# 只忽略根目录下的 TODO(不忽略子目录里的)
/TODO
# 忽略所有目录下叫 build 的文件夹
build/
# doc 下的 .txt,但不含子目录更深处的
doc/*.txt
# doc 及所有子目录下的 .pdf
doc/**/*.pdf

新手最常见的困惑

.gitignore 只对未跟踪文件生效。一个文件已经被 git add 过,再写进 .gitignore 不会自动忽略它——先 git rm --cached <file> 停止跟踪。GitHub 上有针对几十种语言/框架的现成模板:https://github.com/github/gitignore

小结

  • 两种建仓方式:git init(空仓库)与 git clone(完整历史)。
  • 三种状态:已修改 → add → 已暂存 → commit → 已提交。
  • 三个区域:工作区、暂存区(下次提交购物车)、仓库。
  • git add . + git status + git diff 是提交前的固定三连。
  • 新仓库第一步先写 .gitignore

学完你会得到:

  • 一张能解释 90% Git 行为的心智模型图
  • status → diff → add → commit 的完整肌肉记忆
  • 会用 .gitignore 挡住编译产物

自测

下一篇:🕰️ 历史与撤销:log、restore、远程仓库与标签