“ git reset”和“ git checkout”有什么区别?


440

从某种意义上说,我git reset和我一直都git checkout一样,都将项目带回到特定的提交。但是,我觉得它们不可能完全相同,因为那将是多余的。两者之间的实际区别是什么?我有点困惑,因为svn只svn co需要还原提交。

添加

VonC和查尔斯解释之间的差异git resetgit 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 

在此处输入图片说明



回复:“是错误的还是过度简化的?” 是的,第一张图对于检出和重置之间的差异具有误导性。(关于-- files变体,可能还可以;我不确定。)该图使其看起来主要区别在于它们是影响索引还是WD。看到我对此的回答。第二和第三张图对于了解实际差异非常有帮助。第4和第5个图对于检查您是否了解这些命令的功能很有用,但并不能真正帮助您到达那里。
LarsH

我发现“ Git Tools Reset Demystified”的“签出”部分提供了最有用的摘要。
乔西亚·约德

1
prosseek:如果您同意@LarsH的第一个图表具有误导性,可以将其删除吗?
约西亚·约德

请注意,检出和重设仅模拟基准库的第二部分,并且需要其他步骤(在链接的think-like-a-git.net文章中提供)来防止数据丢失。
cowlinator

Answers:


198
  • 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 checkoutupdateswitch)只有一个在git的命令:git checkout
但是由于git也具有索引的概念(存储库和工作树之间的“临时区域”),因此您也具有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并不意味着您已经与HEAD分离,这意味着head与分支ref分离了,例如,您可以从中看到git log --pretty=format:"%d" -1

  • 连接头的状态将开始(HEAD ->
  • detached仍将显示(HEAD,但没有指向分支引用的箭头。

7
我要说的git reset是关于修改分支“标签”并有选择地更新索引或工作树作为副作用。git checkout关于更新工作树并切换当前“选定”的分支(HEAD)。
Mikko Rantalainen

2
@MikkoRantalainen不。git reset是的100%HEAD。即使在分离的HEAD模式(stackoverflow.com/a/3965714/6309)下,它也可以工作,这意味着没有分支(!)。git checkout在分离的HEAD模式下也可以使用,或者可以在分离的HEAD模式下用于检出SHA1:在这种情况下,也没有分支。
VonC

3
进一步阅读由搜索引擎发送给这里的​​所有迷失的灵魂,我认为这是值得的:git-scm.com/blog/2011/07/11/reset.html
Thinkeye 2014年

2
@Thinkeye很好的参考。我将它以及相关的摘录包括在答案中,以提高可见性。
VonC

2
Reset Demystified的解释很好。但是,此答案的第一段是令人误解的:“ git checkout ... 仅当您签出一个分支时才会更新HEAD(否则,您将得到一个分离的HEAD)”。不正确,即使您检出不是分支的提交, git checkout 也会更新HEAD(是的,您最终得到一个分离的HEAD,但它仍然得到更新)。也许我误会了“更新”的含义?git checkout a839e8f更新HEAD指向提交a839e8f
LarsH

67

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

重置索引以匹配match HEAD,剩下的工作树:

git reset

从概念上讲,这会将索引签出到工作树中。要使其实际执行任何操作,您将不得不-f强制其覆盖任何本地更改。这是一项安全功能,可确保“无参数”形式不具有破坏性:

git checkout

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

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

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

如果你当前有一个分支签出出之间有一个关键的不同resetcheckout当你提供一个选择分支或提交。reset将更改当前分支以指向所选提交,而checkout将单独保留当前分支,但将签出提供的分支或提交。

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

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

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


2
说“结帐”不会更改索引是不正确的:它在用于从一个分支到另一个分支时会更改它。
wiki1000

以最简单的形式,reset重置索引而不触动工作树,而checkout更改工作树而不触动索引。:那是多么令人困惑:|
Aditya Gupta,

41

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


1
完美的答案。谢谢。
user358591

11

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

正如Pro Git本书在“ 重置神秘面纱”下所述

首先reset要做的是移动HEAD指向的位置。这与更改HEAD本身不同checkout确实如此);reset 移动 HEAD指向的分支。这意味着,如果将HEAD设置为master分支(即您当前在master分支上),git reset 9e5e6a4则将master指向指向 开始运行9e5e6a4。[重点添加]

另请参见VonC的答案,以获取同一篇文章中非常有用的文本和图表摘录,在此不再赘述。

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


2
除了我的较旧答案外,反馈很好。+1
VonC

2

这两个命令(重置和签出)完全不同。

checkout X 不是 reset --hard X

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


2
但是,如果X是文件或文件夹,则它们是相同的。
Ted Bigham '17

1

简短的助记符:

git reset HEAD           :             index = HEAD
git checkout             : file_tree = index
git reset --hard HEAD    : file_tree = index = HEAD
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.