git restore

Let's review the different use cases of the git restore command

The git restore command is used to revert files in the working directory to the state they were in at a specific commit.

It was added to Git in version 2.23.0 and is intended to replace the git checkout command for the use cases described below.

This article explains how the git restore command helps to simplify the git checkout command that has way too many usages.

The git restore command does not modify any commits, so it can be safely used in a multi-user environment.

Please note that, as stated in the documentation, this command is experimental and its behavior may change.

initial state

Let’s begin with a file file.txt in a clean repository:

alina

alina git:( main ) echo "A" > file.txt

alina git:( main ) git add file.txt

alina git:( main ) git commit -m 'A'

[main bc57535] A

 1 file changed, 1 insertion(+)

 create mode 100644 file.txt

alina git:( main )

Both the working directory and the index are clean and synchronized.

alina

alina git:( main ) git status

On branch main

Your branch is ahead of 'origin/main' by 1 commit.

  (use "git push" to publish your local commits)

nothing to commit, working tree clean

alina git:( main ) cat file.txt

A

alina git:( main )

restore before git add

Now, let’s make some changes to that file.

alina

alina git:( main ) echo "B" >> file.txt

alina git:( main ) cat file.txt

A

B

alina git:( main )

Running git status will indicate that we are out of sync.

alina

alina git:( main ) git status

On branch main

Your branch is ahead of 'origin/main' 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:   file.txt

no changes added to commit (use "git add" and/or "git commit -a")

alina git:( main )

As shown by the output of git status, the git restore command will revert the file to the state it was in at the last commit.

alina

alina git:( main ) git restore file.txt

alina git:( main ) cat file.txt

A

alina git:( main )

The git status command will confirm that we are once again in sync.

alina

alina git:( main ) git status

On branch main

Your branch is ahead of 'origin/main' by 1 commit.

  (use "git push" to publish your local commits)

nothing to commit, working tree clean

alina git:( main )

git restore after a git add and a git commit

Let’s modify the file again, but this time we will stage the changes and create a commit.

alina

alina git:( main ) echo "B" >> file.txt

alina git:( main ) git add file.txt

alina git:( main ) git commit -m "B" file.txt

[main ec94472] B

 1 file changed, 1 insertion(+)

alina git:( main ) cat file.txt

A

B

alina git:( main )

Running git status will show that we are in sync.

alina

alina git:( main ) git status

On branch main

Your branch is ahead of 'origin/main' by 2 commits.

  (use "git push" to publish your local commits)

nothing to commit, working tree clean

alina git:( main )

Now, let’s try using the git restore command again:

alina

alina git:( main ) git restore file.txt

alina git:( main )

Nothing happens because the file is already in sync with the index.

alina

alina git:( main ) cat file.txt

A

B

alina git:( main )

The content of the index is as follows:

alina

alina git:( main ) git ls-files --stage

100644 0c50cb1098bda2e5c32fe2667cb64453434edc8f 0       README.md

100644 35d242ba79ae89ac695e26b3d4c27a8e6f028f9e 0       file.txt

alina git:( main )

And we can see that the hash of file.txt matches the one in the index.

alina

alina git:( main ) git hash-object file.txt

35d242ba79ae89ac695e26b3d4c27a8e6f028f9e

alina git:( main )

git restore —staged

The git restore --staged command will revert the file in the index to the state it was in at the last commit.

alina

alina git:( main ) git restore --staged file.txt

alina git:( main )

Our file in the working directory remains unchanged.

alina

alina git:( main ) cat file.txt

A

B

alina git:( main )

Since our changes were already committed, the index is also left unchanged.

alina

alina git:( main ) git status

On branch main

Your branch is ahead of 'origin/main' by 2 commits.

  (use "git push" to publish your local commits)

nothing to commit, working tree clean

alina git:( main )

alina

alina git:( main ) git ls-files --stage

100644 0c50cb1098bda2e5c32fe2667cb64453434edc8f 0       README.md

100644 35d242ba79ae89ac695e26b3d4c27a8e6f028f9e 0       file.txt

alina git:( main )

git restore with a source

The git restore command can also be used with the --source option to revert the file to a specific commit.

Let’s use HEAD~1 as the source, which refers to the commit before the last one. In that commit, file.txt contained only one line of text.

The git restore --staged --source command will revert the file in the index to the state it was in at the specified commit, leaving the working directory unaffected.

alina

alina git:( main ) git restore --staged --source HEAD~1 file.txt

alina git:( main )

As a result, git status will now report that the working directory is out of sync with the index.

alina

alina git:( main ) cat file.txt

A

B

alina git:( main ) git status

On branch main

Your branch is ahead of 'origin/main' by 2 commits.

  (use "git push" to publish your local commits)

Changes to be committed:

  (use "git restore --staged <file>..." to unstage)

        modified:   file.txt

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:   file.txt

alina git:( main )

And git diff confirms that the file in the index has only one line of text:

alina

alina git:( main ) git diff

diff --git a/file.txt b/file.txt

index f70f10e..35d242b 100644

--- a/file.txt

+++ b/file.txt

@@ -1 +1,2 @@

 A

+B

alina git:( main )

git restore —staged —worktree

Let’s re-add that file and commit it.

alina

alina git:( main ) git add file.txt

alina git:( main ) git commit -m "B2"

On branch main

Your branch is ahead of 'origin/main' by 2 commits.

  (use "git push" to publish your local commits)

nothing to commit, working tree clean

alina git:( main )

Returning to a clean index.

alina

alina git:( main ) git status

On branch main

Your branch is ahead of 'origin/main' by 2 commits.

  (use "git push" to publish your local commits)

nothing to commit, working tree clean

alina git:( main ) cat file.txt

A

B

alina git:( main )

Running the command git restore --staged --worktree --source HEAD~1 file.txt will revert file.txt in both the working directory and the index to the state it was in at the specified commit.

alina

alina git:( main ) git restore --staged --worktree --source HEAD~1 file.txt

alina git:( main )

However, the git status command shows that the file is modified.

alina

alina git:( main ) git status

On branch main

Your branch is ahead of 'origin/main' by 2 commits.

  (use "git push" to publish your local commits)

Changes to be committed:

  (use "git restore --staged <file>..." to unstage)

        modified:   file.txt

alina git:( main )

Even though the file is in sync with the index.

alina

alina git:( main ) cat file.txt

A

alina git:( main ) git diff

alina git:( main )

To investigate further, let’s examine the content of the index:

alina

alina git:( main ) git ls-files --stage

100644 0c50cb1098bda2e5c32fe2667cb64453434edc8f 0       README.md

100644 f70f10e4db19068f79bc43844b49f3eece45c4e8 0       file.txt

alina git:( main )

And display the content of file.txt in the index:

alina

alina git:( main ) git cat-file blob f70f10e4db19068f79bc43844b49f3eece45c4e8f70f10e4db19068f79bc43844b49f3eece45c4e8

A

alina git:( main )

We can observe that the content of the index is the same as the content of the file in the working directory.

alina

alina git:( main ) cat file.txt

A

alina git:( main )

My suspicion is that Git first compares the “modification time” on the file system, which is also stored in the index but not displayed. If the modification time differs between the index and the working directory, then the file is considered modified.

Hence, the file appears as modified even though its content matches the index.

alina

alina git:( main ) git add file.txt

alina git:( main )

git restore can be destructive

Suppose we commit the file file.txt with the two lines A and B, and then we make local changes to the file.

alina

alina git:( main ) cat file.txt

1

2

alina git:( main )

If we use the git restore command to revert it, it will be restored to the state it was in at the last commit, containing only A and B.

alina

alina git:( main ) git restore file.txt

alina git:( main ) cat file.txt

A

B

alina git:( main )

Be cautious as the git restore command will delete any changes in your working directory!