Difference between "git checkout -." and "git checkout -."
I am a little confused with these two options when using "git checkout"
I've always used git checkout -- .
to clear the working directory.
but today when I type git checkout - .
by mistake. I didn't find any error warning from git.
I've read the git documentation and also don't know what this option does -
. And that's hard for Google.
So please, does anyone have this?
source to share
When used, git checkout
you can use -
as shorthand for @{-1}
.
From man git-checkout
(highlighting the second paragraph):
<branch>
Branch to checkout; if it refers to a branch (i.e., a name that, when
prepended with "refs/heads/", is a valid ref), then that branch is checked
out. Otherwise, if it refers to a valid commit, your HEAD becomes
"detached" and you are no longer on any branch (see below for details).
As a special case, the "@{-N}" syntax for the N-th last branch/commit
checks out branches (instead of detaching). You may also specify - which is
synonymous with "@{-1}".
Try this on an empty repo by creating a file on a branch master
, modifying it on another branch and using checkout -
from master:
$ git init
Initialized empty Git repository in workspace/git-test/.git/
git:(master) $ echo a > a
git:(master*) $ git add a
git:(master*) $ git commit
[master (root-commit) 8433343] Add a to master
1 file changed, 1 insertion(+)
create mode 100644 a
git:(master) $ git checkout -b other
Switched to a new branch 'other'
git:(other) $ echo b > a
git:(other*) $ git add a
git:(other*) $ git commit
[other be2298f] Replace a by b
1 file changed, 1 insertion(+), 1 deletion(-)
git:(other) $ git checkout -
Switched to branch 'master'
git:(master) $ git checkout -- . # no changes (no * next to master in the line below)
git:(master) $ git checkout - . # checking out files from alternate branch 'other'
git:(master*) $ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: a
git:(master*) $ cat a
b
source to share
UPDATE . I can reproduce this behavior now, but that is because I followed the information Mart received in their answer.
The main thing I would add: for diagnostic purposes, if you are not sure about the action of a git command, for example checkout
, use git status
and see what. Since it checkout
mainly affects the index and the working tree, you should be able to at least see what the command changed in this way.
Synopsis: You stumbled upon a less common syntax that almost certainly didn't do what you intended (and certainly can't count on what you have to do).
Original Answer
I cannot reproduce the behavior you described. In particular, if I say git checkout - .
, I get an error, a nonzero return code and no validation occurs.
-
should be interpreted as a path specification (and the error I get reflects this); another possibility would be "tree" (which means branch, tag, commit id, etc.); but is -
not a valid branch name.
So, if you have a tracked file named in your working directory -
, git will just update that file and not report any warnings / errors.
I'm not sure what you mean by "hard to google"; if you need help with a git command just run a google command (like 'git checkout`) and at least in my experience this should lead to the final git documentation for that command (which includes the command syntax which should tell you how the given argument will be interpreted).
In this case when I google the git checkout
first result is https://git-scm.com/docs/git-checkout
source to share
As in Marth's answer (which is correct and I supported it), we start with what git checkout -
is the synonym git checkout @{-1}
that Git says: Look at my HEAD reflog and find which branch I was on before I ran git checkout branchname
and check that forking again. The general idea behind this syntax is you can:
$ git checkout feature/X
... hack for a while ...
$ git commit -m "save some temporary work"
$ git checkout bug1234
... do a quick fix or look for something ...
$ git checkout -
... now back on feature/X ...
Unfortunately this does not fit well with the general command format git checkout [ tree-ish ] [ -- ] path [path ...]
, which I think means:
- If tree-ish is specified, find it as a tree (i.e., if committed, convert it to the commit source tree, if it is a tag, clear the tag until we reach the tree). Otherwise (no tree specified), extract the files from the index.
-
--
: This part is optional and serves to separate the tree, if present, from any path arguments. We need this if we have, for example, a named filezorg
that we want to check out from the index, but also have a named branchzorg
. If we wrotegit checkout zorg
insteadgit checkout -- zorg
, Git would try to checkout the branch. - path: at least one must be specified; these are the specific files to extract (from the index or from the ish tree). When retrieving files from the index, Git writes them to the work tree (not the index, because they are already in the index). However, when retrieving files from the tree, Git first writes them to the index and then to the work tree.
Thus, when we write:
git checkout -- f1 f2 f3
Git sees it --
as meaning "no branch and therefore not tree-like, so use an index"; so the different filenames are files to be retrieved from the index to overwrite them in the tree.
One of the reasons why we can do this - is to edit f1
, f2
and f3
to think that they were good. We then git add
released them to copy these versions into the index. Then we edited them some more ... but decided that the versions of the work tree were bad and that we want the indexed versions to return to the work tree. These index versions are not yet committed: they are only stored in the index.
(Another reason we can use git checkout -- f1 f2 f3
is that we edited them, but not git add
ed. In this case, the versions of the index were identical to the versions HEAD
... and therefore, in this case, we are "great: we can just re- extract versions HEAD
.)
Problem
Unfortunately, if we seal it like:
git checkout - f1 f2 f3
then Git treats it as if we had entered:
git checkout @{-1} f1 f2 f3
which means finding the previous branch with reflogs and then fetching files from that branch tip into the index and then into the work tree. This overwrites the versions of the indexes that we planned to restore (if, for some reason, the versions to be restored are not the same as the versions of the previous versions).
Recovery
There is a way to get versions with a single index back , but this is quite painful: run git fsck --lost-found
and then search the Git hash filenames in .git/lost-found/other
. There are probably a lot (the actual number depends on the number of dangling drops you have in your repository). Three of them - but it is not said which three without careful examination of each recovered blob file - are our three versions of the files f1
, f2
and f3
which we wanted to return.
Musings
I have often thought that there git checkout
should be at least two and possibly three or more separate commands:
- One to checkout a branch or commit: this will have standard security checks. It would always be safe to run
git checkout foo
as it will never overwrite any files;git checkout --force foo
will only be dangerous. - One to check files from the index. (Actually, there is such a command, viz
git checkout-index
. You can use that insteadgit checkout
. This is the plumbing command, so it is not that user-centered and hurts more.) This might be possible as wellgit checkout
, although it is a bit dangerous as it might overwrite work of a tree that simplegit checkout
does and won't. For this can be the right china chinagit co-index
. - One to check specific files from specific commits. Since this would be written in different ways (perhaps
git co-tree
or some of them), although it is "dangerous" (overwriting existing files and / or index entries), it would be clear that we are not using the "safe" varietygit checkout
. The tree view here would not be optional, so it would be harder to make the-
vs error--
(which is why it should probably be a separate command fromgit co-index
).
But that's not what we have.
source to share