{"thread":{"id":"58044","subject":"stashing only unstaged changes?","startedAt":"2022-06-21T19:46:55Z","lastAt":"2022-06-24T21:17:33Z","messageCount":10,"participants":["Tim Chase","Reto","Konstantin Khomoutov","Erik Cervin Edin","Ævar Arnfjörð Bjarmason","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"457637","messageId":"20220621142618.239b02cd@bigbox.attlocal.net","threadId":"58044","inReplyTo":null,"subject":"stashing only unstaged changes?","fromName":"Tim Chase","fromEmail":"git@tim.thechases.com","sentAt":"2022-06-21T19:26:18Z","receivedAt":"2022-06-21T19:46:55Z","isPatch":false,"sender":{"key":"git@tim.thechases.com","avatar":null},"body":"I recently had composed a commit with some `git add -p` leaving some\nportions unstaged. I wanted to stash the unstaged changes to make\nsure that the staged code ran as expected, so I did  a `git stash`\nonly to find that it unstaged my staged changes and stashed\n*everything*.\n\nUsing `git stash --saved` does the opposite of what I want (stashing\nthe index, not the difference between the index and the working-copy)\n\nSo I carefully re-`git add -p`'ed everything and tried `git stash\n--keep-index` which sounded promising (my index remained the same),\nbut popping my stash ended up causing conflicts because it had\nstashed the diff of HEAD..working-copy, not INDEX..working-copy.  A\n`git stash show -p` confirmed that the stash included things that I\nhad already staged.\n\nSo I carefully re-`git add -p`ed everything yet again, but then got\nstuck trying to convince `stash` to save a snapshot of only the diff\nin my working directory. To work around it, I did a `git diff >\ntemp.patch` to obtain the stuff I'd wanted to stash, a `git reset\n--staged` to clear out those changes, ran my code to verify\n(eventually committing it), and then applied the `temp.patch` back on\ntop of my changes.  It worked, but felt convoluted.\n\nI did see the `git stash -p` option, to manually choose the inverse\nbits, but for what I was doing, it was more sensible to `git add -p`\nand try to stash the rest.\n\nSo is there some option I've missed to tell `git stash` to stash only\nthe delta between the uncommitted-index and the working-copy?\n\nThanks,\n\n-Tim\n\n\n(I'd posted this on /r/git\n\nhttps://www.reddit.com/r/git/comments/vchu83/stashing_only_unstaged_changes/\n\nbut figured I'd try my hand here in the hope of more answers)\n"},{"id":"457833","messageId":"20220624081615.zhurjajwuvtvzs2y@feather","threadId":"58044","inReplyTo":"20220621142618.239b02cd@bigbox.attlocal.net","subject":"Re: stashing only unstaged changes?","fromName":"Reto","fromEmail":"reto@labrat.space","sentAt":"2022-06-24T08:16:15Z","receivedAt":"2022-06-24T08:20:32Z","isPatch":false,"sender":{"key":"reto@labrat.space","avatar":null},"body":"You should be using proper branches, not stash.\n\nHere's an email with some background from Junio:\n\nhttps://lore.kernel.org/git/xmqq5ylior3l.fsf@gitster.g/\n\nSome quotes:\n\n> The intended use of \"stash\" is to clear the deck as quickly as\n> possible to deal with \"emergencies\"\n\n> Users are better off doing any large scale \"I made a mess in the\n> working tree with mixed changes, and I want to take time to separate\n> them out\" on separate (possibly temporary) branches, instead of\n> using \"stash save\" + \"stash pop\"\n\nIn other words, you are using the wrong tool for the job probably.\n\nCheers,\nReto\n"},{"id":"457836","messageId":"20220624095558.hegsfj4e4nbqyooo@carbon","threadId":"58044","inReplyTo":"20220621142618.239b02cd@bigbox.attlocal.net","subject":"Re: stashing only unstaged changes?","fromName":"Konstantin Khomoutov","fromEmail":"kostix@bswap.ru","sentAt":"2022-06-24T09:55:58Z","receivedAt":"2022-06-24T09:56:22Z","isPatch":false,"sender":{"key":"kostix@bswap.ru","avatar":null},"body":"On Tue, Jun 21, 2022 at 02:26:18PM -0500, Tim Chase wrote:\n\n> I recently had composed a commit with some `git add -p` leaving some\n> portions unstaged. I wanted to stash the unstaged changes to make\n> sure that the staged code ran as expected, so I did  a `git stash`\n> only to find that it unstaged my staged changes and stashed\n> *everything*.\n\nI tend to rely on reflogs for this.\nBasically, I roll like this:\n\n 1. Stage the necessary changes, commit.\n \n 2. Stage the remaining changes - what you've left unstaged in your case, -\n    commit.\n \n 3. Go to the commit I need to test; in this simple case that'd be\n\n      $ git checkout HEAD~\n\n    Then test the changes.\n\n 4. Go back to the previous state using the HEAD's reflog:\n\n     $ git checkout HEAD@{1}\n\nIf you now need to have the situation where the changes committed on step 2\nare left only in the work tree, run\n\n  $ git reset HEAD~\n\nso that you have the HEAD and the index reset to the commit recorded on step 1\nwith the changes from the commit 2 left in the work tree.\n\n\nHaving said that, I'd note that I tend to do work on the detached HEAD\nbut you could apply the same logic to working on a a branch.\n\n"},{"id":"457839","messageId":"CA+JQ7M9jBSB8tdpz85imER4SF1yhn3jes8ThnzkA_O9+mus1Ng@mail.gmail.com","threadId":"58044","inReplyTo":"20220621142618.239b02cd@bigbox.attlocal.net","subject":"Re: stashing only unstaged changes?","fromName":"Erik Cervin Edin","fromEmail":"erik@cervined.in","sentAt":"2022-06-24T11:23:34Z","receivedAt":"2022-06-24T11:24:23Z","isPatch":false,"sender":{"key":"erik@cervined.in","avatar":null},"body":"My $0.02\n\nOn Tue, Jun 21, 2022 at 9:57 PM Tim Chase <git@tim.thechases.com> wrote:\n>\n> I recently had composed a commit with some `git add -p` leaving some\n> portions unstaged. I wanted to stash the unstaged changes to make\n> sure that the staged code ran as expected, so I did  a `git stash`\n> only to find that it unstaged my staged changes and stashed\n> *everything*.\n\nWhat you wanted to do was\n  git stash --keep-index\nwhich creates a stash with the staged and unstaged changes but leaves\nthe staged ones in the working tree.\n\nIf you forget to do this, what you do is try\n  git stash pop --index\nand then\n  git stash --keep-index\n\n> Using `git stash --saved` does the opposite of what I want (stashing\n> the index, not the difference between the index and the working-copy)\n\nI'm unaware of a --saved option\n\nMy understanding (which may be incorrect) is that a shash is always of\nthe staged/unstaged changes and there's no way to stash only one or\nthe other in a single stash operation.\n\n> So I carefully re-`git add -p`'ed everything and tried `git stash\n> --keep-index` which sounded promising (my index remained the same),\n> but popping my stash ended up causing conflicts because it had\n> stashed the diff of HEAD..working-copy, not INDEX..working-copy.  A\n> `git stash show -p` confirmed that the stash included things that I\n> had already staged.\n\nSuch conflicts are usually trivially be resolved by taking \"theirs\"\nI have a helper script that does this and it's basically\n  git ls-files --unmerged -z |\\\n    xargs -0 sed -i -e '/^<\\{7\\}/,/^=\\{7\\}/d' --e '/^>\\{7\\}/d' &&\n    git ls-files --unmerged -z | xargs -0 git add --\nthough, unfortunately, it also stages the content as a part of marking\nresolution.\n\n> So I carefully re-`git add -p`ed everything yet again, but then got\n> stuck trying to convince `stash` to save a snapshot of only the diff\n> in my working directory.\n\nA stash is always both staged and unstaged changes of the files.\n\nTo stash only staged you may do\n  git stash --keep-index\n  git stash\nThe first stash will include staged/unstaged and the second only staged\n\nTo create a stash of only unstaged\n  git commit -m tmp # create temporary commit w staged\n  git stash # stash unstaged\n  git reset HEAD~ &&  git stash # stash the previous staged as\nunstaged (optionally git add  in the middle)\n  git stash apply/pop stash@{1} # get the \"unstaged\" stash\nAs you noted such a stash is still based on a tree that may have\ncontained staged changes (ORIG_HEAD).\nIe. if you staged line 1 but not 2-3 the \"unstaged\" stash will also\ncontain line 1\nThis is doesn't happen if the staged/unstaged contain different files\n\n> To work around it, I did a `git diff >\n> temp.patch` to obtain the stuff I'd wanted to stash, a `git reset\n> --staged` to clear out those changes, ran my code to verify\n> (eventually committing it), and then applied the `temp.patch` back on\n> top of my changes. It worked, but felt convoluted.\n\nThat's basically what you have to do if you only want certain changes.\n(and also what --patch does under the hood)\n\n> I did see the `git stash -p` option, to manually choose the inverse\n> bits, but for what I was doing, it was more sensible to `git add -p`\n> and try to stash the rest.\n\ngit stash --patch is MUCH slower than git add -p, so I personally never use it.\nIn my workflow I find it better to either\n  git add -p\nand then\n  git stash --keep-index\nor creating regular temporary commits, and fiddling with those,\nperhaps using rebase and friends.\n\n> So is there some option I've missed to tell `git stash` to stash only\n> the delta between the uncommitted-index and the working-copy?\n\nNo, there is none.\n\nIn my experience, using regular\nadd/commit/reset/branch/checkout/rebase is superior to using the stash\nfor separating changes into discrete commits.\n"},{"id":"457842","messageId":"220624.86tu8ai4mr.gmgdl@evledraar.gmail.com","threadId":"58044","inReplyTo":"20220621142618.239b02cd@bigbox.attlocal.net","subject":"Re: stashing only unstaged changes?","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-06-24T13:09:51Z","receivedAt":"2022-06-24T13:16:03Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Jun 21 2022, Tim Chase wrote:\n\n> I recently had composed a commit with some `git add -p` leaving some\n> portions unstaged. I wanted to stash the unstaged changes to make\n> sure that the staged code ran as expected, so I did  a `git stash`\n> only to find that it unstaged my staged changes and stashed\n> *everything*.\n>\n> Using `git stash --saved` does the opposite of what I want (stashing\n> the index, not the difference between the index and the working-copy)\n>\n> So I carefully re-`git add -p`'ed everything and tried `git stash\n> --keep-index` which sounded promising (my index remained the same),\n> but popping my stash ended up causing conflicts because it had\n> stashed the diff of HEAD..working-copy, not INDEX..working-copy.  A\n> `git stash show -p` confirmed that the stash included things that I\n> had already staged.\n>\n> So I carefully re-`git add -p`ed everything yet again, but then got\n> stuck trying to convince `stash` to save a snapshot of only the diff\n> in my working directory. To work around it, I did a `git diff >\n> temp.patch` to obtain the stuff I'd wanted to stash, a `git reset\n> --staged` to clear out those changes, ran my code to verify\n> (eventually committing it), and then applied the `temp.patch` back on\n> top of my changes.  It worked, but felt convoluted.\n>\n> I did see the `git stash -p` option, to manually choose the inverse\n> bits, but for what I was doing, it was more sensible to `git add -p`\n> and try to stash the rest.\n>\n> So is there some option I've missed to tell `git stash` to stash only\n> the delta between the uncommitted-index and the working-copy?\n\nIs what you want equivalent to:\n\n    # save the \"git add -p\"'d chunks\n    git stash push --staged\n    # save the \"uncommitted\"\n    git stash push\n    # pop the previously staged\n    git stash pop --index stash@{1}\n\n?\n\nI.e. this (ab)uses the stash itself to juggle the two around. I don't\nthink there's a way to do this in one step, but I'm not very familiar\nwith git-stash.\n\nIf that is what you want (and we don't have a way to do it) perhaps we\nshould have a a:\n\n    git stash push --unstaged\n\nWhich could start out as an alias for the above sequence, with e.g. an\noptional \"--include-untracked\" being passed to the second \"git stash\npush\" command above.\n\nI also found this past thread (CC'd the author, in case it helps), which\nseems to be asking the same question:\nhttps://lore.kernel.org/git/CAC4jX8GEg5=9BPepYLntGRG7n_84ju7rTSYO82SQyuiiff0UcQ@mail.gmail.com/\n"},{"id":"457845","messageId":"CA+JQ7M8gaEWHHdx2or2kQfYpp=XBxQS=pXtEOS4x5SBdpPWdkQ@mail.gmail.com","threadId":"58044","inReplyTo":"220624.86tu8ai4mr.gmgdl@evledraar.gmail.com","subject":"Re: stashing only unstaged changes?","fromName":"Erik Cervin Edin","fromEmail":"erik@cervined.in","sentAt":"2022-06-24T13:49:08Z","receivedAt":"2022-06-24T13:49:52Z","isPatch":false,"sender":{"key":"erik@cervined.in","avatar":null},"body":"On Fri, Jun 24, 2022 at 3:20 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> Is what you want equivalent to:\n>\n>     # save the \"git add -p\"'d chunks\n>     git stash push --staged\n>     # save the \"uncommitted\"\n>     git stash push\n>     # pop the previously staged\n>     git stash pop --index stash@{1}\n>\n> ?\n\nNever noticed --staged before.\nI just tried to\n  seq 1 3 >> bar\nthen git add -p to stage with 2 and 3\nfollowed by a git stash push --staged but that gave me the error\n\n> error: patch failed: bar:2\n> error: bar: patch does not apply\n> Cannot remove worktree changes\n\nBut I'm guessing the command (and approach) work fine if the\nstaged/unstaged are in different files.\n"},{"id":"457849","messageId":"220624.86czeyhy8x.gmgdl@evledraar.gmail.com","threadId":"58044","inReplyTo":"CA+JQ7M8gaEWHHdx2or2kQfYpp=XBxQS=pXtEOS4x5SBdpPWdkQ@mail.gmail.com","subject":"Re: stashing only unstaged changes?","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-06-24T15:32:23Z","receivedAt":"2022-06-24T15:33:59Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Jun 24 2022, Erik Cervin Edin wrote:\n\n> On Fri, Jun 24, 2022 at 3:20 PM Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>>\n>> Is what you want equivalent to:\n>>\n>>     # save the \"git add -p\"'d chunks\n>>     git stash push --staged\n>>     # save the \"uncommitted\"\n>>     git stash push\n>>     # pop the previously staged\n>>     git stash pop --index stash@{1}\n>>\n>> ?\n>\n> Never noticed --staged before.\n> I just tried to\n>   seq 1 3 >> bar\n> then git add -p to stage with 2 and 3\n> followed by a git stash push --staged but that gave me the error\n>\n>> error: patch failed: bar:2\n>> error: bar: patch does not apply\n>> Cannot remove worktree changes\n>\n> But I'm guessing the command (and approach) work fine if the\n> staged/unstaged are in different files.\n\nHuh!\n\nNo, it does work when you stage chunks in the same file, I tested this\nby modifying the top & bottom of our README.md.\n\nBut we do indeed have that error (which looks like an unrelated bug) if\nyou try to \"git stash pust --staged\" a hunk when that hunk overlaps with\nchanges that are unstaged.\n"},{"id":"457857","messageId":"xmqqk095vo7e.fsf@gitster.g","threadId":"58044","inReplyTo":"220624.86czeyhy8x.gmgdl@evledraar.gmail.com","subject":"Re: stashing only unstaged changes?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-06-24T19:47:01Z","receivedAt":"2022-06-24T19:47:12Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> No, it does work when you stage chunks in the same file, I tested this\n> by modifying the top & bottom of our README.md.\n\nThis is primarily because of the three-way merge magic.  If you\nconsider what \"git stash pop\" does after \"git stash push -k\"\nfollowed by a \"git commit\", you can compare the difference between\nrecorded HEAD and recorded working tree (which is replayed on top of\nthe result of the \"git commit\" step) and what is in the working tree\nafter \"git commit\".  We do replay both the changes already committed\n(and is already in the working tree) and leftover ones (removed from\nthe working tree when \"push -k\" was run), and the former is *often*\nresolved cleanly as \"both sides (meaning: the \"stash\" and the human\nuser who did \"git commit\") made the same change\", while the latter\nis resolved cleanly because only the \"stash\" side.\n\nHere, *often* is a key phrase.  If you did a tricky \"add -p\" that\nedited the patch, such a three-way merge may not resolve itself\ncleanly.\n\nIn addition, if you did something after the \"git commit\", the former\nmay get conflicts because what the \"working tree\" side did may not\nmatch what was in \"stash\", hence \"both sides made the same change\"\nno longer applies.\n\nThe story is very similar when the testing after \"git stash push -k\"\nturned out to be unsuccessful and the user decides not to commit.\n\"git reset --hard HEAD && git stash pop\" is how you would go back to\nthe state before \"git stash push -k\" in such a case, but if you edit\nbetween these two operations, you can make \"both sides did the same\nchange\" rule not to apply any more.\n"},{"id":"457859","messageId":"20220624153804.44089117@bigbox.attlocal.net","threadId":"58044","inReplyTo":"CA+JQ7M9jBSB8tdpz85imER4SF1yhn3jes8ThnzkA_O9+mus1Ng@mail.gmail.com","subject":"Re: stashing only unstaged changes?","fromName":"Tim Chase","fromEmail":"git@tim.thechases.com","sentAt":"2022-06-24T20:38:04Z","receivedAt":"2022-06-24T20:59:13Z","isPatch":false,"sender":{"key":"git@tim.thechases.com","avatar":null},"body":"On 2022-06-24 13:23, Erik Cervin Edin wrote:\n> > Using `git stash --saved` does the opposite of what I want\n> > (stashing the index, not the difference between the index and the\n> > working-copy)  \n> \n> I'm unaware of a --saved option\n\nDerp, meant to type \"--keep-index\" there.\n\n> My understanding (which may be incorrect) is that a shash is always\n> of the staged/unstaged changes and there's no way to stash only one\n> or the other in a single stash operation.\n\nThat seems to be what I'm experiencing.\n\n> A stash is always both staged and unstaged changes of the files.\n> \n> To stash only staged you may do\n>   git stash --keep-index\n>   git stash\n> The first stash will include staged/unstaged and the second only\n> staged\n\nRight, which is what I'd tried.  Except\n\n  git stash\n\neffectively takes a diff of HEAD..{working copy} and stashes that,\nwhile --keep-index also takes note of what was in the index.\n\nMy hope had been for an option to have git-stash use the index as its\nbaseline rather than use HEAD.\n\n> To create a stash of only unstaged\n>   git commit -m tmp # create temporary commit w staged\n>   git stash # stash unstaged\n>   git reset HEAD~ &&  git stash # stash the previous staged as\n> unstaged (optionally git add  in the middle)\n>   git stash apply/pop stash@{1} # get the \"unstaged\" stash\n> As you noted such a stash is still based on a tree that may have\n> contained staged changes (ORIG_HEAD).\n> Ie. if you staged line 1 but not 2-3 the \"unstaged\" stash will also\n> contain line 1\n> This is doesn't happen if the staged/unstaged contain different\n> files\n\nYeah, those are among the issues I was bumping against.  With\ndifferent files, it's a little less troublesome. But when there might\nbe some overlap, became problematic.\n\n> > To work around it, I did a `git diff >\n> > temp.patch` to obtain the stuff I'd wanted to stash, a `git reset\n> > --staged` to clear out those changes, ran my code to verify\n> > (eventually committing it), and then applied the `temp.patch`\n> > back on top of my changes. It worked, but felt convoluted.  \n> \n> That's basically what you have to do if you only want certain\n> changes. (and also what --patch does under the hood)\n\nOkay, based on the other replies, it sounds like it might be the most\npractical route to go since I'm not missing some existing\nfunctionality of git-stash.\n\n> > I did see the `git stash -p` option, to manually choose the\n> > inverse bits, but for what I was doing, it was more sensible to\n> > `git add -p` and try to stash the rest.  \n> \n> git stash --patch is MUCH slower than git add -p, so I personally\n> never use it.\n\nI don't find the speed an issue as much as I have trouble with any of\nthe --patch variants that aren't `git add -p` because I'm never 100%\npositive which direction + vs - means.  Which is partly why I wanted\nto stick with `add -p` and stash the non-staged stuff.\n\n> > So is there some option I've missed to tell `git stash` to stash\n> > only the delta between the uncommitted-index and the\n> > working-copy?  \n> \n> No, there is none.\n\nThanks! That's pretty much what I'm getting from the rest of the\nreplies, too.\n\n-Tim\n\n\n\n\n\n"},{"id":"457860","messageId":"20220624155326.1a0494ea@bigbox.attlocal.net","threadId":"58044","inReplyTo":"20220624081615.zhurjajwuvtvzs2y@feather","subject":"Re: stashing only unstaged changes?","fromName":"Tim Chase","fromEmail":"git@tim.thechases.com","sentAt":"2022-06-24T20:53:26Z","receivedAt":"2022-06-24T21:17:33Z","isPatch":false,"sender":{"key":"git@tim.thechases.com","avatar":null},"body":"On 2022-06-24 10:16, Reto wrote:\n> https://lore.kernel.org/git/xmqq5ylior3l.fsf@gitster.g/\n>   \n> > The intended use of \"stash\" is to clear the deck as quickly as\n> > possible to deal with \"emergencies\"    \n\nUnderstandable.  Just in my case, the deck I wanted to clear to\nquickly was the contents of the index, not the HEAD commit.\n\n-Tim\n\n"}]}