From: Shroom Moo Date: Wed, 06 May 2026 20:27:51 GMT Subject: [PATCH v6 3/3] git-gui: handle GIT_DIR and GIT_WORK_TREE early Message-ID: In-Reply-To: <20260506202751.3294-1-egg_mushroomcow@foxmail.com> Users expect these two invocations to be equivalent: GIT_WORK_TREE=/some/path GIT_DIR=/some/path/.git git gui git -C /some/path gui Currently, the environment variable variant often brings up the repository picker or ignores the requested worktree because GIT_WORK_TREE is processed too late. Moreover, after determining the working tree, git-gui unconditionally exports GIT_WORK_TREE. When no worktree is found (e.g., in a bare repository with a read-only subcommand like blame), an empty value is exported, which confuses commands like `git branch --show-current`. Fix both issues: - Introduce resolve_initial_environment, which is called before any repository detection. When both GIT_DIR and GIT_WORK_TREE are set, it changes to the specified worktree, verifies that it is a valid worktree, and adopts the given GIT_DIR. In this case, the subsequent automatic detection is skipped. After successful validation, GIT_WORK_TREE is unset so that later steps do not inherit it. - At the end of startup, only export GIT_DIR. The current directory is already at the top of the worktree (when a worktree exists), so Git can discover the worktree automatically. Not exporting an empty GIT_WORK_TREE fixes `git gui blame` in bare repositories. When only GIT_DIR is set without GIT_WORK_TREE, the existing logic remains unchanged for backward compatibility. Setting only GIT_WORK_TREE is not a valid Git use-case and is silently ignored. Helped-by: Mark Levedahl Helped-by: Johannes Sixt Signed-off-by: Shroom Moo --- git-gui/git-gui.sh | 84 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 18 deletions(-) diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index 1191e6654c..ca651dc576 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -1122,25 +1122,69 @@ unset argv0dir ## ## repository setup +proc resolve_initial_environment {} { + global _gitdir env + + # Only act if both GIT_DIR and GIT_WORK_TREE are set + if {[info exists env(GIT_DIR)] && [info exists env(GIT_WORK_TREE)]} { + # Validate GIT_DIR by resolving its absolute path + if {[catch {set _abs [git rev-parse --absolute-git-dir]} err]} { + catch {wm withdraw .} + error_popup [strcat [mc "Invalid GIT_DIR:"] "\n\n$err"] + exit 1 + } + + # Change current directory to GIT_WORK_TREE + if {[catch {cd $env(GIT_WORK_TREE)} err]} { + catch {wm withdraw .} + error_popup [strcat [mc "Cannot change to GIT_WORK_TREE:"] "\n\n$err"] + exit 1 + } + + # Verify that GIT_WORK_TREE is a valid Git worktree + if {[catch {git rev-parse --show-toplevel} err]} { + catch {wm withdraw .} + error_popup [strcat [mc "GIT_WORK_TREE is not a valid worktree:"] "\n\n$err"] + exit 1 + } + + # Use repository path specified by environment variables + set _gitdir $_abs + set ::_prefix {} + + # Unset GIT_WORK_TREE to prevent it from being inherited by child processes + unset env(GIT_WORK_TREE) + + return 1 + } + + # Other cases (only GIT_DIR, only GIT_WORK_TREE, or neither): + # Do nothing here and let the existing logic handle it later + return 0 +} +set force_gitdir [resolve_initial_environment] + set picked 0 -if {[catch { - set _gitdir $env(GIT_DIR) - set _prefix {} - }] - && [catch { - # beware that from the .git dir this sets _gitdir to . - # and _prefix to the empty string; this is handled by - # the startup safety checks below - set _gitdir [git rev-parse --git-dir] - set _prefix [git rev-parse --show-prefix] - } err]} { - load_config 1 - apply_config - choose_repository::pick - if {![file isdirectory $_gitdir]} { - exit 1 +if {!$force_gitdir} { + if {[catch { + set _gitdir $env(GIT_DIR) + set _prefix {} + }] + && [catch { + # beware that from the .git dir this sets _gitdir to . + # and _prefix to the empty string; this is handled by + # the startup safety checks below + set _gitdir [git rev-parse --git-dir] + set _prefix [git rev-parse --show-prefix] + } err]} { + load_config 1 + apply_config + choose_repository::pick + if {![file isdirectory $_gitdir]} { + exit 1 + } + set picked 1 } - set picked 1 } if {![file isdirectory $_gitdir]} { @@ -1228,7 +1272,11 @@ if {[lindex $_reponame end] eq {.git}} { # Export the final paths set env(GIT_DIR) $_gitdir -set env(GIT_WORK_TREE) $_gitworktree +# Do not export `GIT_WORK_TREE`. The current directory is already +# the top-level of the working tree (if it exists), and Git will +# automatically identify the working tree. For bare repositories, +# `_gitworktree` is empty, and exporting an empty value would cause +# commands like `git branch --show-current` to fail ###################################################################### ## -- 2.52.0.windows.1