I've always thought of git reset
and git checkout
as the same, in the sense that both bring the project back to a specific commit. However, I feel they can't be exactly the same, as that would be redundant. What is the actual difference between the two? I'm a bit confused, as the svn only has svn co
to revert the commit.
VonC and Charles explained the differences between git reset
and git checkout
really well. My current understanding is that git reset
reverts all of the changes back to a specific commit, whereas git checkout
more or less prepares for a branch. I found the following two diagrams quite useful in coming to this understanding:
From http://think-like-a-git.net/sections/rebase-from-the-ground-up/using-git-cherry-pick-to-simulate-git-rebase.html, checkout and reset can emulate the rebase.
git checkout bar
git reset --hard newbar
git branch -d newbar
git reset
is specifically about updating the index, moving the HEAD.git checkout
is about updating the working tree (to the index or the specified tree). It will update the HEAD only if you checkout a branch (if not, you end up with a detached HEAD).git restore
, not necessarily git checkout
) By comparison, since svn has no index, only a working tree, svn checkout
will copy a given revision on a separate directory.
The closer equivalent for git checkout
would:
svn update
(if you are in the same branch, meaning the same SVN URL)svn switch
(if you checkout for instance the same branch, but from another SVN repo URL)All those three working tree modifications (svn checkout
, update
, switch
) have only one command in git: git checkout
.
But since git has also the notion of index (that "staging area" between the repo and the working tree), you also have git reset
.
Thinkeye mentions in the comments the article "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 rungit 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 commitA
, but how we do so is very different.reset
will move the branchHEAD
points to, checkout movesHEAD
itself to point to another branch.
On those points, though:
LarsH adds in the comments:
The first paragraph of this answer, though, is misleading: "
git checkout
... will update the HEAD only if you checkout a branch (if not, you end up with a detached HEAD)".
Not true:git checkout
will update the HEAD even if you checkout a commit that's not a branch (and yes, you end up with a detached HEAD, but it still got updated).git checkout a839e8f updates HEAD to point to commit a839e8f.
De Novo concurs in the comments:
@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.