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

添加

VonC和Charles很好地解释了git reset和git checkout之间的区别。我目前的理解是git重置将所有的更改恢复到特定的提交,而git签出或多或少为分支做准备。我发现下面两张图对理解这个问题非常有用:

增加了3

从http://think-like-a-git.net/sections/rebase-from-the-ground-up/using-git-cherry-pick-to-simulate-git-rebase.html,签出和重置可以模拟rebase。

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


当前回答

git reset是关于更新索引,移动HEAD。 Git签出是关于更新工作树(到索引或指定的树)。只有当您签出一个分支时,它才会更新HEAD(否则,您最终会得到一个分离的HEAD)。 (实际上,对于Git 2.23 Q3 2019,这将是Git恢复,而不一定是Git检出)

相比之下,由于svn没有索引,只有一个工作树,svn checkout将把给定的修订版复制到一个单独的目录上。 git签出的类似内容如下:

svn update(如果你在同一个分支,意味着相同的svn URL) svn switch(如果您签出同一个分支,但是从另一个svn repo URL)

所有这三种工作树修改(svn checkout, update, switch)在git中只有一个命令:git checkout。 但是因为git也有索引的概念(在repo和工作树之间的“暂存区域”),你也需要git重置。


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

For instance, if we have two branches, 'master' and 'develop' pointing at different commits, and we're currently on 'develop' (so HEAD points to it) and we run git reset master, 'develop' itself will now point to the same commit that 'master' does. On the other hand, if we instead run git checkout master, 'develop' will not move, HEAD itself will. HEAD will now point to 'master'. So, in both cases we're moving HEAD to point to commit A, but how we do so is very different. reset will move the branch HEAD points to, checkout moves HEAD itself to point to another branch.

不过,在这些方面:

LarsH在评论中补充道:

然而,这个答案的第一段是误导性的:“git checkout…只有当你签出分支时才会更新HEAD(如果没有,你最终会得到一个分离的HEAD)”。 不对:git checkout会更新HEAD,即使你签出了一个不是分支的提交(是的,你最终得到了一个分离的HEAD,但它仍然被更新了)。 git checkout a839e8f更新HEAD指向提交a839e8f。

新竞赛在评论中:

@LarsH is correct. The second bullet has a misconception about what HEAD is in will update the HEAD only if you checkout a branch. HEAD goes wherever you are, like a shadow. Checking out some non-branch ref (e.g., a tag), or a commit directly, will move HEAD. Detached head doesn't mean you've detached from the HEAD, it means the head is detached from a branch ref, which you can see from, e.g., git log --pretty=format:"%d" -1. Attached head states will start with (HEAD ->, detached will still show (HEAD, but will not have an arrow to a branch ref.

其他回答

git reset是关于更新索引,移动HEAD。 Git签出是关于更新工作树(到索引或指定的树)。只有当您签出一个分支时,它才会更新HEAD(否则,您最终会得到一个分离的HEAD)。 (实际上,对于Git 2.23 Q3 2019,这将是Git恢复,而不一定是Git检出)

相比之下,由于svn没有索引,只有一个工作树,svn checkout将把给定的修订版复制到一个单独的目录上。 git签出的类似内容如下:

svn update(如果你在同一个分支,意味着相同的svn URL) svn switch(如果您签出同一个分支,但是从另一个svn repo URL)

所有这三种工作树修改(svn checkout, update, switch)在git中只有一个命令:git checkout。 但是因为git也有索引的概念(在repo和工作树之间的“暂存区域”),你也需要git重置。


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

For instance, if we have two branches, 'master' and 'develop' pointing at different commits, and we're currently on 'develop' (so HEAD points to it) and we run git reset master, 'develop' itself will now point to the same commit that 'master' does. On the other hand, if we instead run git checkout master, 'develop' will not move, HEAD itself will. HEAD will now point to 'master'. So, in both cases we're moving HEAD to point to commit A, but how we do so is very different. reset will move the branch HEAD points to, checkout moves HEAD itself to point to another branch.

不过,在这些方面:

LarsH在评论中补充道:

然而,这个答案的第一段是误导性的:“git checkout…只有当你签出分支时才会更新HEAD(如果没有,你最终会得到一个分离的HEAD)”。 不对:git checkout会更新HEAD,即使你签出了一个不是分支的提交(是的,你最终得到了一个分离的HEAD,但它仍然被更新了)。 git checkout a839e8f更新HEAD指向提交a839e8f。

新竞赛在评论中:

@LarsH is correct. The second bullet has a misconception about what HEAD is in will update the HEAD only if you checkout a branch. HEAD goes wherever you are, like a shadow. Checking out some non-branch ref (e.g., a tag), or a commit directly, will move HEAD. Detached head doesn't mean you've detached from the HEAD, it means the head is detached from a branch ref, which you can see from, e.g., git log --pretty=format:"%d" -1. Attached head states will start with (HEAD ->, detached will still show (HEAD, but will not have an arrow to a branch ref.

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

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

git reset

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

git checkout

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

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

同样,如果你很难重置,你可以要求重置覆盖工作树以及重置索引。

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

其他形式的重置和提交涉及提供路径。

如果你提供重置路径,你不能提供——hard和reset只会将所提供路径的索引版本更改为所提供提交中的版本(如果你没有指定提交,则更改为HEAD)。

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

以下是对歧义的澄清:

git checkout会将HEAD移动到另一个提交(也可以使用branchname进行更改),但是: 在任何分支上,指向该分支顶端的指针(例如。,“main”)将保持不变(因此您可能最终处于分离的头部状态)。 同样,暂存区域和工作目录将保持不变(处于签出之前的类似状态)。

例子:

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

git reset also moves the HEAD, however again, with two differences: It moves the pointer that points to the commit at the tip of the current branch too. For instance, let's say the pointer to the current branch is named "main", then you perform a git-reset, now, the main pointer will point to another commit, and the HEAD will point to that commit as well(well basically, HEAD points to that commit indirectly through pointing to the main pointer, it is still an attached head(!), but it doesn't make any difference here). Git-reset doesn't necessarily leave the staging area and the working directory on the same state they were in before the reset was performed. As you know, there are three types of reset: soft, mixed(default) and hard: With the soft reset, the staging area and the working directory both remain in the state they've been on before the reset(similar to checkout in this regard, but don't forget the difference #1). With the mixed reset which is the default type of reset, in addition to difference #1, the staging area's proposed next commit(what you've git-added basically), will also be set to the newly pointed-to-by-HEAD commit. BUT in the working directory, all the files will still have your latest edits to them (that's why this type of reset is the default one, so that you don't lose your work). With the hard reset, in addition to difference #1, all the three trees HEAD, staging-area and ALSO the working-directory will change to the newly pointed-to-by-HEAD commit.

例子:

git reset --soft 3ad2bcf
git reset da3b47

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

结帐X不是重置-硬X

如果X是分支名, checkout X将改变当前的分支 而重置-硬X将不会。

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