Git reset vs checkout
1. Reset
1.1. 3 trees
There are three "trees" that git keeps track of:
- the staging area or working index: this is where files get put when you
git add
. Note that once in the staging area, a file is frozen in the moment it was staged. That is, if I modify a file with changes so it is in state \(A\), stage it, and then modify it to be in state \(B\), then the file will still be in state \(A\) in the staging area. Think of the staging area as holding a snapshot of a change. - the working directory: this is what you see when you open a file with
vim
or some other editor. All the changes, even if they are unstaged or uncommitted, appear here. Note that only tracked files appear in git's working directory. When we begin tracking a file withgit add
, it gets staged immediately. - the commit history: these are the files, frozen in a snapshot, at a particular commit
Note that reset
behaves differently depending on whether it is called on the file level or commit level.
1.2. 3 modes of git reset
git reset
is used to move the branch tip and HEAD
back to a certain commit.
There are three modes to reset
:
1.2.1. soft
--soft
only sets the git commit back to the specified commit. It doesn't change the index or the working directory. When do you want to use this? Whenever, you realize that the past few commits may have been a mistake, so you want to trash them, but you also want to keep your current work, maybe as scratch work or to see what you can salvage.
---before--- master, head | A--B--C ---after--- master, head | A--B--C
1.2.2. mixed
--mixed
will move the branch and HEAD
to the specified commit and will set the staging area to be the snapshot at the specified commit. But it won't change anything in the working directory.
tl;dr – your working directory will stay the same, but you'll lose everything in the staging area
1.2.3. hard
--hard
will do everything that --mixed
does, but it will ALSO set the working tree to be the same as the specified commit.
tl;dr – your working directory, staging area, and commit history will now all match the snapshot in the specified commit
1.3. file level
When we git reset
on a file, we will set its state in the staging area to match its state in the snapshot at a given commit. The HEAD
and branch pointer remain where they are. In the special case where we have staged some changes to a file, and then use reset
to set it to match its state in HEAD
, we are effectively unstaging our changes. The file in the working directory will still have the changes, but they won't be in the staging area.
The modes soft
, hard
, mixed
have no effect on the file level behavior.
2. Checkout
git checkout
also differs between the file and commit levels.
2.1. commit level
git checkout
moves the HEAD
to the specified commit. The working directory will be changed to reflect that of the commit. If there are any uncommitted changes in your current working directory, git
will ask you to commit them so they don't get overwritten.
Note that the staging area is set to match that of the snapshot in the specified commit
2.2. file level
This overwrites the file in your working directory with the file in the specified commit. Note that it also resets your staging area to reflect your current commit (see here).
In the special case where the commit is HEAD
, this trashes all the changes you've made in the working directory for a file. We can also use git restore
to accomplish the same effect.