Code Spirit
Code Spirit

Reputation: 5041

GIT checkout -- twice

When I want to revert changes of a file I use git checkout -- path/to/file.ext. But somehow I have to execute the command twice now.
After the first checkout git diff shows nothing but the file is still listed as modified on my stage. Only after the second checkout the file is removed from the stage.
Is this intended behaviour?

Upvotes: 2

Views: 615

Answers (2)

Marlon Abeykoon
Marlon Abeykoon

Reputation: 12465

You have to git reset the file.

git reset path/to/file.ext

This will unstage a file. git checkout will discard the changes in working directory. In your case if it's staged you can do a reset first to unstage it and then git checkout the file.

It's not the intended behavior of checkout to do it twice to unstage.

Upvotes: 1

torek
torek

Reputation: 487805

The command:

git checkout -- path/to/file.ext

copies the contents of path/to/file.ext from the staging-area to the work-tree. It does not change the contents stored in the staging-area, and running it twice should make no difference at all: copying from staging-area to work-tree, then copying again from staging-area to work-tree, should result in the same work-tree contents on the second copy as on the first.


Note that there is a third version of the file—well, we should really call it the first one—which is the copy that is in the current commit. When you check out some commit, Git:

  • copies the content of each file stored in that commit into your staging-area, and (at the same time)
  • copies the content of each such file into your work-tree.

This means that there are three active versions of the file at all times:

  • HEAD:path/to/file.ext: this one is read-only, permanently stored in the commit, in a special, compressed, Git-only form. Use git show HEAD:path/to/file.ext to see it, for instance.

  • :path/to/file.ext: this one is read/write, and not permanent. It is stored in the staging-area (also called the index, or sometimes the cache), also in a special compressed Git-only form. The difference between this and the first file is that this one, you can overwrite, and whatever is in the index/staging-area copy is what will go into the next commit you make. Use git show :path/to/file.ext to see this copy.

    • Using git add path/to/file.ext copies the work-tree version to the index / staging-area.

    • Using git checkout -- path/to/file.ext copies the index / staging-area version to the work-tree.

    • Using git reset path/to/file.ext copies the HEAD:path/to/file.ext version from the commit to the index / staging-area, without affecting the work-tree version.

  • Last, there is the work-tree version, path/to/file.ext. This file is stored in your computer's normal format, so that you and your computer can work with it.

(If you want to copy the HEAD version of the file to both the index/staging-area and the work-tree, you can do that with git checkout HEAD path/to/file.ext.)


Note that when you run git status, this runs, in effect, two diffs:

  1. HEAD vs index: whatever is different here is "staged for commit".
  2. index vs work-tree: whatever is different here is "not staged for commit".

You never see the index / staging-area directly: you only see it in comparison to either the HEAD commit, or in comparison to the work-tree. This makes sense because, in a large project, the index / staging-area has thousands of files all the time, but usually only a few are different from either the current commit, or the work-tree, or both.

Upvotes: 4

Related Questions