(使用“git reset HEAD …”取消暂存)
然而,Atlassian的Git教程只是简单地说:
git reset 这似乎更加直接,那么为什么会有差异呢?
在默认参数方面,与git reset
手册页面无差异:
<tree-ish>/<commit>
在所有形式中都默认为HEAD
。
该消息最初没有包括 HEAD: 2007年1月3c1eb9c提交, git 1.5.0-rc1, 但由于默认值不总是已知的,帮助信息明确说明了应该将其重置为哪个提交。
HEAD
出现在2007年11月367c988提交,Git 1.5.4中:
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
请参考 "删除一个命名不当的 git 分支" 中关于双短横线语法 (通过指定
HEAD
,你保证在HEAD
后面的第一个单词被视为路径名。
例如,假设你运行git reset zorg
。那么zorg
是一个树对象吗,比如标签名,还是一个路径名,./zorg
?
Git 的答案是:如果git rev-parse
可以将其转换为树 ID,则它是一个树对象,否则它是一个路径名。
你可以写成git reset -- zorg
或者git reset HEAD zorg
来确保 Git 将其视为路径。
--
) 的更多信息。
顺便说一下,他们建议使用这种方法来丢弃工作目录中的更改
(即git checkout -- <file>
)。
这似乎与git reset HEAD <file>
不一致。
虽然git reset
手册清楚地指出,在git reset <tree-ish> -- <paths>
中缺少树对象时意味着 HEAD,但git checkout <tree-ish> -- <paths>
则不是这样。
git checkout <tree-ish> -- <pathspec>
<paths>
时,git checkout
不会切换分支。
它会从索引文件或命名的<tree-ish>(通常是提交)中更新工作树中的指定路径。
这意味着git checkout -- path
会用已经暂存(git add
)的内容覆盖工作区。
而git reset -- PATH
(作为git reset的mixed形式)将使用HEAD所包含的内容重置索引(有效地取消暂存已添加的内容)。
git reset
和git checkout
没有使用相同的默认值,因此:
git reset <tree-ish> <file>
,你可以表示默认树: HEAD
,因此使用git reset HEAD <file>
;git checkout
时,你无法表示默认参数,它是索引。因此使用git checkout -- file
。这里必须使用--
,因为git checkout
有一个参数,为了明确该参数是代表文件,需要使用--
。
请注意git checkout HEAD files
与之不同: torek在评论中提到了此事
git checkout HEAD path
会将HEAD
提交(树状物)复制到索引,然后再复制到工作目录。
git restore
代替。
请参见示例:
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作为源。
默认情况下,恢复工作树和索引的源分别是索引和HEAD。
可以使用--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
是一个更自然的命令名称,没有歧义。
HEAD
,您可以确保在 HEAD
后的第一个单词被视为路径名。例如,假设您运行了 git reset zorg
。zorg
是树对象,如标签名,还是路径名 ./zorg
?Git 的答案是:如果 git rev-parse
可以将其转换为树对象 ID,则它是树对象,否则它是路径名。您可以写成 git reset -- zorg
或 git reset HEAD zorg
来确保 Git 将其作为路径名处理。 - torekgit reset -- <file>
和 git reset HEAD <file>
在功能上是相同的,并且两者都比简单的 git reset <file>
更安全一些,为什么 git 建议使用 HEAD
版本呢?当然,输入 --
比输入 HEAD
更容易。顺便说一句,他们建议在工作目录中放弃更改(即 git checkout -- <file>
)。这似乎有些不一致。 - skube<treeish>
的语法。 git reset -- <file>
完全掩盖了这一点。 - VonCgit checkout
命令时,--
版本 (git checkout -- path
) 会告诉 git 从索引中复制(提取索引版本到工作目录),而 git checkout HEAD path
则会从 HEAD
提交中复制到索引,然后再复制到工作目录。我发现这个特定的差异特别令人困惑,但由于 git reset
是重置索引,所以实际上是有道理的。 - torek默认情况下,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 ofgit add <paths>
.
从这里可以看出行为没有实际上的区别。
这似乎更加直接,那么差异在哪里呢?
既然它们是相同的,你可以使用两个中最短的。
第一次提交之前,HEAD 不存在,然后我们得到:
$git reset HEAD stagedFile
fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree
git restore --staged -- afile
这个命令,自Git 2.23(2019年8月)起可用。详见我下面编辑后的回答。 - VonC