ChatGPT解决这个技术问题 Extra ChatGPT

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

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

添加

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

https://i.stack.imgur.com/C4BCo.png

添加 3

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

https://i.stack.imgur.com/EYijy.png

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

https://i.stack.imgur.com/6F3ZK.png

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

V
VonC

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 开关(例如,如果您从另一个 SVN 存储库 URL 签出相同的分支)

所有这三个工作树修改(svn checkoutupdateswitch)在 git 中只有一个命令:git checkout
但是由于 git 也有索引的概念(即“暂存区”之间的repo 和工作树),您还有 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 指向的分支,checkout 将 HEAD 本身移动到另一个分支。

https://i.stack.imgur.com/M6HAw.png

不过,在这些方面:

LarsH 添加 in the comments

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

De Novo同意in the comments

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


我会说 git reset 是关于修改分支“标签”并可选地更新索引或工作树作为副作用。 git checkout 是关于更新工作树和切换当前“选定”的分支(HEAD)。
@MikkoRantalainen 不。 git reset 是关于 HEAD 的 100%。它甚至可以在分离的 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
t
thSoft

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

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

git reset

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

git checkout

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

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

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

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

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

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

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


说“结帐”不会改变索引是不正确的:当它从一个分支转到另一个时它会改变它。
在最简单的形式中,reset 会在不触及工作树的情况下重置索引,而 checkout 会在不触及索引的情况下更改工作树。 :那是多么令人困惑:|
J
John Doe

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


完美的答案。谢谢你。
是的,除了关于索引和工作树的精确但过于冗长的评论之外,也以同样简单的方式说,重置(重置索引 = 在分支上提交),而结帐基本上改变了一个分支(不管它可以创建一个临时分支从提交 = 分离的头)
L
LarsH

简而言之,主要区别在于 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
Ф
Филя Усков

简短的助记符:

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

M
Mo.

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

checkout X 不是 reset --hard X

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


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

以下是对歧义的澄清:

git checkout 会将 HEAD 移动到另一个提交(也可以是使用分支名称的更改),但是:在您所在的任何分支上,指向该分支尖端的指针(例如,“main”)将保持不变(因此您可能最终处于分离的头部状态)。此外,暂存区和工作目录将保持不变(处于结帐前的相似状态)。

在任何分支上,指向该分支尖端的指针(例如,“main”)将保持不变(因此您可能最终处于分离的头部状态)。

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

例子:

git checkout 3ad2bcf <--- checkout to another commit
git checkout another-branch <--- checkout to another commit using a branchname

git reset 也移动了 HEAD,但是同样有两个不同之处:它也移动了指向当前分支尖端的提交的指针。例如,假设指向当前分支的指针名为“main”,然后执行 git-reset,现在主指针将指向另一个提交,HEAD 也将指向该提交(基本上, HEAD 通过指向主指针间接指向该提交,它仍然是附加的头(!),但在这里没有任何区别)。 Git-reset 不一定会使暂存区和工作目录保持在执行重置之前的相同状态。如您所知,有三种类型的重置:软、混合(默认)和硬:通过软重置,暂存区和工作目录都保持在重置之前的状态(类似于 checkout in这方面,但不要忘记区别#1)。混合重置是默认类型的重置,除了差异#1,暂存区建议的下一次提交(基本上你已经 git 添加的内容)也将设置为新指向的 HEAD犯罪。但是在工作目录中,所有文件仍然会有你对它们的最新编辑(这就是为什么这种类型的重置是默认的,这样你就不会丢失你的工作)。通过硬重置,除了差异 #1 之外,所有三棵树 HEAD、staging-area 和 ALSO 工作目录都将更改为新指向的 HEAD 提交。

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

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

使用软重置,暂存区和工作目录都保持在重置之前的状态(在这方面类似于 checkout,但不要忘记区别#1)。

混合重置是默认类型的重置,除了差异#1,暂存区建议的下一次提交(基本上你已经 git 添加的内容)也将设置为新指向的 HEAD犯罪。但是在工作目录中,所有文件仍然会有你对它们的最新编辑(这就是为什么这种类型的重置是默认的,这样你就不会丢失你的工作)。

通过硬重置,除了差异 #1 之外,所有三棵树 HEAD、staging-area 和 ALSO 工作目录都将更改为新指向的 HEAD 提交。

例子:

git reset --soft 3ad2bcf
git reset da3b47