“git重置”和“git重置”有什么区别?还有“git checkout”?

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

添加

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

158865 次浏览
  • git reset专门关于更新索引,移动HEAD。
  • git checkout是关于更新工作树的(指向索引或指定树)。只有当你签出一个分支时,它才会更新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也有索引的概念(即“staging area”;在repo和工作树之间),你也有git reset.


Thinkeye提到在评论中,文章“重置您"

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

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

因此,在这两种情况下,我们都将HEAD移动到指向提交A,但这样做的方式是非常不同的。reset将移动HEAD所指向的分支,checkout将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.

新创并发在评论中:

@LarsH是正确的。
第二个项目对HEAD所在的位置有误解,只有在签出分支时才会更新HEAD。
不管你在哪里,头就像影子一样。
检出一些非分支引用(例如,一个标签),或直接提交,将移动HEAD。分离头并不意味着你已经从head中分离了,它意味着头从一个分支引用中分离了,你可以从,例如git log --pretty=format:"%d" -1.

  • 附加的正面状态将以(HEAD ->开始,
  • detached仍然会显示(HEAD,但不会有指向分支ref的箭头。

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

重置索引以匹配HEAD,工作树保持不变:

git reset

从概念上讲,这会将索引检出到工作树中。为了让它实际做任何事情,你必须使用-f来强制它覆盖任何局部更改。这是一个安全特性,以确保“无参数”形式不具有破坏性:

git checkout

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

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

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

如果你现在签出了一个分支,当你提供一个替代分支或提交时,resetcheckout之间有一个关键的区别。reset将更改当前分支以指向所选的提交,而checkout将保留当前分支,但将检出所提供的分支或提交。

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

如果你将路径提供给reset,你就不能提供--hard,并且reset只会将所提供路径的索引版本更改为所提供提交中的版本(或者HEAD,如果你没有指定提交)。

如果你将路径提供给checkout,比如reset,它将更新所提供路径的索引版本以匹配所提供的提交(或HEAD),但它总是将所提供路径的索引版本签入工作树。

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

__abc0不是__abc1

如果X是分支名, checkout X将改变当前分支 而reset --hard X则不会。< / p >

还原更改时的一个简单用例 1. 如果你想撤销已修改文件的暂存,使用reset 2.

.

.

.

.

简单地说,关键的区别是reset 移动当前分支引用,而checkout没有(它移动HEAD)。

正如Pro Git书在重置您下解释的那样,

reset要做的第一件事是移动HEAD所指向的。这不是 与改变HEAD本身相同(这是checkout所做的);reset HEAD指向的移动分支。这意味着如果设置了HEAD 到master分支(即你目前在master分支上), 运行git reset 9e5e6a4将首先使master指向 9e5e6a4。(重点补充道)< / p >

另请参阅同一篇文章中VonC对非常有用的文本和图表摘录的回答,这里不再重复。

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

短暂的助记符:

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

以下是对歧义的澄清:

  • 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重置也会移动HEAD,然而,再次使用两个的差异:

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

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

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

例子:

git reset --soft 3ad2bcf
git reset da3b47