{"thread":{"id":"38493","subject":"Weird output of git status in pre-commit hook when providing a pathspec on commit","startedAt":"2015-02-03T13:14:06Z","lastAt":"2015-02-04T22:45:51Z","messageCount":3,"participants":["Michaël Fortin","Jeff King"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"255572","messageId":"80E24BA2-84FE-47FC-A5C0-D291E3C63BD5@gmail.com","threadId":"38493","inReplyTo":null,"subject":"Weird output of git status in pre-commit hook when providing a pathspec on commit","fromName":"Michaël Fortin","fromEmail":"fortinmike@gmail.com","sentAt":"2015-02-03T13:14:06Z","receivedAt":"2015-02-03T13:14:06Z","isPatch":false,"sender":{"key":"fortinmike@gmail.com","avatar":"https://gravatar.com/avatar/c564734f25f0c8a52dfd1cae6aaef18131f10f8a03c3d5173ac951b0d51f1afe?d=mp&s=160"},"body":"Hi all,\n\nI'm seeing behavior that I *think* might be a bug in git (I'm running 2.2.2). At least I couldn't find anything about this anywhere, so here goes:\n\nI'm trying to run git commands outside of the current working copy (e.g. inside another repo) from a pre-commit hook. However I'm encountering a very weird issue that occurs when a pathspec is provided when performing a commit.\n\nMore precisely, what I'm trying to do is check whether a repository beside the current repository is clean (no uncommitted changes) or not, from a pre-commit hook.\n\nMy repositories look like this on disk:\n\nContainerDirectory\n|-- Repo1 (contains one untracked file)\n|-- Repo2 (no uncommitted changes)\n\nRepo1 has the following pre-commit hook:\n\n#!/bin/bash\ngit -C \"../Repo2\" status --porcelain\n\nI then commit in Repo1 using the following (this is actually ran by a GUI, I have no control over the commands themselves):\n\ngit add --force -- MyNewFile\ngit commit -m \"My message\" -o -- MyNewFile\n\nThis results in some very puzzling output. I would expect the hook to output nothing because Repo2 contains no changes. However, it returns the following:\n\nD  Repo2-File1.txt\nD  Repo2-File2.txt\nD  Repo2-File3.txt\n?? Repo2-File1.txt\n?? Repo2-File2.txt\n?? Repo2-File3.txt\n\nAs you can see, the files are listed as both deleted and untracked, which makes no sense to me because there are in fact no uncommitted changes in that repo. There are only these three files in the repo. Removing the pathspec (\"-- MyNewFile\") from the commit command shows the expected output (nothing, because there are no changes in Repo2).\n\nI have reproduced this behavior on OS X and Windows (with the latest git for Windows).\n\nCan you explain what's going on?\n\nThanks in advance for any info!\n\nMichaël Fortin\nwww.irradiated.net\n"},{"id":"255576","messageId":"20150203163235.GA9325@peff.net","threadId":"38493","inReplyTo":"80E24BA2-84FE-47FC-A5C0-D291E3C63BD5@gmail.com","subject":"Re: Weird output of git status in pre-commit hook when providing a pathspec on commit","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-02-03T16:32:35Z","receivedAt":"2015-02-03T16:32:35Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Feb 03, 2015 at 08:14:06AM -0500, Michaël Fortin wrote:\n\n> Repo1 has the following pre-commit hook:\n> \n> #!/bin/bash\n> git -C \"../Repo2\" status --porcelain\n> \n> I then commit in Repo1 using the following (this is actually ran by a\n> GUI, I have no control over the commands themselves):\n> \n> git add --force -- MyNewFile\n> git commit -m \"My message\" -o -- MyNewFile\n\nBecause the commit command uses \"-o\", git must use a temporary index\nfile to stage the commit. It sets GIT_INDEX_FILE in the environment, and\nthen runs your hook.\n\nWhen the hook runs \"git -C\" it moves into another repository, but the\nGIT_INDEX_FILE in the environment takes precedence over the local index\nfound in that repository. So you see a HEAD and working tree from Repo2,\nbut the index from Repo1.\n\nThis is a specific case of a more general problem. If you were to run\ngit like:\n\n  git --git-dir=/path/to/.git commit ...\n\nyou would have similar problems. You would have $GIT_DIR set in the\nenvironment, and would see the HEAD from Repo1, but the working tree\nfrom Repo2.\n\nIn other words, moving into another git directory from inside a hook is\nnot as easy as just going there. You also need to clear any state from\nthe environment. E.g., by adding this to the top of your hook:\n\n  unset $(git rev-parse --local-env-vars)\n\nShould \"-C\" (or \"--git-dir\") make this easier by doing it for you? I'm\nnot sure it is a good idea. It is right now valid to do:\n\n  GIT_INDEX_FILE=/path/to/index.tmp git -C /path/to/repo ...\n\nwhich would break if we cleared git variables. OTOH, maybe using \"-C\"\n(instead of chdir-ing yourself) is a good indication that you want that\ncleared. I dunno. It is probably too late at this point, as we would be\nsubtly breaking backwards compatibility. Perhaps it would make more\nsense to add a new option, so you could do:\n\n  git --reset-git-vars -C ../Repo2 status --porcelain\n\nBut this is a fairly obscure use case, and the rev-parse invocation\nabove is not so bad. I think it is more about knowing that you need to\nuse it, rather than the pain of typing it. And I am not sure how to make\nthat more clear. Is there a specific place in the documentation you\nlooked and might have been warned? Maybe the description for \"-C\" should\ncover this.\n\n-Peff\n"},{"id":"255613","messageId":"FBB67809-5585-4D53-BF55-53248FD1369F@gmail.com","threadId":"38493","inReplyTo":"20150203163235.GA9325@peff.net","subject":"Re: Weird output of git status in pre-commit hook when providing a pathspec on commit","fromName":"Michaël Fortin","fromEmail":"fortinmike@gmail.com","sentAt":"2015-02-04T22:45:51Z","receivedAt":"2015-02-04T22:45:51Z","isPatch":false,"sender":{"key":"fortinmike@gmail.com","avatar":"https://gravatar.com/avatar/c564734f25f0c8a52dfd1cae6aaef18131f10f8a03c3d5173ac951b0d51f1afe?d=mp&s=160"},"body":"Interesting!\n\nThanks for the info; unsetting git environment variables as you suggested fixes my issue on OS X (and Linux I suppose). I’m going to try this on Windows soon. Would that work as-is on Windows, or would I need to unset the vars using some other Windows-specific syntax?\n\nYes, I looked at the documentation for “-C\", and “-o\". Because this issue is triggered by the use of “-o”, maybe the appropriate place to mention this behaviour would be in the documentation for “-o”? I don’t know.\n\nThanks again,\n\nMichaël Fortin\nwww.irradiated.net\n\n> On Feb 3, 2015, at 11:32 AM, Jeff King <peff@peff.net> wrote:\n> \n> On Tue, Feb 03, 2015 at 08:14:06AM -0500, Michaël Fortin wrote:\n> \n>> Repo1 has the following pre-commit hook:\n>> \n>> #!/bin/bash\n>> git -C \"../Repo2\" status --porcelain\n>> \n>> I then commit in Repo1 using the following (this is actually ran by a\n>> GUI, I have no control over the commands themselves):\n>> \n>> git add --force -- MyNewFile\n>> git commit -m \"My message\" -o -- MyNewFile\n> \n> Because the commit command uses \"-o\", git must use a temporary index\n> file to stage the commit. It sets GIT_INDEX_FILE in the environment, and\n> then runs your hook.\n> \n> When the hook runs \"git -C\" it moves into another repository, but the\n> GIT_INDEX_FILE in the environment takes precedence over the local index\n> found in that repository. So you see a HEAD and working tree from Repo2,\n> but the index from Repo1.\n> \n> This is a specific case of a more general problem. If you were to run\n> git like:\n> \n>  git --git-dir=/path/to/.git commit ...\n> \n> you would have similar problems. You would have $GIT_DIR set in the\n> environment, and would see the HEAD from Repo1, but the working tree\n> from Repo2.\n> \n> In other words, moving into another git directory from inside a hook is\n> not as easy as just going there. You also need to clear any state from\n> the environment. E.g., by adding this to the top of your hook:\n> \n>  unset $(git rev-parse --local-env-vars)\n> \n> Should \"-C\" (or \"--git-dir\") make this easier by doing it for you? I'm\n> not sure it is a good idea. It is right now valid to do:\n> \n>  GIT_INDEX_FILE=/path/to/index.tmp git -C /path/to/repo ...\n> \n> which would break if we cleared git variables. OTOH, maybe using \"-C\"\n> (instead of chdir-ing yourself) is a good indication that you want that\n> cleared. I dunno. It is probably too late at this point, as we would be\n> subtly breaking backwards compatibility. Perhaps it would make more\n> sense to add a new option, so you could do:\n> \n>  git --reset-git-vars -C ../Repo2 status --porcelain\n> \n> But this is a fairly obscure use case, and the rev-parse invocation\n> above is not so bad. I think it is more about knowing that you need to\n> use it, rather than the pain of typing it. And I am not sure how to make\n> that more clear. Is there a specific place in the documentation you\n> looked and might have been warned? Maybe the description for \"-C\" should\n> cover this.\n> \n> -Peff\n"}]}