Subject: [PATCH v3 1/1] Fix gitdir e.g. to respect core.hooksPath

From: "Johannes Schindelin via GitGitGadget" <[email-masked]>
Date: Fri, 04 Oct 2019 14:41:52 -0700 (PDT)

From: Johannes Schindelin <[email-masked]>

Since v2.9.0, Git knows about the config variable core.hookspath
that allows overriding the path to the directory containing the
Git hooks.

Since v2.10.0, the `--git-path` option respects that config
variable, too, so we may just as well use that command.

For Git versions older than v2.5.0 (which was the first version to
support the `--git-path` option for the `rev-parse` command), we
simply fall back to the previous code.

An original patch handled only the hooksPath setting, however, during
the code submission it was deemed better to fix all call to the `gitdir`
function.

To avoid spawning a gazillion `git rev-parse --git-path` instances, we
cache the returned paths, priming the cache upon startup in a single
`git rev-parse invocation` with the known entries.

This fixes https://github.com/git-for-windows/git/issues/1755

Initial-patch-by: Philipp Gortan <[email-masked]>
Signed-off-by: Johannes Schindelin <[email-masked]>
---
 git-gui.sh | 52 +++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 49 insertions(+), 3 deletions(-)

diff --git a/git-gui.sh b/git-gui.sh
index fd476b6999..8b72e59cd0 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -158,6 +158,7 @@ if {[tk windowingsystem] eq "aqua"} {
 
 set _appname {Git Gui}
 set _gitdir {}
+array set _gitdir_cache {}
 set _gitworktree {}
 set _isbare {}
 set _gitexec {}
@@ -197,12 +198,57 @@ proc appname {} {
 	return $_appname
 }
 
+proc prime_gitdir_cache {} {
+	global _gitdir _gitdir_cache
+
+	set gitdir_cmd [list git rev-parse --git-dir]
+
+	# `--git-path` is only supported since Git v2.5.0
+	if {[package vcompare $::_git_version 2.5.0] >= 0} {
+		set gitdir_keys [list \
+			CHERRY_PICK_HEAD FETCH_HEAD GITGUI_BCK GITGUI_EDITMSG \
+			GITGUI_MSG HEAD hooks hooks/prepare-commit-msg \
+			index.lock info info/exclude logs MERGE_HEAD MERGE_MSG \
+			MERGE_RR objects "objects/4\[0-1\]/*" \
+			"objects/4\[0-3\]/*" objects/info \
+			objects/info/alternates objects/pack packed-refs \
+			PREPARE_COMMIT_MSG rebase-merge/head-name remotes \
+			rr-cache rr-cache/MERGE_RR SQUASH_MSG \
+		]
+
+		foreach key $gitdir_keys {
+			lappend gitdir_cmd --git-path $key
+		}
+	}
+
+	set i -1
+	foreach path [split [eval $gitdir_cmd] "\n"] {
+		if {$i eq -1} {
+			set _gitdir $path
+		} else {
+			set _gitdir_cache([lindex $gitdir_keys $i]) $path
+		}
+		incr i
+	}
+}
+
 proc gitdir {args} {
-	global _gitdir
+	global _gitdir _gitdir_cache
+
 	if {$args eq {}} {
 		return $_gitdir
 	}
-	return [eval [list file join $_gitdir] $args]
+
+	set args [eval [list file join] $args]
+	if {![info exists _gitdir_cache($args)]} {
+		if {[package vcompare $::_git_version 2.5.0] >= 0} {
+			set _gitdir_cache($args) [git rev-parse --git-path $args]
+		} else {
+			set _gitdir_cache($args) [file join $_gitdir $args]
+		}
+	}
+
+	return $_gitdir_cache($args)
 }
 
 proc gitexec {args} {
@@ -1242,7 +1288,7 @@ if {[catch {
 	&& [catch {
 		# beware that from the .git dir this sets _gitdir to .
 		# and _prefix to the empty string
-		set _gitdir [git rev-parse --git-dir]
+		prime_gitdir_cache
 		set _prefix [git rev-parse --show-prefix]
 	} err]} {
 	load_config 1
-- 
gitgitgadget