Why do we use "git checkout" to discard file changes?

When you’re using git and you want to discard your local changes to a file, this is how git recommends you do it:

use "git checkout -- <file>..." to discard changes in working directory
This instruction is found in the output from running "git status".

I’ve used this command for years, but I’ve never really understood why it uses “git checkout”. Isn’t checkout for switching branches? Why not something like “discard” or “reset” or some other command that sounds more like I’m trying to undo something.

Well, today I learned why.

The “git status instructions” fail to mention an optional argument for git checkout. You can see that argument in the help files:

git checkout <tree-ish> -- <pathspec>

What on earth is <tree-ish>? Well, it can mean a bunch of things but it most commonly refers to a revision or branch name. Here’s what a file checkout looks like with a branch name specified:

git checkout mybranch -- index.html

This makes more sense, because now we are saying: “I want to check out some code from mybranch, but instead of grabbing the entire branch, I just want one file (index.html) from that branch.” And that’s what happens: your existing index.html is replaced by the index.html, as it exists on mybranch.*

When you omit the branch name, git just assumes you mean your current branch:

git checkout -- index.html
This replaces your changed file with the version that exists on your current branch (or HEAD)

So checkout makes sense after all. And now, with your mental model of checkout restored, you can go forth and checkout files, folders, or entire branches, from any point in git’s history.

*There’s a small difference: when you specify a branch name, git also adds the file to your index. This behavior is inconsistent with every other checkout, but it’s pretty minor so I’m going to try not to think about it too much. 😅