threads / discuss / 64155

Why does git-status suggest different commands to unstage files depending on whether there is a commit yet or not?

Subject: Why does git-status suggest different commands to unstage files depending on whether there is a commit yet or not?

## tl;dr

6 messages between Sep 16, 2025 and Sep 17, 2025.

replies: 5people: 3as markdown or json

Anselm Schüler· Sep 16, 2025, 20:29 UTC · lore

After a new repository has been created, before a commit has been made, when files are staged, git-status suggests using git-rm to unstage files: $ git status [...] No commits yet Changes to be committed:   (use "git rm --cached <file>..." to unstage) [...] After a commit has been made, git-status suggests using git-restore instead: $ git status [...] Changes to be committed: (use "git restore --staged <file>..." to unstage) [...] Why is this the case?

Anselm Schüler· Sep 16, 2025, 20:34 UTC · re: Anselm Schüler · lore

Re: Why does git-status suggest different commands to unstage files depending on whether there is a commit yet or not?

Sorry for that. I apologise for the messed-up formatting. Let’s try that again.

—

After a new repository has been created, before a commit has been made, when files are staged, git-status suggests using git-rm to unstage files:

$ git status [...] No commits yet

Changes to be committed:
    (use "git rm --cached <file>..." to unstage)
[...]
After a commit has been made, git-status suggests using git-restore instead:
$ git status
[...]
Changes to be committed:
    (use "git restore --staged <file>..." to unstage)
[...]
Why is this the case?
Junio C Hamano· Sep 16, 2025, 20:48 UTC · re: Anselm Schüler · lore

Re: Why does git-status suggest different commands to unstage files depending on whether there is a commit yet or not?

Anselm Schüler <mail@anselmschueler.com> writes:
Show 7 quoted lines
> $ git status
> [...]
> No commits yet
>
> Changes to be committed:
>    (use "git rm --cached <file>..." to unstage)
> [...]

As this is a very initial commit, any file you are including would only exist in the index and in the working tree files. The index is where you prepare the contents of the commit you are going to create, and "git rm --cached <file>" is the way to remove <file> from there without losing or clobbering the <file> in the working tree. As you do not have a commit yet, you wouldn't have anywhere to "restore" from, would you?

Show 7 quoted lines
> After a commit has been made, git-status suggests using git-restore instead:
>
> $ git status
> [...]
> Changes to be committed:
>    (use "git restore --staged <file>..." to unstage)
> [...]

Compared to the previous situation, you do have a commit, so you can restore to the version in that commit. During the course of development that led you to this state, you may have added <file> in a commit way before the current commit, and you may have made changes to the <file> multiple times in different commits before the current commit. "git rm --cached <file>" would not be how you would go back to the version in the current commit in such a situation, as it would take you to the state _before_ you originally added that file. You would "restore" the contents in the index to that of the current commit (i.e. HEAD) to go back to the state.

So, isn't a short answer to the "why" question, "because that is what you need to do", I guess?

D. Ben Knoble· Sep 16, 2025, 20:52 UTC · re: Anselm Schüler · lore

Re: Why does git-status suggest different commands to unstage files depending on whether there is a commit yet or not?

On Tue, Sep 16, 2025 at 4:36 PM Anselm Schüler <mail@anselmschueler.com> wrote:
Show 8 quoted lines
>
> After a new repository has been created, before a commit has been made,
> when files are staged, git-status suggests using git-rm to unstage
> files: $ git status [...] No commits yet Changes to be committed:   (use
> "git rm --cached <file>..." to unstage) [...] After a commit has been
> made, git-status suggests using git-restore instead: $ git status [...]
> Changes to be committed: (use "git restore --staged <file>..." to
> unstage) [...] Why is this the case?

By following builtin/commit.c:cmd_status() to wt-status.c and searching for "rm --cached" (and then blaming), I found the following relevant commits:

- 80f537f79c (doc: promote "git restore", 2019-04-25): conversion from
reset to restore
- 4d4d5726ae (status: show worktree status of conflicted paths
separately, 2009-08-05): introduced the is_initial check
Unfortunately the latter does not appear to describe or justify the difference.
However, a quick test of the kind
    git init foo
    echo a > foo/a && git -C foo add a
    git -C foo restore --staged a
gives
    fatal: could not resolve HEAD

Contrary to Junio's mail about "where to restore from," the following does work in a non-unborn repo to move "newfile" from "untracked" to "staged" and back again:

    echo >newfile && git add newfile
    git restore --staged newfile
So we don't need "rm --cached" to
> remove <file>
> from there [the index] without losing or clobbering the <file> in the working
> tree
(But the point about having nowhere to restore from stands!)
-- 
D. Ben Knoble
Junio C Hamano· Sep 17, 2025, 12:12 UTC · re: D. Ben Knoble · lore

Re: Why does git-status suggest different commands to unstage files depending on whether there is a commit yet or not?

