Git development
 help / color / mirror / Atom feed
* [PATCH v3 04/12] git-gui: do not change global vars in choose_repository::pick
From: Mark Levedahl @ 2026-05-31 23:02 UTC (permalink / raw)
  To: git; +Cc: j6t, egg_mushroomcow, bootaina702, Mark Levedahl
In-Reply-To: <20260531230225.126817-1-mlevedahl@gmail.com>

The repository picker (choose_repository::pick, AKA pick) on success
always returns with the current directory at the root of the selected
worktree, with the global variable _gitdir holding the name of the
git repository, possibly as a relative path, and _prefix {}. The
worktree root (_gitworktree) is not filled out, and if the selection was
from the "recent" list, no validation has occurred beyond testing that
the worktree root exists. So, repository and worktree validation are
still needed to be sure the new repo + worktree is usable.

pick only supports worktrees with a .git entry in the worktree root, so
git repository and worktree discovery will work starting in the current
directory on return. In cases of error, or user abort, pick exits the
process rather than returning.

So, let's change pick to not alter any global values, with success
indicated by the process returning to the caller. In this case, the
current directory is the worktree root, with a .git entry. The caller
then proceeds with normal discovery to find and validate both repository
and worktree.

With this, pick now returns 1 in the success case, but additional work
would be necessary to return from conditions where 0 should be returned.
Checking this return value would be superfluous.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-gui.sh                |  7 ++++++-
 lib/choose_repository.tcl | 21 ++++++++-------------
 2 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/git-gui.sh b/git-gui.sh
