◆

Git add 添加文件到暂存区详解

2023-02-02 01:370

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 之后又改」的情况一眼就能识别。

💬留言板0

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

PAGE LOAD / LIVEGit add 添加文件到暂存区详解加载中