Pro Git这样解释git reset
:
回顾
reset
命令按特定顺序覆盖这三棵树,当您告诉它时停止:
- 将分支HEAD点移动到(如果
--soft
,则在此停止)- 使索引看起来像HEAD(除非
--hard
,否则在此停止)- 使工作目录看起来像索引
我的理解是,如果我执行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 HEAD
或git rm --cached <file list>
将有助于保留这些新文件(将它们从索引中删除)
Junio C.Hamano仍然认为:
如果HEAD中不存在路径,但在其他情况下存在于索引中,则希望"
reset --hard
"删除该路径
根据我的经验,这往往是可取的。
(就像摆脱冲突合并中的漏洞)
请注意,在这种情况下,git fsck
仍然可以帮助恢复那些已删除的新文件。