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

时间:2010-09-03 20:21:43

标签: git git-checkout git-reset

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

ADDED

VonC和Charles非常清楚地解释了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,结帐和重置可以模拟rebase。

enter image description here

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

enter image description here

7 个答案:

答案 0 :(得分:170)

  • git reset专门针对更新索引,移动HEAD。
  • git checkout是关于更新工作树(指向索引或指定的树)。只有在结账时才会更新HEAD(如果没有,最后会得到detached HEAD)。

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

  • svn update(如果您在同一分支中,即相同的SVN网址)
  • svn switch(如果你签收的是同一个分支,但来自另一个SVN repo URL)

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


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

  

例如,如果我们有两个分支,“master”和“develop”指向不同的提交,我们当前处于“develop”(因此HEAD指向我们运行git reset master,'develop'本身现在将指向'master'所做的相同提交。

     

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

     

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

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

在这些方面,但是:

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,这意味着头部与分支参考分离,你可以从中看到,例如git log --pretty=format:"%d" -1

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

答案 1 :(得分:61)

以最简单的形式,reset重置索引而不触及工作树,而checkout更改工作树而不触及索引。

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

git reset

从概念上讲,这会将索引检出到工作树中。要让它实际执行任何操作,您必须使用-f强制它覆盖任何本地更改。这是一个安全功能,以确保“无论证”形式不具有破坏性:

git checkout

一旦开始添加参数,就会出现一些重叠。

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

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

如果您当前已检出分支,则在提供备用分支或提交时,resetcheckout之间存在重要差异。 reset会将当前分支更改为指向所选提交,而checkout将仅保留当前分支,但会检出提供的分支或提交。

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

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

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

答案 2 :(得分:27)

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

答案 3 :(得分:9)

Atlassian为我们提供了关于 git reset git checkout 以及 git revert 的出色解释。在本文中,将解释这些命令在不同级别上的不同用法 - 文件,暂存快照和提交。

https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting

答案 4 :(得分:8)

简而言之,关键区别是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对索引和工作树的影响,具体取决于使用的参数。两个命令之间可能存在许多相似之处和不同之处。但正如我所看到的,最关键的区别在于它们是否会移动当前分支的尖端。

答案 5 :(得分:1)

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

checkout X不是reset --hard X

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

答案 6 :(得分:1)

简短助记符:

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