I have taken a look at this stackoverflow link but I think the subtle difference between what I am asking is the usage of HEAD
within the checkout cmd as their suggestions don't' seem to be working :
Is there a difference between git reset --hard HEAD and git checkout .?
git checkout HEAD -- .
cleans out my staging area as well.
Additionally, the second answer, regarding deleted files added to staging area,
seem to be coming back with git checkout HEAD -- .
Is there a situation where one would get different results?
Yes, there is a difference, besides that implied by
.
if you're not at the top level of your repository. I won't go so far as to claim that what follows is the only difference; it suffices to show one example, to see that they are different.Set-up for illustrating a difference
Start by creating a new repository with at least one commit (here I have made two, as is my habit):
At this point,
HEAD
and the index match—both contain the content from commitf505609
—and the work-tree contains the (normal format) files that match that commit as well. Now let's add a new file, and copy it into the index:Technically, the
git add foo
created blob objecta9e2570d6af8c05b57e2cefecaebeedfabc98bf2
in the repository and then put that hash ID into the index:(The hash ID of this blob object is predictable due to the known content for file
foo
. That's true of the other three files as well, but they're actually committed, so those blob objects are permanent. The one forfoo
could be GCed, if we never actually commit it and instead remove the entry from the index.)Using
git checkout HEAD
If we use
git checkout HEAD
, we direct Git to copy fromHEAD
into the index and then expand them into normal work-tree files.HEAD
contains three files (README
,file-a
, andfile-b
), so this does that and updates the three work-tree files with the contents they already have—so there's no observable effect.1Note that file
foo
remains, both in the index (rungit ls-files
again to see) and in the work-tree.1Unless, that is, we inspect things like file modification times or system calls executed, via whatever OS-level tools we have available. In this case we can tell if Git really did overwrite the work-tree files or not. On my system, it actually didn't, because the index hashes matched the
HEAD
hashes and thestat
data cached in the index matched thestat
data from the work-tree files, so it didn't bother. But in principle Git copiedHEAD
to the index, and then the index to the work-tree, and if it were necessary based on hashes and/or stat data, Git would have actually touched the work-tree files here.Using
git reset --hard
If we tell Git to reset the index to match the current commit, and reset the work-tree to match changes to the index, the action is different. This time, Git examines the index and sees that file
foo
is present, while it's absent in the commit. So Git removes filefoo
from the index, and updates the work-tree accordingly:File
foo
has vanished from the work-tree.If we were to use
git reset --mixed HEAD
, Git would removefoo
from the index, but not from the work-tree. (The default action for this kind of reset—there are many other kinds—is--mixed
.)Using
git restore
With the new Git 2.23+
git restore
command, we can control the index and work-tree separately. First we have to putfoo
back into the index and work-tree:We can now choose whether to copy
HEAD
to the index or not, and whether to manage the work-tree similarly. Its documentation is a bit more explicit too:What it means for a path to be "tracked" is that the path is in the index. In this case,
foo
is in the index now (due to thegit add
) so it is tracked. If we restore the index fromHEAD
,foo
will be removed from the index, just as withgit reset --hard
orgit reset --mixed
. So let's try VonC's command, but with.
(the current directory and all sub-directories)2 as the pathname, here:So you can see that this had the same effect as
git reset --hard
. Unlikegit reset
,git restore
has only one job—though with two parts to it—so we need not worry about other modes of operation.(This is why both
git switch
andgit restore
were added: the mostly do the same things you can already do withgit checkout
andgit reset
, but they only have one job, even if it has several parts. By contrast,git checkout
has anywhere from about three to about seven different jobs, depending on how you count, andgit reset
has anywhere from about three to about five.3)2This particular repository has only the one top level directory, so we need not worry that you've done a
cd subdir
within the work-tree. If you had, though,.
would mean apply this to thesubdir/*
files, so that checkout and reset would be even more different.3For
git checkout
, consider:git checkout-index
)git checkout -m
with a file name)git checkout -m
but with a branch name)While that's just five, we can
git checkout --ours
andgit checkout --theirs
and some might wish to count these as separate from the usual "extract from index" flavor. We can get even more when you add create branch (git checkout -b
) and forcibly reset branch (git checkout -B
). Alas,git switch
has the create and forcibly-reset options as well!Some might, of course, lump some or all of these into one operation, just as
git checkout
does. That's why I say "depending on how you count".For
git reset
, consider:HEAD
HEAD
HEAD
)HEAD
)git reset -p
(cannot moveHEAD
)all of which are lumped under the one command
git reset
.To avoid the confusion, you should use the new experimental command
git restore
(Gti 2.23+, August 2019).As I explained in "What is
git restore
Command ? What is the different betweengit restore
andgit reset
?", you can specify what to restore withgit restore
.This is more explicit than relying on HEAD or no HEAD in
git checkout
.To do a
reset --hard
withgit restore
:or the short form, which is more practical but less readable: