2020-08-09
|~5 min read
|856 words
The restore
command for Git is still experimental, but is a more intuitive solution to some of the problems I’ve come across regularly.
Similarly to Git’s reset
and revert
commands, restore
is a way to update the working tree’s files. From the documentation, Reset, restore and revert:
There are three commands with similar names: git reset, git restore and git revert.
git-revert
is about making a new commit that reverts the changes made by other commits.
git-restore
is about restoring files in the working tree from either the index or another commit.This command does not update your branch.
The command can also be used to restore files in the index from another commit.
git-reset
is about updating your branch, moving the tip in order to add or remove commits from the branch. This operation changes the commit history.
git reset
can also be used to restore the index, overlapping with git restore.
Notice that revert
and reset
are history altering (making a new commit in former, and moving the tip in the latter), where as restore
does not modify history.
Let’s consider an example of when you’d want to use restore
instead of revert
or reset
.
Imagine making some changes to your branch for some temporary edits, but now you want to get back to the original state of the branch. In the past I’ve used a custom bash function, gsdf
(an abbreviation for “git stash, drop file”) for this purpose. Now, however, this outcome is built directly into Git in the form of the restore
command.
Looking at our current status we can get our bearings:
git status
From the output we can see that I’m editing my notes, which I happen to track with Git:
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: drafts/.ideas.md
modified: notes/exercise-log.md
modified: notes/git-reset-vs-revert-redux.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
drafts/git-restore-vs-reset.md
drafts/temp-untracked.md
no changes added to commit (use "git add" and/or "git commit -a")
The changes to .ideas
, however, can be discarded. Additionally, temp-untracked
was an experiment that I can discard. Note, in the latter’s case, it was never tracked / committed to the branch.
To clear out the changes to .ideas
, I can use restore
:
git restore drafts/.ideas.md
git status
Then, printing the results with our git status
, we can see .ideas
no longer has any changes to be staged:
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: notes/exercise-log.md
modified: notes/git-reset-vs-revert-redux.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
drafts/git-restore-vs-reset.md
drafts/temp-untracked.md
no changes added to commit (use "git add" and/or "git commit -a")
I want to restore
in this case. There are no commits to the .ideas
file that I want to unwind, so revert
wouldn’t make sense. Similarly, I don’t want to change my current HEAD
, I just want to erase the modifications I’ve made to .ideas
, so reset
isn’t appropriate.
This same approach to the temp-untracked
file, however will not work, because it’s never been committed:
git restore drafts/temp-untracked.md
This throws an error:
error: pathspec 'drafts/temp-untracked.md' did not match any file(s) known to git
This makes sense because what I really want to do in the case of temp-untracked
has nothing to do with Git at this point. Git is aware of the file, but it’s not in any commits or logs (which also means revert
and reset
wouldn’t work either). Instead, getting rid of that file is done by deleting the file:
rm drafts/temp-untracked.md
git status
Which, as we might expect, removes the file and therefore removes the reference from Git
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: notes/exercise-log.md
modified: notes/git-reset-vs-revert-redux.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
drafts/git-restore-vs-reset.md
For what it’s worth, gsdf
would also not work for this file for the exact same reason: Git doesn’t know about it!
Restore is significantly more powerful than the basic example shown here and I intend to continue to explore different methods of leveraging its power in time. For now, however, it’s always nice to continue to refine my understanding of the differences between reset
and revert
(which I’ve written about here and here before) and now add a new arrow to the quiver in the form of restore
.
Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!