Every time a file has been staged, Git offers helpful instructions in the event you needed to unstage a file:
(use "git reset HEAD <file>..." to unstage)
However the decent Git Tutorials by Atlassian simply say:
git reset <file>
This seems more straightforward, so why the difference?
No difference (from git reset
man page) in term of default parameter:
The
<tree-ish>/<commit>
defaults toHEAD
in all forms.
That message initially did not include HEAD: commit 3c1eb9c, Jan. 2007, git 1.5.0-rc1, but since the default is not always known, the help message makes it clear to which commit you are supposed to reset.
HEAD
appears in 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 in the comments:
By specifying
HEAD
, you guarantee that the first word afterHEAD
is taken as a path name.
For instance, suppose you rungit reset zorg
. Iszorg
a tree-ish, such as a tag name, or is it a path name,./zorg
?
Git's answer is: it's a tree-ish ifgit rev-parse
can turn it into a tree ID, otherwise it's a path.
You can either writegit reset -- zorg
orgit reset HEAD zorg
to make sure that git treats it as a path.
See more on the double hyphen syntax ( --
) in "Deleting a badly named git branch".
The OP skube adds in the comments:
As an aside, they do suggest it for discarding changes in working directory
(i.egit checkout -- <file>
).
It just seems inconsistent withgit reset HEAD <file>
.
While git reset
man page clearly indicates the lack of tree-ish in git reset <tree-ish> -- <paths>
means HEAD, it is not so for 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).
That means git checkout -- path
will override the working tree with what has already been staged (git add
'ed).
While git reset -- PATH
(being the mixed form of git reset) will reset the index with what HEAD contains (effectively un-staging what was added)
git reset
and git checkout
don't use the same default, and:
git reset <tree-ish> <file>
: HEAD
.git reset HEAD <file>
;git checkout
: it is the index.git checkout -- file
.The --
has to be used in the git checkout
case, since there is only one parameter, and it needs to be clear that parameter represents files.
Note that git checkout HEAD files
is different: torek mentions in the comments
git checkout HEAD path
copies from theHEAD
commit (the tree-ish) to the index and then on to the working dir.
Note: with Git 2.23+, August 2019, you might use git restore
instead
See 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
man page:
git restore --staged hello.c
does not specify a source, and restore the index only (--staged
): it does so (by default) using HEAD as source.
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.
Other examples:
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
is a more natural command name, and has no ambiguity.