index 2cf14d0dd5..44914bddcf 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -1153,9 +1153,14 @@ if {[catch {
 	load_config 1
 	apply_config
 	choose_repository::pick
-	if {![file isdirectory $_gitdir]} {
+	if {[catch {
+			set _gitdir [git rev-parse --git-dir]
+		} err]} {
+		catch {wm withdraw .}
+		error_popup [strcat [mc "Unusable repo/worktree:"] " [pwd] \n\n$err"]
 		exit 1
 	}
+	set _prefix {}
 	set picked 1
 }
 
diff --git a/lib/choose_repository.tcl b/lib/choose_repository.tcl
index 7e1462a20c..4b06afee93 100644
--- a/lib/choose_repository.tcl
+++ b/lib/choose_repository.tcl
@@ -15,7 +15,7 @@ field w_recentlist ; # Listbox containing recent repositories
 field w_localpath  ; # Entry widget bound to local_path
 
 field done              0 ; # Finished picking the repository?
-field clone_ok      false ; # clone succeeeded
+field pick_ok           0 ; # true if repo pick/clone succeeded
 field local_path       {} ; # Where this repository is locally
 field origin_url       {} ; # Where we are cloning from
 field origin_name  origin ; # What we shall call 'origin'
@@ -220,6 +220,8 @@ constructor pick {} {
 	if {$top eq {.}} {
 		eval destroy [winfo children $top]
 	}
+
+	return $pick_ok
 }
 
 method _center {} {
@@ -327,8 +329,7 @@ method _git_init {} {
 	}
 
 	_append_recentrepos [pwd]
-	set ::_gitdir .git
-	set ::_prefix {}
+	set pick_ok 1
 	return 1
 }
 
@@ -409,6 +410,7 @@ method _do_new2 {} {
 	if {![_git_init $this]} {
 		return
 	}
+	set pick_ok 1
 	set done 1
 }
 
@@ -621,7 +623,7 @@ method _do_clone2 {} {
 	}
 
 	tkwait variable @done
-	if {!$clone_ok} {
+	if {!$pick_ok} {
 		error_popup [mc "Clone failed."]
 		return
 	}
@@ -632,18 +634,12 @@ method _do_clone2_done {ok} {
 	if {$ok} {
 		if {[catch {
 			cd $local_path
-			set ::_gitdir .git
-			set ::_prefix {}
 			_append_recentrepos [pwd]
 		} err]} {
 			set ok 0
 		}
 	}
-	if {!$ok} {
-		set ::_gitdir {}
-		set ::_prefix {}
-	}
-	set clone_ok $ok
+	set pick_ok $ok
 	set done 1
 }
 
@@ -721,8 +717,7 @@ method _do_open2 {} {
 	}
 
 	_append_recentrepos [pwd]
-	set ::_gitdir $actualgit
-	set ::_prefix {}
+	set pick_ok 1
 	set done 1
 }
 
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v3 03/12] git-gui: guard set/unset of GIT_DIR and GIT_WORK_TREE
From: Mark Levedahl @ 2026-05-31 23:02 UTC (permalink / raw)
  To: git; +Cc: j6t, egg_mushroomcow, bootaina702, Mark Levedahl
In-Reply-To: <20260531230225.126817-1-mlevedahl@gmail.com>

git-gui unconditionally exports _gitdir as GIT_DIR, and _gitworktree as
GIT_WORK_TREE, to the environment, and unconditionally
unsets these environment variables before invoking gitk or git-gui when
a submodule is involved. This export happens even if _gitworktree is
empty, which happens when running from a bare repository. However,
exporting GIT_WORK_TREE as empty is never valid, and causes errors in
git.

GIT_DIR must be exported if the repository is not discoverable from the
worktree (or current directory if there is no worktree). The user might
have configured this.

If there is a worktree, git-gui makes this the current directory.
However, if the repository sets core.worktree, this value can only be
overridden by GIT_WORK_TREE so the latter must be exported.

As we cannot eliminate conditions where either variable is needed, let's
implement a pair of functions to set / unset these variables without
error, and without ever exporting an empty GIT_WORK_TREE.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-gui.sh | 37 +++++++++++++++++++++----------------
 1 file changed, 21 insertions(+), 16 deletions(-)

diff --git a/git-gui.sh b/git-gui.sh
index 52897fbd09..2cf14d0dd5 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -1125,6 +1125,20 @@ unset argv0dir
 ##
 ## repository setup
 
+proc set_gitdir_vars {} {
+	global _gitdir _gitworktree env
+	set env(GIT_DIR) $_gitdir
+	if {$_gitworktree ne {}} {
+		set env(GIT_WORK_TREE) $_gitworktree
+	}
+}
+
+proc unset_gitdir_vars {} {
+	global env
+	catch {unset env(GIT_DIR)}
+	catch {unset env(GIT_WORK_TREE)}
+}
+
 set picked 0
 if {[catch {
 		set _gitdir $env(GIT_DIR)
@@ -1210,8 +1224,8 @@ if {[lindex $_reponame end] eq {.git}} {
 	set _reponame [lindex $_reponame end]
 }
 
-set env(GIT_DIR) $_gitdir
-set env(GIT_WORK_TREE) $_gitworktree
+# Export the final paths
+set_gitdir_vars
 
 ######################################################################
 ##
@@ -2010,7 +2024,7 @@ proc incr_font_size {font {amt 1}} {
 
 proc do_gitk {revs {is_submodule false}} {
 	global current_diff_path file_states current_diff_side ui_index
-	global _gitdir _gitworktree
+	global _gitworktree
 
 	# -- Always start gitk through whatever we were loaded with.  This
 	#    lets us bypass using shell process on Windows systems.
@@ -2020,8 +2034,6 @@ proc do_gitk {revs {is_submodule false}} {
 	if {$exe eq {}} {
 		error_popup [mc "Couldn't find gitk in PATH"]
 	} else {
-		global env
-
 		set pwd [pwd]
 
 		if {$is_submodule} {
@@ -2049,13 +2061,11 @@ proc do_gitk {revs {is_submodule false}} {
 			# TODO we could make life easier (start up faster?) for gitk
 			# by setting these to the appropriate values to allow gitk
 			# to skip the heuristics to find their proper value
-			unset env(GIT_DIR)
-			unset env(GIT_WORK_TREE)
+			unset_gitdir_vars
 		}
 		safe_exec_bg [concat $cmd $revs "--" "--"]
 
-		set env(GIT_DIR) $_gitdir
-		set env(GIT_WORK_TREE) $_gitworktree
+		set_gitdir_vars
 		cd $pwd
 
 		if {[info exists main_status]} {
@@ -2078,21 +2088,16 @@ proc do_git_gui {} {
 	if {$exe eq {}} {
 		error_popup [mc "Couldn't find git gui in PATH"]
 	} else {
-		global env
-		global _gitdir _gitworktree
-
 		# see note in do_gitk about unsetting these vars when
 		# running tools in a submodule
-		unset env(GIT_DIR)
-		unset env(GIT_WORK_TREE)
+		unset_gitdir_vars
 
 		set pwd [pwd]
 		cd $current_diff_path
 
 		safe_exec_bg [concat $exe gui]
 
-		set env(GIT_DIR) $_gitdir
-		set env(GIT_WORK_TREE) $_gitworktree
+		set_gitdir_vars
 		cd $pwd
 
 		set status_operation [$::main_status \
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v3 02/12] git-gui: remove unnecessary 'cd $_gitworktree' from do_gitk
From: Mark Levedahl @ 2026-05-31 23:02 UTC (permalink / raw)
  To: git; +Cc: j6t, egg_mushroomcow, bootaina702, Mark Levedahl
In-Reply-To: <20260531230225.126817-1-mlevedahl@gmail.com>

From: Johannes Sixt <j6t@kdbg.org>

In the procedure that invokes Gitk, we have a 'cd $_gitworktree'. Such
a change of the current directory is not necessary, because

- if we have a working tree, then the startup routine has already
  changed the current directory to the root of the working tree, which
  *is* $_gitworktree; or

- if we are in a bare repository, then there is no point in changing
  the current directory anywhere. (And $_gitworktree is empty.)

Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-gui.sh | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/git-gui.sh b/git-gui.sh
index f70a54a61b..52897fbd09 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -2024,11 +2024,7 @@ proc do_gitk {revs {is_submodule false}} {
 
 		set pwd [pwd]
 
-		if {!$is_submodule} {
-			if {![is_bare]} {
-				cd $_gitworktree
-			}
-		} else {
+		if {$is_submodule} {
 			cd $current_diff_path
 			if {$revs eq {--}} {
 				set s $file_states($current_diff_path)
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v3 01/12] git-gui: use HEAD as current branch when detached
From: Mark Levedahl @ 2026-05-31 23:02 UTC (permalink / raw)
  To: git; +Cc: j6t, egg_mushroomcow, bootaina702, Mark Levedahl
In-Reply-To: <20260531230225.126817-1-mlevedahl@gmail.com>

commit f87a36b697 ("git-gui: use git-branch --show-current", 2024-02-12)
changed git-gui to use git-branch to access refs, rather than directly
reading files as doing the latter is not compatible with the reftable
backend. git branch --show-current reports an empty branch name when the
head is detached, and in this case load_current_branch needs to report
HEAD using special case logic as it did prior to the above commit. Make
it do so.

This addresses an issue with git-gui browser failing with a detached
head.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-gui.sh | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/git-gui.sh b/git-gui.sh
index 23fe76e498..f70a54a61b 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -670,6 +670,9 @@ proc load_current_branch {} {
 
 	set current_branch [git branch --show-current]
 	set is_detached [expr [string length $current_branch] == 0]
+	if {$is_detached} {
+		set current_branch {HEAD}
+	}
 }
 
 auto_load tk_optionMenu
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v3 00/12] Improve git gui operation without a worktree
From: Mark Levedahl @ 2026-05-31 23:02 UTC (permalink / raw)
  To: git; +Cc: j6t, egg_mushroomcow, bootaina702, Mark Levedahl
In-Reply-To: <20260520202411.108764-1-mlevedahl@gmail.com>

git gui has a number of inter-related problems that result in problems
during startup from anything but a checked out worktree pointing at a
valid git repository. Some of the symptoms are:
- blame / browser subcommands, and launching gitk, are intended to be
  useful without a worktree, but fail to work.
- unlike git, git-gui is supposed to use the parent directory as a
  worktree if started from the .git subdirectory in the very common
  single worktree + embedded git repository format. This does not
  work.
- git-gui includes a repository picker allowing a user to select a
  worktree from a list and/or start a new repo+worktree: this dialog can
  appear at unexpected times, masking useful error feedback on
  configuration problems.

This patch series addresses the above issues, substantially rewriting
the initial repository/worktree process to rely upon git rev-parse so
that git's knowledge of access rules, repository configuration, and use
of GIT_DIR / GIT_WORK_TREE (or git --gitdir / --work-tree) is used
throughout, replacing code largely based upon what git did in 2008. This
also means that git gui will naturally gain any new rules implmented in
git-core.

With this, git-gui only exports GIT_WORK_TREE when non-empty.
GIT_WORK_TREE is needed, and must be exported, if the user is overriding
core.worktree in the git repository. But, GIT_WORK_TREE cannot be used
to specify the lack of a worktree, so exporting an empty GIT_WORK_TREE
is one of the problems fixed by this series.

v3 of this series addresses j6t's review of v2, with some reordering of
patches (1 from j6t added, patch #8 moved to #1), adds another rewrite
of the browser / blame parser that eliminates the notion of path before
rev on the command line, blame works correctly with a user modified file
in the worktree. Clarification is added on the need for GTI_WORK_TREE,
and the logic in finding a worktree from the gitdir is simplified.

Johannes Sixt (1):
  git-gui: remove unnecessary 'cd $_gitworktree' from do_gitk

Mark Levedahl (11):
  git-gui: use HEAD as current branch when detached
  git-gui: guard set/unset of GIT_DIR and GIT_WORK_TREE
  git-gui: do not change global vars in choose_repository::pick
  git-gui: use --absolute-git-dir
  git-gui: use rev-parse exclusively to find a repository
  git-gui: use git rev-parse for worktree discovery
  git-gui: simplify [is_bare] to report if a worktree is known
  git-gui: try harder to find worktree from gitdir
  git-gui: allow specifying path '.' to the browser
  git-gui: check browser/blame arguments carefully
  git-gui: add gui and pick as explicit subcommands

 git-gui.sh                | 377 ++++++++++++++++++++++----------------
 lib/choose_repository.tcl |  21 +--
 2 files changed, 223 insertions(+), 175 deletions(-)

Interdiff against v2:
diff --git a/git-gui.sh b/git-gui.sh
index 299c1a0292..933e72c9b2 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -1114,33 +1114,29 @@ unset argv0dir
 ## repository setup
 
 proc find_worktree_from_gitdir {} {
-	# Directory 'parent' of a repository named 'parent/.git' might be the worktree.
-	# Assure parent is a worktree and using the git repository already discovered.
-	# Also, handle case of being in a worktree's gitdir, where file "gitdir" points to
-	# gitlink file .git in the real worktree.
+	# this is invoked only if the current directory is inside the repository
 	set worktree {}
 	if {[file tail $::_gitdir] eq {.git}} {
+		# the dir containing .git is a worktree if repo allows it
+		# Check that git reports parent as a worktree (gitdir might not allow a worktree)
 		if {[catch {
-			set gitdir_parent [file dirname $::_gitdir]
-			set worktree [git -C $gitdir_parent rev-parse --show-toplevel]
-			set parent_gitdir [git -C $worktree rev-parse --absolute-git-dir]
-			if {$::_gitdir ne $parent_gitdir} {
-				set worktree {}
-			}
-		}]} {
+				set parent [file dirname $::_gitdir]
+				set worktree [git -C $parent rev-parse --show-toplevel]
+			}]} {
 			set worktree {}
 		}
 	} elseif [file exists {gitdir}] {
+		# a worktree gitdir has .gitdir naming worktree/.git
+		# assure git run there reports this dir as the gitdir (links might be broken)
 		if {[catch {
-			set fd_gitdir [open {gitdir} {r}]
-			set gitlink_parent [file dirname [read $fd_gitdir]]
-			catch {close $fd_gitdir}
-			set worktree [git -C $gitlink_parent rev-parse --show-toplevel]
-			set parent_gitdir [git -C $worktree rev-parse --absolute-git-dir]
-			if {$::_gitdir ne $parent_gitdir} {
-				set worktree {}
-			}
-		}]} {
+				set fd_gitdir [open {gitdir} {r}]
+				set worktree [file dirname [read $fd_gitdir]]
+				catch {close $fd_gitdir}
+				set worktree_gitdir [git -C $worktree rev-parse --absolute-git-dir]
+				if {$::_gitdir ne $worktree_gitdir} {
+					set worktree {}
+				}
+			}]} {
 			catch {close $fd_gitdir}
 			set worktree {}
 		}
@@ -1153,7 +1149,7 @@ proc is_gitvars_error {err} {
 	set GIT_DIR {}
 	set GIT_WORK_TREE {}
 	catch {set GIT_DIR $::env(GIT_DIR); set havevars 1}
-	catch {set GIT_WORK_TREE $::env(GIT_WORK_TREE) ; set havevars 1}
+	catch {set GIT_WORK_TREE $::env(GIT_WORK_TREE); set havevars 1}
 
 	if {$havevars} {
 		catch {wm withdraw .}
@@ -1168,9 +1164,7 @@ proc is_gitvars_error {err} {
 
 proc set_gitdir_vars {} {
 	global _gitdir _gitworktree env
-	if {$_gitdir ne {}} {
-		set env(GIT_DIR) $_gitdir
-	}
+	set env(GIT_DIR) $_gitdir
 	if {$_gitworktree ne {}} {
 		set env(GIT_WORK_TREE) $_gitworktree
 	}
@@ -1182,12 +1176,12 @@ proc unset_gitdir_vars {} {
 	catch {unset env(GIT_WORK_TREE)}
 }
 
-# find repository.
+# find repository
 set _gitdir {}
 if {[is_enabled gitdir_discovery]} {
 	if {[catch {
-		set _gitdir [git rev-parse --absolute-git-dir]
-	} err]} {
+			set _gitdir [git rev-parse --absolute-git-dir]
+		} err]} {
 		if {[is_gitvars_error $err]} {
 			exit 1
 		}
@@ -1200,14 +1194,13 @@ if {$_gitdir eq {} && [is_enabled picker]} {
 	unset_gitdir_vars
 	load_config 1
 	apply_config
-	if {![choose_repository::pick]} {
-		exit 1
-	}
+	choose_repository::pick
 	if {[catch {
-		set _gitdir [git rev-parse --absolute-git-dir]
-	} err]} {
+			set _gitdir [git rev-parse --absolute-git-dir]
+		} err]} {
 		catch {wm withdraw .}
-		error_popup [strcat [mc "Unusable repo/worktree:"] " [pwd] "\n\n$err"]
+		error_popup [strcat [mc "Unusable repo/worktree:"] " [pwd] \n\n$err"]
+		exit 1
 	}
 	set picked 1
 }
@@ -1220,9 +1213,9 @@ if {$_gitdir eq {}} {
 
 # find worktree, continue without if not required
 if {[catch {
-	set _gitworktree [git rev-parse --show-toplevel]
-	set _prefix [git rev-parse --show-prefix]
-} err]} {
+		set _gitworktree [git rev-parse --show-toplevel]
+		set _prefix [git rev-parse --show-prefix]
+	} err]} {
 	if {[is_gitvars_error $err]} {
 		exit 1
 	}
@@ -1238,17 +1231,14 @@ if {[is_bare]} {
 }
 
 if {![is_bare]} {
-	if {[catch {
-		cd $_gitworktree
-	} err]} {
+	if {[catch {cd $_gitworktree} err]} {
 		catch {wm withdraw .}
-		error_popup [strcat [mc "Cannot change to discovered worktree: "] \
-			"$_gitworktree" "\n\n$err"]
-		exit 1;
+		error_popup [strcat [mc "No working directory"] " $_gitworktree:\n\n$err"]
+		exit 1
 	}
 } elseif {![is_enabled bare]} {
 	catch {wm withdraw .}
-	error_popup [strcat [mc "Cannot use bare repository:"] "\n\n" $_gitdir]
+	error_popup [strcat [mc "Cannot use bare repository:"] "\n\n$_gitdir"]
 	exit 1
 }
 
@@ -2086,11 +2076,7 @@ proc do_gitk {revs {is_submodule false}} {
 	} else {
 		set pwd [pwd]
 
-		if {!$is_submodule} {
-			if {![is_bare]} {
-				cd $_gitworktree
-			}
-		} else {
+		if {$is_submodule} {
 			cd $current_diff_path
 			if {$revs eq {--}} {
 				set s $file_states($current_diff_path)
@@ -3026,26 +3012,18 @@ proc normalize_relpath {path} {
 	if {$elements ne {}} {
 		return [eval file join $elements]
 	} else {
-		return {./}
+		return {.}
 	}
 }
 
-proc find_path_type {head path} {
-	if {$path eq {./}} {
-		# the root-tree exists in every rev, ls-tree gives data on the contents,
-		# not the type of tree itself. So, if the rev exists, return {tree}
-		if {[catch {set objtype [git ls-tree $head]}]} {
-			set objtype {}
-		} else {
-			set objtype {tree}
-		}
+proc show_parse_err {err} {
+	if {[tk windowingsystem] eq "win32"} {
+		catch {wm withdraw .}
+		error_popup $err
 	} else {
-		# test that the path exists in head, ls-tree gives info on the path only
-		if {[catch {set objtype [git ls-tree {--format=%(objecttype)} $head $path]}]} {
-			set objtype {}
-		}
+		puts stderr $err
 	}
-	return $objtype
+	exit 1
 }
 
 # -- Not a normal commit type invocation?  Do that instead!
@@ -3054,91 +3032,80 @@ switch -- $subcommand {
 browser -
 blame {
 	if {$subcommand eq "blame"} {
-		set subcommand_args {[--line=<num>] <[rev] [--] filename | [--] filename rev>}
-		set required_objtype blob
+		set subcommand_args {[--line=<num>] [rev] [--] <filename>}
+		set required_pathtype blob
 	} else {
-		set subcommand_args {<[rev] [--] directory | [--] directory rev>}
-		set required_objtype tree
+		set subcommand_args {[rev] [--] <dirname>}
+		set required_pathtype tree
 	}
 	set maxargs [llength $subcommand_args]
 	set nargs [llength $argv]
 	if {$nargs < 1 || $nargs > $maxargs} usage
 	set head {}
-	set althead {}
 	set path {}
-	set altpath {}
-	set canswap 1
 	set jump_spec {}
 
-	# assume: [--line=num] [head] [--] path as the possible arguments, in order.
-	# head and path may need a swap later.
-	for {set iarg 0} {$iarg < $nargs} {incr iarg} {
-		set arg [lindex $argv $iarg]
-		if {$arg eq {--}} {
-			# next arg is the path, prevent or FORCE swap?
-			if {$iarg == $nargs - 2} {
-				set canswap 0
-			} elseif {$iarg == $nargs - 3} {
-				set canswap 2
-			} else {
+	set iarg 0
+	foreach a $argv {
+		incr iarg
+		if {$iarg == $nargs} {
+			# final argument is path
+			set path [normalize_relpath [file join $_prefix $a]]
+		} elseif {$a eq {--}} {
+			# allow before required final arg that must be path
+			if {$iarg != $nargs - 1} {
 				usage
 			}
-		} elseif {[regexp {^--line=(\d+)$} $arg arg lnum]} {
+		} elseif {[regexp {^--line=(\d+)$} $a a lnum]} {
 			# --line can only be the first arg
-			if {$iarg != 0 || $maxargs < 4} usage
+			if {$iarg != 1 || $subcommand ne {blame}} usage
 			set jump_spec [list $lnum]
-		} elseif {$iarg == $nargs - 1} {
-			# assume final argument is path
-			set path [normalize_relpath [file join $_prefix $arg]]
-			set althead $arg
 		} elseif {$head eq {}} {
-			# assume the other argument is head
-			set head $arg
-			set altpath [normalize_relpath [file join $_prefix $arg]]
+			set head $a
 		} else {
 			usage
 		}
 	}
 
-	# no swapping allowed if head not given, use current branch (HEAD)
+	# If head not given, use current branch (HEAD),
+	# and blame will use worktree if there is one.
+	set use_worktree 0
 	if {$head eq {}} {
 		load_current_branch
 		set head $current_branch
-		set canswap 0
-	}
-
-	# -- before "rev" arg means we got -- path head
-	if {$canswap == 2} {
-		set head $althead
-		set path $altpath
-		set canswap 0
-	}
-
-	set objtype [find_path_type $head $path]
-	if {$objtype eq {} && $canswap} {
-		set objtype [find_path_type $althead $altpath]
-		if {$objtype ne {}} {
-			set head $althead
-			set path $altpath
+		if {$subcommand eq {blame} && ![is_bare]} {
+			if {![file isfile $path]} {
+				show_parse_err [mc "fatal: no such file '%s' in worktree" $path]
+			}
+			set use_worktree 1
 		}
-	}
-	set current_branch $head
-
-	# check that path exists in head, and objtype matches need
-	if {$objtype ne $required_objtype} {
-		switch -- $required_objtype {
-			tree {set err [strcat \
-				[mc "'%s' is not a directory in rev '%s'" $path $head]]}
-			blob {set err [strcat \
-				[mc "'%s' is not a filename in rev '%s'" $path $head]]}
-		}
-		if {[tk windowingsystem] eq "win32"} {
-			catch {wm withdraw .}
-			error_popup $err
+	} else {
+		if {[catch {
+				set commitid \
+					[git rev-parse --verify --end-of-options \
+					[strcat $head "^{commit}"]]
+			}]} {
+			show_parse_err [mc "fatal: '%s' is not a valid rev'" $head]
 		} else {
-			puts stderr $err
+			set current_branch $head
+		}
+	}
+
+	# check path is known in head, and is file / directory as required
+	set pathtype {}
+	catch {set pathtype [git ls-tree {--format=%(objecttype)} $head $path]}
+	if {$pathtype ne {} && $path eq {.}} {
+		# ls-tree gives contents of root-dir, we need root-dir itself
+		set pathtype {tree}
+	}
+
+	if {$pathtype ne $required_pathtype} {
+		switch -- $required_pathtype {
+			tree {show_parse_err \
+				[mc "'%s' is not a directory in rev '%s'" $path $head]}
+			blob {show_parse_err \
+				[mc "'%s' is not a filename in rev '%s'" $path $head]}
 		}
-		exit 1
 	}
 
 	wm deiconify .
@@ -3146,8 +3113,8 @@ blame {
 	browser {
 		browser::new $head $path
 	}
-	blame {
-		blame::new $head $path $jump_spec
+	blame   {
+		blame::new [expr {$use_worktree ? {} : $head}] $path $jump_spec
 	}
 	}
 	return
-- 
2.54.0.99.14


^ permalink raw reply related

* [PATCH v2] doc: fix typos via codespell
From: Andrew Kreimer @ 2026-05-31 18:43 UTC (permalink / raw)
  To: git; +Cc: Andrew Kreimer
In-Reply-To: <20260506101631.18127-1-algonell@gmail.com>

There are some typos in the documentation, comments, etc.
Fix them via codespell.

Signed-off-by: Andrew Kreimer <algonell@gmail.com>
---
v2:
  - Drop typos under po/ and git-gui/ (different projects).

 Documentation/SubmittingPatches            |  2 +-
 Documentation/git-sparse-checkout.adoc     |  2 +-
 Documentation/technical/build-systems.adoc |  6 +++---
 builtin/pack-objects.c                     |  2 +-
 commit-graph.h                             |  2 +-
 compat/precompose_utf8.c                   |  2 +-
 hook.h                                     |  2 +-
 meson_options.txt                          |  2 +-
 midx-write.c                               |  2 +-
 odb/source.h                               |  2 +-
 packfile.h                                 |  2 +-
 path.h                                     |  2 +-
 reftable/system.h                          |  2 +-
 t/README                                   |  2 +-
 t/chainlint.pl                             |  2 +-
 t/chainlint/chain-break-false.expect       |  2 +-
 t/chainlint/chain-break-false.test         |  2 +-
 t/t1700-split-index.sh                     |  2 +-
 t/t3909-stash-pathspec-file.sh             |  6 +++---
 t/t4052-stat-output.sh                     |  2 +-
 t/t4067-diff-partial-clone.sh              |  2 +-
 t/t9150/svk-merge.dump                     | 10 +++++-----
 t/t9151/svn-mergeinfo.dump                 | 18 +++++++++---------
 t/unit-tests/clar/README.md                |  2 +-
 24 files changed, 40 insertions(+), 40 deletions(-)

diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index d570184ec8..35b4952c8a 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -92,7 +92,7 @@ input and avoids unnecessary churn from many rapid iterations.
   topic are appropriate, so such an incremental updates are limited to
   small corrections and polishing.  After a topic cooks for some time
   (like 7 calendar days) in 'next' without needing further tweaks on
-  top, it gets merged to the 'master' branch and wait to become part
+  top, it gets merged to the 'master' branch and waits to become part
   of the next major release.
 
 In the following sections, many techniques and conventions are listed
diff --git a/Documentation/git-sparse-checkout.adoc b/Documentation/git-sparse-checkout.adoc
index 0d1618f161..e286584c67 100644
--- a/Documentation/git-sparse-checkout.adoc
+++ b/Documentation/git-sparse-checkout.adoc
@@ -134,7 +134,7 @@ the `clean.requireForce` config option is set to `false`.
 +
 The `--dry-run` option will list the directories that would be removed
 without deleting them. Running in this mode can be helpful to predict the
-behavior of the clean comand or to determine which kinds of files are left
+behavior of the clean command or to determine which kinds of files are left
 in the sparse directories.
 +
 The `--verbose` option will list every file within the directories that
diff --git a/Documentation/technical/build-systems.adoc b/Documentation/technical/build-systems.adoc
index 3c5237b9fd..ca5b5d96f1 100644
--- a/Documentation/technical/build-systems.adoc
+++ b/Documentation/technical/build-systems.adoc
@@ -47,7 +47,7 @@ Auto-detection of the following items is considered to be important:
 
   - Check for the existence of headers.
   - Check for the existence of libraries.
-  - Check for the existence of exectuables.
+  - Check for the existence of executables.
   - Check for the runtime behavior of specific functions.
   - Check for specific link order requirements when multiple libraries are
     involved.
@@ -106,7 +106,7 @@ by the build system:
 
   - C: the primary compiled language used by Git, must be supported. Relevant
     toolchains are GCC, Clang and MSVC.
-  - Rust: candidate as a second compiled lanugage, should be supported. Relevant
+  - Rust: candidate as a second compiled language, should be supported. Relevant
     toolchains is the LLVM-based rustc.
 
 Built-in support for the respective languages is preferred over support that
@@ -142,7 +142,7 @@ The following list of build systems are considered:
 
 === GNU Make
 
-- Platform support: ubitquitous on all platforms, but not well-integrated into Windows.
+- Platform support: ubiquitous on all platforms, but not well-integrated into Windows.
 - Auto-detection: no built-in support for auto-detection of features.
 - Ease of use: easy to use, but discovering available options is hard. Makefile
   rules can quickly get out of hand once reaching a certain scope.
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 480cc0bd8c..558cf821fe 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -1349,7 +1349,7 @@ static void write_pack_file(void)
 			 * length of them as buffer length.
 			 *
 			 * Note that we need to subtract one though to
-			 * accomodate for the sideband byte.
+			 * accommodate for the sideband byte.
 			 */
 			struct hashfd_options opts = {
 				.progress = progress_state,
diff --git a/commit-graph.h b/commit-graph.h
index f6a5433641..13ca4ff010 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -18,7 +18,7 @@
  * This method is only used to enhance coverage of the commit-graph
  * feature in the test suite with the GIT_TEST_COMMIT_GRAPH and
  * GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS environment variables. Do not
- * call this method oustide of a builtin, and only if you know what
+ * call this method outside of a builtin, and only if you know what
  * you are doing!
  */
 void git_test_write_commit_graph_or_die(struct odb_source *source);
diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c
index 43b3be0114..6e709bd138 100644
--- a/compat/precompose_utf8.c
+++ b/compat/precompose_utf8.c
@@ -85,7 +85,7 @@ const char *precompose_string_if_needed(const char *in)
 		out = reencode_string_iconv(in, inlen, ic_prec, 0, &outlen);
 		if (out) {
 			if (outlen == inlen && !memcmp(in, out, outlen))
-				free(out); /* no need to return indentical */
+				free(out); /* no need to return identical */
 			else
 				in = out;
 		}
diff --git a/hook.h b/hook.h
index b4372b636f..27bb1aeb2e 100644
--- a/hook.h
+++ b/hook.h
@@ -128,7 +128,7 @@ struct run_hooks_opt {
 	 * While the callback allows piecemeal writing, it can also be
 	 * used for smaller inputs, where it gets called only once.
 	 *
-	 * Add hook callback initalization context to `feed_pipe_ctx`.
+	 * Add hook callback initialization context to `feed_pipe_ctx`.
 	 * Add hook callback internal state to `feed_pipe_cb_data`.
 	 *
 	 */
diff --git a/meson_options.txt b/meson_options.txt
index 80a8025f20..d936ada098 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -106,7 +106,7 @@ option('highlight_bin', type: 'string', value: 'highlight')
 
 # Documentation.
 option('docs', type: 'array', choices: ['man', 'html'], value: [],
-  description: 'Which documenattion formats to build and install.')
+  description: 'Which documentation formats to build and install.')
 option('default_help_format', type: 'combo', choices: ['man', 'html', 'platform'], value: 'platform',
   description: 'Default format used when executing git-help(1).')
 option('docs_backend', type: 'combo', choices: ['asciidoc', 'asciidoctor', 'auto'], value: 'auto',
diff --git a/midx-write.c b/midx-write.c
index 561e9eedc0..19e1cd10b7 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -1461,7 +1461,7 @@ static int write_midx_internal(struct write_midx_opts *opts)
 
 		/*
 		 * Attempt opening the pack index to populate num_objects.
-		 * Ignore failiures as they can be expected and are not
+		 * Ignore failures as they can be expected and are not
 		 * fatal during this selection time.
 		 */
 		open_pack_index(oldest);
diff --git a/odb/source.h b/odb/source.h
index 0a440884e4..2f51fcb9ff 100644
--- a/odb/source.h
+++ b/odb/source.h
@@ -341,7 +341,7 @@ static inline int odb_source_read_object_stream(struct odb_read_stream **out,
  * are only iterated over once.
  *
  * The optional `request` structure serves as a template for retrieving the
- * object info for each indvidual iterated object and will be populated as if
+ * object info for each individual iterated object and will be populated as if
  * `odb_source_read_object_info()` was called on the object. It will not be
  * modified, the callback will instead be invoked with a separate `struct
  * object_info` for every object. Object info will not be read when passing a
diff --git a/packfile.h b/packfile.h
index 49d6bdecf6..5729a37018 100644
--- a/packfile.h
+++ b/packfile.h
@@ -124,7 +124,7 @@ struct packfile_store {
 	 * that packs that contain a lot of accessed objects will be located
 	 * towards the front.
 	 *
-	 * This is usually desireable, but there are exceptions. One exception
+	 * This is usually desirable, but there are exceptions. One exception
 	 * is when the looking up multiple objects in a loop for each packfile.
 	 * In that case, we may easily end up with an infinite loop as the
 	 * packfiles get reordered to the front repeatedly.
diff --git a/path.h b/path.h
index 0434ba5e07..4c2958a903 100644
--- a/path.h
+++ b/path.h
@@ -217,7 +217,7 @@ void safe_create_dir(struct repository *repo, const char *dir, int share);
  *
  *   - It always adjusts shared permissions.
  *
- * Returns a negative erorr code on error, 0 on success.
+ * Returns a negative error code on error, 0 on success.
  */
 int safe_create_dir_in_gitdir(struct repository *repo, const char *path);
 
diff --git a/reftable/system.h b/reftable/system.h
index c0e2cbe0ff..628232a46f 100644
--- a/reftable/system.h
+++ b/reftable/system.h
@@ -84,7 +84,7 @@ struct reftable_flock {
  * to acquire the lock. If `timeout_ms` is 0 we don't wait, if it is negative
  * we block indefinitely.
  *
- * Retrun 0 on success, a reftable error code on error. Specifically,
+ * Return 0 on success, a reftable error code on error. Specifically,
  * `REFTABLE_LOCK_ERROR` should be returned in case the target path is already
  * locked.
  */
diff --git a/t/README b/t/README
index adbbd9acf4..085921be4b 100644
--- a/t/README
+++ b/t/README
@@ -972,7 +972,7 @@ see test-lib-functions.sh for the full list and their options.
  - test_lazy_prereq <prereq> <script>
 
    Declare the way to determine if a test prerequisite <prereq> is
-   satisified or not, but delay the actual determination until the
+   satisfied or not, but delay the actual determination until the
    prerequisite is actually used by "test_have_prereq" or the
    three-arg form of the test_expect_* functions.  For example, this
    is how the SYMLINKS prerequisite is declared to see if the platform
diff --git a/t/chainlint.pl b/t/chainlint.pl
index f0598e3934..2d07a99700 100755
--- a/t/chainlint.pl
+++ b/t/chainlint.pl
@@ -35,7 +35,7 @@
 #
 # In other languages, `1+2` would typically be scanned as three tokens
 # (`1`, `+`, and `2`), but in shell it is a single token. However, the similar
-# `1 + 2`, which embeds whitepace, is scanned as three token in shell, as well.
+# `1 + 2`, which embeds whitespace, is scanned as three token in shell, as well.
 # In shell, several characters with special meaning lose that meaning when not
 # surrounded by whitespace. For instance, the negation operator `!` is special
 # when standing alone surrounded by whitespace; whereas in `foo!uucp` it is
diff --git a/t/chainlint/chain-break-false.expect b/t/chainlint/chain-break-false.expect
index f6a0a301e9..db6f8b12a4 100644
--- a/t/chainlint/chain-break-false.expect
+++ b/t/chainlint/chain-break-false.expect
@@ -1,4 +1,4 @@
-2 if condition not satisified
+2 if condition not satisfied
 3 then
 4 	echo it did not work...
 5 	echo failed!
diff --git a/t/chainlint/chain-break-false.test b/t/chainlint/chain-break-false.test
index f78ad911fc..924c9627c0 100644
--- a/t/chainlint/chain-break-false.test
+++ b/t/chainlint/chain-break-false.test
@@ -1,6 +1,6 @@
 test_expect_success 'chain-break-false' '
 # LINT: broken &&-chain okay if explicit "false" signals failure
-if condition not satisified
+if condition not satisfied
 then
 	echo it did not work...
 	echo failed!
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
index ac4a5b2734..869fb4a14e 100755
--- a/t/t1700-split-index.sh
+++ b/t/t1700-split-index.sh
@@ -502,7 +502,7 @@ test_expect_success 'do not refresh null base index' '
 		git checkout main &&
 		git update-index --split-index &&
 		test_commit more &&
-		# must not write a new shareindex, or we wont catch the problem
+		# must not write a new shareindex, or we won't catch the problem
 		git -c splitIndex.maxPercentChange=100 merge --no-edit side-branch 2>err &&
 		# i.e. do not expect warnings like
 		# could not freshen shared index .../shareindex.00000...
diff --git a/t/t3909-stash-pathspec-file.sh b/t/t3909-stash-pathspec-file.sh
index 73f2dbdeb0..3afa6bff3d 100755
--- a/t/t3909-stash-pathspec-file.sh
+++ b/t/t3909-stash-pathspec-file.sh
@@ -29,7 +29,7 @@ verify_expect () {
 test_expect_success 'simplest' '
 	restore_checkpoint &&
 
-	# More files are written to make sure that git didnt ignore
+	# More files are written to make sure that git didn't ignore
 	# --pathspec-from-file, stashing everything
 	echo A >fileA.t &&
 	echo B >fileB.t &&
@@ -47,7 +47,7 @@ test_expect_success 'simplest' '
 test_expect_success '--pathspec-file-nul' '
 	restore_checkpoint &&
 
-	# More files are written to make sure that git didnt ignore
+	# More files are written to make sure that git didn't ignore
 	# --pathspec-from-file, stashing everything
 	echo A >fileA.t &&
 	echo B >fileB.t &&
@@ -66,7 +66,7 @@ test_expect_success '--pathspec-file-nul' '
 test_expect_success 'only touches what was listed' '
 	restore_checkpoint &&
 
-	# More files are written to make sure that git didnt ignore
+	# More files are written to make sure that git didn't ignore
 	# --pathspec-from-file, stashing everything
 	echo A >fileA.t &&
 	echo B >fileB.t &&
diff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh
index e009585925..62d3d2604c 100755
--- a/t/t4052-stat-output.sh
+++ b/t/t4052-stat-output.sh
@@ -420,7 +420,7 @@ test_expect_success 'merge --stat respects COLUMNS with long name' '
 # enough terminal display width, will contain the following line:
 #     "<RED>|<RESET>  ${FILENAME} | 0"
 # where "<RED>" and "<RESET>" are ANSI escape codes to color the text.
-# To calculate the minimium terminal display width MIN_TERM_WIDTH so that the
+# To calculate the minimum terminal display width MIN_TERM_WIDTH so that the
 # FILENAME in the diffstat will not be shortened, we take the FILENAME length
 # and add 9 to it.
 # To check if the diffstat width, when the line_prefix (the "<RED>|<RESET>" of
diff --git a/t/t4067-diff-partial-clone.sh b/t/t4067-diff-partial-clone.sh
index 30813109ac..a9dec84c30 100755
--- a/t/t4067-diff-partial-clone.sh
+++ b/t/t4067-diff-partial-clone.sh
@@ -159,7 +159,7 @@ test_expect_success 'diff succeeds even if prefetch triggered by break-rewrites'
 	# We need baz to trigger break-rewrites detection.
 	git -C client reset --hard HEAD &&
 
-	# break-rewrites detction in reset.
+	# break-rewrites detection in reset.
 	git -C client reset HEAD~1
 '
 
diff --git a/t/t9150/svk-merge.dump b/t/t9150/svk-merge.dump
index 42f70dbec7..6a8ac81b11 100644
--- a/t/t9150/svk-merge.dump
+++ b/t/t9150/svk-merge.dump
@@ -77,7 +77,7 @@ Content-length: 2411
 PROPS-END
 # -DCOLLISION_CHECK if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
-# enough guarantees about no collisions between objects ever hapenning.
+# enough guarantees about no collisions between objects ever happening.
 #
 # -DNSEC if you want git to care about sub-second file mtimes and ctimes.
 # Note that you need some new glibc (at least >2.2.4) for this, and it will
@@ -206,7 +206,7 @@ Content-length: 2465
 
 # -DCOLLISION_CHECK if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
-# enough guarantees about no collisions between objects ever hapenning.
+# enough guarantees about no collisions between objects ever happening.
 #
 # -DNSEC if you want git to care about sub-second file mtimes and ctimes.
 # Note that you need some new glibc (at least >2.2.4) for this, and it will
@@ -310,7 +310,7 @@ Content-length: 2521
 
 # -DCOLLISION_CHECK if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
-# enough guarantees about no collisions between objects ever hapenning.
+# enough guarantees about no collisions between objects ever happening.
 #
 # -DNSEC if you want git to care about sub-second file mtimes and ctimes.
 # Note that you need some new glibc (at least >2.2.4) for this, and it will
@@ -417,7 +417,7 @@ Content-length: 2593
 
 # -DCOLLISION_CHECK if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
-# enough guarantees about no collisions between objects ever hapenning.
+# enough guarantees about no collisions between objects ever happening.
 #
 # -DNSEC if you want git to care about sub-second file mtimes and ctimes.
 # Note that you need some new glibc (at least >2.2.4) for this, and it will
@@ -534,7 +534,7 @@ Content-length: 2713
 
 # -DCOLLISION_CHECK if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
-# enough guarantees about no collisions between objects ever hapenning.
+# enough guarantees about no collisions between objects ever happening.
 #
 # -DNSEC if you want git to care about sub-second file mtimes and ctimes.
 # Note that you need some new glibc (at least >2.2.4) for this, and it will
diff --git a/t/t9151/svn-mergeinfo.dump b/t/t9151/svn-mergeinfo.dump
index 47cafcf528..d5e1695637 100644
--- a/t/t9151/svn-mergeinfo.dump
+++ b/t/t9151/svn-mergeinfo.dump
@@ -87,7 +87,7 @@ Content-length: 2411
 PROPS-END
 # -DCOLLISION_CHECK if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
-# enough guarantees about no collisions between objects ever hapenning.
+# enough guarantees about no collisions between objects ever happening.
 #
 # -DNSEC if you want git to care about sub-second file mtimes and ctimes.
 # Note that you need some new glibc (at least >2.2.4) for this, and it will
@@ -260,7 +260,7 @@ Content-length: 2465
 
 # -DCOLLISION_CHECK if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
-# enough guarantees about no collisions between objects ever hapenning.
+# enough guarantees about no collisions between objects ever happening.
 #
 # -DNSEC if you want git to care about sub-second file mtimes and ctimes.
 # Note that you need some new glibc (at least >2.2.4) for this, and it will
@@ -365,7 +365,7 @@ Content-length: 2521
 
 # -DCOLLISION_CHECK if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
-# enough guarantees about no collisions between objects ever hapenning.
+# enough guarantees about no collisions between objects ever happening.
 #
 # -DNSEC if you want git to care about sub-second file mtimes and ctimes.
 # Note that you need some new glibc (at least >2.2.4) for this, and it will
@@ -473,7 +473,7 @@ Content-length: 2529
 
 # -DCOLLISION_CHECK if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
-# enough guarantees about no collisions between objects ever hapenning.
+# enough guarantees about no collisions between objects ever happening.
 #
 # -DNSEC if you want git to care about sub-second file mtimes and ctimes.
 # Note that you need some new glibc (at least >2.2.4) for this, and it will
@@ -578,7 +578,7 @@ Content-length: 2593
 
 # -DCOLLISION_CHECK if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
-# enough guarantees about no collisions between objects ever hapenning.
+# enough guarantees about no collisions between objects ever happening.
 #
 # -DNSEC if you want git to care about sub-second file mtimes and ctimes.
 # Note that you need some new glibc (at least >2.2.4) for this, and it will
@@ -767,7 +767,7 @@ Content-length: 2593
 
 # -DCOLLISION_CHECK if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
-# enough guarantees about no collisions between objects ever hapenning.
+# enough guarantees about no collisions between objects ever happening.
 #
 # -DNSEC if you want git to care about sub-second file mtimes and ctimes.
 # Note that you need some new glibc (at least >2.2.4) for this, and it will
@@ -948,7 +948,7 @@ Content-length: 2713
 
 # -DCOLLISION_CHECK if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
-# enough guarantees about no collisions between objects ever hapenning.
+# enough guarantees about no collisions between objects ever happening.
 #
 # -DNSEC if you want git to care about sub-second file mtimes and ctimes.
 # Note that you need some new glibc (at least >2.2.4) for this, and it will
@@ -1172,7 +1172,7 @@ Content-length: 2713
 
 # -DCOLLISION_CHECK if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
-# enough guarantees about no collisions between objects ever hapenning.
+# enough guarantees about no collisions between objects ever happening.
 #
 # -DNSEC if you want git to care about sub-second file mtimes and ctimes.
 # Note that you need some new glibc (at least >2.2.4) for this, and it will
@@ -1414,7 +1414,7 @@ Content-length: 2713
 
 # -DCOLLISION_CHECK if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
-# enough guarantees about no collisions between objects ever hapenning.
+# enough guarantees about no collisions between objects ever happening.
 #
 # -DNSEC if you want git to care about sub-second file mtimes and ctimes.
 # Note that you need some new glibc (at least >2.2.4) for this, and it will
diff --git a/t/unit-tests/clar/README.md b/t/unit-tests/clar/README.md
index 41595989ca..a45b9c8e5d 100644
--- a/t/unit-tests/clar/README.md
+++ b/t/unit-tests/clar/README.md
@@ -138,7 +138,7 @@ raise errors during test execution.
 __Caution:__ If you use assertions inside of `test_suitename__initialize`,
 make sure that you do not rely on `__initialize` being completely run
 inside your `test_suitename__cleanup` function. Otherwise you might
-encounter ressource cleanup twice.
+encounter resource cleanup twice.
 
 ## How does Clar work?
 

Interdiff against v1:
  diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
  index 40e95bccb4..23fe76e498 100755
  --- a/git-gui/git-gui.sh
  +++ b/git-gui/git-gui.sh
  @@ -109,7 +109,7 @@ foreach p [split $env(PATH) $_path_sep] {
   	if {[file pathtype $p] ne {absolute}} {
   		continue
   	}
  -	# Keep only the first occurrence of any duplicates.
  +	# Keep only the first occurence of any duplicates.
   	set norm_p [file normalize $p]
   	dict set _path_seen $norm_p 1
   }
  diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl
  index a4703af028..7e1462a20c 100644
  --- a/git-gui/lib/choose_repository.tcl
  +++ b/git-gui/lib/choose_repository.tcl
  @@ -15,7 +15,7 @@ field w_recentlist ; # Listbox containing recent repositories
   field w_localpath  ; # Entry widget bound to local_path
   
   field done              0 ; # Finished picking the repository?
  -field clone_ok      false ; # clone succeeded
  +field clone_ok      false ; # clone succeeeded
   field local_path       {} ; # Where this repository is locally
   field origin_url       {} ; # Where we are cloning from
   field origin_name  origin ; # What we shall call 'origin'
  diff --git a/git-gui/lib/themed.tcl b/git-gui/lib/themed.tcl
  index f4cffeac66..c18e201d85 100644
  --- a/git-gui/lib/themed.tcl
  +++ b/git-gui/lib/themed.tcl
  @@ -4,7 +4,7 @@
   
   namespace eval color {
   	# Variable colors
  -	# Preferred way to set widget colors is using add_option.
  +	# Preffered way to set widget colors is using add_option.
   	# In some cases, like with tags in_diff/in_sel, we use these colors.
   	variable select_bg				lightgray
   	variable select_fg				black
  diff --git a/po/el.po b/po/el.po
  index c45560c996..703f46d0c7 100644
  --- a/po/el.po
  +++ b/po/el.po
  @@ -2748,7 +2748,7 @@ msgid "Low-level Commands / Interrogators"
   msgstr "Εντολές Χαμηλού Επιπέδου / Ερωτημάτων"
   
   #: help.c:37
  -msgid "Low-level Commands / Syncing Repositories"
  +msgid "Low-level Commands / Synching Repositories"
   msgstr "Εντολές Χαμηλού Επιπέδου / Συγχρονισμού Αποθετηρίων"
   
   #: help.c:38
  diff --git a/po/ko.po b/po/ko.po
  index 6bc20a43e3..7a6847f023 100644
  --- a/po/ko.po
  +++ b/po/ko.po
  @@ -2062,7 +2062,7 @@ msgid "Low-level Commands / Interrogators"
   msgstr "보조 명령 / 정보 획득 기능"
   
   #: help.c:37
  -msgid "Low-level Commands / Syncing Repositories"
  +msgid "Low-level Commands / Synching Repositories"
   msgstr "보조 명령 / 저장소 동기화 기능"
   
   #: help.c:38

Range-diff against v1:
1:  381e2c1fc3 ! 1:  674edefc99 doc: fix typos via codespell
    @@ compat/precompose_utf8.c: const char *precompose_string_if_needed(const char *in
      				in = out;
      		}
     
    - ## git-gui/git-gui.sh ##
    -@@ git-gui/git-gui.sh: foreach p [split $env(PATH) $_path_sep] {
    - 	if {[file pathtype $p] ne {absolute}} {
    - 		continue
    - 	}
    --	# Keep only the first occurence of any duplicates.
    -+	# Keep only the first occurrence of any duplicates.
    - 	set norm_p [file normalize $p]
    - 	dict set _path_seen $norm_p 1
    - }
    -
    - ## git-gui/lib/choose_repository.tcl ##
    -@@ git-gui/lib/choose_repository.tcl: field w_recentlist ; # Listbox containing recent repositories
    - field w_localpath  ; # Entry widget bound to local_path
    - 
    - field done              0 ; # Finished picking the repository?
    --field clone_ok      false ; # clone succeeeded
    -+field clone_ok      false ; # clone succeeded
    - field local_path       {} ; # Where this repository is locally
    - field origin_url       {} ; # Where we are cloning from
    - field origin_name  origin ; # What we shall call 'origin'
    -
    - ## git-gui/lib/themed.tcl ##
    -@@
    - 
    - namespace eval color {
    - 	# Variable colors
    --	# Preffered way to set widget colors is using add_option.
    -+	# Preferred way to set widget colors is using add_option.
    - 	# In some cases, like with tags in_diff/in_sel, we use these colors.
    - 	variable select_bg				lightgray
    - 	variable select_fg				black
    -
      ## hook.h ##
     @@ hook.h: struct run_hooks_opt {
      	 * While the callback allows piecemeal writing, it can also be
    @@ path.h: void safe_create_dir(struct repository *repo, const char *dir, int share
      int safe_create_dir_in_gitdir(struct repository *repo, const char *path);
      
     
    - ## po/el.po ##
    -@@ po/el.po: msgid "Low-level Commands / Interrogators"
    - msgstr "Εντολές Χαμηλού Επιπέδου / Ερωτημάτων"
    - 
    - #: help.c:37
    --msgid "Low-level Commands / Synching Repositories"
    -+msgid "Low-level Commands / Syncing Repositories"
    - msgstr "Εντολές Χαμηλού Επιπέδου / Συγχρονισμού Αποθετηρίων"
    - 
    - #: help.c:38
    -
    - ## po/ko.po ##
    -@@ po/ko.po: msgid "Low-level Commands / Interrogators"
    - msgstr "보조 명령 / 정보 획득 기능"
    - 
    - #: help.c:37
    --msgid "Low-level Commands / Synching Repositories"
    -+msgid "Low-level Commands / Syncing Repositories"
    - msgstr "보조 명령 / 저장소 동기화 기능"
    - 
    - #: help.c:38
    -
      ## reftable/system.h ##
     @@ reftable/system.h: struct reftable_flock {
       * to acquire the lock. If `timeout_ms` is 0 we don't wait, if it is negative
-- 
2.54.0


^ permalink raw reply related

* [PATCH] prio-queue: use cascade-down sift for faster extract-min
From: Kristofer Karlsson via GitGitGadget @ 2026-05-31 17:57 UTC (permalink / raw)
  To: git; +Cc: Kristofer Karlsson, Kristofer Karlsson

From: Kristofer Karlsson <krka@spotify.com>

Replace the standard sift-down in prio_queue_get() with a
cascade-down approach.

The standard approach places the last array element at the root,
then sifts it down.  At each level this requires two comparisons
(left vs right child, then element vs winner) and, when the
element is larger, a swap (three 16-byte copies).

The cascade approach instead promotes the smaller child into the
vacant root slot at each level — one comparison and one copy.
The vacancy sinks to a leaf, where the last array element is
placed and sifted up if needed — typically zero levels since the
last array element tends to be large.

In the common case, work per extract drops from 2d comparisons
+ 3d copies to d comparisons + d copies: roughly half the
comparisons and a third of the data movement.  The sift-up phase
can add work when the last element is smaller than ancestors of
the leaf vacancy, but this is rare in practice.

Simplify prio_queue_replace() to a plain get+put sequence.  This
is semantically equivalent: the old implementation wrote to slot 0
and sifted down, which has the same observable effect as removing
the root and inserting a new element.  No caller observes queue
state between the two operations.  The previous implementation
shared sift_down_root() with get, but the cascade approach no
longer accommodates that cleanly since sift_down_root() now
expects the element to reinsert at queue->array[queue->nr], left
there by prio_queue_get() after decrementing nr.  This is fine in
practice: replace is only called from pop_most_recent_commit()
(fetch-pack, object-name, walker) and show-branch — none of
which appear in any hot path.

A synthetic benchmark (10 rounds of 10M put+get cycles, ascending
integer keys, CPU-pinned, median of 3 runs, same compiler and
Makefile flags) shows consistent improvement across all queue
sizes, with no regressions:

    queue width       baseline    cascade    speedup
    ------------------------------------------------
             10        4.32s      3.97s      1.09x
            100        7.95s      6.49s      1.23x
          1,000       11.30s      9.66s      1.17x
         10,000       16.34s     14.15s      1.16x
        100,000       21.43s     18.66s      1.15x

With descending keys (worst case — the last element always sinks
to a leaf in both approaches) the cascade still wins slightly
(1-4%) by replacing swaps with copies, and never regresses.

In end-to-end git commands the improvement is modest because
sift_down_root is only ~8% of total runtime.  Profiling
rev-list --count on a 2.5M-commit monorepo shows sift_down_root
dropping from 8.2% to 0.4% of total runtime.  The improvement
scales with DAG width: wider DAGs produce larger priority queues,
amplifying the per-level savings.  In small or narrow repos the
queues stay shallow and the effect is negligible.

Signed-off-by: Kristofer Karlsson <krka@spotify.com>
---
    prio-queue: use cascade-down sift for faster extract-min
    
    Hi, I am not sure this is just noise or not but I thought it at least
    was interesting.
    
    I looked into the internals of prio_queue and found it was technically
    doing too much work and could be simplified/optimized. I found I could
    optimize it by ~20% for the common case (adding commits that would
    typically end up far back in the queue) but only ~1% for the reverse
    case (adding things to the front of the prio queue). The average speedup
    is somewhere in between I suppose. That said, this is not really the
    bottleneck so the overall boost seems to be around ~3-4% improvement for
    repos with wide DAGs.
    
    I would normally classify this as not urgent or important, but I think
    the advantage is that the change is very small and simple and it already
    has good unit tests (t/unit-tests/u-prio-queue.c).
    
    With that said, here are the details:
    
    The prio_queue_get impl is based on removing the root entry, then moving
    the very last element into the root slot, then sifting it down into the
    right place. This uses both comparisons between sibling elements in the
    heap as well as comparisons between the element to add and one of the
    siblings. Then it uses swap operations to move things correctly.
    
    This patch instead promotes the smaller child upward at each level,
    leaving a vacancy that sinks to a leaf, then places the removed element
    there with a short sift-up to keep the heap balanced.
    
    We can analytically compare this - for a sift-distance of d we can
    reason about the number of operations to execute.
    
    Before: 2d comparisons + 3d copies
    After:   d comparisons +  d copies
    
    
    After changing sift_down in this way, the replace operation can't simply
    depend on it anymore, so I reimplemented it as a sequence of get + put.
    This is technically correct but maybe not as efficient. However, I am
    not sure that it matters, since I couldn't see any usage of the replace
    operation in any hot path.
    
    Performance: Profiling git rev-list --count on a 2.5M-commit monorepo
    shows sift_down_root dropping from 8.2% to 0.4% of total runtime,
    effectively eliminated as significant overhead.
    
    Synthetic benchmark 10 rounds of 10M put+get cycles, CPU-pinned, median
    of 3 runs, same compiler and Makefile flags.
    
    Ascending keys (git's typical pattern -- parents have lower priority
    than children):
    
    queue width  baseline  patched  speedup
             10     4.32s    3.97s    1.09x
            100     7.95s    6.49s    1.23x
          1,000    11.30s    9.66s    1.17x
         10,000    16.34s   14.15s    1.16x
        100,000    21.43s   18.66s    1.15x
    
    
    Descending keys (worst case — last element always sinks to leaf in both
    approaches):
    
    queue width  baseline  patched  speedup
             10     4.84s    4.78s    1.01x
            100     9.43s    9.20s    1.03x
          1,000    15.28s   14.71s    1.04x
         10,000    23.61s   23.49s    1.01x
        100,000    29.16s   28.22s    1.03x
    
    
    No regressions in any scenario.
    
    End-to-end benchmarks
    
    All benchmarks use a benchmark setup of 1 warmup run followed by 10
    timed runs. Each configuration is built from the same source tree and
    tested on the same repo in alternating order.
    
    linux kernel (1.4M commits) — range v5.0..v6.0 (311K commits):
    
    Command                      baseline  patched  speedup
    rev-list --count v5.0..v6.0     455ms    440ms    1.04x
    
    
    I also ran it on git.git but did not see any performance diff at all,
    due to the size and narrow DAG.
    
    The improvement scales with DAG width: wider DAGs produce larger
    priority queues, amplifying the per-level savings. In small or narrow
    repositories the priority queues stay shallow and the sift-down cost is
    already negligible, so the change is not noticeable.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2132%2Fspkrka%2Fcascade-sift-down-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2132/spkrka/cascade-sift-down-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2132

 prio-queue.c | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/prio-queue.c b/prio-queue.c
index 9748528ce6..18005c43c4 100644
--- a/prio-queue.c
+++ b/prio-queue.c
@@ -62,17 +62,21 @@ static void sift_down_root(struct prio_queue *queue)
 {
 	size_t ix, child;
 
-	/* Push down the one at the root */
-	for (ix = 0; ix * 2 + 1 < queue->nr; ix = child) {
-		child = ix * 2 + 1; /* left */
+	for (ix = 0; (child = ix * 2 + 1) < queue->nr; ix = child) {
 		if (child + 1 < queue->nr &&
 		    compare(queue, child, child + 1) >= 0)
 			child++; /* use right child */
+		queue->array[ix] = queue->array[child];
+	}
 
-		if (compare(queue, ix, child) <= 0)
+	/* Place queue->array[queue->nr] (left by caller) and sift up. */
+	queue->array[ix] = queue->array[queue->nr];
+	while (ix) {
+		size_t parent = (ix - 1) / 2;
+		if (compare(queue, parent, ix) <= 0)
 			break;
-
-		swap(queue, child, ix);
+		swap(queue, parent, ix);
+		ix = parent;
 	}
 }
 
@@ -89,7 +93,6 @@ void *prio_queue_get(struct prio_queue *queue)
 	if (!--queue->nr)
 		return result;
 
-	queue->array[0] = queue->array[queue->nr];
 	sift_down_root(queue);
 	return result;
 }
@@ -111,8 +114,7 @@ void prio_queue_replace(struct prio_queue *queue, void *thing)
 		queue->array[queue->nr - 1].ctr = queue->insertion_ctr++;
 		queue->array[queue->nr - 1].data = thing;
 	} else {
-		queue->array[0].ctr = queue->insertion_ctr++;
-		queue->array[0].data = thing;
-		sift_down_root(queue);
+		prio_queue_get(queue);
+		prio_queue_put(queue, thing);
 	}
 }

base-commit: c69baaf57ba26cf117c2b6793802877f19738b0d
-- 
gitgitgadget

^ permalink raw reply related

* Draft of Git Rev News edition 135
From: Christian Couder @ 2026-05-31 11:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Jakub Narebski, Markus Jansen, Kaartic Sivaraam,
	Štěpán Němec, Taylor Blau,
	Johannes Schindelin, Derrick Stolee, Elijah Newren, Jeff King,
	Matthias A.

Hi everyone,

A draft of a new Git Rev News edition is available here:

  https://github.com/git/git.github.io/blob/master/rev_news/drafts/edition-135.md

Everyone is welcome to contribute in any section either by editing the
above page on GitHub and sending a pull request, or by commenting on
this GitHub issue:

  https://github.com/git/git.github.io/issues/844

You can also reply to this email.

In general all kinds of contributions, for example proofreading,
suggestions for articles or links, help on the issues in GitHub,
volunteering for being interviewed and so on, are very much
appreciated.

I tried to Cc everyone who appears in this edition, but maybe I missed
some people, sorry about that.

Jakub, Markus, Kaartic and I plan to publish this edition on Tuesday
June 2nd, 2026.

Thanks,
Christian.

^ permalink raw reply

* Re: [PATCH v3 0/6] [RFC] diff: add diff.<driver>.process for external hunk providers
From: Junio C Hamano @ 2026-05-31 10:44 UTC (permalink / raw)
  To: Michael Montalbo via GitGitGadget; +Cc: git, Michael Montalbo
In-Reply-To: <pull.2120.v3.git.1780087700.gitgitgadget@gmail.com>

"Michael Montalbo via GitGitGadget" <gitgitgadget@gmail.com> writes:

> Language-aware diff tools (e.g., Difftastic) and format-specific analyzers
> can produce better line matching than Git's builtin diff algorithm, but
> diff.<driver>.command replaces Git's output entirely, losing downstream
> features like word diff, function context, color, and blame.

This seems to break CI on Windows; take a look at

  https://github.com/git/git/actions/runs/26709491830/job/78717295153

for an example.

Thanks.

^ permalink raw reply

* Re: doc: document '@' prefix for raw timestamps
From: Luna Schwalbe @ 2026-05-31  8:29 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Kristoffer Haugsbakk, git
In-Reply-To: <xmqqpl2de41m.fsf@gitster.g>

 >> This was introduced in 116eb3ab (parse_date(): allow ancient
>> git-timestamp, 2012-02-02) and 2c733fb2 (parse_date(): '@' prefix
>> forces git-timestamp, 2012-02-02) to allow specifying "ancient"
>> timestamps (like 0 +0000) without conflicting with YYYYMMDD date
>> formats.  I do not think neither commit added documentation for this
>> '@' prefix, and Documentation/date-formats would be an excellent
>> place to do so.
>>
>> Care to whip up a patch?
> It might look something like this.

Thanks! And sure, I'll try to submit something later; this will be my 
first time using the send-mail workflow, I hope I don't mess anything up.

^ permalink raw reply

* Re: [PATCH] doc: add missing --message long option to merge docs
From: Brandon @ 2026-05-31  6:37 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Brandon Dong via GitGitGadget, git
In-Reply-To: <xmqqo6hyiz9g.fsf@gitster.g>

Junio C Hamano <gitster@pobox.com> writes:

> Hmph.  This is still not consistent with "git merge -h" output has,
> which seems to accept --[no-]message as well.
>
> It is not exactly your fault, but there are a few options other than
> this one that support optional [no-] and they are not documented as
> such, even though they appear in "git merge -h".  "git merge -m foo
> --no-message other" behaves as if "GIT_EDITOR=: git merge other" was
> run, it seems.

Looking at the code, I believe this might be intentional or maybe a
stylistic choice to document this way?

The overwhelming majority of long name flags have a [no-] variant as
it comes for free when defining a new option and otherwise requires
an explicit opt out (via PARSE_OPT_NONEG).

The -h output auto-generates the inclusion of [no-] but for the
handwritten docs, most examples I see where it's included are for
cases where the [no-] variant has some behavior nuance that needs to
be explained or it's for a bool-like flag. Most string-valued options
do not include mention of the [no-] variant and they share the
default behavior where passing the [no-] variant unsets the option.

For -m/--message in particular, none of the
git-commit/git-notes/git-svn/git-tag docs mention the --no variant
either and I think merge should be consistent with them.

^ permalink raw reply

* Re: [PATCH v2 2/2] status: improve rebase todo list parsing
From: Junio C Hamano @ 2026-05-31  0:46 UTC (permalink / raw)
  To: Phillip Wood; +Cc: git, Elijah Newren, Patrick Steinhardt
In-Reply-To: <b80bc1e0a298e2773a2fdab3e73651d59b8d39b7.1777648598.git.phillip.wood@dunelm.org.uk>

Phillip Wood <phillip.wood123@gmail.com> writes:

> +static void abbrev_oid_in_line(struct repository *r,
> +			       struct strbuf *line, char **pp)
> +{
> ...
> +	have_oid = !repo_get_oid(r, p, &oid);
> +	*end_of_object_name = saved;
> +	if (!have_oid)
> +		goto out; /* object name was a label */

Can there be a label "deadbeef123" that is unrelated to an object whose
object name happens to abbreviate to "deadbeef123"?

> +	case TODO_MERGE:
> +		skip_dash_c(&p);
> +		while (true) {
> +			p += strspn(p, " \t");
> +			if (!p[0] || (p[0] == '#' && (!p[1] || isspace(p[1]))))
> +				break;
> +			abbrev_oid_in_line(r, line, &p);
> +		}
> +		break;

What does this loop do?  A "merge" command may look like "merge
[[-C|-c] <commit>] <label>", and we give each whitespace-separated
token to abbrev_oid_in_line()?  Would "<label>" that is ambiguous
cause an issue?  You may want to limit the scope of what the loop
does a bit, e.g., massage only the token after -C/-c, or something?

> +	case TODO_FIXUP:
> +		skip_dash_c(&p);
> +		/* fallthrough */
> +	case TODO_DROP:
> +	case TODO_EDIT:
> +	case TODO_PICK:
> +	case TODO_RESET:

Doesn't RESET also take a <label>?  And if it happens to be the same
as an abbreviated object name, e.g., "deadbeef123", of an unrelated
object, would wt-status say "reset deadbeef1", causing a mismatch?
If this is indeed an issue, would moving this to the "no-op" section
below, next to TODO_LABEL, solve it?

> +	case TODO_REVERT:
> +	case TODO_REWORD:
> +	case TODO_SQUASH:
> +		abbrev_oid_in_line(r, line, &p);
> +		break;
> +
> +	/*
> +	 * Avoid "default" and instead list all the other commands so
> +	 * that -Wswitch (which is included in -Wall) warns if a new
> +	 * command is added without handling it in this function.
> +	 */
> +	case TODO_BREAK:
> +	case TODO_EXEC:
> +	case TODO_LABEL:
> +	case TODO_NOOP:
> +	case TODO_UPDATE_REF:
> +		break;
>  	}
> -	string_list_clear(&split, 0);
> +
> +	return true;
>  }

^ permalink raw reply

* Re: [PATCH] describe: fix --exclude, --match with --contains and --all
From: Junio C Hamano @ 2026-05-30 23:47 UTC (permalink / raw)
  To: Jacob Keller; +Cc: git, Jacob Keller, Tuomas Ahola
In-Reply-To: <20260528232950.187002-2-jacob.e.keller@intel.com>

Jacob Keller <jacob.e.keller@intel.com> writes:

> From: Jacob Keller <jacob.keller@gmail.com>
>
> git describe --contains acts as a wrapper around git name-rev. When
> operating with --contains and --all, the --match and --exclude patterns
> are not properly forwarded to name-rev as --exclude and --refs options.
>
> This results in the command silently discarding match and exclude
> requests from the user when operating in --all mode.
>
> We could check and die() if the user provides --contains, --all, and
> --match/--exclude. However, its also straight forward to just pass the
> filters down to git name-rev.
>
> Notice that the documentation for --match and --exclude mention the
> --all mode. It explains that they operate on refs with the prefix
> refs/tags, and additionally refs/heads and refs/remotes when using
> --all.
>
> Fix the describe logic to pass the patterns down with the appropriate
> prefixes when --all is provided. This fixes the support to match the
> documented behavior.
>
> Add tests to check that this works as expected.
>
> Reported-by: Tuomas Ahola <taahol@utu.fi>
> Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
> ---
>
> I was looking into reviving the patch that just added a simple die() and
> realized that its actually pretty straight forward to just fix the support
> instead. I'm open to either route, if we think this support isn't
> necessary... I'm not sure if there are any gotchas or other issues with how
> I implemented this.

It is curious that this fails in some but not all CI jobs, and even
more curious that these failures look the same.

e.g., https://github.com/git/git/actions/runs/26671595367/job/78615760984#step:4:1984

  +++ diff -u expect actual
  --- expect	2026-05-30 02:21:23
  +++ actual	2026-05-30 02:21:23
  @@ -1 +1 @@
  -branch_A
  +remotes/origin/remote_branch_A
  error: last command exited with $?=1
  not ok 70 - describe --contains --all --exclude
  #	
  #		echo "branch_A" >expect &&
  #		tagged_commit=$(git rev-parse "refs/tags/A^0") &&
  #		git describe --contains --all --exclude="A" --exclude="c" --exclude="test*" $tagged_commit >actual &&
  #		test_cmp expect actual

Rings any bell?

^ permalink raw reply

* Re: [PATCH v1 3/4] environment: move 'trust_executable_bit' into repo_config_values
From: Junio C Hamano @ 2026-05-30 23:17 UTC (permalink / raw)
  To: Tian Yuchen
  Cc: git, christian.couder, ps, Ayush Chandekar, Olamide Caleb Bello
In-Reply-To: <20260530160520.77859-4-cat@malon.dev>

Tian Yuchen <cat@malon.dev> writes:

> diff --git a/apply.c b/apply.c
> index 249248d4f2..73ca9907f8 100644
> --- a/apply.c
> +++ b/apply.c
> @@ -3890,10 +3890,12 @@ static int check_preimage(struct apply_state *state,
>  	}
>  
>  	if (!state->cached && !previous) {
> +		struct repo_config_values *cfg = repo_config_values(the_repository);
> +
>  		if (*ce && !(*ce)->ce_mode)
>  			BUG("ce_mode == 0 for path '%s'", old_name);
>  
> -		if (trust_executable_bit || !S_ISREG(st->st_mode))
> +		if (cfg->trust_executable_bit || !S_ISREG(st->st_mode))
>  			st_mode = ce_mode_from_stat(*ce, st->st_mode);
>  		else if (*ce)
>  			st_mode = (*ce)->ce_mode;
> diff --git a/read-cache.c b/read-cache.c
> index 54150fe756..18af533649 100644
> --- a/read-cache.c
> +++ b/read-cache.c
> @@ -204,10 +204,12 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
>  
>  unsigned int ce_mode_from_stat(const struct cache_entry *ce, unsigned int mode)
>  {
> +	struct repo_config_values *cfg = repo_config_values(the_repository);
> +
>  	if (!has_symlinks && S_ISREG(mode) &&
>  	    ce && S_ISLNK(ce->ce_mode))
>  		return ce->ce_mode;
> -	if (!trust_executable_bit && S_ISREG(mode)) {
> +	if (!cfg->trust_executable_bit && S_ISREG(mode)) {
>  		if (ce && S_ISREG(ce->ce_mode))
>  			return ce->ce_mode;
>  		return create_ce_mode(0666);

How hot are the code paths that call into this helper function?  In
the original under some condition, it was possible to return without
even consulting the trust_executable_bit variable, but in the
updated code, the helper unconditionally makes a call to the
repo_config_values() helper function even before it knows it needs
to know the value of trust_executable_bit.

> @@ -217,11 +219,13 @@ unsigned int ce_mode_from_stat(const struct cache_entry *ce, unsigned int mode)
>  
>  static unsigned int st_mode_from_ce(const struct cache_entry *ce)
>  {
> +	struct repo_config_values *cfg = repo_config_values(the_repository);
> +
>  	switch (ce->ce_mode & S_IFMT) {
>  	case S_IFLNK:
>  		return has_symlinks ? S_IFLNK : (S_IFREG | 0644);
>  	case S_IFREG:
> -		return (ce->ce_mode & (trust_executable_bit ? 0755 : 0644)) | S_IFREG;
> +		return (ce->ce_mode & (cfg->trust_executable_bit ? 0755 : 0644)) | S_IFREG;
>  	case S_IFGITLINK:
>  		return S_IFDIR | 0755;
>  	case S_IFDIR:

Ditto.

> @@ -321,6 +325,7 @@ static int ce_modified_check_fs(struct index_state *istate,
>  static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
>  {
>  	unsigned int changed = 0;
> +	struct repo_config_values *cfg = repo_config_values(the_repository);
>  
>  	if (ce->ce_flags & CE_REMOVE)
>  		return MODE_CHANGED | DATA_CHANGED | TYPE_CHANGED;
> @@ -331,7 +336,7 @@ static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
>  		/* We consider only the owner x bit to be relevant for
>  		 * "mode changes"
>  		 */
> -		if (trust_executable_bit &&
> +		if (cfg->trust_executable_bit &&
>  		    (0100 & (ce->ce_mode ^ st->st_mode)))
>  			changed |= MODE_CHANGED;
>  		break;

Ditto.

> @@ -732,6 +737,8 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
>  			  (intent_only ? ADD_CACHE_NEW_ONLY : 0));
>  	unsigned hash_flags = pretend ? 0 : INDEX_WRITE_OBJECT;
>  
> +	struct repo_config_values *cfg = repo_config_values(the_repository);
> +

Lose the excess blank line before the new declaration.

>  	if (flags & ADD_CACHE_RENORMALIZE)
>  		hash_flags |= INDEX_RENORMALIZE;
>  
> @@ -752,7 +759,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
>  		ce->ce_flags |= CE_INTENT_TO_ADD;
>  
>  
> -	if (trust_executable_bit && has_symlinks) {
> +	if (cfg->trust_executable_bit && has_symlinks) {
>  		ce->ce_mode = create_ce_mode(st_mode);
>  	} else {
>  		/* If there is an existing entry, pick the mode bits and type

Almost all of these places that care about trust_executable_bit also
cares about has_symlinks.  I wonder if they should be converted to
repo-local settings in the same series.

^ permalink raw reply

* Re: [PATCH 4/4] doc: replay: move “default” to the right-hand-side
From: Junio C Hamano @ 2026-05-30 22:37 UTC (permalink / raw)
  To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk, Siddharth Asthana
In-Reply-To: <default_RHS.70d@msgid.xyz>

kristofferhaugsbakk@fastmail.com writes:

> -`update` (default);; Update refs directly using an atomic transaction.
> +`update`;; (default) Update refs directly using an atomic transaction.

This looks sensible.  Nice.

>  	All refs are updated or none are (all-or-nothing behavior).
>  `print`;; Output update-ref commands for pipeline use. This is the
>  	traditional behavior where output can be piped to `git update-ref --stdin`.

^ permalink raw reply

* Re: [PATCH 3/4] doc: replay: use a nested definition list
From: Junio C Hamano @ 2026-05-30 22:37 UTC (permalink / raw)
  To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk, Siddharth Asthana
In-Reply-To: <--ref-action_definition_list.70c@msgid.xyz>

kristofferhaugsbakk@fastmail.com writes:

> From: Kristoffer Haugsbakk <code@khaugsbakk.name>
>
> This bullet list for `--ref-action` introduces a term with a colon.
> This is exactly what a definition list is, structurally. Let’s be
> sylistically consistent and use the definition list markup construct.

Makes sense.

>  --
> -	* `update` (default): Update refs directly using an atomic transaction.
> -	  All refs are updated or none are (all-or-nothing behavior).
> -	* `print`: Output update-ref commands for pipeline use. This is the
> -	  traditional behavior where output can be piped to `git update-ref --stdin`.
> +`update` (default);; Update refs directly using an atomic transaction.
> +	All refs are updated or none are (all-or-nothing behavior).
> +`print`;; Output update-ref commands for pipeline use. This is the
> +	traditional behavior where output can be piped to `git update-ref --stdin`.
>  --
>  +

The transition from a bulleted list to a nested definition list
(`;;`) for the `--ref-action` modes indeed makes the document
structure much cleaner.

>  The default mode can be configured via the `replay.refAction` configuration variable.

^ permalink raw reply

* Re: [PATCH 2/4] doc: replay: simplify replay.refAction description
From: Junio C Hamano @ 2026-05-30 22:37 UTC (permalink / raw)
  To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk, Siddharth Asthana
In-Reply-To: <simplify_replay.refAction.70b@msgid.xyz>

kristofferhaugsbakk@fastmail.com writes:

>  replay.refAction::
> -	Specifies the default mode for handling reference updates in
> -	`git replay`. The value can be:
> -+
> ---
> -	* `update`: Update refs directly using an atomic transaction (default behavior).
> -	* `print`: Output update-ref commands for pipeline use.
> ---
> -+
> -This setting can be overridden with the `--ref-action` command-line option.
> -When not configured, `git replay` defaults to `update` mode.
> +	Specifies the default mode for handling reference updates. Either `update` or `print`.
> +ifdef::git-replay[]
> +See `--ref-action`.
> +endif::git-replay[]
> +ifndef::git-replay[]
> +See `--ref-action` for linkgit:git-replay[1] for details.
> +endif::git-replay[]

This makes it a bit roundabout for "git config --help" readers who
wanted to figure out what value to set to the configuration
variable, because the valid choices are no longer listed here.

Finding `--ref-action=<mode>` and its description in the other page
is straight-forward, so it may not be too bad, though.

> diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc
> index f9ca2db2833..4de85088d6c 100644
> --- a/Documentation/git-replay.adoc
> +++ b/Documentation/git-replay.adoc
> @@ -211,6 +211,7 @@ to use bare commit IDs instead of branch names.
>  
>  CONFIGURATION
>  -------------
> +:git-replay: 1
>  include::config/replay.adoc[]

The use of conditional attributes (`ifdef::git-replay[]`) is a neat
and standard way to tailor the description depending on whether it
is read as part of `git-config(1)` or `git-replay(1)`. It correctly
points the reader to `--ref-action` in the latter case, and provides
a full `linkgit` reference in the former. Clean and correct.

^ permalink raw reply

* Re: [PATCH 2/4] doc: replay: simplify replay.refAction description
From: Junio C Hamano @ 2026-05-30 22:29 UTC (permalink / raw)
  To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk, Siddharth Asthana
In-Reply-To: <simplify_replay.refAction.70b@msgid.xyz>

kristofferhaugsbakk@fastmail.com writes:

>  replay.refAction::
> -	Specifies the default mode for handling reference updates in
> -	`git replay`. The value can be:
> -+
> ---
> -	* `update`: Update refs directly using an atomic transaction (default behavior).
> -	* `print`: Output update-ref commands for pipeline use.
> ---
> -+
> -This setting can be overridden with the `--ref-action` command-line option.
> -When not configured, `git replay` defaults to `update` mode.
> +	Specifies the default mode for handling reference updates. Either `update` or `print`.
> +ifdef::git-replay[]
> +See `--ref-action`.
> +endif::git-replay[]
> +ifndef::git-replay[]
> +See `--ref-action` for linkgit:git-replay[1] for details.
> +endif::git-replay[]

This makes it a bit roundabout for "git config --help" readers who
wanted to figure out what value to set to the configuration
variable, because the valid choices are no longer listed here.

Finding `--ref-action=<mode>` and its description in the other page
is straight-forward, so it may not be too bad, though.

> diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc
> index f9ca2db2833..4de85088d6c 100644
> --- a/Documentation/git-replay.adoc
> +++ b/Documentation/git-replay.adoc
> @@ -211,6 +211,7 @@ to use bare commit IDs instead of branch names.
>  
>  CONFIGURATION
>  -------------
> +:git-replay: 1
>  include::config/replay.adoc[]

The use of conditional attributes (`ifdef::git-replay[]`) is a neat
and standard way to tailor the description depending on whether it
is read as part of `git-config(1)` or `git-replay(1)`. It correctly
points the reader to `--ref-action` in the latter case, and provides
a full `linkgit` reference in the former. Clean and correct.

^ permalink raw reply

* Re: [PATCH 1/4] doc: link to config for git-replay(1)
From: Junio C Hamano @ 2026-05-30 22:18 UTC (permalink / raw)
  To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk, Siddharth Asthana
In-Reply-To: <doc_replay_link_config.70a@msgid.xyz>

kristofferhaugsbakk@fastmail.com writes:

> From: Kristoffer Haugsbakk <code@khaugsbakk.name>
>
> This config doc was added in 336ac90c (replay: add replay.refAction
> config option, 2025-11-06) but never included anywhere. Include it in
> git-replay(1) and git-config(1).
>
> Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
> ---
>  Documentation/config.adoc     | 2 ++
>  Documentation/git-replay.adoc | 4 ++++
>  2 files changed, 6 insertions(+)

It is always nice to see documentation gaps filled.

The `replay.refAction` configuration variable was indeed left
dangling without a proper link from the main command documentation,
which is embarrassing.  I wonder if we can add simple "doc-lint"
rule or two to prevent similar mistakes from happening again?

> diff --git a/Documentation/config.adoc b/Documentation/config.adoc
> index 62eebe7c545..51fabecb9b0 100644
> --- a/Documentation/config.adoc
> +++ b/Documentation/config.adoc
> @@ -511,6 +511,8 @@ include::config/remotes.adoc[]
>  
>  include::config/repack.adoc[]
>  
> +include::config/replay.adoc[]
> +
>  include::config/rerere.adoc[]
>  
>  include::config/revert.adoc[]

Placing `include::config/replay.adoc[]` in `config.adoc`
alphabetically between `repack` and `rerere` is correct, as the list
is alphabetical.

> diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc
> index a32f72aead3..f9ca2db2833 100644
> --- a/Documentation/git-replay.adoc
> +++ b/Documentation/git-replay.adoc
> @@ -209,6 +209,10 @@ This replays the range `aabbcc..ddeeff` onto commit `112233` and updates
>  `refs/heads/mybranch` to point at the result. This can be useful when you want
>  to use bare commit IDs instead of branch names.
>  
> +CONFIGURATION
> +-------------
> +include::config/replay.adoc[]
> +

Adding the `CONFIGURATION` section near the end of `git-replay.adoc`
is also the standard way we expose configuration variables to the
command's manual page.

Looking good.

>  GIT
>  ---
>  Part of the linkgit:git[1] suite

^ permalink raw reply

* Re: [PATCH 0/4] doc: replay: fix config link
From: Junio C Hamano @ 2026-05-30 22:18 UTC (permalink / raw)
  To: kristofferhaugsbakk; +Cc: git, Kristoffer Haugsbakk, Siddharth Asthana
In-Reply-To: <CV_doc_replay_config.709@msgid.xyz>

kristofferhaugsbakk@fastmail.com writes:

> From: Kristoffer Haugsbakk <code@khaugsbakk.name>
>
> [1/4] doc: link to config for git-replay(1)
> [2/4] doc: replay: simplify replay.refAction description
> [3/4] doc: replay: use a nested definition list
> [4/4] doc: replay: move “default” to the right-hand-side

It is always nice to see documentation gaps filled.

^ permalink raw reply

* Re: [PATCH v1 4/4] read-cache: pass 'istate' to stat/mode helper functions
From: Christian Couder @ 2026-05-30 18:14 UTC (permalink / raw)
  To: Tian Yuchen; +Cc: git, ps, Ayush Chandekar, Olamide Caleb Bello
In-Reply-To: <20260530160520.77859-5-cat@malon.dev>

On Sat, May 30, 2026 at 6:05 PM Tian Yuchen <cat@malon.dev> wrote:
>
> In the previous commit, the gloabl 'trust_executable_bit' was

s/gloabl/global/

> migrated into 'repo_config_values', but low-level helpers in
> read-cache.c still relied on 'the_repository' to access it.
>
> Refactor the signatures of ce_mode_from_stat(), st_mode_from_ce(),
> fake_lstat(), and check_removed() to accept a 'struct
> index_state *istate'. This allows these functions to retrieve the
> repository context via 'istate->repo'.

The cover letter contains:

"In other words, this series of patches is laying the groundwork for
the eventual elimination of 'the_repository'."

but I think it would be also interesting to have something similar
here, as this is especially relevant to this commit.

For example maybe add something like "which will help with removing
'the_repository' in the future" to the last sentence?

^ permalink raw reply

* Re: [PATCH v1 3/4] environment: move 'trust_executable_bit' into repo_config_values
From: Christian Couder @ 2026-05-30 18:02 UTC (permalink / raw)
  To: Tian Yuchen; +Cc: git, ps, Ayush Chandekar, Olamide Caleb Bello
In-Reply-To: <20260530160520.77859-4-cat@malon.dev>

On Sat, May 30, 2026 at 6:05 PM Tian Yuchen <cat@malon.dev> wrote:

> @@ -720,5 +719,6 @@ void repo_config_values_init(struct repo_config_values *cfg)
>  {
>         cfg->attributes_file = NULL;
>         cfg->apply_sparse_checkout = 0;
> +       cfg->trust_executable_bit = 1;

Here `trust_executable_bit` is processed after `apply_sparse_checkout` ...

>         cfg->branch_track = BRANCH_TRACK_REMOTE;
>  }
> diff --git a/environment.h b/environment.h
> index 123a71cdc8..72c400923d 100644
> --- a/environment.h
> +++ b/environment.h
> @@ -90,6 +90,7 @@ struct repository;
>  struct repo_config_values {
>         /* section "core" config values */
>         char *attributes_file;
> +       int trust_executable_bit;
>         int apply_sparse_checkout;

... but here it is before `apply_sparse_checkout`.

I think it would make more sense to put `trust_executable_bit` after
`apply_sparse_checkout` here.

>         /* section "branch" config values */

^ permalink raw reply

* [PATCH v1 1/4] read-cache: remove redundant extern declarations
From: Tian Yuchen @ 2026-05-30 16:05 UTC (permalink / raw)
  To: git; +Cc: christian.couder, ps, Tian Yuchen, Ayush Chandekar,
	Olamide Caleb Bello
In-Reply-To: <20260530160520.77859-1-cat@malon.dev>

The 'read-cache.c' file already includes 'environment.h', which provides
the extern declarations for variables like 'trust_executable_bit' and
'has_symlinks'.

Remove the redundant extern declarations inside 'st_mode_from_ce()' to
clean up the code.

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>
Mentored-by: Olamide Caleb Bello <belkid98@gmail.com>
Signed-off-by: Tian Yuchen <cat@malon.dev>
---
 read-cache.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/read-cache.c b/read-cache.c
index 38a04b8de3..c44e4d128f 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -204,8 +204,6 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
 
 static unsigned int st_mode_from_ce(const struct cache_entry *ce)
 {
-	extern int trust_executable_bit, has_symlinks;
-
 	switch (ce->ce_mode & S_IFMT) {
 	case S_IFLNK:
 		return has_symlinks ? S_IFLNK : (S_IFREG | 0644);
-- 
2.43.0


^ permalink raw reply related

* [PATCH v1 0/4] environment.c: migrate 'trust_executable_bit' into 'repo_config_values'
From: Tian Yuchen @ 2026-05-30 16:05 UTC (permalink / raw)
  To: git; +Cc: christian.couder, ps, Tian Yuchen, Ayush Chandekar,
	Olamide Caleb Bello

The 'core.filemode' configuration, which is stored as a global
variable 'trust_executable_bit', is a core filesystem capability flag.

Move it into 'repo_config_values' to tie it to the specific
repository instance it was read from. Eager parsing is maintained
because this flag is heavily consulted in hot paths.

To avoid falling back to 'the_repository', refactor the signatures
of helper functions:

 - ce_mode_from_stat()
 - st_mode_from_ce()
 - fake_lstat()
 - check_removed()

These functions now accept a 'struct index_state *istate', ensuring
the correct context is seamlessly passed down to the lowest levels.

Note: 'repo_config_values()' still does not support any 'struct
repository' other than 'the_repository'. In other words, this series
of patches is laying the groundwork for the eventual elimination of
'the_repository'.

Previous related work:

 - [PATCH 2/6] config: add trust_executable_bit to global config

 - [PATCH] Refactor 'trust_executable_bit' to repository-scoped setting 
 (This series of patches was unsuccessful because the target location selected
 was 'struct repo_settings', which our analysis indicated was not the
 optimal choice. For further details, please see: [1])

[1] https://lore.kernel.org/git/837b5360b40f992351f489a0ae05fedf49884c6e.1685716420.git.gitgitgadget@gmail.com/
[2] https://lore.kernel.org/git/20260301190017.53539-1-dronarajgyawali@gmail.com/
[3] https://lore.kernel.org/git/xmqq1pht6nyx.fsf@gitster.g/

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>
Mentored-by: Olamide Caleb Bello <belkid98@gmail.com>
Signed-off-by: Tian Yuchen <cat@malon.dev>

Tian Yuchen (4):
  read-cache: remove redundant extern declarations
  read-cache: move 'ce_mode_from_stat()' to 'read-cache.c'
  environment: move 'trust_executable_bit' into repo_config_values
  read-cache: pass 'istate' to stat/mode helper functions

 apply.c                |  6 ++++--
 builtin/update-index.c |  2 +-
 diff-lib.c             | 20 +++++++++---------
 environment.c          |  4 ++--
 environment.h          |  2 +-
 read-cache-ll.h        |  2 +-
 read-cache.c           | 47 ++++++++++++++++++++++++++++++++----------
 read-cache.h           | 17 +++------------
 8 files changed, 58 insertions(+), 42 deletions(-)

-- 
2.43.0


^ permalink raw reply

* [PATCH v1 4/4] read-cache: pass 'istate' to stat/mode helper functions
From: Tian Yuchen @ 2026-05-30 16:05 UTC (permalink / raw)
  To: git; +Cc: christian.couder, ps, Tian Yuchen, Ayush Chandekar,
	Olamide Caleb Bello
In-Reply-To: <20260530160520.77859-1-cat@malon.dev>

In the previous commit, the gloabl 'trust_executable_bit' was
migrated into 'repo_config_values', but low-level helpers in
read-cache.c still relied on 'the_repository' to access it.

Refactor the signatures of ce_mode_from_stat(), st_mode_from_ce(),
fake_lstat(), and check_removed() to accept a 'struct
index_state *istate'. This allows these functions to retrieve the
repository context via 'istate->repo'.

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>
Mentored-by: Olamide Caleb Bello <belkid98@gmail.com>
Signed-off-by: Tian Yuchen <cat@malon.dev>
---
 apply.c                |  4 ++--
 builtin/update-index.c |  2 +-
 diff-lib.c             | 20 ++++++++++----------
 read-cache-ll.h        |  2 +-
 read-cache.c           | 31 +++++++++++++++++++------------
 read-cache.h           |  3 ++-
 6 files changed, 35 insertions(+), 27 deletions(-)

diff --git a/apply.c b/apply.c
index 73ca9907f8..a81bb29a6f 100644
--- a/apply.c
+++ b/apply.c
@@ -3890,13 +3890,13 @@ static int check_preimage(struct apply_state *state,
 	}
 
 	if (!state->cached && !previous) {
-		struct repo_config_values *cfg = repo_config_values(the_repository);
+		struct repo_config_values *cfg = repo_config_values(state->repo);
 
 		if (*ce && !(*ce)->ce_mode)
 			BUG("ce_mode == 0 for path '%s'", old_name);
 
 		if (cfg->trust_executable_bit || !S_ISREG(st->st_mode))
-			st_mode = ce_mode_from_stat(*ce, st->st_mode);
+			st_mode = ce_mode_from_stat(state->repo->index, *ce, st->st_mode);
 		else if (*ce)
 			st_mode = (*ce)->ce_mode;
 		else
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 8a5907767b..3f6967bd84 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -293,7 +293,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
 	ce->ce_flags = create_ce_flags(0);
 	ce->ce_namelen = len;
 	fill_stat_cache_info(the_repository->index, ce, st);
-	ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
+	ce->ce_mode = ce_mode_from_stat(the_repository->index, old, st->st_mode);
 
 	if (index_path(the_repository->index, &ce->oid, path, st,
 		       info_only ? 0 : INDEX_WRITE_OBJECT)) {
diff --git a/diff-lib.c b/diff-lib.c
index ae91027a02..95fd3ba4b9 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -39,14 +39,14 @@
  * exists for ce that is a submodule -- it is a submodule that is not
  * checked out).  Return negative for an error.
  */
-static int check_removed(const struct cache_entry *ce, struct stat *st)
+static int check_removed(struct index_state *istate, const struct cache_entry *ce, struct stat *st)
 {
 	int stat_err;
 
 	if (!(ce->ce_flags & CE_FSMONITOR_VALID))
 		stat_err = lstat(ce->name, st);
 	else
-		stat_err = fake_lstat(ce, st);
+		stat_err = fake_lstat(istate, ce, st);
 	if (stat_err < 0) {
 		if (!is_missing_file_error(errno))
 			return -1;
@@ -158,9 +158,9 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
 			int num_compare_stages = 0;
 			struct stat st;
 
-			changed = check_removed(ce, &st);
+			changed = check_removed(revs->repo->index, ce, &st);
 			if (!changed)
-				wt_mode = ce_mode_from_stat(ce, st.st_mode);
+				wt_mode = ce_mode_from_stat(revs->repo->index, ce, st.st_mode);
 			else {
 				if (changed < 0) {
 					perror(ce->name);
@@ -193,7 +193,7 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
 					num_compare_stages++;
 					oidcpy(&dpath->parent[stage - 2].oid,
 					       &nce->oid);
-					dpath->parent[stage-2].mode = ce_mode_from_stat(nce, mode);
+					dpath->parent[stage-2].mode = ce_mode_from_stat(revs->repo->index, nce, mode);
 					dpath->parent[stage-2].status =
 						DIFF_STATUS_MODIFIED;
 				}
@@ -249,7 +249,7 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
 		} else {
 			struct stat st;
 
-			changed = check_removed(ce, &st);
+			changed = check_removed(revs->repo->index, ce, &st);
 			if (changed) {
 				if (changed < 0) {
 					perror(ce->name);
@@ -262,7 +262,7 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
 				continue;
 			} else if (revs->diffopt.ita_invisible_in_index &&
 				   ce_intent_to_add(ce)) {
-				newmode = ce_mode_from_stat(ce, st.st_mode);
+				newmode = ce_mode_from_stat(revs->repo->index, ce, st.st_mode);
 				diff_addremove(&revs->diffopt, '+', newmode,
 					       null_oid(the_hash_algo), 0, ce->name, 0);
 				continue;
@@ -270,7 +270,7 @@ void run_diff_files(struct rev_info *revs, unsigned int option)
 
 			changed = match_stat_with_submodule(&revs->diffopt, ce, &st,
 							    ce_option, &dirty_submodule);
-			newmode = ce_mode_from_stat(ce, st.st_mode);
+			newmode = ce_mode_from_stat(revs->repo->index, ce, st.st_mode);
 		}
 
 		if (!changed && !dirty_submodule) {
@@ -324,7 +324,7 @@ static int get_stat_data(const struct cache_entry *ce,
 	if (!cached && !ce_uptodate(ce)) {
 		int changed;
 		struct stat st;
-		changed = check_removed(ce, &st);
+		changed = check_removed(diffopt->repo->index, ce, &st);
 		if (changed < 0)
 			return -1;
 		else if (changed) {
@@ -338,7 +338,7 @@ static int get_stat_data(const struct cache_entry *ce,
 		changed = match_stat_with_submodule(diffopt, ce, &st,
 						    0, dirty_submodule);
 		if (changed) {
-			mode = ce_mode_from_stat(ce, st.st_mode);
+			mode = ce_mode_from_stat(diffopt->repo->index, ce, st.st_mode);
 			oid = null_oid(the_hash_algo);
 		}
 	}
diff --git a/read-cache-ll.h b/read-cache-ll.h
index 2c8b4b21b1..9fb9bedfbf 100644
--- a/read-cache-ll.h
+++ b/read-cache-ll.h
@@ -442,7 +442,7 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
  * for lstat() for a tracked path that is known to be up-to-date via
  * some out-of-line means (like fsmonitor).
  */
-int fake_lstat(const struct cache_entry *ce, struct stat *st);
+int fake_lstat(struct index_state *istate, const struct cache_entry *ce, struct stat *st);
 
 #define REFRESH_REALLY                   (1 << 0) /* ignore_valid */
 #define REFRESH_UNMERGED                 (1 << 1) /* allow unmerged */
diff --git a/read-cache.c b/read-cache.c
index 18af533649..28e7f24382 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -202,9 +202,12 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
 	}
 }
 
-unsigned int ce_mode_from_stat(const struct cache_entry *ce, unsigned int mode)
+unsigned int ce_mode_from_stat(struct index_state *istate,
+			       const struct cache_entry *ce,
+			       unsigned int mode)
 {
-	struct repo_config_values *cfg = repo_config_values(the_repository);
+	struct repository *repo = (istate && istate->repo) ? istate->repo : the_repository;
+	struct repo_config_values *cfg = repo_config_values(repo);
 
 	if (!has_symlinks && S_ISREG(mode) &&
 	    ce && S_ISLNK(ce->ce_mode))
@@ -217,9 +220,10 @@ unsigned int ce_mode_from_stat(const struct cache_entry *ce, unsigned int mode)
 	return create_ce_mode(mode);
 }
 
-static unsigned int st_mode_from_ce(const struct cache_entry *ce)
+static unsigned int st_mode_from_ce(struct index_state *istate, const struct cache_entry *ce)
 {
-	struct repo_config_values *cfg = repo_config_values(the_repository);
+	struct repository *repo = (istate && istate->repo) ? istate->repo : the_repository;
+	struct repo_config_values *cfg = repo_config_values(repo);
 
 	switch (ce->ce_mode & S_IFMT) {
 	case S_IFLNK:
@@ -235,10 +239,10 @@ static unsigned int st_mode_from_ce(const struct cache_entry *ce)
 	}
 }
 
-int fake_lstat(const struct cache_entry *ce, struct stat *st)
+int fake_lstat(struct index_state *istate, const struct cache_entry *ce, struct stat *st)
 {
 	fake_lstat_data(&ce->ce_stat_data, st);
-	st->st_mode = st_mode_from_ce(ce);
+	st->st_mode = st_mode_from_ce(istate, ce);
 
 	/* always succeed as lstat() replacement */
 	return 0;
@@ -322,10 +326,12 @@ static int ce_modified_check_fs(struct index_state *istate,
 	return 0;
 }
 
-static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
+static int ce_match_stat_basic(struct index_state *istate,
+			       const struct cache_entry *ce, struct stat *st)
 {
 	unsigned int changed = 0;
-	struct repo_config_values *cfg = repo_config_values(the_repository);
+	struct repository *repo = (istate && istate->repo) ? istate->repo : the_repository;
+	struct repo_config_values *cfg = repo_config_values(repo);
 
 	if (ce->ce_flags & CE_REMOVE)
 		return MODE_CHANGED | DATA_CHANGED | TYPE_CHANGED;
@@ -430,7 +436,7 @@ int ie_match_stat(struct index_state *istate,
 	if (ce_intent_to_add(ce))
 		return DATA_CHANGED | TYPE_CHANGED | MODE_CHANGED;
 
-	changed = ce_match_stat_basic(ce, st);
+	changed = ce_match_stat_basic(istate, ce, st);
 
 	/*
 	 * Within 1 second of this sequence:
@@ -737,7 +743,8 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
 			  (intent_only ? ADD_CACHE_NEW_ONLY : 0));
 	unsigned hash_flags = pretend ? 0 : INDEX_WRITE_OBJECT;
 
-	struct repo_config_values *cfg = repo_config_values(the_repository);
+	struct repository *repo = (istate && istate->repo) ? istate->repo : the_repository;
+	struct repo_config_values *cfg = repo_config_values(repo);
 
 	if (flags & ADD_CACHE_RENORMALIZE)
 		hash_flags |= INDEX_RENORMALIZE;
@@ -769,7 +776,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
 		int pos = index_name_pos_also_unmerged(istate, path, namelen);
 
 		ent = (0 <= pos) ? istate->cache[pos] : NULL;
-		ce->ce_mode = ce_mode_from_stat(ent, st_mode);
+		ce->ce_mode = ce_mode_from_stat(istate, ent, st_mode);
 	}
 
 	/* When core.ignorecase=true, determine if a directory of the same name but differing
@@ -2592,7 +2599,7 @@ static void ce_smudge_racily_clean_entry(struct index_state *istate,
 
 	if (lstat(ce->name, &st) < 0)
 		return;
-	if (ce_match_stat_basic(ce, &st))
+	if (ce_match_stat_basic(istate, ce, &st))
 		return;
 	if (ce_modified_check_fs(istate, ce, &st)) {
 		/* This is "racily clean"; smudge it.  Note that this
diff --git a/read-cache.h b/read-cache.h
index 3c4af2faeb..61299ed95b 100644
--- a/read-cache.h
+++ b/read-cache.h
@@ -5,7 +5,8 @@
 #include "object.h"
 #include "pathspec.h"
 
-unsigned int ce_mode_from_stat(const struct cache_entry *ce,
+unsigned int ce_mode_from_stat(struct index_state *istate,
+				const struct cache_entry *ce,
 				unsigned int mode);
 
 static inline int ce_to_dtype(const struct cache_entry *ce)
-- 
2.43.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox