【问题标题】:What's the difference between "git reset" and "git checkout"?“git reset”和“git checkout”有什么区别?
【发布时间】:2011-04-08 01:10:52
【问题描述】:

我一直认为git resetgit checkout 是相同的,因为两者都将项目带回特定的提交。但是,我觉得它们不可能完全相同,因为那将是多余的。两者之间的实际区别是什么?我有点困惑,因为 svn 只有 svn co 来恢复提交。

添加

VonC 和 Charles 很好地解释了 git resetgit checkout 之间的区别。我目前的理解是git reset 将所有更改恢复到特定提交,而git checkout 或多或少为分支做准备。我发现以下两个图表对理解这一点非常有用:

增加了 3 个

http://think-like-a-git.net/sections/rebase-from-the-ground-up/using-git-cherry-pick-to-simulate-git-rebase.html,结帐和重置可以模拟变基。

git checkout bar 
git reset --hard newbar 
git branch -d newbar 

【问题讨论】:

  • 回复:“它是错误的还是过于简化了?”是的,第一个图表在结帐和重置之间的区别方面具有误导性。 (关于-- files 变体可能没问题;我不确定。)该图看起来主要区别在于它们是否影响索引或WD。请参阅我的回答。第 2 和第 3 图表对于查看真正的差异非常有帮助。第 4 和第 5 个图表有助于检查您是否了解这些命令的作用,但并不能真正帮助您实现目标。
  • 我发现“Git Tools Reset Demystified”的 "Check it out" 部分给出了最有用的总结。
  • prosseek:如果你同意@LarsH 第一张图有误导性,你能把它删掉吗?
  • 请注意,checkout 和 reset 只模拟 rebase 的第二部分,并且需要额外的步骤(在链接的think-like-a-git.net 文章中提供)以防止数据丢失。

标签: git git-checkout git-reset


