"git checkout - . "和"git checkout -- ."之间的区别

  • 本文关键字:checkout git 区别 之间 git
  • 更新时间 :
  • 英文 :


使用"git checkout"时,我对这两个选项感到有些困惑

我总是使用git checkout -- .来清除我的工作目录。

但是今天当我错误地输入git checkout - .时。我没有发现来自 git 的错误警报。

我已经阅读了 git 文档,也不知道这个-选项的作用。 这很难谷歌。

所以请有人对此有任何想法吗?

使用git checkout时,可以使用-作为@{-1}的简写。
man git-checkout(强调第二段):

<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}".

通过在分支master中创建文件,在另一个分支中修改它并使用 master 中的checkout -,在空存储库中尝试此操作:

$ 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

更新- 我现在可以重现该行为,但那是因为我遵循了 Marth 在他们的答案中提供的信息。

我要补充的主要内容是:出于诊断目的,如果您不确定像checkout这样的 git 命令的效果,请使用git status看看是什么。 由于checkout主要影响索引和工作树,因此您至少应该能够看到命令以这种方式更改的内容。

总结是你偶然发现了一个不太常见的语法,几乎可以肯定它没有做你想要做的事情(当然也不能指望它做你想做的事情)。


原答案

我无法重现您描述的行为。 具体来说,如果我说git checkout - .我收到错误,则返回代码为非 0,并且不会发生结帐。

-应该被解释为路径规范(我得到的错误反映了这一点);另一种可能性是"树式"(这意味着分支、标签、提交 ID 等);但-不是有效的分支名称。

因此,如果您碰巧在工作目录中有一个名为-的跟踪文件,git 只会刷新该文件并报告任何警告/错误。

我不确定你所说的"难以谷歌"是什么意思;如果你需要git命令的帮助,只需谷歌命令(例如'git checkout'),至少根据我的经验,这应该会导致该命令的权威git文档(其中包括命令语法,它应该告诉你如何解释给定的参数)。

在这种情况下,当我谷歌git checkout时,第一个结果是 https://git-scm.com/docs/git-checkout

就像 Marth 的答案一样(这是正确的,我已经投了赞成票),我们从git checkout -git checkout @{-1}的同义词的概念开始,它告诉 Git:查看我的 HEAD 引用日志,找到我在运行git checkoutbranchname之前所在的分支,然后再次检查该分支。此语法背后的一般思想是,您可以执行以下操作:

$ 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 ...

不幸的是,这与一般的git checkout [tree-ish] [ -- ]path[path...]命令格式结合得很差,我想你知道,这意味着:

如果
  • 指定了tree-ish,则将其查找为树(即,如果它是一个提交,则将其转换为提交的源树;如果它是一个标签,则剥离标签,直到我们到达一棵树)。 否则(未指定树形),从索引中提取文件。
  • --:这部分是可选的,用于将树形(如果存在)与任何路径参数分开。 例如,如果我们有一个名为zorg的文件,我们想从索引中提取它,但也有一个名为zorg的分支,我们需要这个。 如果我们写git checkout zorg而不是git checkout -- zorg,Git 会尝试检查分支。
  • path:必须至少指定一个;这些是要提取的特定文件(从索引或从树中提取)。 从索引中提取文件时,Git 将它们写入工作树(而不是索引,因为它们已经在索引)。 但是,当从树中提取文件时,Git 首先将它们写入索引,然后再写入工作树。

因此,当我们写:

git checkout -- f1 f2 f3

Git 将--视为"没有分支,因此没有树,所以使用索引";因此,各种文件名是从索引中提取的文件,以覆盖工作树中的内容。

我们这样做的一个原因是我们已经编辑了f1f2f3,并认为它们很好。 然后,我们git add编辑它们以将这些版本复制到索引中。 然后我们又编辑了它们一些...但是决定工作树版本不好,我们希望索引版本回到工作树中。 这些索引版本尚未在任何提交中:它们存储在索引中。

(我们可能使用git checkout -- f1 f2 f3的另一个原因是我们已经编辑了它们,但没有git add编辑它们。 在这种情况下,索引版本与HEAD版本相同...因此在这种情况下,我们很好:我们可以重新提取HEAD版本。

问题所在

不幸的是,如果我们将其拼写为:

git checkout - f1 f2 f3

然后 Git 会像我们输入的那样处理它:

git checkout @{-1} f1 f2 f3

这意味着通过 reflogs 查找前一个分支,然后将该分支的提示提交中的文件提取到索引中,然后提取到工作树中。这将覆盖我们打算还原的索引版本(除非那些"打算还原"的版本与以前的分支版本匹配)。

恢复

有一种方法可以恢复仅在索引中的版本,但这非常痛苦:运行git fsck --lost-found然后搜索 Git 写入.git/lost-found/other的哈希命名文件。 可能有很多(实际数量取决于存储库中悬空的斑点的数量)。 其中三个 - 但如果不仔细检查每个恢复的blob文件,就不知道是哪三个 - 是我们想要返回的三个版本的文件f1f2f3

沉思

我经常认为git checkout应该至少是两个,可能是三个或更多,单独的命令:

  • 一个用于检查分支或提交:这个将具有标准的安全检查。 运行git checkout foo始终是安全的,因为它永远不会覆盖任何文件;只有git checkout --force foo才是危险的。
  • 一个用于从索引中签出文件。 (实际上,有这样一个命令,即git checkout-index. 您可以使用它代替git checkout. 不过,这是一个管道命令,因此它不是面向用户的,使用起来更痛苦。这也许也是git checkout,尽管它有点危险,因为它可以覆盖工作树工作,而普通git checkout可以和不会这样做。 前端瓷git co-index可能是正确的名称。
  • 一个用于从特定提交中签出特定文件。 由于拼写方式不同(可能是git co-tree或类似),因此即使它是"危险的"(覆盖现有文件和/或索引条目),很明显我们没有使用"安全"的git checkout。 这里的不是可选的,所以很难犯---错误(这就是为什么它可能应该是与git co-index分开的命令)。

但这不是我们所拥有的。

最新更新