This is not a general question about what '--' does, as in the marked duplicate. This is a git-specific question asking for clarity on what the operational differences are between the mentioned commands.
If I want to clean out my current directory without stashing or committing, I usually use these commands:
git reset HEAD --hard
git clean -fd
A co-worker also mentioned using this command:
git checkout -- .
It's a difficult command to google, and it's not clear to me from the git documentation what this command actually does. It seems to be one of the later-mentioned usages in the manual.
At a guess it replicates git reset HEAD --hard
, but what exactly does it do as compared to the commands I'm already using?
Does it replicate one or both of the commands, or is it similar yet subtly different?
First, let's just address the double hyphen or double dash, to get it out of the way (especially since this question no longer has a marked duplicate).
Git mostly uses this in the POSIX-approved fashion (see Guideline 10), to indicate a dividing line between option arguments and non-option arguments. Since
git checkout
accepts branch names, as ingit checkout master
, and also file (path) names, as ingit checkout README.txt
, you can use the--
to force Git to interpret whatever comes after the--
as a file name, even if it would otherwise be a valid branch name. That is, if you have both a branch and a file namedmaster
:will check out the branch, but:
will check out the file (confusingly, from the current index).
Branches, index, and files, oh my
Next, we need to address a quirk of
git checkout
. As one can see from the documentation, there are many "modes" ofgit checkout
(the documentation lists six separate invocations in the synposis!). There are various rants (of varying quality: Steve Bennet's is actually useful, in my opinion, though naturally I do not agree with it 100% :-) ) about Git's poor "user experience" model, including the fact thatgit checkout
has too many modes of operation.In particular, you can
git checkout
a branch (to switch branches), orgit checkout
one or more files. The latter extracts the files from a particular commit, or from the index. When Git extracts files from a commit, it first copies them to the index, and then copies them from the index, to the work-tree.There is an underlying implementation reason for this sequence, but the fact that it shows through at all is a key element. We need to know a lot about Git's index, because both
git checkout
andgit reset
use it, and sometimes in different ways.It's a good idea, I think, to draw a three-way diagram or table illustrating the current—or
HEAD
—commit, the index, and the work-tree. Suppose that:README.md
andfile.txt
;git add
-ed but uncommittednew.txt
;rmd.txt
that has beengit rm
-ed but not committed;untr.txt
.Each entity—the
HEAD
commit, the index, and the work-tree—holds three files right now, but each holds a different set of files. The table of the entire state then looks like this:There are many more possible states than just these: in fact, for each file-name, there are seven possible combinations of "in/not-in" HEAD, index, and work-tree (the eighth combination is "not in all three", in which case, what file are we even talking about in the first place?!).
The
checkout
andreset
commandsThe two commands you're asking about,
git checkout
andgit reset
, are both able to do many things. The specific invocations of each, however, reduce the "things done" to one of two, to which I will add several more:git checkout -- .
: copies from index, to work-tree, onlygit checkout HEAD -- .
: copies from HEAD, to index, and then to work-treegit reset --mixed
: resets index from HEAD (and then leaves work-tree alone)git reset --hard
: resets index from HEAD, then resets work-tree from indexThese overlap a lot, but there are several crucially-different parts.
Let's consider the file named
new.txt
above in particular. It's in the index right now, so if we copy from the index, to the work-tree, we replace the work-tree copy with the index copy. This is whatgit checkout -- new.txt
does, for instance.If, instead, we start by copying from
HEAD
to the index, nothing happens tonew.txt
in the index:new.txt
doesn't exist inHEAD
. Hence an explicitgit checkout HEAD -- new.txt
just fails, while agit checkout HEAD -- .
copies the files that are inHEAD
and leaves the two existingnew.txt
versions undisturbed.The file
rmd.txt
is gone from the index, so if wegit checkout -- .
, Git does not see it and does nothing about it. But if wegit checkout HEAD -- .
, Git copiesrmd.txt
fromHEAD
into the index (now it's back) and then from the index to the work-tree (and now it's back there, too).The
git reset
command has a key difference when used with no path name arguments. Here, it literally re-sets the index to match the commit. That means that fornew.txt
, it notices that the file is not inHEAD
, so it removes the index entry. If used with--hard
, it therefore also removes the work-tree entry. Meanwhilermd.txt
is inHEAD
, so it copies that back to the index, and with--hard
, to the work-tree as well.If there are unstaged, i.e., work-tree only, changes to the other two files
README.md
andfile.txt
, both forms ofgit checkout
and the--hard
form ofgit reset
wipe out those changes.If there are staged changes to those files—changes that have been copied into the work-tree—then
git reset
un-stages them. So does the variant ofgit checkout
where you give it the nameHEAD
. However, the variant ofgit checkout
where you copy the index files back to the work-tree keeps those staged changes staged!Top level vs current directory
Last, it's worth noting that
.
, meaning the current directory, may at any time be different from "top of Git repository":Here, I am in the
Documentation
sub-directory of the top level directorygit
, so.
means everything inDocumentation
and its subdirectories. Usinggit checkout -- .
will check out (from the index) all theDocumentation
andDocumentation/RelNotes
files, but not any of the../builtin
files, for instance. Butgit reset
, when used without path names, will reset all entries, including those for..
and../builtin
.