git reset
命令有个 --hard
选项,描述如下:
--hard
Resets the index and working tree. Any changes to tracked files in the working tree since<commit>
are discarded. Any untracked files or directories in the way of writing any tracked files are simply deleted.
让我们从 a tracked file 的定义开始,它非常简单:A tracked file is a file that is in Git’s index 。这样的文件有一个名称,例如 path/to/file.ext
。请注意,这里没有目录或文件夹这回事,它只是一个长名称,带有正斜杠,即使主机操作系统(Windows)要调用这个 path\to\file.ext
,它也总是带有正斜杠。
那么,简单的答案就是观察 Git 将把 path/to/file.ext
写入到你的工作树中,因为 path/to/file.ext
在Git的 index 中,git reset --hard
因此需要写入 path\to\file.ext
(Windows)或 path/to/file.ext
(其他操作系统)。要做到这一点,path
必须是一个目录(AKA文件夹),使用的 path/to
或 path\to
也必须是文件夹。
因此,如果有一个名为 path
或 path/to
的现有文件,Git 会删除这个文件(只会有一个,因为这些操作系统认为,一些路径名要么被识别为文件,要么被识别为文件夹,或者,至少在类 Unix 系统上,可以被识别为 fifos 、devices 和 sockets 等特殊项目,但无论哪种方式,它都会碍事的)。这就是你引用的短语的意思的简单答案。
下面有两个例子可以说明这一点:
第一个例子,先在工作树中创建一个文件,并保持未跟踪状态:
echo data > some-file
运行 git status
会将其显示为 untracked (例如,在 git status -s
输出中会显示 ?? some-file
)。正如您已经看到的,git reset --hard
不会改变这一点。
现在使用以下方法将文件复制到Git的 index 中:
git add some-file
现在 git status
将显示为一个要 commit 的新文件。它不在当前 (HEAD
) commit 中,所以执行 diff
从 HEAD commit 到 index 将显示 some-file
是新的。它同时存在于 Git 的 index 和您的工作树中,所以执行 diff
从 index 到工作树 则不会显示任何信息。
接下来运行:
git reset --hard
其重置为当前 commit 。这使得Git将文件 some-file
从其 index 中剥离出来,因为一个名为 some-file
的文件现在位于 Git 的 index 中,但它不在(新选择的)HEAD commit 中(“佯装无意地” “恰好”与之前的 HEAD commit 相同,因为我们没有选择其他 commit )。我们在做这件事的时候使用了 --hard
,所以 Git 同时删除了 index 与 工作区的这个文件。
第二个例子,git reset --hard
会对 untracked 文件产生影响:
touch a.txt
git add .
git commit -m'init'
echo hello >> a.txt
git add .
git commit -m'hello'
git rm a.txt
echo world > a.txt
git status -s
status 的输出会是:
D a.txt
?? a.txt
index 中的 a.txt
被删除,而工作树中的 a.txt
则是 untracked 。
cat a.txt
world
然后再用 --hard
模式进行重置:
git reset --hard
cat a.txt
hello
git status
On branch master
nothing to commit, working tree clean
工作树中未跟踪的 a.txt
文件将被删除。但我们也可以说它被 HEAD
中跟踪的 a.txt
文件覆盖了。