Say I have a git repo with a modified file named readme.txt, and I want to undo the modifications I've made and set it back to the state of the most recent commit. It seems there are two commands to do this:
git checkout -- readme.txt
and
git restore readme.txt
Is there any difference between these two commands? I've noticed that git status
offers the advice "(use "git restore <file>..." to discard changes in working directory)
". Is this the canonical way to undo uncommitted modifications? Are both ways equally valid and popular?
The old git checkout command could overwrite unsaved work: if your typo turns a branch name into a file name, for instance, well, oops. git revert (I added this for completeness): this makes a new commit. The point of the new commit is to back out what someone did in some existing commit.
git restore: this got split off from git checkout. Basically, this does the same thing as the various forms of git checkout and git reset that clobber files (in your working tree and/or in Git's index).
Actually, git restore and git checkout commands will remove all changes made after the last commit by default or in simple words, the file will be restored as per the last commit. Now we need to say explicitly to git checkout command to revert changes for a commit.
--hard – The staged snapshot and the working directory are both updated to match the specified commit. The git checkout command is used to update the state of the repository to a specific point in the projects history. When passed with a branch name, it lets you switch between branches.
git restore
is a new command that has been introduced (last summer) in Git 2.23 together with git switch
. Their purposes are to simplify and separate the use cases of git checkout
that does too many things.
git checkout
can be used to switch branches (and also to create a new branch before switching to it). This functionality has been extracted into git switch
.
git checkout
can also be used to restore files to the state they were on a specified commit. This functionality has been extracted into git restore
.
They can still be performed by git checkout
but the new commands are easier to use and less confusing.
To summarize,
git restore readme.txt
is a new way to do what you used to do with:
git checkout -- readme.txt
As stated in the docs:
or you can restore both the index and the working tree (this the same as using git-checkout1)
It's a convenient way of doing the same thing. (first version in Git 2.23)
Blog post from GitHub about highlights of Git 2.23:
Git 2.23 brings a new pair of experimental commands to the suite of existing ones:
git switch
andgit restore
. These two are meant to eventually provide a better interface for the well-knowngit checkout
. The new commands intend to each have a clear separation, neatly divvying up what the many responsibilities ofgit checkout
, as we’ll show below.
I like the two answers on this at the time I write mine—both axiac's answer and kapsiR's, but the way I would put it is this:
git checkout
combines too many commands into one front end. Some of these commands are "safe", in that if you have uncommitted work, they won't destroy it, and some of these are "unsafe", in that if you have uncommitted work and tell them destroy my uncommitted work, they will do that.
git switch
implements the "safe" subset.
git restore
implements the "unsafe" subset.
So far, there's no particular reason to favor either the old single command or the new pair of commands. But we add one more item:
git checkout name
may execute either the unsafe one or the safe one, depending on something you might not even be thinking about!Now, this last bullet point is fixed in the latest Git versions. Suppose you have a remote-tracking origin/dev
name and you'd like to create branch dev
accordingly. Normally, you could just run:
git checkout dev
Suppose, though, that your current (probably master
) checkout has a directory named dev
, and in that directory, you've done a bunch of work and have not yet committed it. You just now remembered: I should commit all this work I just did on the dev
branch, not the master
branch.
Now, there's no existing dev
branch at all, but there are dev/
files. Here's what an old Git does:
sh-3.2$ git --version
git version 2.20.1
sh-3.2$ git branch -r
origin/dev
(that is, origin/dev
exists; branch dev
doesn't and I'm on master
here)
sh-3.2$ git status --short
M dev/file
sh-3.2$ git diff
diff --git a/dev/file b/dev/file
index e69de29..c238a0b 100644
--- a/dev/file
+++ b/dev/file
@@ -0,0 +1 @@
+look at all this work I did
Adding that line took weeks of hard work! :-)
sh-3.2$ git checkout dev
Uh oh. Why didn't it tell me about creating a branch named dev
set up to track origin/dev
?
sh-3.2$ git status
On branch master
nothing to commit, working tree clean
Gah! My hard work is all gone!
The problem here is that git checkout
ran the unsafe command. I might have been expecting it to use the safe one ... but it didn't.
A more modern Git (2.24.0) will tell me that git checkout dev
is ambiguous, which is good: it does not just clobber my file. (I have not tested Git 2.23 itself, where the split-into-two-commands first went in.)
Anyway, using the new commands, you at least know, right as you type in the command, whether you will get the safe mode or the unsafe one. If your habits are set and you keep using git checkout
, or you're concerned about the note that says that these new commands are still experimental, you can still use the old one—and it no longer just wipes out work, in the ambiguous case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With