{"thread":{"id":"40444","subject":"Why not git reset --hard <path>?","startedAt":"2015-09-28T20:34:49Z","lastAt":"2015-09-29T19:40:34Z","messageCount":11,"participants":["George Spelvin","Junio C Hamano","Jacob Keller","Theodore Ts'o","Philip Oakley"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"270838","messageId":"20150928203449.29024.qmail@ns.horizon.com","threadId":"40444","inReplyTo":null,"subject":"Why not git reset --hard <path>?","fromName":"George Spelvin","fromEmail":"linux@horizon.com","sentAt":"2015-09-28T20:34:49Z","receivedAt":"2015-09-28T20:34:49Z","isPatch":false,"sender":{"key":"linux@horizon.com","avatar":null},"body":"I was applying an old forgotten stash to see if there were any edits in\nit I wanted to preserve, and my old changes to one file made no sense\nany more.  I wanted to drop then all and keep the version in HEAD.\n\nI'd been using git reset <path> after resolving conflicts, to leave\nthe changes in the same un-staged state they were before the stash,\nso I tried using \"git reset --hard crypto/842.c\" to throw away\nmy local changes.\n\nAnd I got\nfatal: Cannot do hard reset with paths.\n\nSo I did \"git reset <path>\" followed by \"git checkout <path>\", which\nachieved what I wanted.\n\nBut what I don't understand is why git reset couldn't do it for me in one\nstep.\n\nI understand that \"git reset --soft\" makes no sense with a path, but\nwhy not --hard?\n"},{"id":"270839","messageId":"xmqq612ucm3p.fsf@gitster.mtv.corp.google.com","threadId":"40444","inReplyTo":"20150928203449.29024.qmail@ns.horizon.com","subject":"Re: Why not git reset --hard <path>?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-09-28T20:42:02Z","receivedAt":"2015-09-28T20:42:02Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"George Spelvin\" <linux@horizon.com> writes:\n\n> I was applying an old forgotten stash to see if there were any edits in\n> it I wanted to preserve, and my old changes to one file made no sense\n> any more.  I wanted to drop then all and keep the version in HEAD.\n>\n> I'd been using git reset <path> after resolving conflicts, to leave\n> the changes in the same un-staged state they were before the stash,\n> so I tried using \"git reset --hard crypto/842.c\" to throw away\n> my local changes.\n>\n> And I got\n> fatal: Cannot do hard reset with paths.\n>\n> So I did \"git reset <path>\" followed by \"git checkout <path>\", which\n> achieved what I wanted.\n>\n> But what I don't understand is why git reset couldn't do it for me in one\n> step.\n>\n> I understand that \"git reset --soft\" makes no sense with a path, but\n> why not --hard?\n\nI do not think there is anything fundamentally wrong for wishing for\n\"reset --hard <pathspec>\".  It probably is just that nobody needed\nit, because \"git checkout HEAD <pathspec>\" is a 99% acceptable\nsubstitute for it (the only case where it makes a difference is when\nyou added a path to the index that did not exist in HEAD).\n"},{"id":"270840","messageId":"CA+P7+xoTHFL_KU+qBz1KwytxqNTxf1JkjXK7_Ej79uLLnCWD8g@mail.gmail.com","threadId":"40444","inReplyTo":"xmqq612ucm3p.fsf@gitster.mtv.corp.google.com","subject":"Re: Why not git reset --hard <path>?","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2015-09-28T20:53:45Z","receivedAt":"2015-09-28T20:53:45Z","isPatch":false,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Mon, Sep 28, 2015 at 1:42 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> \"George Spelvin\" <linux@horizon.com> writes:\n>> I understand that \"git reset --soft\" makes no sense with a path, but\n>> why not --hard?\n>\n> I do not think there is anything fundamentally wrong for wishing for\n> \"reset --hard <pathspec>\".  It probably is just that nobody needed\n> it, because \"git checkout HEAD <pathspec>\" is a 99% acceptable\n> substitute for it (the only case where it makes a difference is when\n> you added a path to the index that did not exist in HEAD).\n>\n\nPersonally, I would like to see this simply given the number of times\nthat I use git reset --hard <path> and then realize I should have used\ngit checkout instead. I conceptually think reset --hard should do\nthat, and that checkout is really not meant to do that as a concept.\n\nI may have some time to try and give this a look in the next few days...\n\nRegards,\nJake\n"},{"id":"270841","messageId":"xmqqvbaub5s4.fsf@gitster.mtv.corp.google.com","threadId":"40444","inReplyTo":"CA+P7+xoTHFL_KU+qBz1KwytxqNTxf1JkjXK7_Ej79uLLnCWD8g@mail.gmail.com","subject":"Re: Why not git reset --hard <path>?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-09-28T21:19:55Z","receivedAt":"2015-09-28T21:19:55Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jacob Keller <jacob.keller@gmail.com> writes:\n\n> On Mon, Sep 28, 2015 at 1:42 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> \"George Spelvin\" <linux@horizon.com> writes:\n>>> I understand that \"git reset --soft\" makes no sense with a path, but\n>>> why not --hard?\n>>\n>> I do not think there is anything fundamentally wrong for wishing for\n>> \"reset --hard <pathspec>\".  It probably is just that nobody needed\n>> it, because \"git checkout HEAD <pathspec>\" is a 99% acceptable\n>> substitute for it (the only case where it makes a difference is when\n>> you added a path to the index that did not exist in HEAD).\n>\n> Personally, I would like to see this simply given the number of times\n> that I use git reset --hard <path> and then realize I should have used\n> git checkout instead. I conceptually think reset --hard should do\n> that, and that checkout is really not meant to do that as a concept.\n\nI agree with you if we limit the scope to \"reset --hard\" that does\nnot mention any commit on the command line (or says \"HEAD\").\n\nHowever, for things like:\n\n    $ git reset --hard HEAD^ Makefile\n    $ git reset --hard HEAD@{4.hours.ago} Makefile\n\nI do not think \"reset --hard\" is a good match.  Conceptually, you\nare grabbing what was stored in a given commit and checking that out\nto your current workspace (that is, the index and the working tree).\n\nAll modes of \"git reset\" are primarily about updating where in the\nhistory DAG your HEAD points at, and then adjusting your current\nworkspace to that update, taking into account the reason why you are\nrepointing your HEAD in the history DAG (e.g. when doing --hard\nreset, you want the workspace to match what the commit your HEAD now\npoints at; when doing --soft reset, you don't want any changes\ndone).\n\nIt is only when you use \"git reset\" to update your HEAD to point at\nthe exact same commit when interaction with <pathspec> could make\nsense, but conceptually the use case is covered better by\n\"checkout\", which does _not_ futz with the history at all.  The\ncommand is primarily about preparing your workspace (that is, the\nindex and the working tree) toward the work you do to prepare for\nthe next commit you are going to make.  Either you check out a\nbranch so that your next commit goes to that branch (and you take\nyour local changes with you when you do so), or you grab contents\nfrom other existing commits to prepare for your work.\n\nSo while I am OK with loosening \"reset --hard\" with <pathspec> if\nthe command weren't given any commit from the command line, I doubt\nthat would be a real improvement.\n\nI suspect it may even hurt the users by placing them in a wrong\nmental model (i.e. I doubt you would wish \"reset --hard <path>\" to\nwork if your starting point were \"reset is primarily about moving in\nthe history DAG and adjust the workspace accordingly\").\n"},{"id":"270842","messageId":"20150928213601.GA4071@thunk.org","threadId":"40444","inReplyTo":"xmqqvbaub5s4.fsf@gitster.mtv.corp.google.com","subject":"Re: Why not git reset --hard <path>?","fromName":"Theodore Ts'o","fromEmail":"tytso@mit.edu","sentAt":"2015-09-28T21:36:01Z","receivedAt":"2015-09-28T21:36:01Z","isPatch":false,"sender":{"key":"tytso@mit.edu","avatar":"https://avatars.githubusercontent.com/u/51416?v=4"},"body":"I personally have in my .gitconfig:\n\n[alias]\n\trevert-file = checkout HEAD --\n\nI'm not sure revert-file is the best name, but it's what I've used\nbecause I've been contaminated by the concept/naming of \"p4 revert\",\nwhich I do use a fair amount to undo local edits for one or more files\nwhen I've been forced to use perforce or perforce-like systems.  Given\nthat it confuses the concept of how \"git revert\" works, maybe\nsomething like \"git unedit <pathspec>\" would work better.\n\nGiven though it's so easy to address this with a single line in a\nuser's .gitconfig, I guess the question is whether it's worthwhile to\nmake a change that would be visible to all users, and perhaps more\nimportantly, all new users to git.\n\n\t     \t     \t     \t      - Ted\n"},{"id":"270887","messageId":"20150928223607.15594.qmail@ns.horizon.com","threadId":"40444","inReplyTo":"xmqq612ucm3p.fsf@gitster.mtv.corp.google.com","subject":"Re: Why not git reset --hard <path>?","fromName":"George Spelvin","fromEmail":"linux@horizon.com","sentAt":"2015-09-28T22:36:07Z","receivedAt":"2015-09-28T22:36:07Z","isPatch":false,"sender":{"key":"linux@horizon.com","avatar":null},"body":"Junio C Hamano <gitster@pobox.com> wrote:\n> \"git checkout HEAD <pathspec>\" is a 99% acceptable substitute for it\n> (the only case where it makes a difference is when you added a path to\n> the index that did not exist in HEAD).\n\nEr, wait a minute...\n\n\"git checkout <tree-ish> <pathspec>\" modifies the index?\n\nDamn, I've been using git for years and I never knew that.\nI thought it only modified the working tree.\n\nBut I just tested, and it does.  Damn, now I have to figure out\nhow to \"leapfrog\" a file from history into the working tree without\noverwriting the index; that's occasionally useful.\n"},{"id":"270888","messageId":"xmqqr3lib189.fsf@gitster.mtv.corp.google.com","threadId":"40444","inReplyTo":"20150928223607.15594.qmail@ns.horizon.com","subject":"Re: Why not git reset --hard <path>?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-09-28T22:58:14Z","receivedAt":"2015-09-28T22:58:14Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"George Spelvin\" <linux@horizon.com> writes:\n\n> Junio C Hamano <gitster@pobox.com> wrote:\n>> \"git checkout HEAD <pathspec>\" is a 99% acceptable substitute for it\n>> (the only case where it makes a difference is when you added a path to\n>> the index that did not exist in HEAD).\n>\n> Er, wait a minute...\n>\n> \"git checkout <tree-ish> <pathspec>\" modifies the index?\n>\n> Damn, I've been using git for years and I never knew that.\n\n... which would be an indication that the behaviour is most likely\nthe most natural one.\n\n> But I just tested, and it does.  Damn, now I have to figure out\n> how to \"leapfrog\" a file from history into the working tree without\n> overwriting the index; that's occasionally useful.\n\n... and indeed it is useful in some rare cases.  Either\n\n    git diff <tree-ish> <pathspec> | git apply -R\n\nor\n\n    git checkout <tree-ish> <pathspec> &&\n    git reset <pathspec>\n\nare what I would use, and as an old-timer, the former is what I do.\n"},{"id":"270901","messageId":"20150928235209.29725.qmail@ns.horizon.com","threadId":"40444","inReplyTo":"xmqqr3lib189.fsf@gitster.mtv.corp.google.com","subject":"Re: Why not git reset --hard <path>?","fromName":"George Spelvin","fromEmail":"linux@horizon.com","sentAt":"2015-09-28T23:52:09Z","receivedAt":"2015-09-28T23:52:09Z","isPatch":false,"sender":{"key":"linux@horizon.com","avatar":null},"body":"Junio C Hamano <gitster@pobox.com> wrote:\n> \"George Spelvin\" <linux@horizon.com> writes:\n>> \"git checkout <tree-ish> <pathspec>\" modifies the index?\n>>\n>> Damn, I've been using git for years and I never knew that.\n>\n> ... which would be an indication that the behaviour is most likely\n> the most natural one.\n\nI think it's more that often, staging a file to the index\nis pretty harmless, so it doesn't actually matter.\n\nBoth of the commonest forms of commit (\"git commit -a\" and \"git commit\n[-o] <paths>\") aren't affected, and if I'm doing something trickier,\nI'll usually examine the status and make sure I've got it right.\n\nSo I could have encountered it and put it down to fat-fingering on\nmy part.\n\n>> But I just tested, and it does.  Damn, now I have to figure out\n>> how to \"leapfrog\" a file from history into the working tree without\n>> overwriting the index; that's occasionally useful.\n\n> ... and indeed it is useful in some rare cases.  Either\n>\n>    git diff <tree-ish> <pathspec> | git apply -R\n>\n> or\n> \n>    git checkout <tree-ish> <pathspec> &&\n>    git reset <pathspec>\n\nThe former would work, and thanks for the idea, but for a single file\nI'd probably do one of\n\tgit show <tree-ish>:<path> > <path>\n\tgit cat-file blob <tree-ish>:<path> > <path>\n\nThe checkout/reset wouldn't work in the case I'm thinking about, which\nis when I want to import a small piece (say, helper function that got\ndeleted) from an old or other-branch version of a file.  I.e. a partial\nrevert or cherry-pick.\n\nIf I had some current changes to merge with, I'd stage them, pull the\n*other* version into the tree, and use something like git-gui to add the\nhunk I want to the staged version.\n\nThe whole point is that the staged state is important and I don't\nwant it overwritten.\n\nThere are other ways, of course:\n- Show or cat-file the other version into a temporary file and manually\n  copy and paste.  No need to stage anything.\n- Commit the current change, then add the additional changes and amend the\n  commit.\n"},{"id":"270903","messageId":"CA+P7+xrzmDHyseGaJFkyUGUi=Uep0LLhPdZDeo3NeBBmXJTZWw@mail.gmail.com","threadId":"40444","inReplyTo":"xmqqvbaub5s4.fsf@gitster.mtv.corp.google.com","subject":"Re: Why not git reset --hard <path>?","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2015-09-29T07:06:41Z","receivedAt":"2015-09-29T07:06:41Z","isPatch":false,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Mon, Sep 28, 2015 at 2:19 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> I agree with you if we limit the scope to \"reset --hard\" that does\n> not mention any commit on the command line (or says \"HEAD\").\n>\n> However, for things like:\n>\n>     $ git reset --hard HEAD^ Makefile\n>     $ git reset --hard HEAD@{4.hours.ago} Makefile\n>\n> I do not think \"reset --hard\" is a good match.  Conceptually, you\n> are grabbing what was stored in a given commit and checking that out\n> to your current workspace (that is, the index and the working tree).\n>\n\nAgreed. I just get used to thinking about using it against HEAD. it's\njust weird to me that something which sometimes switches branches is\nalso the thing to grab a version of a file.\n\nreset hard really would be weird in this case, because you really\ndon't know if the user meant \"rewind the history, but keep everything\n*except* that listed file..\n\nThat makes sense now that I think about it. Thanks.\n\nRegards,\nJake\n"},{"id":"270915","messageId":"20150929161543.23444.qmail@ns.horizon.com","threadId":"40444","inReplyTo":"xmqqvbaub5s4.fsf@gitster.mtv.corp.google.com","subject":"Re: Why not git reset --hard <path>?","fromName":"George Spelvin","fromEmail":"linux@horizon.com","sentAt":"2015-09-29T16:15:43Z","receivedAt":"2015-09-29T16:15:43Z","isPatch":false,"sender":{"key":"linux@horizon.com","avatar":null},"body":"> I agree with you if we limit the scope to \"reset --hard\" that does\n> not mention any commit on the command line (or says \"HEAD\").\n> \n> However, for things like:\n>\n>     $ git reset --hard HEAD^ Makefile\n>     $ git reset --hard HEAD@{4.hours.ago} Makefile\n>\n> I do not think \"reset --hard\" is a good match.  Conceptually, you\n> are grabbing what was stored in a given commit and checking that out\n> to your current workspace (that is, the index and the working tree).\n\nI actually disagree, BUT that was based on an inaccurate mental model,\nso I'm not sure if my judgement can be trusted.  Still, I'll blather on\njust to give a different perspective.\n\nTo me, \"git reset --hard\" is \"git reset\" plus checking out from the index\nto the working directory.  That's the difference and the only difference.\n\nSo any difference in behaviour between\n\tgit reset --hard <revision> -- <paths>\nand\n\tgit reset --mixed <revision> -- <paths>\n\tgit checkout -- <paths>\nneeds to be justified.  (There might be some if a file does not exist in\nthe revision.)\n\n> All modes of \"git reset\" are primarily about updating where in the\n> history DAG your HEAD points at, and then adjusting your current\n> workspace to that update, taking into account the reason why you are\n> repointing your HEAD in the history DAG (e.g. when doing --hard\n> reset, you want the workspace to match what the commit your HEAD now\n> points at; when doing --soft reset, you don't want any changes\n> done).\n\nEr... no.  Re-pointing HEAD can *only* be done as a global operation.\nThat's the single most fundamental difference between git and CVS.\n\nAny time you specify a path, *obviously* that part can't be done, so\ngit-reset just skips that part and goes on to the index-updating part..\n\n\nGit reset also skips that part in the single most common invocation\nscenario: un-doing git-add.  That's for a different reason (pointing\nHEAD to HEAD is a no-op), but it contributes to the mental model that\ngit-reset is fundamentally used for copying from history to the index.\n\n\nThat's my mental model, for what little it's worth (given the caveat above):\n- git checkout is fundamentally about copying from the index to the\n  working directory.  If can also move HEAD first (and create branches!)\n  as a convenience feature.\n- git reset is fundamentally about copying from HEAD to the index.\n  Like git-checkout, it can also move HEAD first (and copy to the working\n  directory) as a convenience feature.\n\nTo me, \"git reset\" is \"throw the changes away and get me back to\nsome previous state\".  That's why it's the tool I reached for in the\nmerge-conflict situation that started this thread.\n\nI didn't think to try \"git checkout\" because I needed to overwrite the\nmerge conflict in the index, and I don't think of \"git checkout\" as\ndoing that.  (Globally, it fails if there are unresolved conflits.)\n"},{"id":"270921","messageId":"600ECF9ED63D4A3BAC16F3E29DB656D4@PhilipOakley","threadId":"40444","inReplyTo":"CA+P7+xoTHFL_KU+qBz1KwytxqNTxf1JkjXK7_Ej79uLLnCWD8g@mail.gmail.com","subject":"Re: Why not git reset --hard <path>?","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":"2015-09-29T19:40:34Z","receivedAt":"2015-09-29T19:40:34Z","isPatch":false,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"From: \"Jacob Keller\" <jacob.keller@gmail.com>\n> On Mon, Sep 28, 2015 at 1:42 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> \"George Spelvin\" <linux@horizon.com> writes:\n>>> I understand that \"git reset --soft\" makes no sense with a path, but\n>>> why not --hard?\n>>\n>> I do not think there is anything fundamentally wrong for wishing for\n>> \"reset --hard <pathspec>\".  It probably is just that nobody needed\n>> it, because \"git checkout HEAD <pathspec>\" is a 99% acceptable\n>> substitute for it (the only case where it makes a difference is when\n>> you added a path to the index that did not exist in HEAD).\n>>\n>\n> Personally, I would like to see this simply given the number of times\n> that I use git reset --hard <path> and then realize I should have used\n> git checkout instead. I conceptually think reset --hard should do\n> that, and that checkout is really not meant to do that as a concept.\n>\n> I may have some time to try and give this a look in the next few days...\n>\n> Regards,\n> Jake\n> --\n\nI also recently had this problem of expecting to be able to use something \nlike `git reset --hard -- <path>` to clear up some crud and having to cast \naround for the right approach.\n\nWould it at least be worth flagging up the alternate ` git checkout  --  \npath` a little better within the 'get reset' man pages? At the moment its \nhidden at the end of the git reset [-q] [<tree-ish>] [--] <paths>…​ section, \nso is easily missed.\n\n(i.e. should I flesh out a patch, or would the nuances bury it...?)\n\nPhilip\n"}]}