{"thread":{"id":"45785","subject":"Submodule/contents conflict","startedAt":"2017-04-24T08:06:58Z","lastAt":"2017-05-01T00:15:12Z","messageCount":18,"participants":["Orgad Shaneh","Stefan Beller","Philip Oakley","Junio C Hamano","Jeff King"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"317649","messageId":"CAGHpTBJCjNa8gQRkMah30ehESdsVVKNy+6CuLSf9hfDedR+tPA@mail.gmail.com","threadId":"45785","inReplyTo":null,"subject":"Submodule/contents conflict","fromName":"Orgad Shaneh","fromEmail":"orgads@gmail.com","sentAt":"2017-04-24T08:06:50Z","receivedAt":"2017-04-24T08:06:58Z","isPatch":false,"sender":{"key":"orgads@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1246544?v=4"},"body":"Hi,\n\nI've noticed a strange behavior with submodule/content conflict. My\ncurrent Git version is 2.12.2, but the problem exists since I\nremember.\n\nBranch A has a submodule.\nIn branch B which diverged from A, I replaced the submodule with its contents.\n\nNow, every time I merge A into B, and A had changed the submodule\nreference, all the files inside the ex-submodule directory in B are\nbeing \"re-added\".\n\nMoreover, aborting the merge prints an error, but seems to work\nnevertheless, and if I run git reset --hard all the files in that\ndirectory are actually written to the disk, even though they haven't\nchanged at all.\n\nWhen the submodule is small, it might be ok. But in my project we have\na huge submodule with ~16K files, and on each merge all the files are\nlisted, and even mixed reset takes several minutes.\n\nThe following script demonstrates this:\n#!/bin/sh\n\nrm -rf super sub\nmkdir sub\ncd sub\ngit init\ntouch foo\ngit add foo\ngit commit -m 'Initial commit'\nmkdir ../super; cd ../super\ngit init\ngit submodule add ../sub\ntouch foo; git add foo sub\ngit commit -m 'Initial commit'\ngit checkout -b update-sub\ngit update-index --cacheinfo 160000,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,sub\ngit commit -m 'Update submodule'\ngit checkout -b remove-sub HEAD^\ngit rm sub\nmkdir sub\ntouch sub/foo sub/bar\ngit add sub\ngit commit -m 'Replaced submodule with contents'\ngit checkout -b remove-2 HEAD^\ngit merge --no-ff remove-sub\ngit merge update-sub\n# Adding sub/foo\n# Adding sub/bar\n# CONFLICT (modify/delete): sub deleted in HEAD and modified in\nupdate-sub. Version update-sub of sub left in tree at sub~update-sub.\n# Automatic merge failed; fix conflicts and then commit the result.\ngit merge --abort\n# error: 'sub' appears as both a file and as a directory\n# error: sub: cannot drop to stage #0\n\n- Orgad\n"},{"id":"317709","messageId":"CAGZ79kZ5440r1EHOVP3eXxe5u=u16y_jXTA0C4hLJA2kUkF-kg@mail.gmail.com","threadId":"45785","inReplyTo":"CAGHpTBJCjNa8gQRkMah30ehESdsVVKNy+6CuLSf9hfDedR+tPA@mail.gmail.com","subject":"Re: Submodule/contents conflict","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-04-24T17:40:36Z","receivedAt":"2017-04-24T17:40:42Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, Apr 24, 2017 at 1:06 AM, Orgad Shaneh <orgads@gmail.com> wrote:\n> Hi,\n>\n> I've noticed a strange behavior with submodule/content conflict. My\n> current Git version is 2.12.2, but the problem exists since I\n> remember.\n>\n> Branch A has a submodule.\n> In branch B which diverged from A, I replaced the submodule with its contents.\n>\n> Now, every time I merge A into B, and A had changed the submodule\n> reference, all the files inside the ex-submodule directory in B are\n> being \"re-added\".\n>\n> Moreover, aborting the merge prints an error, but seems to work\n> nevertheless, and if I run git reset --hard all the files in that\n> directory are actually written to the disk, even though they haven't\n> changed at all.\n>\n> When the submodule is small, it might be ok. But in my project we have\n> a huge submodule with ~16K files, and on each merge all the files are\n> listed, and even mixed reset takes several minutes.\n>\n\nA similar bug report\nhttps://public-inbox.org/git/CAG0BQX=wvpkJ=PQWV-NbmhuPV8yzvd_KYKzJmsfWq9xStZ2bnQ@mail.gmail.com/\n\n\"checkout --recurse-submodules\" (as mentioned in that report)\nmade it into Git by now, but this bug goes unfixed, still.\n\n> The following script demonstrates this:\n> #!/bin/sh\n>\n> rm -rf super sub\n> mkdir sub\n> cd sub\n> git init\n> touch foo\n> git add foo\n> git commit -m 'Initial commit'\n> mkdir ../super; cd ../super\n> git init\n> git submodule add ../sub\n> touch foo; git add foo sub\n> git commit -m 'Initial commit'\n> git checkout -b update-sub\n> git update-index --cacheinfo 160000,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,sub\n> git commit -m 'Update submodule'\n> git checkout -b remove-sub HEAD^\n> git rm sub\n> mkdir sub\n> touch sub/foo sub/bar\n> git add sub\n> git commit -m 'Replaced submodule with contents'\n> git checkout -b remove-2 HEAD^\n> git merge --no-ff remove-sub\n> git merge update-sub\n> # Adding sub/foo\n> # Adding sub/bar\n> # CONFLICT (modify/delete): sub deleted in HEAD and modified in\n> update-sub. Version update-sub of sub left in tree at sub~update-sub.\n> # Automatic merge failed; fix conflicts and then commit the result.\n> git merge --abort\n> # error: 'sub' appears as both a file and as a directory\n> # error: sub: cannot drop to stage #0\n>\n> - Orgad\n"},{"id":"317738","messageId":"4BF0A1BFFFFD421EB8C5F7E6FEF14357@PhilipOakley","threadId":"45785","inReplyTo":"CAGZ79kZ5440r1EHOVP3eXxe5u=u16y_jXTA0C4hLJA2kUkF-kg@mail.gmail.com","subject":"Re: Submodule/contents conflict","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":"2017-04-24T23:33:38Z","receivedAt":"2017-04-24T23:33:45Z","isPatch":false,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"From: \"Stefan Beller\" <sbeller@google.com>\n> On Mon, Apr 24, 2017 at 1:06 AM, Orgad Shaneh <orgads@gmail.com> wrote:\n>> Hi,\n>>\n>> I've noticed a strange behavior with submodule/content conflict. My\n>> current Git version is 2.12.2, but the problem exists since I\n>> remember.\n>>\n>> Branch A has a submodule.\n>> In branch B which diverged from A, I replaced the submodule with its \n>> contents.\n>>\n>> Now, every time I merge A into B, and A had changed the submodule\n>> reference, all the files inside the ex-submodule directory in B are\n>> being \"re-added\".\n>>\n>> Moreover, aborting the merge prints an error, but seems to work\n>> nevertheless, and if I run git reset --hard all the files in that\n>> directory are actually written to the disk, even though they haven't\n>> changed at all.\n>>\n\nThis is almost the same as just reported by 'vvs' [1] \nhttps://public-inbox.org/git/CAM1zWBtfgHT=pT0pidQo1HD=DfrXLG3gNaUvs0vZKvYfG1BHFw@mail.gmail.com/, \noriginally on the 'git user' list \nhttps://groups.google.com/forum/?hl=en#!topic/git-users/9ziZ6yq-BfU\n\nIt also has a similarity to \nhttps://public-inbox.org/git/1492287435.14812.2.camel@gmail.com/  regarding \nhow checkout operates.\n\nIt does feel as if there are two slightly different optimisations that could \nbe used when the desired file pre-exists in the worktree, but isn't \nimmediately known to the index.\n\n\n>> When the submodule is small, it might be ok. But in my project we have\n>> a huge submodule with ~16K files, and on each merge all the files are\n>> listed, and even mixed reset takes several minutes.\n\nThat sounds like a wait that is not wanted!\n>>\n>\n> A similar bug report\n> https://public-inbox.org/git/CAG0BQX=wvpkJ=PQWV-NbmhuPV8yzvd_KYKzJmsfWq9xStZ2bnQ@mail.gmail.com/\n>\n> \"checkout --recurse-submodules\" (as mentioned in that report)\n> made it into Git by now, but this bug goes unfixed, still.\n>\n>> The following script demonstrates this:\n>> #!/bin/sh\n>>\n>> rm -rf super sub\n>> mkdir sub\n>> cd sub\n>> git init\n>> touch foo\n>> git add foo\n>> git commit -m 'Initial commit'\n>> mkdir ../super; cd ../super\n>> git init\n>> git submodule add ../sub\n>> touch foo; git add foo sub\n>> git commit -m 'Initial commit'\n>> git checkout -b update-sub\n>> git update-index --cacheinfo \n>> 160000,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,sub\n>> git commit -m 'Update submodule'\n>> git checkout -b remove-sub HEAD^\n>> git rm sub\n>> mkdir sub\n>> touch sub/foo sub/bar\n>> git add sub\n>> git commit -m 'Replaced submodule with contents'\n>> git checkout -b remove-2 HEAD^\n>> git merge --no-ff remove-sub\n>> git merge update-sub\n>> # Adding sub/foo\n>> # Adding sub/bar\n>> # CONFLICT (modify/delete): sub deleted in HEAD and modified in\n>> update-sub. Version update-sub of sub left in tree at sub~update-sub.\n>> # Automatic merge failed; fix conflicts and then commit the result.\n>> git merge --abort\n>> # error: 'sub' appears as both a file and as a directory\n>> # error: sub: cannot drop to stage #0\n>>\n>> - Orgad\n>\n--\nPhilip \n\n"},{"id":"317739","messageId":"CAGZ79kZDzuSHw4siSkiekw73bBq9R8gg_R+a7NyNUG6bYxc2Og@mail.gmail.com","threadId":"45785","inReplyTo":"4BF0A1BFFFFD421EB8C5F7E6FEF14357@PhilipOakley","subject":"Re: Submodule/contents conflict","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-04-24T23:43:28Z","receivedAt":"2017-04-24T23:43:35Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, Apr 24, 2017 at 4:33 PM, Philip Oakley <philipoakley@iee.org> wrote:\n\n> This is almost the same as just reported by 'vvs' [1]\n> https://public-inbox.org/git/CAM1zWBtfgHT=pT0pidQo1HD=DfrXLG3gNaUvs0vZKvYfG1BHFw@mail.gmail.com/,\n> originally on the 'git user' list\n> https://groups.google.com/forum/?hl=en#!topic/git-users/9ziZ6yq-BfU\n\nFor this issue, +cc Jeff King due to this pointer:\n\n>> On the main list thare is a similar \"issue\" [1] regarding the expectation for `git checkout`,\n>> and importantly (for me) these collected views regarding the \"Git Data Protection and\n>> Management Principles\" is not within the Git documentation.\n>\n> Yes, that's an interesting point. What concerns me is that the commit\n> c5326bd62b7e168ba1339dacb7ee812d0fe98c7c which introduced this\n> into checkout isn't consistent with reset. Seems that nobody noticed this before.\n\nIt seems as if we'd want to see the code from\nc5326bd62b7e168ba1339dacb7ee812d0fe98c7c\nto be part of any worktree touching command, specifically reset?\n\n> It also has a similarity to\n> https://public-inbox.org/git/1492287435.14812.2.camel@gmail.com/  regarding\n> how checkout operates.\n\nThis seems to be orthogonal to the original topic (no submodules, nor checkout\nbugs, just a doc update?)\n\n\n> It does feel as if there are two slightly different optimisations that could\n> be used when the desired file pre-exists in the worktree, but isn't\n> immediately known to the index.\n>\n"},{"id":"317754","messageId":"xmqqfugxxmkn.fsf@gitster.mtv.corp.google.com","threadId":"45785","inReplyTo":"CAGZ79kZ5440r1EHOVP3eXxe5u=u16y_jXTA0C4hLJA2kUkF-kg@mail.gmail.com","subject":"Re: Submodule/contents conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-04-25T02:12:08Z","receivedAt":"2017-04-25T02:12:14Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n> A similar bug report\n> https://public-inbox.org/git/CAG0BQX=wvpkJ=PQWV-NbmhuPV8yzvd_KYKzJmsfWq9xStZ2bnQ@mail.gmail.com/\n>\n> \"checkout --recurse-submodules\" (as mentioned in that report)\n> made it into Git by now, but this bug goes unfixed, still.\n\nI do not mind reverting that merge out of 'master'.  Please holler\nif you feel these \"go recursive\" topics are premature.\n\nThanks.\n"},{"id":"317763","messageId":"20170425032242.wlmqrkgkeg3ytfle@sigill.intra.peff.net","threadId":"45785","inReplyTo":"CAGZ79kZDzuSHw4siSkiekw73bBq9R8gg_R+a7NyNUG6bYxc2Og@mail.gmail.com","subject":"Re: Submodule/contents conflict","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2017-04-25T03:22:42Z","receivedAt":"2017-04-25T03:22:51Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Apr 24, 2017 at 04:43:28PM -0700, Stefan Beller wrote:\n\n> >> On the main list thare is a similar \"issue\" [1] regarding the expectation for `git checkout`,\n> >> and importantly (for me) these collected views regarding the \"Git Data Protection and\n> >> Management Principles\" is not within the Git documentation.\n> >\n> > Yes, that's an interesting point. What concerns me is that the commit\n> > c5326bd62b7e168ba1339dacb7ee812d0fe98c7c which introduced this\n> > into checkout isn't consistent with reset. Seems that nobody noticed this before.\n> \n> It seems as if we'd want to see the code from\n> c5326bd62b7e168ba1339dacb7ee812d0fe98c7c\n> to be part of any worktree touching command, specifically reset?\n\nNote that that commit is just about \"git checkout <commit> -- <paths>\".\nThe matching reset command, \"git reset <commit> -- <paths>\" does handle\nthis the same way. Or at least I claimed so here:\n\n  http://public-inbox.org/git/20141107081324.GA19845@peff.net/\n\nand that is what I modeled the code in c5326bd62b after.\n\nBut note that none of that should ever affect _what_ gets checked out at\na file or content level. It may only affect the timestamps on the\nresulting files. And I think those timestamps are not something Git\nmakes any promises about.\n\nSo if Git can elide a write and keep a timestamp up to date, that is a\ngreat optimization and we should do that. But from an outside viewer's\nperspective, we guarantee nothing. We might choose to rewrite a\nstat-dirty file (updating its timestamp) rather than read its contents\nto see if it might have the same content that we're about to write. And\nthe file may look stat dirty only because of the racy-git file/index\ntimestamp problem, even if it wasn't actually modified.\n\n> > It also has a similarity to\n> > https://public-inbox.org/git/1492287435.14812.2.camel@gmail.com/  regarding\n> > how checkout operates.\n\nI didn't look too deeply into this one, but it really looks like\nsomebody caring too much about when git needs to write (and I'd suspect\nit's impacted by the racy-git thing, too).\n\n-Peff\n"},{"id":"317765","messageId":"20170425033907.klufatsa7afcnfuu@sigill.intra.peff.net","threadId":"45785","inReplyTo":"20170425032242.wlmqrkgkeg3ytfle@sigill.intra.peff.net","subject":"Re: Submodule/contents conflict","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2017-04-25T03:39:08Z","receivedAt":"2017-04-25T03:39:19Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Apr 24, 2017 at 11:22:42PM -0400, Jeff King wrote:\n\n> > > It also has a similarity to\n> > > https://public-inbox.org/git/1492287435.14812.2.camel@gmail.com/  regarding\n> > > how checkout operates.\n> \n> I didn't look too deeply into this one, but it really looks like\n> somebody caring too much about when git needs to write (and I'd suspect\n> it's impacted by the racy-git thing, too).\n\nOh, sorry, I got this confused with:\n\n  https://public-inbox.org/git/CAM1zWBtfgHT=pT0pidQo1HD=DfrXLG3gNaUvs0vZKvYfG1BHFw@mail.gmail.com/\n\nwhich was mentioned earlier (and which I do think is just about\ntimestamps).\n\nI don't think any of what I said is related to the doc update in\n\n  https://public-inbox.org/git/1492287435.14812.2.camel@gmail.com/\n\nwhich seems reasonable to me.\n\n-Peff\n"},{"id":"317800","messageId":"C3A8A88B8DDD468095B7FD256330EEF7@PhilipOakley","threadId":"45785","inReplyTo":"CAGZ79kZDzuSHw4siSkiekw73bBq9R8gg_R+a7NyNUG6bYxc2Og@mail.gmail.com","subject":"Re: Submodule/contents conflict","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":"2017-04-25T11:10:01Z","receivedAt":"2017-04-25T11:10:08Z","isPatch":false,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"From: \"Stefan Beller\" <sbeller@google.com>\n> On Mon, Apr 24, 2017 at 4:33 PM, Philip Oakley <philipoakley@iee.org> \n> wrote:\n>\n>> This is almost the same as just reported by 'vvs' [1]\n>> https://public-inbox.org/git/CAM1zWBtfgHT=pT0pidQo1HD=DfrXLG3gNaUvs0vZKvYfG1BHFw@mail.gmail.com/,\n>> originally on the 'git user' list\n>> https://groups.google.com/forum/?hl=en#!topic/git-users/9ziZ6yq-BfU\n>\n> For this issue, +cc Jeff King due to this pointer:\n>\n>>> On the main list thare is a similar \"issue\" [1] regarding the \n>>> expectation for `git checkout`,\n>>> and importantly (for me) these collected views regarding the \"Git Data \n>>> Protection and\n>>> Management Principles\" is not within the Git documentation.\n>>\n>> Yes, that's an interesting point. What concerns me is that the commit\n>> c5326bd62b7e168ba1339dacb7ee812d0fe98c7c which introduced this\n>> into checkout isn't consistent with reset. Seems that nobody noticed this \n>> before.\n>\n> It seems as if we'd want to see the code from\n> c5326bd62b7e168ba1339dacb7ee812d0fe98c7c\n> to be part of any worktree touching command, specifically reset?\n>\n>> It also has a similarity to\n>> https://public-inbox.org/git/1492287435.14812.2.camel@gmail.com/ \n>> regarding\n>> how checkout operates.\n>\n> This seems to be orthogonal to the original topic (no submodules, nor \n> checkout\n> bugs, just a doc update?)\n\nAt the moment that topic has ended up as an attempt at a documentation \nupdate, but I think it may not succeed ynless there is clarity about the \n(reported) underlying problem.\n\nAs I recall Christoph was using checkout to copy a file (e.g. a template \nfile) from an older commit/revision into his worktree, and was suprised that \nthis (git checkout <tree> <path>) also _staged_ the file, rather than simply \nletting it be in a modified/untracked state.\n\nIt in a sense is the same issue of relating the worktree content to the \nindex status, though I think that checkout is acting as designed/intended. \nI don't think that there is a command (is ther?) to do what Christoph hoped \nfor of not staging the new content (e.g. a template file), but the index \nknowing/remembering its stat details, just in case.\n\nThe submodule case appears to be more about deciding if the content of \nsubmodule's index should be considered for inclusion in the super project \nprior to the checkout of the other branch's tree that is already unchanged \nand in-place.\n\n>\n>\n>> It does feel as if there are two slightly different optimisations that \n>> could\n>> be used when the desired file pre-exists in the worktree, but isn't\n>> immediately known to the index.\n>>\n>\n--\nPhilip \n\n"},{"id":"317817","messageId":"CAGZ79kbr_LJ+mviXWxdyDWgSjAdc8VAXtfE_NG9qVOOCBM=JVQ@mail.gmail.com","threadId":"45785","inReplyTo":"xmqqfugxxmkn.fsf@gitster.mtv.corp.google.com","subject":"Re: Submodule/contents conflict","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-04-25T15:57:18Z","receivedAt":"2017-04-25T15:57:24Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, Apr 24, 2017 at 7:12 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Stefan Beller <sbeller@google.com> writes:\n>\n>> A similar bug report\n>> https://public-inbox.org/git/CAG0BQX=wvpkJ=PQWV-NbmhuPV8yzvd_KYKzJmsfWq9xStZ2bnQ@mail.gmail.com/\n>>\n>> \"checkout --recurse-submodules\" (as mentioned in that report)\n>> made it into Git by now, but this bug goes unfixed, still.\n>\n> I do not mind reverting that merge out of 'master'.  Please holler\n> if you feel these \"go recursive\" topics are premature.\n\nThe bugs reported here are not for the recursive checkout,\nI was merely noting it as a side effect of time having passed. :)\n\nI did not imply the recursive topics being premature.\n\nThanks,\nStefan\n"},{"id":"317893","messageId":"xmqqa873ubiw.fsf@gitster.mtv.corp.google.com","threadId":"45785","inReplyTo":"C3A8A88B8DDD468095B7FD256330EEF7@PhilipOakley","subject":"Re: Submodule/contents conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-04-26T02:51:19Z","receivedAt":"2017-04-26T02:51:28Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Philip Oakley\" <philipoakley@iee.org> writes:\n\n> As I recall Christoph was using checkout to copy a file (e.g. a\n> template file) from an older commit/revision into his worktree, and\n> was suprised that this (git checkout <tree> <path>) also _staged_ the\n> file, rather than simply letting it be in a modified/untracked state.\n\nThis probably is taking it even further than the original topic, but\nI raise this weather-balloon to see if anybody is interested.\n\nIn the modern day, it might be useful if the \"--working-tree-only\"\nmode added a new file as an intent-to-add entry to the index, but\nthat is not what \"git apply (no other options)\" (which is the gold\nstandard for command that operates on the working tree and/or on the\nindex) does, so it is not done in this patch.  IOW, if you grab a\npath that does not exist in your index out of <tree-ish>, you will\nwrite out an untracked file to the working tree.\n\n-- >8 --\nSubject: [PATCH] checkout: add --working-tree-only option\n\n\"git checkout <tree-ish> <pathspec>\" has always copied the blob from\nthe tree-ish to the index before checking them out to the working tree.\n\nSome users may want to grab a blob out of a tree-ish directly to the\nworking tree, without updating the index, so that \"git diff\" can be\nused to assess the damage and adjust the file contents taken from a\ndifferent branch to be more appropriate for the current branch.\n\nThe new option \"--working-tree-only\" allows exactly that.\n\nIn the hindsight, when a command works on the working tree and/or\nthe index, the usual convention is:\n\n - with no other option, the command works only on the working tree;\n\n - with \"--cached\" option, the command works only on the index; and\n\n - with \"--index\" option, the command works on both the working tree\n   and the index.\n\nSo we probably should have triggered the default behaviour under the\n\"--index\" option, and triggered this \"--working-tree-only\" mode of\nbehaviour when \"--index\" option is not given.  From the same point\nof view, \"git checkout --cached <tree-ish> <pathspec>\" would have\ndone the same as \"git reset <tree-ish> <pathspec>\" would do.  And\nthat may have made the command set a bit more consistent.\n\nBut that is merely a hindsight being 20/20, oh well.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/git-checkout.txt | 22 +++++++++++++++-------\n builtin/checkout.c             | 10 +++++++++-\n t/t2022-checkout-paths.sh      | 21 +++++++++++++++++++++\n 3 files changed, 45 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex 8e2c0662dd..201677752e 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -14,6 +14,7 @@ SYNOPSIS\n 'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]\n 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...\n 'git checkout' [-p|--patch] [<tree-ish>] [--] [<paths>...]\n+'git checkout' --working-tree-only <tree-ish> [--] [<paths>...]\n \n DESCRIPTION\n -----------\n@@ -81,13 +82,14 @@ Omitting <branch> detaches HEAD at the tip of the current branch.\n 'git checkout' [-p|--patch] [<tree-ish>] [--] <pathspec>...::\n \n \tWhen <paths> or `--patch` are given, 'git checkout' does *not*\n-\tswitch branches.  It updates the named paths in the working tree\n-\tfrom the index file or from a named <tree-ish> (most often a\n-\tcommit).  In this case, the `-b` and `--track` options are\n-\tmeaningless and giving either of them results in an error.  The\n-\t<tree-ish> argument can be used to specify a specific tree-ish\n-\t(i.e.  commit, tag or tree) to update the index for the given\n-\tpaths before updating the working tree.\n+\tswitch branches.  In this case, the `-b` and `--track` options\n+\tare meaningless and giving either of them results in an error.\n++\n+The command checks out blobs for paths that match the given\n+<pathspec> from the index to the working tree.  When an optional\n+<tree-ish> is given, the blobs for paths that match the given\n+<pathspec> are copied from the <tree-ish> to the index before\n+they are checked out of the index.\n +\n 'git checkout' with <paths> or `--patch` is used to restore modified or\n deleted paths to their original contents from the index or replace paths\n@@ -101,6 +103,12 @@ specific side of the merge can be checked out of the index by\n using `--ours` or `--theirs`.  With `-m`, changes made to the working tree\n file can be discarded to re-create the original conflicted merge result.\n \n+'git checkout' --working-tree-only <tree-ish> [--] <pathspec>...::\n+\tSimilar to `git checkout <tree-ish> [--] <pathspec>`, but\n+\tthe index file is left in the same state as it was before\n+\trunning this command.\n+\n+\n OPTIONS\n -------\n -q::\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 9b2a5b31d4..d214e99521 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -37,6 +37,7 @@ struct checkout_opts {\n \tint overwrite_ignore;\n \tint ignore_skipworktree;\n \tint ignore_other_worktrees;\n+\tint no_index;\n \tint show_progress;\n \n \tconst char *new_branch;\n@@ -268,6 +269,9 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\tdie(_(\"Cannot update paths and switch to branch '%s' at the same time.\"),\n \t\t    opts->new_branch);\n \n+\tif (opts->no_index && !opts->source_tree)\n+\t\tdie(_(\"'--working-tree-only' cannot be used without tree-ish\"));\n+\n \tif (opts->patch_mode)\n \t\treturn run_add_interactive(revision, \"--patch=checkout\",\n \t\t\t\t\t   &opts->pathspec);\n@@ -370,7 +374,9 @@ static int checkout_paths(const struct checkout_opts *opts,\n \t\t}\n \t}\n \n-\tif (write_locked_index(&the_index, lock_file, COMMIT_LOCK))\n+\tif (opts->no_index)\n+\t\t; /* discard the in-core index */\n+\telse if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))\n \t\tdie(_(\"unable to write new index file\"));\n \n \tread_ref_full(\"HEAD\", 0, rev.hash, NULL);\n@@ -1161,6 +1167,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL(0, \"ignore-other-worktrees\", &opts.ignore_other_worktrees,\n \t\t\t N_(\"do not check if another worktree is holding the given ref\")),\n \t\tOPT_BOOL(0, \"progress\", &opts.show_progress, N_(\"force progress reporting\")),\n+\t\tOPT_BOOL(0, \"working-tree-only\", &opts.no_index, N_(\"checkout to working tree only without touching the index\")),\n+\n \t\tOPT_END(),\n \t};\n \ndiff --git a/t/t2022-checkout-paths.sh b/t/t2022-checkout-paths.sh\nindex f46d0499bc..8ea2e34c90 100755\n--- a/t/t2022-checkout-paths.sh\n+++ b/t/t2022-checkout-paths.sh\n@@ -78,4 +78,25 @@ test_expect_success 'do not touch files that are already up-to-date' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'working-tree-only option leaves checked out files unadded' '\n+\tgit reset --hard &&\n+\tgit checkout -b pu next &&\n+\techo another >>file1 &&\n+\techo exists >file3 &&\n+\tgit add file3 &&\n+\tgit commit -a -m another &&\n+\tgit checkout next &&\n+\n+\t! grep another file1 &&\n+\tgit checkout --working-tree-only pu file1 file3 &&\n+\tgrep another file1 &&\n+\ttest_must_fail git grep --cached another file1 &&\n+\n+\tgrep exists file3 &&\n+\tgit ls-files file3 >actual &&\n+\t>expect &&\n+\ttest_cmp expect actual\n+\n+'\n+\n test_done\n-- \n2.13.0-rc0-309-gb63395ed9e\n\n"},{"id":"317945","messageId":"CAGZ79kZEveo8jQodvd0n6fEXc1OXDVa26BCumM0etjst74F_Hw@mail.gmail.com","threadId":"45785","inReplyTo":"xmqqa873ubiw.fsf@gitster.mtv.corp.google.com","subject":"Re: Submodule/contents conflict","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2017-04-26T17:41:11Z","receivedAt":"2017-04-26T17:41:31Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Tue, Apr 25, 2017 at 7:51 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> \"Philip Oakley\" <philipoakley@iee.org> writes:\n>\n>> As I recall Christoph was using checkout to copy a file (e.g. a\n>> template file) from an older commit/revision into his worktree, and\n>> was suprised that this (git checkout <tree> <path>) also _staged_ the\n>> file, rather than simply letting it be in a modified/untracked state.\n>\n> This probably is taking it even further than the original topic, but\n> I raise this weather-balloon to see if anybody is interested.\n>\n> In the modern day, it might be useful if the \"--working-tree-only\"\n> mode added a new file as an intent-to-add entry to the index, but\n> that is not what \"git apply (no other options)\" (which is the gold\n> standard for command that operates on the working tree and/or on the\n> index) does, so it is not done in this patch.  IOW, if you grab a\n> path that does not exist in your index out of <tree-ish>, you will\n> write out an untracked file to the working tree.\n>\n> -- >8 --\n> Subject: [PATCH] checkout: add --working-tree-only option\n>\n> \"git checkout <tree-ish> <pathspec>\" has always copied the blob from\n> the tree-ish to the index before checking them out to the working tree.\n>\n> Some users may want to grab a blob out of a tree-ish directly to the\n> working tree, without updating the index, so that \"git diff\" can be\n> used to assess the damage and adjust the file contents taken from a\n> different branch to be more appropriate for the current branch.\n\nThat makes sense for the in-repo-point-of-view.\nI assumed a use case like this:\n\n  A user may want to extract a file from a given tree-ish via\n  GIT_WORK_TREE=/tmp/place git checkout <tree> -- <file>\n  without modifying the repository (i.e. index) at all. For this\n  we'd need an option to modify the working tree only.\n\n> The new option \"--working-tree-only\" allows exactly that.\n>\n> In the hindsight, when a command works on the working tree and/or\n\ns/the// ?\n\n> the index, the usual convention is:\n>\n>  - with no other option, the command works only on the working tree;\n>\n>  - with \"--cached\" option, the command works only on the index; and\n>\n>  - with \"--index\" option, the command works on both the working tree\n>    and the index.\n\nI never realized this as a usual convention explicitly. Thanks for pointing\nit out.\n\n> So we probably should have triggered the default behaviour under the\n> \"--index\" option, and triggered this \"--working-tree-only\" mode of\n> behaviour when \"--index\" option is not given.  From the same point\n> of view, \"git checkout --cached <tree-ish> <pathspec>\" would have\n> done the same as \"git reset <tree-ish> <pathspec>\" would do.  And\n> that may have made the command set a bit more consistent.\n>\n> But that is merely a hindsight being 20/20, oh well.\n>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n>  Documentation/git-checkout.txt | 22 +++++++++++++++-------\n>  builtin/checkout.c             | 10 +++++++++-\n>  t/t2022-checkout-paths.sh      | 21 +++++++++++++++++++++\n>  3 files changed, 45 insertions(+), 8 deletions(-)\n>\n> diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\n> index 8e2c0662dd..201677752e 100644\n> --- a/Documentation/git-checkout.txt\n> +++ b/Documentation/git-checkout.txt\n> @@ -14,6 +14,7 @@ SYNOPSIS\n>  'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]\n>  'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...\n>  'git checkout' [-p|--patch] [<tree-ish>] [--] [<paths>...]\n> +'git checkout' --working-tree-only <tree-ish> [--] [<paths>...]\n>\n>  DESCRIPTION\n>  -----------\n> @@ -81,13 +82,14 @@ Omitting <branch> detaches HEAD at the tip of the current branch.\n>  'git checkout' [-p|--patch] [<tree-ish>] [--] <pathspec>...::\n>\n>         When <paths> or `--patch` are given, 'git checkout' does *not*\n> -       switch branches.  It updates the named paths in the working tree\n> -       from the index file or from a named <tree-ish> (most often a\n> -       commit).  In this case, the `-b` and `--track` options are\n> -       meaningless and giving either of them results in an error.  The\n> -       <tree-ish> argument can be used to specify a specific tree-ish\n> -       (i.e.  commit, tag or tree) to update the index for the given\n> -       paths before updating the working tree.\n> +       switch branches.  In this case, the `-b` and `--track` options\n> +       are meaningless and giving either of them results in an error.\n> ++\n> +The command checks out blobs for paths that match the given\n> +<pathspec> from the index to the working tree.  When an optional\n> +<tree-ish> is given, the blobs for paths that match the given\n> +<pathspec> are copied from the <tree-ish> to the index before\n> +they are checked out of the index.\n>  +\n>  'git checkout' with <paths> or `--patch` is used to restore modified or\n>  deleted paths to their original contents from the index or replace paths\n> @@ -101,6 +103,12 @@ specific side of the merge can be checked out of the index by\n>  using `--ours` or `--theirs`.  With `-m`, changes made to the working tree\n>  file can be discarded to re-create the original conflicted merge result.\n>\n> +'git checkout' --working-tree-only <tree-ish> [--] <pathspec>...::\n> +       Similar to `git checkout <tree-ish> [--] <pathspec>`, but\n> +       the index file is left in the same state as it was before\n> +       running this command.\n\nAdding this as a new mode seems like a \"patch after the fact\",\nwhereas the wording hints that this may be included in the prior\npart, but I find it hard to come up with a good description there.\n\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 9b2a5b31d4..d214e99521 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -37,6 +37,7 @@ struct checkout_opts {\n>         int overwrite_ignore;\n>         int ignore_skipworktree;\n>         int ignore_other_worktrees;\n> +       int no_index;\n>         int show_progress;\n>\n>         const char *new_branch;\n> @@ -268,6 +269,9 @@ static int checkout_paths(const struct checkout_opts *opts,\n>                 die(_(\"Cannot update paths and switch to branch '%s' at the same time.\"),\n>                     opts->new_branch);\n>\n> +       if (opts->no_index && !opts->source_tree)\n> +               die(_(\"'--working-tree-only' cannot be used without tree-ish\"));\n\ndouble negation, maybe:\n\n  \"--working-tree-only requires tree-ish\"\n\n> @@ -370,7 +374,9 @@ static int checkout_paths(const struct checkout_opts *opts,\n>                 }\n>         }\n>\n> -       if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))\n> +       if (opts->no_index)\n> +               ; /* discard the in-core index */\n> +       else if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))\n>                 die(_(\"unable to write new index file\"));\n>\n>         read_ref_full(\"HEAD\", 0, rev.hash, NULL);\n> @@ -1161,6 +1167,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>                 OPT_BOOL(0, \"ignore-other-worktrees\", &opts.ignore_other_worktrees,\n>                          N_(\"do not check if another worktree is holding the given ref\")),\n>                 OPT_BOOL(0, \"progress\", &opts.show_progress, N_(\"force progress reporting\")),\n> +               OPT_BOOL(0, \"working-tree-only\", &opts.no_index, N_(\"checkout to working tree only without touching the index\")),\n> +\n\nnit: no need for extra empty line here.\n\n> +test_expect_success 'working-tree-only option leaves checked out files unadded' '\n> +       git reset --hard &&\n> +       git checkout -b pu next &&\n> +       echo another >>file1 &&\n> +       echo exists >file3 &&\n> +       git add file3 &&\n> +       git commit -a -m another &&\n> +       git checkout next &&\n\nUp to here it is all preparation; I started to give an argument\non why using \"another\" for both the commit message and the file content\nwas suboptimal, but I was wrong. This seems to be best after some consideration.\n\nThe next paragraph checks for\n'working-tree-only option populates the working tree, but doesn't touch index'\n\n> +       ! grep another file1 &&\n> +       git checkout --working-tree-only pu file1 file3 &&\n> +       grep another file1 &&\n> +       test_must_fail git grep --cached another file1 &&\n\nbut only for file1, whereas the next paragraph checks it for file3.\n\n> +       grep exists file3 &&\n> +       git ls-files file3 >actual &&\n> +       >expect &&\n> +       test_cmp expect actual\n\nThanks,\nStefan\n"},{"id":"318016","messageId":"xmqqmvb2r91e.fsf@gitster.mtv.corp.google.com","threadId":"45785","inReplyTo":"CAGZ79kZEveo8jQodvd0n6fEXc1OXDVa26BCumM0etjst74F_Hw@mail.gmail.com","subject":"Re: Submodule/contents conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-04-27T00:25:33Z","receivedAt":"2017-04-27T00:25:41Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n> I assumed a use case like this:\n>\n>   A user may want to extract a file from a given tree-ish via\n>   GIT_WORK_TREE=/tmp/place git checkout <tree> -- <file>\n>   without modifying the repository (i.e. index) at all. For this\n>   we'd need an option to modify the working tree only.\n\nI somehow do not see that as a very useful use case.  It is not just\nkeeping the index, but it is depositing the file also out of the\nworking tree, and if you do not want to work with the resulting\n<file> (actually, if it is a single file, \"cat-file -t blob\" is the\nmost natural thing to use for such an operation, so you should say\n<pathspec> that may match multiple files and perhaps a collection of\ndirectories) within the context of your working tree, I would think\nthat \"archive | tar xf -\" is a better choice.\n\nThe feature is more about in-tree case.  You clobber what you did in\nyour working tree by extracting a pristine copy out of some known\ntree, which may not be HEAD.  And if the tree is not HEAD, you would\nwant \"git diff <pathspec>\" would give a useful preview of what would\nhappen when you do \"git add\" them.  As \"checkout\" adds to the index\nbefore updating the working tree (which can be argued as an ancient\ndesign mistake, given that we typically do worktree-only by default\nand have \"--index/--cached\" to work with the index, e.g. \"apply\",\n\"grep\"), the user needs to do \"git diff HEAD <pathspec>\" instead in\nthis case.\n\n> I never realized this as a usual convention explicitly. Thanks for pointing\n> it out.\n\nI think it is somewhere in gitcli(7).\n\n>> +       if (opts->no_index && !opts->source_tree)\n>> +               die(_(\"'--working-tree-only' cannot be used without tree-ish\"));\n>\n> double negation, maybe:\n>\n>   \"--working-tree-only requires tree-ish\"\n\nMuch more concise.  Thanks.\n\n>> +test_expect_success 'working-tree-only option leaves checked out files unadded' '\n>> +       git reset --hard &&\n>> +       git checkout -b pu next &&\n>> +       echo another >>file1 &&\n>> +       echo exists >file3 &&\n>> +       git add file3 &&\n>> +       git commit -a -m another &&\n>> +       git checkout next &&\n>\n> Up to here it is all preparation; I started to give an argument\n> on why using \"another\" for both the commit message and the file content\n> was suboptimal, but I was wrong. This seems to be best after some consideration.\n\nIt does add to the mental burden.  Will think of a pair of different\nwords and phrases.\n\n> The next paragraph checks for\n> 'working-tree-only option populates the working tree, but doesn't touch index'\n>\n>> +       ! grep another file1 &&\n>> +       git checkout --working-tree-only pu file1 file3 &&\n>> +       grep another file1 &&\n>> +       test_must_fail git grep --cached another file1 &&\n>\n> but only for file1, whereas the next paragraph checks it for file3.\n>\n>> +       grep exists file3 &&\n>> +       git ls-files file3 >actual &&\n>> +       >expect &&\n>> +       test_cmp expect actual\n\nYes.  This demonstrates that paths only in tree-ish is not added to\nthe index (i.e. ls-files does not show it, because HEAD and the\noriginal index didn't have it).  If you did the same \"grep --cached\"\nthat expects a non-match, you cannot tell if we added the path with\nthe --intent-to-add or if we didn't add it at all, so it deliberately\nchecks different thing from the case for file1.\n"},{"id":"318017","messageId":"xmqqinlqr8vm.fsf@gitster.mtv.corp.google.com","threadId":"45785","inReplyTo":"CAGZ79kZEveo8jQodvd0n6fEXc1OXDVa26BCumM0etjst74F_Hw@mail.gmail.com","subject":"Re: Submodule/contents conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-04-27T00:29:01Z","receivedAt":"2017-04-27T00:29:09Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Stefan Beller <sbeller@google.com> writes:\n\n>> +'git checkout' --working-tree-only <tree-ish> [--] <pathspec>...::\n>> +       Similar to `git checkout <tree-ish> [--] <pathspec>`, but\n>> +       the index file is left in the same state as it was before\n>> +       running this command.\n>\n> Adding this as a new mode seems like a \"patch after the fact\",\n> whereas the wording hints that this may be included in the prior\n> part, but I find it hard to come up with a good description there.\n\nYes, having three distinct modes of operation covered in a single\nentry makes the description unnecessarily messy, as you have to say\n\"generally these things happen, but if you use X then additionally\nthis happens before that, and if you do not use Y then this happens\ninstead\".  We _might_ want to separate the [-p|--patch] mode out of\nthe main one, making these into three entries, for this reason.\n"},{"id":"318098","messageId":"DAB03BFCBBCA4C4F95D21A86E3B1DF2F@PhilipOakley","threadId":"45785","inReplyTo":"xmqqa873ubiw.fsf@gitster.mtv.corp.google.com","subject":"Re: Submodule/contents conflict","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":"2017-04-27T22:07:57Z","receivedAt":"2017-04-27T22:08:05Z","isPatch":false,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"From: \"Junio C Hamano\" <gitster@pobox.com> Sent: Wednesday, April 26, 2017 \n3:51 AM\n> \"Philip Oakley\" <philipoakley@iee.org> writes:\n>\n>> As I recall Christoph was using checkout to copy a file (e.g. a\n>> template file) from an older commit/revision into his worktree, and\n>> was suprised that this (git checkout <tree> <path>) also _staged_ the\n>> file, rather than simply letting it be in a modified/untracked state.\n>\n> This probably is taking it even further than the original topic, but\n> I raise this weather-balloon to see if anybody is interested.\n>\n> In the modern day, it might be useful if the \"--working-tree-only\"\n> mode added a new file as an intent-to-add entry to the index, but\n> that is not what \"git apply (no other options)\" (which is the gold\n\ndid you mean `git add` ? Or am I missing something.\n\n> standard for command that operates on the working tree and/or on the\n> index) does, so it is not done in this patch.  IOW, if you grab a\n> path that does not exist in your index out of <tree-ish>, you will\n> write out an untracked file to the working tree.\n\nIt sound like a good idea, as I wasn't aware of another easy way of doing \nit.\n\n>\n> -- >8 --\n> Subject: [PATCH] checkout: add --working-tree-only option\n>\n> \"git checkout <tree-ish> <pathspec>\" has always copied the blob from\n> the tree-ish to the index before checking them out to the working tree.\n>\n> Some users may want to grab a blob out of a tree-ish directly to the\n> working tree, without updating the index, so that \"git diff\" can be\n> used to assess the damage and adjust the file contents taken from a\n> different branch to be more appropriate for the current branch.\n>\n> The new option \"--working-tree-only\" allows exactly that.\n>\n> In the hindsight, when a command works on the working tree and/or\n> the index, the usual convention is:\n>\n> - with no other option, the command works only on the working tree;\n>\n> - with \"--cached\" option, the command works only on the index; and\n>\n> - with \"--index\" option, the command works on both the working tree\n>   and the index.\n>\n> So we probably should have triggered the default behaviour under the\n> \"--index\" option, and triggered this \"--working-tree-only\" mode of\n> behaviour when \"--index\" option is not given.  From the same point\n> of view, \"git checkout --cached <tree-ish> <pathspec>\" would have\n> done the same as \"git reset <tree-ish> <pathspec>\" would do.  And\n> that may have made the command set a bit more consistent.\n>\n> But that is merely a hindsight being 20/20, oh well.\n>\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n> ---\n> Documentation/git-checkout.txt | 22 +++++++++++++++-------\n> builtin/checkout.c             | 10 +++++++++-\n> t/t2022-checkout-paths.sh      | 21 +++++++++++++++++++++\n> 3 files changed, 45 insertions(+), 8 deletions(-)\n>\n> diff --git a/Documentation/git-checkout.txt \n> b/Documentation/git-checkout.txt\n> index 8e2c0662dd..201677752e 100644\n> --- a/Documentation/git-checkout.txt\n> +++ b/Documentation/git-checkout.txt\n> @@ -14,6 +14,7 @@ SYNOPSIS\n> 'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] \n> [<start_point>]\n> 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] \n> [--] <paths>...\n> 'git checkout' [-p|--patch] [<tree-ish>] [--] [<paths>...]\n> +'git checkout' --working-tree-only <tree-ish> [--] [<paths>...]\n>\n> DESCRIPTION\n> -----------\n> @@ -81,13 +82,14 @@ Omitting <branch> detaches HEAD at the tip of the \n> current branch.\n> 'git checkout' [-p|--patch] [<tree-ish>] [--] <pathspec>...::\n>\n>  When <paths> or `--patch` are given, 'git checkout' does *not*\n> - switch branches.  It updates the named paths in the working tree\n> - from the index file or from a named <tree-ish> (most often a\n> - commit).  In this case, the `-b` and `--track` options are\n> - meaningless and giving either of them results in an error.  The\n> - <tree-ish> argument can be used to specify a specific tree-ish\n> - (i.e.  commit, tag or tree) to update the index for the given\n> - paths before updating the working tree.\n> + switch branches.  In this case, the `-b` and `--track` options\n> + are meaningless and giving either of them results in an error.\n> ++\n> +The command checks out blobs for paths that match the given\n> +<pathspec> from the index to the working tree.  When an optional\n> +<tree-ish> is given, the blobs for paths that match the given\n> +<pathspec> are copied from the <tree-ish> to the index before\n> +they are checked out of the index.\n> +\n> 'git checkout' with <paths> or `--patch` is used to restore modified or\n> deleted paths to their original contents from the index or replace paths\n> @@ -101,6 +103,12 @@ specific side of the merge can be checked out of the \n> index by\n> using `--ours` or `--theirs`.  With `-m`, changes made to the working tree\n> file can be discarded to re-create the original conflicted merge result.\n>\n> +'git checkout' --working-tree-only <tree-ish> [--] <pathspec>...::\n> + Similar to `git checkout <tree-ish> [--] <pathspec>`, but\n> + the index file is left in the same state as it was before\n> + running this command.\n\nI feel that the docs should also contain a little of the commit message \nhighlighting that `This complements the usual convention of \"--cached\" and \n\"--index\" options for other commands.`, and would pick up on Stefan's \"I \ndidn't know that\" response - A little education of the reader goes a long \nway, maybe ;-)\n\n> +\n> +\n> OPTIONS\n> -------\n> -q::\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 9b2a5b31d4..d214e99521 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -37,6 +37,7 @@ struct checkout_opts {\n>  int overwrite_ignore;\n>  int ignore_skipworktree;\n>  int ignore_other_worktrees;\n> + int no_index;\n>  int show_progress;\n>\n>  const char *new_branch;\n> @@ -268,6 +269,9 @@ static int checkout_paths(const struct checkout_opts \n> *opts,\n>  die(_(\"Cannot update paths and switch to branch '%s' at the same time.\"),\n>      opts->new_branch);\n>\n> + if (opts->no_index && !opts->source_tree)\n> + die(_(\"'--working-tree-only' cannot be used without tree-ish\"));\n> +\n>  if (opts->patch_mode)\n>  return run_add_interactive(revision, \"--patch=checkout\",\n>     &opts->pathspec);\n> @@ -370,7 +374,9 @@ static int checkout_paths(const struct checkout_opts \n> *opts,\n>  }\n>  }\n>\n> - if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))\n> + if (opts->no_index)\n> + ; /* discard the in-core index */\n> + else if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))\n>  die(_(\"unable to write new index file\"));\n>\n>  read_ref_full(\"HEAD\", 0, rev.hash, NULL);\n> @@ -1161,6 +1167,8 @@ int cmd_checkout(int argc, const char **argv, const \n> char *prefix)\n>  OPT_BOOL(0, \"ignore-other-worktrees\", &opts.ignore_other_worktrees,\n>  N_(\"do not check if another worktree is holding the given ref\")),\n>  OPT_BOOL(0, \"progress\", &opts.show_progress, N_(\"force progress \n> reporting\")),\n> + OPT_BOOL(0, \"working-tree-only\", &opts.no_index, N_(\"checkout to working \n> tree only without touching the index\")),\n> +\n>  OPT_END(),\n>  };\n>\n> diff --git a/t/t2022-checkout-paths.sh b/t/t2022-checkout-paths.sh\n> index f46d0499bc..8ea2e34c90 100755\n> --- a/t/t2022-checkout-paths.sh\n> +++ b/t/t2022-checkout-paths.sh\n> @@ -78,4 +78,25 @@ test_expect_success 'do not touch files that are \n> already up-to-date' '\n>  test_cmp expect actual\n> '\n>\n> +test_expect_success 'working-tree-only option leaves checked out files \n> unadded' '\n> + git reset --hard &&\n> + git checkout -b pu next &&\n> + echo another >>file1 &&\n> + echo exists >file3 &&\n> + git add file3 &&\n> + git commit -a -m another &&\n> + git checkout next &&\n> +\n> + ! grep another file1 &&\n> + git checkout --working-tree-only pu file1 file3 &&\n> + grep another file1 &&\n> + test_must_fail git grep --cached another file1 &&\n> +\n> + grep exists file3 &&\n> + git ls-files file3 >actual &&\n> + >expect &&\n> + test_cmp expect actual\n> +\n> +'\n> +\n> test_done\n> -- \n> 2.13.0-rc0-309-gb63395ed9e\n>\n--\nThanks\nPhilip \n\n"},{"id":"318104","messageId":"B12D69ADE0C84BBBB9A35C50F1C74A01@PhilipOakley","threadId":"45785","inReplyTo":"20170425032242.wlmqrkgkeg3ytfle@sigill.intra.peff.net","subject":"Re: Submodule/contents conflict","fromName":"Philip Oakley","fromEmail":"philipoakley@iee.org","sentAt":"2017-04-27T22:52:24Z","receivedAt":"2017-04-27T22:52:34Z","isPatch":false,"sender":{"key":"philipoakley@iee.email","avatar":"https://avatars.githubusercontent.com/u/914343?v=4"},"body":"From: \"Jeff King\" <peff@peff.net> Sent: Tuesday, April 25, 2017 4:22 AM\n\nJust catching up - sorry it's late..\n> On Mon, Apr 24, 2017 at 04:43:28PM -0700, Stefan Beller wrote:\n>\n>> >> On the main list thare is a similar \"issue\" [1] regarding the \n>> >> expectation for `git checkout`,\n>> >> and importantly (for me) these collected views regarding the \"Git Data \n>> >> Protection and\n>> >> Management Principles\" is not within the Git documentation.\n>> >\n>> > Yes, that's an interesting point. What concerns me is that the commit\n>> > c5326bd62b7e168ba1339dacb7ee812d0fe98c7c which introduced this\n>> > into checkout isn't consistent with reset. Seems that nobody noticed \n>> > this before.\n>>\n>> It seems as if we'd want to see the code from\n>> c5326bd62b7e168ba1339dacb7ee812d0fe98c7c\n>> to be part of any worktree touching command, specifically reset?\n>\n> Note that that commit is just about \"git checkout <commit> -- <paths>\".\n> The matching reset command, \"git reset <commit> -- <paths>\" does handle\n> this the same way. Or at least I claimed so here:\n>\n>  http://public-inbox.org/git/20141107081324.GA19845@peff.net/\n>\n> and that is what I modeled the code in c5326bd62b after.\n>\n> But note that none of that should ever affect _what_ gets checked out at\n> a file or content level. It may only affect the timestamps on the\n> resulting files. And I think those timestamps are not something Git\n> makes any promises about.\n\nIt's not actually clear to users what Git promises in cases like this which \nconfuses user expectations - the make file issue does appear to come up \nquite often.\n>\n> So if Git can elide a write and keep a timestamp up to date, that is a\n> great optimization and we should do that. But from an outside viewer's\n> perspective, we guarantee nothing. We might choose to rewrite a\n> stat-dirty file (updating its timestamp) rather than read its contents\n> to see if it might have the same content that we're about to write. And\n> the file may look stat dirty only because of the racy-git file/index\n> timestamp problem, even if it wasn't actually modified.\n\nI'm not sure how in this case we would get the stat-dirty state? We should \nbe able to determine that the file has existed, as originally checked out, \nfor some while (i.e. past the racy FS time) and is unmodified, so that as \nlong as the original checkout OID and the required new OID are the same we \nshould be able to avoid the file overwrite (or merge).\n\nIt would require that the now time be used as a stand-in for the new OID \nfile's stat time (given the object store doesn't store date stamps for \nfiles) to check for the racy-git situation. These negative information \nscenarios can be tricky.\n>\n>> > It also has a similarity to\n>> > https://public-inbox.org/git/1492287435.14812.2.camel@gmail.com/ \n>> > regarding\n>> > how checkout operates.\n>\n> I didn't look too deeply into this one, but it really looks like\n> somebody caring too much about when git needs to write (and I'd suspect\n> it's impacted by the racy-git thing, too).\n>\n> -Peff\n>\n--\nPhilip \n\n"},{"id":"318113","messageId":"xmqq37ctmgh6.fsf@gitster.mtv.corp.google.com","threadId":"45785","inReplyTo":"DAB03BFCBBCA4C4F95D21A86E3B1DF2F@PhilipOakley","subject":"Re: Submodule/contents conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-04-28T02:08:21Z","receivedAt":"2017-04-28T02:08:36Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Philip Oakley\" <philipoakley@iee.org> writes:\n\n>> In the modern day, it might be useful if the \"--working-tree-only\"\n>> mode added a new file as an intent-to-add entry to the index, but\n>> that is not what \"git apply (no other options)\" (which is the gold\n>\n> did you mean `git add` ? Or am I missing something.\n\nI did mean \"git apply\" that applies the patch (0) only to the\nworking tree by default, (1) to the index without touching the\nworking tree with the \"--cached\" option and (2) both to the working\ntree and the index with the \"--index\" option.  As \"git add\" is all\nabout the index, having these three modes wouldn't make much sense\nthere.\n\n>> +'git checkout' --working-tree-only <tree-ish> [--] <pathspec>...::\n>> + Similar to `git checkout <tree-ish> [--] <pathspec>`, but\n>> + the index file is left in the same state as it was before\n>> + running this command.\n>\n> I feel that the docs should also contain a little of the commit\n> message highlighting that `This complements the usual convention of\n> \"--cached\" and \"--index\" options for other commands.`, and would pick\n> up on Stefan's \"I didn't know that\" response - A little education of\n> the reader goes a long way, maybe ;-)\n\nI do not think \"If we were building Git from scratch today without\nhaving any existing user of `git checkout`, we probably would have\nmade this to be the default mode of operation, and instead required\nthe user to say `git checkout --index <tree-ish> <pathspec>' if the\nuser wants checkout to affect both the index and the working tree,\nto be more consistent with other parts of the system\" would help the\nreaders of the current system very much, even if we were promising\nthat such a more consistent system may come in a future (and we are\nnot, at least not yet, with this addition).\n"},{"id":"318125","messageId":"20170428083015.m2vowojjcdgfkcak@sigill.intra.peff.net","threadId":"45785","inReplyTo":"B12D69ADE0C84BBBB9A35C50F1C74A01@PhilipOakley","subject":"Re: Submodule/contents conflict","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2017-04-28T08:30:15Z","receivedAt":"2017-04-28T08:30:33Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Apr 27, 2017 at 11:52:24PM +0100, Philip Oakley wrote:\n\n> > But note that none of that should ever affect _what_ gets checked out at\n> > a file or content level. It may only affect the timestamps on the\n> > resulting files. And I think those timestamps are not something Git\n> > makes any promises about.\n> \n> It's not actually clear to users what Git promises in cases like this which\n> confuses user expectations - the make file issue does appear to come up\n> quite often.\n\nYeah, I wouldn't mind a documentation update there. I have no idea where\nto put it that people would find it, though.\n\n> > So if Git can elide a write and keep a timestamp up to date, that is a\n> > great optimization and we should do that. But from an outside viewer's\n> > perspective, we guarantee nothing. We might choose to rewrite a\n> > stat-dirty file (updating its timestamp) rather than read its contents\n> > to see if it might have the same content that we're about to write. And\n> > the file may look stat dirty only because of the racy-git file/index\n> > timestamp problem, even if it wasn't actually modified.\n> \n> I'm not sure how in this case we would get the stat-dirty state? We should\n> be able to determine that the file has existed, as originally checked out,\n> for some while (i.e. past the racy FS time) and is unmodified, so that as\n> long as the original checkout OID and the required new OID are the same we\n> should be able to avoid the file overwrite (or merge).\n> \n> It would require that the now time be used as a stand-in for the new OID\n> file's stat time (given the object store doesn't store date stamps for\n> files) to check for the racy-git situation. These negative information\n> scenarios can be tricky.\n\nI don't think the \"now\" time during the read is relevant for racy git.\nThe problem is the timestamp of the index versus the timestamp of the\nfile itself. So updating the index in the same second the file was\ntouched (like a test script often does):\n\n  echo foo >bar && git add bar\n\nwill have a racily-dirty entry, and a subsequent index refresh will\nre-read the file. You can verify this with strace:\n\n  $ echo foo >bar && git add bar\n  $ strace git update-index --refresh 2>&1 | grep '\"bar\"'\n  lstat(\"bar\", {st_mode=S_IFREG|0644, st_size=4, ...}) = 0\n  open(\"bar\", O_RDONLY|O_CLOEXEC)         = 4\n\n  [now update the index so it has a later timestamp]\n  $ sleep 1\n  $ echo foo >unrelated && git add unrelated\n  $ strace  git update-index --refresh 2>&1 | grep '\"bar\"'\n  lstat(\"bar\", {st_mode=S_IFREG|0644, st_size=4, ...}) = 0\n\nInterestingly, I would have thought that the first update-index call\nwould \"de-racify\" the entry by rewriting the index. But we don't\nactually write, presumably because we eventually realize that there are\nno entries to update. But it might actually be worth doing the write,\nbecause it avoids further file-content reads later on (and most\nworkflows tend to do a lot of reads; every git-status is going to rehash\nthe file until the next index update).\n\n-Peff\n"},{"id":"318274","messageId":"xmqq7f21l9fc.fsf@gitster.mtv.corp.google.com","threadId":"45785","inReplyTo":"20170428083015.m2vowojjcdgfkcak@sigill.intra.peff.net","subject":"Re: Submodule/contents conflict","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-05-01T00:15:03Z","receivedAt":"2017-05-01T00:15:12Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> Interestingly, I would have thought that the first update-index call\n> would \"de-racify\" the entry by rewriting the index. But we don't\n> actually write, presumably because we eventually realize that there are\n> no entries to update. But it might actually be worth doing the write,\n> because it avoids further file-content reads later on (and most\n> workflows tend to do a lot of reads; every git-status is going to rehash\n> the file until the next index update).\n\nYeah, there is a tradeoff of time being spent on writing the index\n(which could be large) and having to rehash the content (which could\nalso have to happen number of times until the next index writeout),\nand in hindsight I suspect that I got the tradeoff wrong when we did\nthe racy-git-avoidance thing.\n\n"}]}