git 重置 vs git 重置 HEAD

git reset vs git reset HEAD

每次暂存文件时,Git 都会在您需要取消暂存文件时提供有用的说明:

(use "git reset HEAD <file>..." to unstage)

不过体面的Git Tutorials by Atlassian简单的说:

git reset <file>

这看起来更直接,为什么不同?

在默认参数方面没有区别(与git reset man page):

The <tree-ish>/<commit> defaults to HEAD in all forms.

该消息最初 not 包含 HEAD:commit 3c1eb9c, Jan. 2007, git 1.5.0-rc1,但由于默认值 not 始终为人所知,帮助消息清楚地表明您应该重置哪个提交。

HEAD 出现在 commit 367c988, Nov. 2007, Git 1.5.4:

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)

torek points out an actual difference :

By specifying HEAD, you guarantee that the first word after HEAD is taken as a path name.
For instance, suppose you run git reset zorg. Is zorg a tree-ish, such as a tag name, or is it a path name, ./zorg?
Git's answer is: it's a tree-ish if git rev-parse can turn it into a tree ID, otherwise it's a path.
You can either write git reset -- zorg or git reset HEAD zorg to make sure that git treats it as a path.

在“Deleting a badly named git branch”中查看有关双连字符语法 (--) 的更多信息。




OP skube adds :

As an aside, they do suggest it for discarding changes in working directory
(i.e git checkout -- <file>).
It just seems inconsistent with git reset HEAD <file>.

虽然 git reset man page 清楚地表明 git reset <tree-ish> -- <paths> 中缺少树结构表示 HEAD,但 git checkout <tree-ish> -- <paths> 并非如此。

git checkout <tree-ish> -- <pathspec>

When <paths> are given, git checkout does not switch branches.
It updates the named paths in the working tree from the index file or from a named <tree-ish> (most often a commit).

这意味着 git checkout -- path 将使用已经上演(git add 编辑)的内容覆盖工作树。
虽然 git reset -- PATH(作为 git 重置的混合形式)将重置 索引 与 HEAD 包含的内容(有效地取消暂存添加的内容)

git resetgit checkout 不使用相同的默认值,并且:

  • 您可以表示 git reset <tree-ish> <file> 的默认树:HEAD
    因此 git reset HEAD <file>;
  • 但是当你不为 git checkout 提供树时,你不能表示默认参数:它是索引。
    因此 git checkout -- file.

git checkout的情况下必须使用--,因为只有一个参数,需要明确的是参数代表files.

注意git checkout HEAD files是不同的:torek mentions

git checkout HEAD path copies from the HEAD commit (the tree-ish) to the index and then on to the working dir.


注意:对于 Git 2.23+,2019 年 8 月,您可以使用 代替

参见the examples:

To restore a file in the index to match the version in HEAD (this is the same as using git-reset)

$ git restore --staged hello.c

手册页:

git restore --staged hello.c 未指定源,仅恢复索引 (--staged):它这样做(默认情况下)使用 HEAD 作为源。

By default, the restore sources for working tree and the index are the index and HEAD respectively.
--source could be used to specify a commit as the restore source.

其他示例:

You can restore both the index and the working tree (this the same as using git-checkout)

$ git restore --source=HEAD --staged --worktree hello.c

or the short form which is more practical but less readable:

$ git restore -s@ -SW hello.c

git restore是比较自然的命令名,没有歧义。

默认情况下,git reset等同于git reset HEAD

引用手册页(我的重点):

git-reset - Reset current HEAD to the specified state.

git reset [-q] [<tree-ish>] [--] <paths>…
git reset (--patch | -p) [<tree-ish>] [--] [<paths>…​]
git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]

In the first and second form, copy entries from <tree-ish> to the index. In the third form, set the current branch head (HEAD) to <commit>, optionally modifying index and working tree to match. The <tree-ish>/<commit> defaults to HEAD in all forms.

[...]

git reset [-q] [<tree-ish>] [--] <paths>…​

This form resets the index entries for all <paths> to their state at <tree-ish>. (It does not affect the working tree or the current branch.)

This means that git reset <paths> is the opposite of git add <paths>.

由此您可以看出行为上没有实际差异。

This seems more straightforward, so why the difference?

既然它们是一样的,你不妨使用两者中最短的版本。

第一次,在任何提交之前 HEAD 不存在,然后我们得到:

$git reset HEAD stagedFile
fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree