Git add 添加文件到暂存区详解
git add
添加文件到暂存区
三个区域
理解 git add 之前得先分清 Git 的三个区域,很多疑问都是没分清导致的:
- 工作区(Working Directory):你正在编辑的文件,磁盘上看得见的那些
- 暂存区(Index / Staging Area):
git add之后文件进入这里,是「下次提交的候选内容」 - 仓库(Repository):
git commit之后内容进入这里,成为一次历史提交
git add 做的就是把工作区的改动快照复制进暂存区。注意是快照——add 之后再改文件,暂存区里存的还是 add 那一刻的内容,提交进去的也是那一刻的内容,不是最新的。这是新手最容易困惑的地方:
echo "v1" > a.txt
git add a.txt
echo "v2" > a.txt # 又改了
git commit -m "add a"
# 提交进去的是 v1,v2 还在工作区里没提交
git status 这时会同时显示 a.txt 在 "Changes to be committed" 和 "Changes not staged for commit" 两个区块里,就是这个原因。
常用形式
git add <file> # 添加指定文件
git add <dir>/ # 添加目录下所有改动
git add . # 添加当前目录及子目录的所有改动
git add -A # 添加整个仓库的所有改动
git add -u # 只添加已跟踪文件的改动(不含新文件)
.、-A、-u 的区别经常被搞混,这里说清楚:
| 命令 | 新增文件 | 修改文件 | 删除文件 | 作用范围 |
|---|---|---|---|---|
git add . | 是 | 是 | 是 | 当前目录及子目录 |
git add -A | 是 | 是 | 是 | 整个仓库 |
git add -u | 否 | 是 | 是 | 整个仓库(Git 2.x) |
在 Git 1.x 时代 git add . 不处理删除,-A 才处理,所以老文章会强调用 -A。Git 2.0 之后 . 也处理删除了,两者的唯一区别就剩作用范围。如果你在仓库根目录执行,git add . 和 git add -A 完全等价。
实践建议是尽量别用 git add .。它会把你没打算提交的东西一起带进去——临时调试文件、误生成的产物、其他人在你分支上留的改动。更好的做法是 git status 看一眼再逐个 add,或者用下面的交互模式。
交互式添加
想只提交一个文件里的部分改动,用 -p(patch):
git add -p <file>
Git 会把改动按 hunk(代码块)切开,逐个问你要不要暂存。常用的回答:
y暂存这块n跳过这块s把这块拆得更小(split)e手动编辑这块要暂存的内容q退出?看全部选项
这个功能在「一次改动里混了两件不相关的事」时特别有用——比如修 bug 的同时顺手格式化了一段代码,可以用 -p 把它们分成两个提交,让历史更清楚。
git add -i 是完整的交互界面,能选文件、看 diff、切换模式,但实际上 -p 用得更多。
添加被 gitignore 忽略的文件
正常情况下 .gitignore 里的文件 git add 会跳过,而且不报错(这点很坑,你以为加进去了)。要强制添加用 -f:
git add -f dist/bundle.js
想知道某个文件为什么被忽略、是哪条规则忽略的:
git check-ignore -v dist/bundle.js
# .gitignore:12:dist/ dist/bundle.js
输出格式是 规则文件:行号:规则内容 被忽略的文件。这比在 .gitignore 里人肉找规则快得多。
撤销 add
把文件从暂存区撤回工作区(改动保留):
git restore --staged <file> # Git 2.23+ 推荐
git reset <file> # 老写法,等价
git reset # 撤销所有暂存
git restore --staged 和 git reset HEAD <file> 效果一样,但 restore 语义更明确,不会像 reset 那样一不小心带上 --hard 把工作区也清了。git reset --hard 会丢弃工作区改动且不可恢复,撤销 add 永远不需要 --hard。
只 add 不改内容的情况
有两种情况 git add 会有点反直觉。
文件重命名。Git 不记录重命名操作,它记录的是内容。git mv old new 实际上等于 mv + git rm old + git add new。提交后 Git 通过内容相似度自动识别出这是一次重命名(git log --follow 能追踪)。如果重命名的同时大幅改了内容,相似度低于阈值,Git 就会认为是删除 + 新增,历史会断。
只改了文件权限。chmod +x script.sh 也是需要 add 的改动,Git 只跟踪可执行位。如果不想让 Git 管权限(跨平台协作时 Windows 端经常无意改动权限位),可以关掉:
git config core.fileMode false
空目录加不进去
Git 跟踪的是文件不是目录,空目录 git add 之后什么都不会发生。要保留目录结构的惯例做法是在里面放一个空的 .gitkeep 文件(这不是 Git 的特性,只是社区约定,文件名叫什么都行)。
一个实用的检查习惯
提交前用这两个命令确认暂存区里到底有什么:
git diff --staged # 看暂存区 vs 上次提交的差异
git status -s # 简短格式,两列状态码
git status -s 的输出是两个字符:第一列是暂存区状态,第二列是工作区状态。M 表示改动已暂存, M 表示改动未暂存,MM 表示暂存了一部分后又改了,?? 是未跟踪文件。看懂这两列,前面说的「add 之后又改」的情况一眼就能识别。

还没有留言,来发表第一条吧~