488

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

添加

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

http://a.imageshack.us/img651/1559/86421927.png http://a.imageshack.us/img801/1986/resetr.png

添加 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 

在此处输入图像描述

4

7 回答 7

220
  • git reset具体是关于更新索引,移动 HEAD。
  • git checkout是关于更新工作树(到索引或指定的树)。仅当您签出分支时,它才会更新 HEAD (如果没有,您最终会得到一个分离的 HEAD)。
    (实际上,对于 Git 2.23 Q3 2019,这将是git restore,不一定git checkout

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

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

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


Thinkeye在评论中提到了文章“ Reset Demystified ”。

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

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

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

http://git-scm.com/images/reset/reset-checkout.png

不过,在这些方面:

LarsH在评论中补充道:

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

git checkout a839e8f updates HEAD to point to commit a839e8f.

De Novo同意以下评论

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

  • 附加的头部状态将以(HEAD ->,
  • detached 仍会显示(HEAD,但不会有指向分支引用的箭头。
于 2010-09-03T20:29:42.927 回答
73

最简单的形式reset是在不触及工作树的情况下重置索引,同时checkout在不触及索引的情况下更改工作树。

将索引重置为 match HEAD,工作树保持不变:

git reset

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

git checkout

一旦开始添加参数,确实存在一些重叠。

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

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

如果您当前有一个已签出的分支,那么在您提供替代分支或提交之间reset以及何时提供一个关键的不同。将更改当前分支以指向选定的提交,而将不理会当前分支,但将检查提供的分支或提交。checkoutresetcheckout

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

如果您提供路径,reset您将无法提供--hard并且reset只会将提供的路径的索引版本更改为提供的提交中的版本(或者HEAD如果您没有指定提交)。

如果您提供 的路径checkout,就像reset它会更新提供的路径的索引版本以匹配提供的提交(或HEAD),但它会始终将提供的路径的索引版本检出到工作树中。

于 2010-09-03T20:43:54.367 回答
59

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

于 2016-01-28T02:54:56.533 回答
14

简而言之,关键区别在于reset 移动当前分支引用checkout而不移动(它移动 HEAD)。

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

首先reset要做的是移动 HEAD 指向的内容。这与改变 HEAD 本身不同(这是做什么checkout的);移动HEAD 指向reset 的分支。这意味着如果 HEAD 设置为master分支(即您当前在master分支上),运行git reset 9e5e6a4将首先master指向 9e5e6a4. [重点补充]

另请参阅 VonC 的回答,以获取来自同一篇文章的非常有用的文本和图表摘录,我不会在此重复。

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

于 2018-02-20T18:21:02.287 回答
4

简短的助记符:

git reset HEAD           :             index = HEAD
git checkout             : file_tree = index
git reset --hard HEAD    : file_tree = index = HEAD
于 2019-11-17T06:31:44.037 回答
3

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

checkout X不是reset --hard X

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

于 2015-09-11T16:01:15.550 回答
0

以下是对歧义的澄清:

  • 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(!),但在这里没有任何区别)。

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

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

例子:

git reset --soft 3ad2bcf
git reset da3b47
于 2021-10-02T05:40:13.847 回答