git reset --hard 不使工作树看起来像索引?



Pro Git这样解释git reset

回顾

reset命令按特定顺序覆盖这三棵树,当您告诉它时停止:

  1. 将分支HEAD点移动到(如果--soft,则在此停止)
  2. 使索引看起来像HEAD(除非--hard,否则在此停止)
  3. 使工作目录看起来像索引

我的理解是,如果我执行git reset --hard,那么我的索引和工作目录都会像我的HEAD一样变成。所以我继续做了这个:

# make a git repo
mkdir mygitrepo
cd mygitrepo
git init
# init commit
touch old_file
git commit -a
# stage a file
touch staged
git add staged
# create file that is not staged
touch unstaged

到目前为止,我的回购看起来是这样的:

  • HEAD旧文件
  • index旧文件+暂存
  • working dir旧文件+暂存+未暂存

现在,如果我运行git reset --hard,那么我希望我的回购变成:

  • HEAD旧文件
  • index旧文件
  • working dir旧文件

但我会得到这个:

  • HEAD旧文件
  • index旧文件
  • working dir旧文件+未暂存

我通过显式传递目标参数(如git reset --hard target)进行了类似的测试,得到了类似的结果:暂存文件都不见了,但在git reset --hard之后仍然存在未暂存文件。

如果我对git reset有什么误解,有人能解释一下吗?

Git将始终遵循一条重要规则(除非明确告知不要这样做):不要触摸未跟踪的文件

使用默认功能,Git永远不会执行任何会导致未跟踪文件数据丢失的操作。这是因为Git永远无法恢复未跟踪文件中的信息:它不"知道"这些文件,因此不会将其内容存储在任何地方,这使得对它们的任何操作都非常危险。所以它根本不会触及他们。

这也是为什么Git不允许您切换到具有未跟踪文件的分支的原因,如果该分支包含具有相同路径的跟踪文件。因为这样做会覆盖未跟踪的文件,将其替换为该分支的文件,但您永远无法恢复该未跟踪文件。因此,Git不会切换分支,要求您首先处理未跟踪的文件(这可能意味着重命名它,将它添加到Git,甚至删除它)。

因此,当您执行git reset时,您只会影响索引。git reset --hard会影响索引和工作目录。对于Git,工作目录只包括被跟踪的文件。这就是为什么git reset --hard既不会删除未跟踪的文件,也不会删除.gitignore中的文件。Git并不"了解"它们,因此不会触及它们。

如果你想删除未跟踪的文件,有一个命令可以做到这一点:gitclean。该命令带有不同的参数组合,这些参数都执行各种不同的作业。

默认情况git clean -f将删除未跟踪的文件,但保持忽略的文件不变。如果您还想删除被忽略的文件,可以使用git clean -x -f。请注意,您通常必须为git clean指定-f(或--force)才能执行任何操作。这只是一个额外的安全机制,以确保你知道自己在做什么。你可以使用-n来执行试运行,只显示Git将删除哪些文件。这使得避免问题变得容易。

如"撤消更改"中所述

git clean命令通常与git reset --hard一起执行
请记住,重置只会影响被跟踪的文件,因此需要单独的命令来清理未跟踪的文件

结合使用这两个命令,可以将工作目录返回到特定提交的确切状态。

--hard选项记录为:

重置索引和工作树
<commit>以来,对跟踪的工作树中文件的任何更改都将被丢弃。


早在2008年就已经讨论过了。
简单地说,Linux Torvalds:

如果您习惯于执行"git checkout -f"或"git reset --hard",则这两个检查(*)都将被忽略。毕竟,你要求强制切换。

(*checks=脏文件或未跟踪文件)

至少在第二种情况下,我认为git不会删除它不知道的文件,这样你就会留下一个"turd"。


添加了文件。。。即使从未提交(全新),仍然被git reset --hard删除,如2008年所示:

事实上,我昨天不小心删除了数百个新添加的文件这样做。

我的问题是为什么"git reset --hard"不能为新添加的跟踪文件
毕竟,"git status"知道它们是"新文件",而"git reset --hard"可能会意识到,把它们从地球上抹去并不是最有帮助的事情。

作为一个建议,在git reset --hard之前的git read-tree -m HEADgit rm --cached <file list>将有助于保留这些新文件(将它们从索引中删除)

Junio C.Hamano仍然认为:

如果HEAD中不存在路径,但在其他情况下存在于索引中,则希望"reset --hard"删除该路径
根据我的经验,这往往是可取的。

(就像摆脱冲突合并中的漏洞)

请注意,在这种情况下,git fsck仍然可以帮助恢复那些已删除的新文件。

相关内容

最新更新