{"thread":{"id":"66332","subject":"GIT_WORK_TREE is not exported for post-checkout-hook","startedAt":"2026-09-15T08:50:13Z","lastAt":"2026-09-25T07:28:33Z","messageCount":3,"participants":["Phil Sainty","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"552747","messageId":"17f331aef833b34fbddbefd66c330465@orcon.net.nz","threadId":"66332","inReplyTo":null,"subject":"GIT_WORK_TREE is not exported for post-checkout-hook","fromName":"Phil Sainty","fromEmail":"psainty@orcon.net.nz","sentAt":"2026-09-15T08:24:31Z","receivedAt":"2026-09-15T08:50:13Z","isPatch":false,"body":"I have a post-checkout-hook shell script which indirectly calls\n\"git rev-parse --show-toplevel\", and I've found that when that hook\nruns for a checkout within a worktree directory, GIT_DIR is set but\nGIT_WORK_TREE is not set, which leads to the CWD being returned\ninstead of the genuine toplevel value.\n\nThe script is seeing a GIT_DIR value like this:\n\n     GIT_DIR=/path/to/repo/.git/worktrees/name\n\nThe only other git variables in the environment are GIT_EXEC_PATH\nand GIT_PREFIX.\n\nThe outcome I'm seeing is explained by\nhttps://git-scm.com/book/en/v2/Git-Internals-Environment-Variables#_repository_locations\nwhich says:\n\n     \"If --git-dir or GIT_DIR is specified but none of --work-tree,\n     GIT_WORK_TREE or core.worktree is specified, the current working\n     directory is regarded as the top level of your working tree.\"\n\nThe current githooks(5) man page talks about this somewhat:\n\n     \"Environment variables, such as GIT_DIR, GIT_WORK_TREE, etc., are\n     exported so that Git commands run by the hook can correctly locate\n     the repository.  If your hook needs to invoke Git commands in a\n     foreign repository or in a different working tree of the same\n     repository, then it should clear these environment variables so\n     they do not interfere with Git operations at the foreign\n     location.\"\n\nUnsetting GIT_DIR certainly fixes the problem, but I don't think that\nought to be necessary in my case, as my hook script is calling git in\nthe *same* working copy where the checkout happens.  It is not dealing\nwith \"a foreign\" repository [or] a different working tree\".\n\nMy impression is that GIT_WORK_TREE should also be set for this hook\nwhen dealing with a worktree.  I have tested this in git version 2.34\nand 2.55 (maint) and also 2.56.0.rc0.355.g945ea7012d (next).\n\nI have only tested post-checkout-hook, so I do not know if this\napplies to other hooks as well.\n\n\n[System Info]\ngit version:\ngit version 2.34.1\ncpu: x86_64\nno commit associated with this build\nsizeof-long: 8\nsizeof-size_t: 8\nshell-path: /bin/sh\nuname: Linux 6.8.0-138-generic #138~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC \nFri Aug  7 13:43:15 UTC  x86_64\ncompiler info: gnuc: 11.4\nlibc info: glibc: 2.35\n$SHELL (typically, interactive shell): /bin/bash\n"},{"id":"552801","messageId":"xmqqqzisvpsq.fsf@gitster.g","threadId":"66332","inReplyTo":"17f331aef833b34fbddbefd66c330465@orcon.net.nz","subject":"Re: GIT_WORK_TREE is not exported for post-checkout-hook","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-09-16T21:39:49Z","receivedAt":"2026-09-16T21:39:53Z","isPatch":false,"body":"Phil Sainty <psainty@orcon.net.nz> writes:\n\n> I have a post-checkout-hook shell script which indirectly calls\n> \"git rev-parse --show-toplevel\", and I've found that when that hook\n> runs for a checkout within a worktree directory, GIT_DIR is set but\n> GIT_WORK_TREE is not set, which leads to the CWD being returned\n> instead of the genuine toplevel value.\n>\n> The script is seeing a GIT_DIR value like this:\n>\n>      GIT_DIR=/path/to/repo/.git/worktrees/name\n>\n> The only other git variables in the environment are GIT_EXEC_PATH\n> and GIT_PREFIX.\n>\n> The outcome I'm seeing is explained by\n> https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables#_repository_locations\n> which says:\n>\n>      \"If --git-dir or GIT_DIR is specified but none of --work-tree,\n>      GIT_WORK_TREE or core.worktree is specified, the current working\n>      directory is regarded as the top level of your working tree.\"\n>\n> The current githooks(5) man page talks about this somewhat:\n>\n>      \"Environment variables, such as GIT_DIR, GIT_WORK_TREE, etc., are\n>      exported so that Git commands run by the hook can correctly locate\n>      the repository.  If your hook needs to invoke Git commands in a\n>      foreign repository or in a different working tree of the same\n>      repository, then it should clear these environment variables so\n>      they do not interfere with Git operations at the foreign\n>      location.\"\n>\n> Unsetting GIT_DIR certainly fixes the problem, but I don't think that\n> ought to be necessary in my case, as my hook script is calling git in\n> the *same* working copy where the checkout happens.  It is not dealing\n> with \"a foreign\" repository [or] a different working tree\".\n>\n> My impression is that GIT_WORK_TREE should also be set for this hook\n> when dealing with a worktree.  I have tested this in git version 2.34\n> and 2.55 (maint) and also 2.56.0.rc0.355.g945ea7012d (next).\n\nThat certainly would be a possible workaround.  I do not know why we\nneed to export GIT_DIR in the first place, though.\n\nThe attached patch would export GIT_WORK_TREE when worktree exists\nand GIT_DIR would also be exported, but there is a large NEEDSWORK\ncomment in the precontext of the hunk, which merits consideration\nbefore moving forward.\n\n setup.c | 5 ++++-\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git c/setup.c w/setup.c\nindex 0d157ac254..d0c0b2f68a 100644\n--- c/setup.c\n+++ w/setup.c\n@@ -2064,8 +2064,11 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)\n \t    startup_info->have_repository ||\n \t    /* GIT_DIR_EXPLICIT */\n \t    getenv(GIT_DIR_ENVIRONMENT)) {\n-\t\tif (discovery.worktree)\n+\t\tif (discovery.worktree) {\n \t\t\tset_git_work_tree(repo, discovery.worktree);\n+\t\t\tif (discovery.gitdir || getenv(GIT_DIR_ENVIRONMENT))\n+\t\t\t\txsetenv(GIT_WORK_TREE_ENVIRONMENT, discovery.worktree, 1);\n+\t\t}\n \n \t\tif (discovery.gitdir) {\n \t\t\tapply_and_export_relative_gitdir(repo, discovery.gitdir, 0);\n"},{"id":"553265","messageId":"a22c36b869d545cf3197f031a339c6b0@orcon.net.nz","threadId":"66332","inReplyTo":"xmqqqzisvpsq.fsf@gitster.g","subject":"Re: GIT_WORK_TREE is not exported for post-checkout-hook","fromName":"Phil Sainty","fromEmail":"psainty@orcon.net.nz","sentAt":"2026-09-25T07:11:39Z","receivedAt":"2026-09-25T07:28:33Z","isPatch":false,"body":"Hi Junio,\n\nOn 2026-09-17 09:39, Junio C Hamano wrote:\n> The attached patch would export GIT_WORK_TREE when\n> worktree exists and GIT_DIR would also be exported,\n\nThanks for looking at this.  I've just tested your patch\nand it seems to me that discovery.worktree may not be\n(or at least may not /always/ be) the right value.  With\nthe patch I see the following in my script's environment:\n\n      GIT_DIR=/path/to/repo/.git/worktrees/name\n      GIT_WORK_TREE='.'\n\nMoreover, I see the '.' value regardless of which sub-\ndirectory of the worktree I was in at the time.\n\n\"git rev-parse --show-toplevel\" still returns the CWD\n(which may now be because it's the path for the explicit\n'.' value?)\n\nI can see a few places in setup.c where that value is\nbeing set:\n\n     ./setup.c:510:\t\tsetenv(GIT_WORK_TREE_ENVIRONMENT, \".\", 1);\n     ./setup.c:1213:\t\trepo_discovery_set_worktree(discovery, \".\");\n     ./setup.c:1269:\trepo_discovery_set_worktree(discovery, \".\");\n"}]}