{"thread":{"id":"64155","subject":"Why does git-status suggest different commands to unstage files depending on whether there is a commit yet or not?","startedAt":"2025-09-16T20:29:11Z","lastAt":"2025-09-17T15:33:22Z","messageCount":6,"participants":["Anselm Schüler","Junio C Hamano","D. Ben Knoble"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"526492","messageId":"5036f7d8-8ac9-4f50-99af-5a3a2d1a07fc@anselmschueler.com","threadId":"64155","inReplyTo":null,"subject":"Why does git-status suggest different commands to unstage files depending on whether there is a commit yet or not?","fromName":"Anselm Schüler","fromEmail":"mail@anselmschueler.com","sentAt":"2025-09-16T20:29:03Z","receivedAt":"2025-09-16T20:29:11Z","isPatch":false,"sender":{"key":"mail@anselmschueler.com","avatar":"https://gravatar.com/avatar/fb911b2f7cf9596f7bbc61cd9799da5fe87733c0394d6ca79531e07368fd757f?d=mp&s=160"},"body":"After a new repository has been created, before a commit has been made, \nwhen files are staged, git-status suggests using git-rm to unstage \nfiles: $ git status [...] No commits yet Changes to be committed:   (use \n\"git rm --cached <file>...\" to unstage) [...] After a commit has been \nmade, git-status suggests using git-restore instead: $ git status [...] \nChanges to be committed: (use \"git restore --staged <file>...\" to \nunstage) [...] Why is this the case?\n\n"},{"id":"526494","messageId":"de49d686-73fa-4bb9-9ec6-b65d5432a30a@anselmschueler.com","threadId":"64155","inReplyTo":"5036f7d8-8ac9-4f50-99af-5a3a2d1a07fc@anselmschueler.com","subject":"Re: Why does git-status suggest different commands to unstage files depending on whether there is a commit yet or not?","fromName":"Anselm Schüler","fromEmail":"mail@anselmschueler.com","sentAt":"2025-09-16T20:34:39Z","receivedAt":"2025-09-16T20:34:43Z","isPatch":false,"sender":{"key":"mail@anselmschueler.com","avatar":"https://gravatar.com/avatar/fb911b2f7cf9596f7bbc61cd9799da5fe87733c0394d6ca79531e07368fd757f?d=mp&s=160"},"body":"Sorry for that. I apologise for the messed-up formatting. Let’s try that \nagain.\n\n—\n\nAfter a new repository has been created, before a commit has been made, \nwhen files are staged, git-status suggests using git-rm to unstage files:\n\n$ git status\n[...]\nNo commits yet\n\nChanges to be committed:\n    (use \"git rm --cached <file>...\" to unstage)\n[...]\n\nAfter a commit has been made, git-status suggests using git-restore instead:\n\n$ git status\n[...]\nChanges to be committed:\n    (use \"git restore --staged <file>...\" to unstage)\n[...]\n\nWhy is this the case?\n\n\n"},{"id":"526498","messageId":"xmqqldmem8ds.fsf@gitster.g","threadId":"64155","inReplyTo":"de49d686-73fa-4bb9-9ec6-b65d5432a30a@anselmschueler.com","subject":"Re: Why does git-status suggest different commands to unstage files depending on whether there is a commit yet or not?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-16T20:48:15Z","receivedAt":"2025-09-16T20:48:17Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Anselm Schüler <mail@anselmschueler.com> writes:\n\n> $ git status\n> [...]\n> No commits yet\n>\n> Changes to be committed:\n>    (use \"git rm --cached <file>...\" to unstage)\n> [...]\n\nAs this is a very initial commit, any file you are including would\nonly exist in the index and in the working tree files.  The index is\nwhere you prepare the contents of the commit you are going to\ncreate, and \"git rm --cached <file>\" is the way to remove <file>\nfrom there without losing or clobbering the <file> in the working\ntree.  As you do not have a commit yet, you wouldn't have anywhere\nto \"restore\" from, would you?\n\n> After a commit has been made, git-status suggests using git-restore instead:\n>\n> $ git status\n> [...]\n> Changes to be committed:\n>    (use \"git restore --staged <file>...\" to unstage)\n> [...]\n\nCompared to the previous situation, you do have a commit, so you can\nrestore to the version in that commit.  During the course of\ndevelopment that led you to this state, you may have added <file> in\na commit way before the current commit, and you may have made\nchanges to the <file> multiple times in different commits before the\ncurrent commit.  \"git rm --cached <file>\" would not be how you would\ngo back to the version in the current commit in such a situation, as\nit would take you to the state _before_ you originally added that\nfile.  You would \"restore\" the contents in the index to that of the\ncurrent commit (i.e. HEAD) to go back to the state.\n\nSo, isn't a short answer to the \"why\" question, \"because that is\nwhat you need to do\", I guess?\n"},{"id":"526500","messageId":"CALnO6CCz6dFX1Est0hhCbvaepBvG4+rpYQRQ2bP5vAQVmaY=OA@mail.gmail.com","threadId":"64155","inReplyTo":"5036f7d8-8ac9-4f50-99af-5a3a2d1a07fc@anselmschueler.com","subject":"Re: Why does git-status suggest different commands to unstage files depending on whether there is a commit yet or not?","fromName":"D. Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2025-09-16T20:52:17Z","receivedAt":"2025-09-16T20:52:32Z","isPatch":false,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"On Tue, Sep 16, 2025 at 4:36 PM Anselm Schüler <mail@anselmschueler.com> wrote:\n>\n> After a new repository has been created, before a commit has been made,\n> when files are staged, git-status suggests using git-rm to unstage\n> files: $ git status [...] No commits yet Changes to be committed:   (use\n> \"git rm --cached <file>...\" to unstage) [...] After a commit has been\n> made, git-status suggests using git-restore instead: $ git status [...]\n> Changes to be committed: (use \"git restore --staged <file>...\" to\n> unstage) [...] Why is this the case?\n\nBy following builtin/commit.c:cmd_status() to wt-status.c and\nsearching for \"rm --cached\" (and then blaming), I found the following\nrelevant commits:\n\n- 80f537f79c (doc: promote \"git restore\", 2019-04-25): conversion from\nreset to restore\n- 4d4d5726ae (status: show worktree status of conflicted paths\nseparately, 2009-08-05): introduced the is_initial check\n\nUnfortunately the latter does not appear to describe or justify the difference.\n\nHowever, a quick test of the kind\n\n    git init foo\n    echo a > foo/a && git -C foo add a\n    git -C foo restore --staged a\n\ngives\n\n    fatal: could not resolve HEAD\n\nContrary to Junio's mail about \"where to restore from,\" the following\ndoes work in a non-unborn repo to move \"newfile\" from \"untracked\" to\n\"staged\" and back again:\n\n    echo >newfile && git add newfile\n    git restore --staged newfile\n\nSo we don't need \"rm --cached\" to\n\n> remove <file>\n> from there [the index] without losing or clobbering the <file> in the working\n> tree\n\n(But the point about having nowhere to restore from stands!)\n\n-- \nD. Ben Knoble\n"},{"id":"526572","messageId":"xmqqqzw5i8fv.fsf@gitster.g","threadId":"64155","inReplyTo":"CALnO6CCz6dFX1Est0hhCbvaepBvG4+rpYQRQ2bP5vAQVmaY=OA@mail.gmail.com","subject":"Re: Why does git-status suggest different commands to unstage files depending on whether there is a commit yet or not?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-09-17T12:12:52Z","receivedAt":"2025-09-17T12:12:55Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"D. Ben Knoble\" <ben.knoble@gmail.com> writes:\n\n> ... \" the following\n> does work in a non-unborn repo to move \"newfile\" from \"untracked\" to\n> \"staged\" and back again:\n>\n>     echo >newfile && git add newfile\n>     git restore --staged newfile\n>\n> So we don't need \"rm --cached\" to\n>\n>> remove <file>\n>> from there [the index] without losing or clobbering the <file> in the working\n>> tree\n\nNow I think about it more, I wonder if \"restore --staged\" is\nmisleading and confusing to new users?\n\nWhen you teach \"git restore\" what would you tell them?\n\nNAME\n       git-restore - Restore working tree files\n\nis how \"git restore --help\" starts.  It is primarily a tool to let\nyou repair messed up files in your working tree by copying out of a\nknown good version from somewhere, be it from the index or from an\nexisting commit.\n\nBut the instruction used to recover from unwanted \"git add\" to\nupdate the index with modified contents does NOT want to destroy\nyour files in the working tree.  You want to repair only the index\nwithout touching your working tree files, because you added modified\ncontents that were not ready to be \"staged\".  Hence \"git restore\" is\nused with \"--staged\" option to tell it to do what it was *not*\nprimarily designed to do, i.e. only touch the index without doing\nits usual \"Restore working tree files\" job.\n\nBut there is a lot more appropriate command whose primary focus is\nabout the index.  \"git reset <file>\" would grab the contents for the\n<file> in HEAD and replace the index entry for <file> with it, which\nis exactly how you would move the \"Changes to be committed\" files to\n\"Changes not staged for commit\" status.\n\nSo I think use of \"git restore --staged\" in the instruction is\nwrong, and it should be replaced with \"git reset\" instead.\n\n> (But the point about having nowhere to restore from stands!)\n\nYes, and the point about having nowhere to reset from stands for the\nstate on an unborn branch.  That one needs \"rm --cached\".\n\n"},{"id":"526594","messageId":"CALnO6CAZHy58X_6T5KR9zUa0TrJvbM8BLZj+is8pB9=fogvr3Q@mail.gmail.com","threadId":"64155","inReplyTo":"xmqqqzw5i8fv.fsf@gitster.g","subject":"Re: Why does git-status suggest different commands to unstage files depending on whether there is a commit yet or not?","fromName":"D. Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2025-09-17T15:33:08Z","receivedAt":"2025-09-17T15:33:22Z","isPatch":false,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"On Wed, Sep 17, 2025 at 8:12 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"D. Ben Knoble\" <ben.knoble@gmail.com> writes:\n>\n> > ... \" the following\n> > does work in a non-unborn repo to move \"newfile\" from \"untracked\" to\n> > \"staged\" and back again:\n> >\n> >     echo >newfile && git add newfile\n> >     git restore --staged newfile\n> >\n> > So we don't need \"rm --cached\" to\n> >\n> >> remove <file>\n> >> from there [the index] without losing or clobbering the <file> in the working\n> >> tree\n>\n> Now I think about it more, I wonder if \"restore --staged\" is\n> misleading and confusing to new users?\n>\n> When you teach \"git restore\" what would you tell them?\n>\n> NAME\n>        git-restore - Restore working tree files\n>\n> is how \"git restore --help\" starts.  It is primarily a tool to let\n> you repair messed up files in your working tree by copying out of a\n> known good version from somewhere, be it from the index or from an\n> existing commit.\n>\n> But the instruction used to recover from unwanted \"git add\" to\n> update the index with modified contents does NOT want to destroy\n> your files in the working tree.  You want to repair only the index\n> without touching your working tree files, because you added modified\n> contents that were not ready to be \"staged\".  Hence \"git restore\" is\n> used with \"--staged\" option to tell it to do what it was *not*\n> primarily designed to do, i.e. only touch the index without doing\n> its usual \"Restore working tree files\" job.\n>\n> But there is a lot more appropriate command whose primary focus is\n> about the index.  \"git reset <file>\" would grab the contents for the\n> <file> in HEAD and replace the index entry for <file> with it, which\n> is exactly how you would move the \"Changes to be committed\" files to\n> \"Changes not staged for commit\" status.\n>\n> So I think use of \"git restore --staged\" in the instruction is\n> wrong, and it should be replaced with \"git reset\" instead.\n\nAs we have in git(1)\n\n   Reset, restore and revert\n       There are three commands with similar names: git reset, git restore and\n       git revert.\n\n       •   git-revert(1) is about making a new commit that reverts the changes\n           made by other commits.\n\n       •   git-restore(1) is about restoring files in the working tree from\n           either the index or another commit. This command does not update your\n           branch. The command can also be used to restore files in the index\n           from another commit.\n\n       •   git-reset(1) is about updating your branch, moving the tip in order\n           to add or remove commits from the branch. This operation changes the\n           commit history.\n\n           git reset can also be used to restore the index, overlapping with git\n           restore.\n\nthat already mentions the overlap, I'm inclined to keep \"restore\n--staged\" as a simpler \"I'm _restoring_ this file in the index to a\ndifferent version.\" (But my personal \"unstage\" alias has been \"reset\nHEAD --\" for a long time…) The Examples in git-restore(1) also mention\nthe reset connection.\n\n> > (But the point about having nowhere to restore from stands!)\n>\n> Yes, and the point about having nowhere to reset from stands for the\n> state on an unborn branch.  That one needs \"rm --cached\".\n\nPerhaps more germane to Anselm—I'm still confused that \"restore\n--staged\" can't cope with unborn branches. Sure, there's no \"place to\nrestore from.\" But the user experience of having 2 different commands\ndepending on this one state is a bit messy, I think. Wouldn't it be\nnice if one command did the right thing (kicking a newly-added file\nout of the staging area back to being untracked) in all situations?\n\nIOW, I think \"rm --cached\" works whether unborn or not—so shouldn't\n\"restore --staged\", too? As I think I saw in a separate reply, plenty\nof commands treat the unborn branch as having an empty tree as the\nparent. It seems that if \"restore --staged\" did so, we could unify the\nhelp here and simplify things for the user.\n\n-- \nD. Ben Knoble\n"}]}