如何理解git reset --hard中「阻碍写入被追踪文件」的含义?
关于git reset --hard中「阻碍写入被追踪文件」的疑问与场景模拟
问题背景
Git官方对git reset --hard的说明提到:
--hard
重置索引和工作树。自指定提交以来工作树中被追踪文件的所有变更都会被丢弃。任何阻碍写入被追踪文件的未追踪文件或目录会被直接删除。
我对「任何阻碍写入被追踪文件的未追踪文件或目录会被直接删除」这句话存在疑问,于是做了一个小实验:
- 创建未追踪文件:
touch dummy.html git status -s
- 当时的提交历史:
b3579f4 (HEAD -> main) Revert "Add a crazzzy experiment" 622be1d Add a crazzzy experiment
- 执行
git reset --hard 622be1d后,dummy文件完好无损:
➜ my-git-repo git:(main) ✗ git reset --hard 622be1d HEAD is now at 622be1d Add a crazzzy experiment ➜ my-git-repo git:(main) ✗ git status -s ?? dummy.html
- 执行
git reset --hard b3579f4后,dummy文件也未被改动:
➜ my-git-repo git:(main) ✗ git reset --hard b3579f4 HEAD is now at b3579f4 Revert "Add a crazzzy experiment" ➜ my-git-repo git:(main) ✗ git status -s ?? dummy.html
看起来--hard模式对未追踪文件没有影响,那么「阻碍写入被追踪文件」到底是什么意思?该如何模拟这种场景?
解答:什么是「阻碍写入被追踪文件」
你实验里的dummy.html是完全独立的未追踪文件,和仓库里已追踪的文件没有路径冲突,所以Git不会动它。所谓「阻碍写入被追踪文件」,指的是未追踪的文件/目录和你要reset到的目标提交里的已追踪文件路径完全重合,Git要把目标提交里的文件内容写入工作区时,这个未追踪文件挡在了路上,Git就会直接删除它来完成写入。
模拟场景步骤
- 先在仓库里创建一个已追踪文件
test.txt并提交:
echo "initial content" > test.txt git add test.txt git commit -m "Add test.txt"
- 删除工作区和索引里的
test.txt,然后创建同名的未追踪文件:
git rm test.txt git commit -m "Remove test.txt" echo "untracked content" > test.txt # 此时test.txt是未追踪状态,git status会显示?? test.txt
- 执行
git reset --hard HEAD~1,回到之前有test.txt的提交:
git reset --hard HEAD~1
此时你会发现,那个未追踪的test.txt已经被Git删除了,工作区里是目标提交里的test.txt(内容为initial content)。因为Git要恢复已追踪的test.txt,但当前工作区的未追踪test.txt占用了这个路径,阻碍了写入,所以Git直接删除了它。
另一种典型场景是未追踪目录和已追踪文件路径冲突:比如目标提交里有docs/index.md,而当前工作区有一个未追踪的docs目录,Git在恢复docs/index.md时,这个未追踪目录会阻碍写入,也会被直接删除。
内容的提问来源于stack exchange,提问作者hustnzj
相关产品推荐
相关产品推荐