【解决方案1】:
  • git reset 专门用于更新索引,移动 HEAD。
  • git checkout 是关于更新工作树(到索引或指定的树)。只有当您签出一个分支时,它才会更新 HEAD(如果没有,您最终会得到一个 detached HEAD)。
    (实际上,在 Git 2.23 Q3 2019 中,这将是 git restore,不一定是 git checkout

相比之下,由于 svn 没有索引,只有工作树,svn checkout 会将给定的修订版复制到单独的目录中。
git checkout 更接近的等价物是:

  • svn update(如果在同一个分支,表示同一个SVN URL)
  • svn switch(例如,如果您从另一个 SVN 存储库 URL 签出相同的分支)

所有这三个工作树修改(svn checkoutupdateswitch)在 git 中只有一个命令:git checkout
但是由于 git 也有索引的概念(repo 和工作树之间的“暂存区”),所以你也有git reset


Thinkeye 提到了in the comments 文章“Reset Demystified ”。

例如,如果我们有两个分支,'master' 和 'develop' 指向不同的提交,并且我们当前在 'develop' 上(所以 HEAD 指向它)并且我们运行 @ 987654355@, 'develop' 本身现在将指向与 'master' 相同的提交。

另一方面,如果我们改为运行git checkout master,'develop' 不会移动,HEAD 本身会移动。 HEAD 现在将指向“master”。

因此,在这两种情况下,我们都将HEAD 移动到提交A,但我们这样做的方式非常不同。 reset 将移动分支 HEAD 指向,结帐移动 HEAD 自身指向另一个分支。

不过,在这些方面:

LarsH 添加in the comments

但是,此答案的第一段具有误导性:“git checkout ... 仅当您签出分支时才会更新 HEAD(如果没有,您最终会得到一个分离的 HEAD)”。
不正确:git checkout 将更新 HEAD,即使您签出不是分支的提交(是的,您最终会得到一个分离的 HEAD,但它仍然得到了更新)。

git checkout a839e8f updates HEAD to point to commit a839e8f.

De Novo 同意in the comments

@LarsH 是正确的。
第二个项目符号对 HEAD 的内容存在误解,只有在您签出分支时才会更新 HEAD。
HEAD 像影子一样随处可见。
签出一些非分支引用(例如,标签)或直接提交,将移动 HEAD。分离的头部并不意味着你已经从 HEAD 分离,这意味着头部从分支 ref 分离,你可以从例如git log --pretty=format:"%d" -1 中看到。

  • 附加的头部状态将以(HEAD ->开头,
  • 分离后仍会显示(HEAD,但不会有指向分支引用的箭头。

【讨论】:

  • 我想说git reset 是关于修改分支“标签”并可选地更新索引或工作树作为副作用。 git checkout 是关于更新工作树和切换当前“选定”的分支(HEAD)。
  • @MikkoRantalainen 不。 git reset 是 100% 关于 HEAD。它甚至可以在分离的 HEAD 模式 (stackoverflow.com/a/3965714/6309) 下工作,这意味着存在 no 分支(!)。 git checkout 也可以在分离的 HEAD 模式下工作,或者可用于在分离的 HEAD 模式下签出 SHA1:在这种情况下再次不涉及分支。
  • 搜索引擎发来的所有迷失灵魂的延伸阅读,我认为值得:git-scm.com/blog/2011/07/11/reset.html
  • @Thinkeye 很好的参考。我已将它与相关摘录一起包含在答案中以提高知名度。
  • Reset Demystified 的解释非常好。但是,此答案的第一段具有误导性:“git checkout ... 仅当您签出分支时才会更新 HEAD(如果没有,您最终会得到一个分离的 HEAD)”。不正确... git checkout 即使您签出不是分支的提交也会更新 HEAD (是的,您最终会得到一个分离的 HEAD,但它仍然得到了更新)。也许我误解了您所说的“更新”是什么意思? git checkout a839e8f 更新 HEAD 以指向提交 a839e8f
【解决方案2】:

在最简单的形式中,reset 在不接触工作树的情况下重置索引,而checkout 在不接触索引的情况下更改工作树。

重置索引以匹配HEAD,工作树单独保留:

git reset

从概念上讲,这会将索引检出到工作树中。要让它真正做任何事情,你必须使用-f 来强制它覆盖任何本地更改。这是一项安全功能,可确保“无参数”形式不具有破坏性:

git checkout

一旦开始添加参数,确实会有一些重叠。

checkout 通常与分支、标签或提交一起使用。在这种情况下,它会将HEAD 和索引重置为给定的提交,并将索引签出到工作树中。

此外,如果您将--hard 提供给reset,您可以要求reset 覆盖工作树以及重置索引。

如果您当前有一个已签出的分支,那么当您提供替代分支或提交时,resetcheckout 之间存在重大差异。 reset 将更改当前分支以指向选定的提交,而 checkout 将不理会当前分支,但将检查提供的分支或提交。

其他形式的resetcommit 涉及提供路径。

如果您提供reset 的路径,则无法提供--hardreset 只会将提供的路径的索引版本更改为提供的提交中的版本(或HEAD,如果您不指定提交)。

如果您提供checkout 的路径,例如reset,它将更新提供的路径的索引版本以匹配提供的提交(或HEAD),但它总是将提供的路径的索引版本检出到工作树。

【讨论】:

  • 说“checkout”不会改变索引是不真实的:当从一个分支转到另一个分支时它会改变它。
  • 在最简单的形式中,reset 会在不触及工作树的情况下重置索引,而 checkout 会在不触及索引的情况下更改工作树。 :这是多么令人困惑:|跨度>
【解决方案3】:

还原更改时的一个简单用例:
1. 如果要撤消已修改文件的暂存,请使用重置。
2. 如果您想放弃对未暂存文件的更改,请使用 checkout。

【讨论】:

  • 完美答案。谢谢。
  • 是的,除了关于索引和工作树的精确但过于冗长的 cmets 之外,还以同样简单的方式说,重置(重置索引 = 在分支上提交),而 checkout 基本上改变了一个分支(没有重要的是它可以从 commit = detached head 创建一个时间分支)
【解决方案4】:

简而言之,主要区别在于reset 移动当前分支引用,而checkout 没有(它移动 HEAD)。

正如 Pro Git 书籍在 Reset Demystified 下解释的那样,

reset 会做的第一件事是移动 HEAD 指向的位置。这不是 与 更改 HEAD 本身 相同(checkout 所做的); reset 移动 HEAD 指向的分支。这意味着如果设置了 HEAD 到master 分支(即您当前在master 分支上), 运行git reset 9e5e6a4 将首先使master 指向 9e5e6a4。 [强调]

另请参阅 VonC 对同一篇文章中 very helpful text and diagram excerpt 的回答,我不会在此重复。

当然,还有更多关于checkoutreset 对索引和工作树的影响的详细信息,具体取决于使用的参数。这两个命令之间可能有很多相似之处和不同之处。但在我看来,最关键的区别是它们是否移动了当前分支的尖端。

【讨论】:

  • 很好的反馈,除了我的旧答案。 +1
【解决方案5】:

简短的助记符:

git reset HEAD           :             index = HEAD
git checkout             : file_tree = index
git reset --hard HEAD    : file_tree = index = HEAD

【讨论】:

    【解决方案6】:

    这两个命令(reset 和 checkout)是完全不同的。

    checkout X 不是 reset --hard X

    如果 X 是分支名称, checkout X 将改变当前分支 而reset --hard X 不会。

    【讨论】:

    • 但是如果X是一个文件或文件夹,那么它们是一样的。
    • 我认为你错了。使用“checkout X”,您只需跳转到分支 X。使用“reset --hard X”,您将丢弃当前分支上 X 之后的所有更改。因此,在后一种情况下,您当前的分支将被更改,而不是在前一种情况下。如果我错了,请纠正我。
    【解决方案7】:

    这里是对歧义的澄清:

    • git checkout 会将 HEAD 移动到另一个提交(也可以使用分支名称进行更改),但是
      1. 在任何分支上,指向该分支尖端的指针(例如,“main”)将保持不变(因此您最终可能会处于 分离的头部状态)。

      2. 此外,暂存区和工作目录将保持不变(与结帐前的状态相似)。

    示例:

    git checkout 3ad2bcf <--- checkout to another commit
    git checkout another-branch <--- checkout to another commit using a branchname
    
    • git reset 也移动了 HEAD,但同样有 两个不同

      1. 它也会移动指向当前分支顶端提交的指针。例如,假设指向当前分支的指针名为“main”,然后执行 git-reset,现在主指针将指向另一个提交,而 HEAD 也将指向该提交(基本上, HEAD 通过指向主指针间接指向那个提交,它仍然是一个附加的head(!),但在这里没有任何区别。 p>

      2. Git-reset 不一定会使暂存区和工作目录保持在执行重置之前的相同状态。如您所知,有三种类型的重置:软、混合(默认)和硬:

        • 通过软重置,暂存区和工作目录都保持在重置前的状态(在这方面类似于 checkout,但不要忘记区别 #1)。
        • 混合重置是默认类型的重置,除了差异#1,暂存区的建议的下一次提交(基本上你已经 git 添加的)也将被设置到新指向的 HEAD 提交。但是在工作目录中,所有文件仍然会有你对它们的最新编辑(这就是为什么这种类型的重置是默认的,这样你就不会丢失你的工作)。
        • 通过硬重置,除了差异 #1 之外,所有三棵树 HEAD、暂存区域和 ALSO 工作目录都将更改为新指向的 HEAD 提交。

    示例:

    git reset --soft 3ad2bcf
    git reset da3b47
    

    【讨论】:

      猜你喜欢
      • 2017-12-10
      • 2012-01-11
      • 2011-09-06
      • 2011-10-18
      • 1970-01-01
      • 2021-07-04
      • 2011-02-02
      • 2015-01-17
      相关资源
      最近更新 更多