"D. Ben Knoble" <ben.knoble@gmail.com> writes:
Show 12 quoted lines
> ... " the following
> does work in a non-unborn repo to move "newfile" from "untracked" to
> "staged" and back again:
>
>     echo >newfile && git add newfile
>     git restore --staged newfile
>
> So we don't need "rm --cached" to
>
>> remove <file>
>> from there [the index] without losing or clobbering the <file> in the working
>> tree

Now I think about it more, I wonder if "restore --staged" is misleading and confusing to new users?

When you teach "git restore" what would you tell them?
NAME
       git-restore - Restore working tree files

is how "git restore --help" starts. It is primarily a tool to let you repair messed up files in your working tree by copying out of a known good version from somewhere, be it from the index or from an existing commit.

But the instruction used to recover from unwanted "git add" to update the index with modified contents does NOT want to destroy your files in the working tree. You want to repair only the index without touching your working tree files, because you added modified contents that were not ready to be "staged". Hence "git restore" is used with "--staged" option to tell it to do what it was *not* primarily designed to do, i.e. only touch the index without doing its usual "Restore working tree files" job.

But there is a lot more appropriate command whose primary focus is about the index. "git reset <file>" would grab the contents for the <file> in HEAD and replace the index entry for <file> with it, which is exactly how you would move the "Changes to be committed" files to "Changes not staged for commit" status.

So I think use of "git restore --staged" in the instruction is wrong, and it should be replaced with "git reset" instead.

> (But the point about having nowhere to restore from stands!)

Yes, and the point about having nowhere to reset from stands for the state on an unborn branch. That one needs "rm --cached".

D. Ben Knoble· Sep 17, 2025, 15:33 UTC · re: Junio C Hamano · lore

Re: Why does git-status suggest different commands to unstage files depending on whether there is a commit yet or not?

On Wed, Sep 17, 2025 at 8:12 AM Junio C Hamano <gitster@pobox.com> wrote:
Show 46 quoted lines
>
> "D. Ben Knoble" <ben.knoble@gmail.com> writes:
>
> > ... " the following
> > does work in a non-unborn repo to move "newfile" from "untracked" to
> > "staged" and back again:
> >
> >     echo >newfile && git add newfile
> >     git restore --staged newfile
> >
> > So we don't need "rm --cached" to
> >
> >> remove <file>
> >> from there [the index] without losing or clobbering the <file> in the working
> >> tree
>
> Now I think about it more, I wonder if "restore --staged" is
> misleading and confusing to new users?
>
> When you teach "git restore" what would you tell them?
>
> NAME
>        git-restore - Restore working tree files
>
> is how "git restore --help" starts.  It is primarily a tool to let
> you repair messed up files in your working tree by copying out of a
> known good version from somewhere, be it from the index or from an
> existing commit.
>
> But the instruction used to recover from unwanted "git add" to
> update the index with modified contents does NOT want to destroy
> your files in the working tree.  You want to repair only the index
> without touching your working tree files, because you added modified
> contents that were not ready to be "staged".  Hence "git restore" is
> used with "--staged" option to tell it to do what it was *not*
> primarily designed to do, i.e. only touch the index without doing
> its usual "Restore working tree files" job.
>
> But there is a lot more appropriate command whose primary focus is
> about the index.  "git reset <file>" would grab the contents for the
> <file> in HEAD and replace the index entry for <file> with it, which
> is exactly how you would move the "Changes to be committed" files to
> "Changes not staged for commit" status.
>
> So I think use of "git restore --staged" in the instruction is
> wrong, and it should be replaced with "git reset" instead.
As we have in git(1)
   Reset, restore and revert
       There are three commands with similar names: git reset, git restore and
       git revert.
       •   git-revert(1) is about making a new commit that reverts the changes
           made by other commits.
       •   git-restore(1) 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(1) 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.

that already mentions the overlap, I'm inclined to keep "restore --staged" as a simpler "I'm _restoring_ this file in the index to a different version." (But my personal "unstage" alias has been "reset HEAD --" for a long time…) The Examples in git-restore(1) also mention the reset connection.

> > (But the point about having nowhere to restore from stands!)
>
> Yes, and the point about having nowhere to reset from stands for the
> state on an unborn branch.  That one needs "rm --cached".

Perhaps more germane to Anselm—I'm still confused that "restore --staged" can't cope with unborn branches. Sure, there's no "place to restore from." But the user experience of having 2 different commands depending on this one state is a bit messy, I think. Wouldn't it be nice if one command did the right thing (kicking a newly-added file out of the staging area back to being untracked) in all situations?

IOW, I think "rm --cached" works whether unborn or not—so shouldn't "restore --staged", too? As I think I saw in a separate reply, plenty of commands treat the unborn branch as having an empty tree as the parent. It seems that if "restore --staged" did so, we could unify the help here and simplify things for the user.

-- 
D. Ben Knoble

← back to recent threads