Git development
 help / color / mirror / Atom feed
* Re: [PATCH v3 2/2] compat/posix.h: simplify GIT_GNUC_PREREQ() comparison
From: Dominik Loidolt @ 2026-06-12 19:04 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, gitster, asedeno, asedeno, avarab
In-Reply-To: <aiwJSBfRbUFZ70gP@pks.im>

Thanks again for taking the time to review my contribution.

On Fri, Jun 12, 2026 at 03:27:36PM +0200, Patrick Steinhardt wrote:
> > It is also more future-proof, as it no longer assumes that GCC version
> > components stay below 65536.
>
> I feel like all the message needs to say is "let's do it for
> consistency, and it's easier to read". That would've been sufficient,
> whereas this argument here feels a bit thin.

Agreed. I'll simplify the commit message. The "future-proof" bit was a joke I
just couldn't resist, but it may cause more confusion than it is worth.
I'll drop it.

> It would've been nice to either move these changes into a preparatory
> commit or at least mention them

Agreed. I'll split the cleanup into a separate commit.

> I'm not sure myself whether this could use another reroll. It's all just
> nits, and the intent is clear enough.

I think it's worth rerolling.

Thanks,
 Dominik

^ permalink raw reply

* [GSoC Patch v3 4/4] repo: add path.gitdir with absolute and relative suffix formatting
From: K Jayatheerth @ 2026-06-12 18:28 UTC (permalink / raw)
  To: jayatheerthkulkarni2005
  Cc: a3205153416, git, gitster, jltobler, kristofferhaugsbakk,
	kumarayushjha123, lucasseikioshiro, phillip.wood, sandals
In-Reply-To: <20260612182847.562816-1-jayatheerthkulkarni2005@gmail.com>

Scripts need a stable way to locate the git directory without
parsing rev-parse output or relying on its flag-driven path format
selection. There is no way to retrieve this path from git repo info
today.

Introduce path.gitdir.absolute and path.gitdir.relative keys,
consistent with the path.commondir keys added in the previous patch.
Reuse the test_repo_info_path helper introduced there to validate
both variants.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  6 ++++++
 builtin/repo.c              | 24 ++++++++++++++++++++++++
 t/t1900-repo-info.sh        |  7 +++++++
 3 files changed, 37 insertions(+)

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 890c34051d..ed7d80c690 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -113,6 +113,12 @@ values that they return:
 	The path to the Git repository's common directory relative to
 	the current working directory.
 
+`path.gitdir.absolute`::
+	The canonical absolute path to the Git repository directory (the `.git` directory).
+
+`path.gitdir.relative`::
+	The path to the Git repository directory relative to the current working directory.
+
 `references.format`::
 	The reference storage format. The valid values are:
 +
diff --git a/builtin/repo.c b/builtin/repo.c
index c4cc3bf3fc..9a312d127a 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -99,6 +99,28 @@ static int get_path_commondir_relative(struct repository *repo, struct strbuf *b
 	return 0;
 }
 
+static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *git_dir = repo_get_git_dir(repo);
+
+	if (!git_dir)
+		return error(_("unable to get git directory"));
+
+	append_formatted_path(buf, git_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_gitdir_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *git_dir = repo_get_git_dir(repo);
+
+	if (!git_dir)
+		return error(_("unable to get git directory"));
+
+	append_formatted_path(buf, git_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_references_format(struct repository *repo, struct strbuf *buf)
 {
 	strbuf_addstr(buf,
@@ -113,6 +135,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "object.format", get_object_format },
 	{ "path.commondir.absolute", get_path_commondir_absolute },
 	{ "path.commondir.relative", get_path_commondir_relative },
+	{ "path.gitdir.absolute", get_path_gitdir_absolute },
+	{ "path.gitdir.relative", get_path_gitdir_relative },
 	{ "references.format", get_references_format },
 };
 
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 28fe76e25b..26acb5fe82 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -216,4 +216,11 @@ test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
 	'commondir-only-gitdir' '.git' '../.git' \
 	'GIT_DIR="../.git" && export GIT_DIR'
 
+test_repo_info_path 'gitdir standard' 'gitdir' 'gitdir-std' \
+	'.git' '../.git'
+
+test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
+	'gitdir-env' '.git' '../.git' \
+	'GIT_DIR="../.git" && export GIT_DIR'
+
 test_done
-- 
2.54.0


^ permalink raw reply related

* [GSoC Patch v3 3/4] repo: add path.commondir with absolute and relative suffix formatting
From: K Jayatheerth @ 2026-06-12 18:28 UTC (permalink / raw)
  To: jayatheerthkulkarni2005
  Cc: a3205153416, git, gitster, jltobler, kristofferhaugsbakk,
	kumarayushjha123, lucasseikioshiro, phillip.wood, sandals
In-Reply-To: <20260612182847.562816-1-jayatheerthkulkarni2005@gmail.com>

Scripts working with worktree setups need a reliable way to discover
the common directory, which diverges from the git directory when
multiple worktrees are in use. There is no way to retrieve this path
from git repo info today.

Introduce path.commondir.absolute and path.commondir.relative keys.
Exposing explicit format variants rather than a single key with a
default avoids ambiguity for scripts that require predictable output.

Add a test helper test_repo_info_path that creates isolated
repositories per test case to prevent state leaks, captures the repo
root before changing directories to avoid eval, and accepts an optional
init_command to cover environment variable overrides such as
GIT_COMMON_DIR and GIT_DIR.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 Documentation/git-repo.adoc |  9 ++++++
 builtin/repo.c              | 26 ++++++++++++++++
 t/t1900-repo-info.sh        | 61 +++++++++++++++++++++++++++++++++++++
 3 files changed, 96 insertions(+)

diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc
index 42262c1983..890c34051d 100644
--- a/Documentation/git-repo.adoc
+++ b/Documentation/git-repo.adoc
@@ -104,6 +104,15 @@ values that they return:
 `object.format`::
 	The object format (hash algorithm) used in the repository.
 
+`path.commondir.absolute`::
+	The canonical absolute path to the Git repository's common
+	directory (the shared `.git` directory containing objects,
+	refs, and global configuration).
+
+`path.commondir.relative`::
+	The path to the Git repository's common directory relative to
+	the current working directory.
+
 `references.format`::
 	The reference storage format. The valid values are:
 +
diff --git a/builtin/repo.c b/builtin/repo.c
index 71a5c1c29c..c4cc3bf3fc 100644
--- a/builtin/repo.c
+++ b/builtin/repo.c
@@ -7,12 +7,14 @@
 #include "hex.h"
 #include "odb.h"
 #include "parse-options.h"
+#include "path.h"
 #include "path-walk.h"
 #include "progress.h"
 #include "quote.h"
 #include "ref-filter.h"
 #include "refs.h"
 #include "revision.h"
+#include "setup.h"
 #include "strbuf.h"
 #include "string-list.h"
 #include "shallow.h"
@@ -75,6 +77,28 @@ static int get_object_format(struct repository *repo, struct strbuf *buf)
 	return 0;
 }
 
+static int get_path_commondir_absolute(struct repository *repo, struct strbuf *buf)
+{
+	const char *common_dir = repo_get_common_dir(repo);
+
+	if (!common_dir)
+		return error(_("unable to get common directory"));
+
+	append_formatted_path(buf, common_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
+	return 0;
+}
+
+static int get_path_commondir_relative(struct repository *repo, struct strbuf *buf)
+{
+	const char *common_dir = repo_get_common_dir(repo);
+
+	if (!common_dir)
+		return error(_("unable to get common directory"));
+
+	append_formatted_path(buf, common_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);
+	return 0;
+}
+
 static int get_references_format(struct repository *repo, struct strbuf *buf)
 {
 	strbuf_addstr(buf,
@@ -87,6 +111,8 @@ static const struct repo_info_field repo_info_field[] = {
 	{ "layout.bare", get_layout_bare },
 	{ "layout.shallow", get_layout_shallow },
 	{ "object.format", get_object_format },
+	{ "path.commondir.absolute", get_path_commondir_absolute },
+	{ "path.commondir.relative", get_path_commondir_relative },
 	{ "references.format", get_references_format },
 };
 
diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh
index 39bb77dda0..28fe76e25b 100755
--- a/t/t1900-repo-info.sh
+++ b/t/t1900-repo-info.sh
@@ -155,4 +155,65 @@ test_expect_success 'git repo info -h shows only repo info usage' '
 	test_grep ! "git repo structure" actual
 '
 
+# Helper function to test path keys in both absolute and relative formats.
+# $1: label for the test
+# $2: field_name (e.g., commondir)
+# $3: unique repo name for isolation
+# $4: expect_absolute (suffix appended to repo root)
+# $5: expect_relative (the relative path string expected)
+# $6: init_command (extra setup like exporting env vars)
+test_repo_info_path () {
+	label=$1
+	field_name=$2
+	repo_name=$3
+	expect_absolute_suffix=$4
+	expect_relative=$5
+	init_command=$6
+
+	absolute_root="$repo_name-absolute"
+	relative_root="$repo_name-relative"
+
+	test_expect_success "setup: $label" '
+		git init "$absolute_root" &&
+		git init "$relative_root" &&
+		mkdir -p "$absolute_root/sub" "$relative_root/sub"
+	'
+
+	test_expect_success "absolute: $label" '
+		(
+			cd "$absolute_root/sub" &&
+			ROOT="$(test-tool path-utils real_path "..")" && export ROOT &&
+			eval "$init_command" &&
+			expect_path="$ROOT${expect_absolute_suffix:+/$expect_absolute_suffix}" &&
+			echo "path.$field_name.absolute=$expect_path" >expect &&
+			git repo info "path.$field_name.absolute" >actual &&
+			test_cmp expect actual
+		)
+	'
+
+	test_expect_success "relative: $label" '
+		(
+			cd "$relative_root/sub" &&
+			ROOT="$(test-tool path-utils real_path "..")" && export ROOT &&
+			eval "$init_command" &&
+			echo "path.$field_name.relative=$expect_relative" >expect &&
+			git repo info "path.$field_name.relative" >actual &&
+			test_cmp expect actual
+		)
+	'
+}
+
+test_repo_info_path 'commondir standard' 'commondir' 'commondir-std' \
+	'.git' '../.git'
+
+test_repo_info_path 'commondir with GIT_COMMON_DIR and GIT_DIR' 'commondir' \
+	'commondir-envs' 'custom-common' '../custom-common' \
+	'GIT_COMMON_DIR="$ROOT/custom-common" && export GIT_COMMON_DIR &&
+	 GIT_DIR="../.git" && export GIT_DIR &&
+	 git init --bare "$ROOT/custom-common"'
+
+test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
+	'commondir-only-gitdir' '.git' '../.git' \
+	'GIT_DIR="../.git" && export GIT_DIR'
+
 test_done
-- 
2.54.0


^ permalink raw reply related

* [GSoC Patch v3 2/4] rev-parse: use append_formatted_path() for path formatting
From: K Jayatheerth @ 2026-06-12 18:28 UTC (permalink / raw)
  To: jayatheerthkulkarni2005
  Cc: a3205153416, git, gitster, jltobler, kristofferhaugsbakk,
	kumarayushjha123, lucasseikioshiro, phillip.wood, sandals
In-Reply-To: <20260612182847.562816-1-jayatheerthkulkarni2005@gmail.com>

Now that path formatting logic lives in a shared helper, keeping a
duplicate implementation in rev-parse is unnecessary and risks the
two diverging over time.

Replace the local format_type and default_type enums and the
hand-rolled formatting logic with a call to append_formatted_path().
Introduce PATH_FORMAT_DEFAULT as the initial value of arg_path_format
so that per-path fallback behavior is resolved in print_path() rather
than leaked into the shared helper.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 builtin/rev-parse.c | 103 ++++++++++----------------------------------
 1 file changed, 23 insertions(+), 80 deletions(-)

diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 218b5f34d6..2dd35361f3 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -632,73 +632,16 @@ static void handle_ref_opt(const char *pattern, const char *prefix)
 	clear_ref_exclusions(&ref_excludes);
 }
 
-enum format_type {
-	/* We would like a relative path. */
-	FORMAT_RELATIVE,
-	/* We would like a canonical absolute path. */
-	FORMAT_CANONICAL,
-	/* We would like the default behavior. */
-	FORMAT_DEFAULT,
-};
-
-enum default_type {
-	/* Our default is a relative path. */
-	DEFAULT_RELATIVE,
-	/* Our default is a relative path if there's a shared root. */
-	DEFAULT_RELATIVE_IF_SHARED,
-	/* Our default is a canonical absolute path. */
-	DEFAULT_CANONICAL,
-	/* Our default is not to modify the item. */
-	DEFAULT_UNMODIFIED,
-};
-
-static void print_path(const char *path, const char *prefix, enum format_type format, enum default_type def)
+static void print_path(const char *path, const char *prefix,
+		       enum path_format arg_path_format, enum path_format def_format)
 {
-	char *cwd = NULL;
-	/*
-	 * We don't ever produce a relative path if prefix is NULL, so set the
-	 * prefix to the current directory so that we can produce a relative
-	 * path whenever possible.  If we're using RELATIVE_IF_SHARED mode, then
-	 * we want an absolute path unless the two share a common prefix, so don't
-	 * set it in that case, since doing so causes a relative path to always
-	 * be produced if possible.
-	 */
-	if (!prefix && (format != FORMAT_DEFAULT || def != DEFAULT_RELATIVE_IF_SHARED))
-		prefix = cwd = xgetcwd();
-	if (format == FORMAT_DEFAULT && def == DEFAULT_UNMODIFIED) {
-		puts(path);
-	} else if (format == FORMAT_RELATIVE ||
-		  (format == FORMAT_DEFAULT && def == DEFAULT_RELATIVE)) {
-		/*
-		 * In order for relative_path to work as expected, we need to
-		 * make sure that both paths are absolute paths.  If we don't,
-		 * we can end up with an unexpected absolute path that the user
-		 * didn't want.
-		 */
-		struct strbuf buf = STRBUF_INIT, realbuf = STRBUF_INIT, prefixbuf = STRBUF_INIT;
-		if (!is_absolute_path(path)) {
-			strbuf_realpath_forgiving(&realbuf, path,  1);
-			path = realbuf.buf;
-		}
-		if (!is_absolute_path(prefix)) {
-			strbuf_realpath_forgiving(&prefixbuf, prefix, 1);
-			prefix = prefixbuf.buf;
-		}
-		puts(relative_path(path, prefix, &buf));
-		strbuf_release(&buf);
-		strbuf_release(&realbuf);
-		strbuf_release(&prefixbuf);
-	} else if (format == FORMAT_DEFAULT && def == DEFAULT_RELATIVE_IF_SHARED) {
-		struct strbuf buf = STRBUF_INIT;
-		puts(relative_path(path, prefix, &buf));
-		strbuf_release(&buf);
-	} else {
-		struct strbuf buf = STRBUF_INIT;
-		strbuf_realpath_forgiving(&buf, path, 1);
-		puts(buf.buf);
-		strbuf_release(&buf);
-	}
-	free(cwd);
+	struct strbuf sb = STRBUF_INIT;
+	enum path_format fmt = (arg_path_format != PATH_FORMAT_DEFAULT) ? arg_path_format : def_format;
+
+	append_formatted_path(&sb, path, prefix, fmt);
+	puts(sb.buf);
+
+	strbuf_release(&sb);
 }
 
 int cmd_rev_parse(int argc,
@@ -717,7 +660,7 @@ int cmd_rev_parse(int argc,
 	const char *name = NULL;
 	struct strbuf buf = STRBUF_INIT;
 	int seen_end_of_options = 0;
-	enum format_type format = FORMAT_DEFAULT;
+	enum path_format arg_path_format = PATH_FORMAT_DEFAULT;
 
 	show_usage_if_asked(argc, argv, builtin_rev_parse_usage);
 
@@ -797,8 +740,8 @@ int cmd_rev_parse(int argc,
 					die(_("--git-path requires an argument"));
 				print_path(repo_git_path_replace(the_repository, &buf,
 								 "%s", argv[i + 1]), prefix,
-						format,
-						DEFAULT_RELATIVE_IF_SHARED);
+						arg_path_format,
+						PATH_FORMAT_RELATIVE_IF_SHARED);
 				i++;
 				continue;
 			}
@@ -820,9 +763,9 @@ int cmd_rev_parse(int argc,
 				if (!arg)
 					die(_("--path-format requires an argument"));
 				if (!strcmp(arg, "absolute")) {
-					format = FORMAT_CANONICAL;
+					arg_path_format = PATH_FORMAT_CANONICAL;
 				} else if (!strcmp(arg, "relative")) {
-					format = FORMAT_RELATIVE;
+					arg_path_format = PATH_FORMAT_RELATIVE;
 				} else {
 					die(_("unknown argument to --path-format: %s"), arg);
 				}
@@ -985,7 +928,7 @@ int cmd_rev_parse(int argc,
 			if (!strcmp(arg, "--show-toplevel")) {
 				const char *work_tree = repo_get_work_tree(the_repository);
 				if (work_tree)
-					print_path(work_tree, prefix, format, DEFAULT_UNMODIFIED);
+					print_path(work_tree, prefix, arg_path_format, PATH_FORMAT_UNMODIFIED);
 				else
 					die(_("this operation must be run in a work tree"));
 				continue;
@@ -993,7 +936,7 @@ int cmd_rev_parse(int argc,
 			if (!strcmp(arg, "--show-superproject-working-tree")) {
 				struct strbuf superproject = STRBUF_INIT;
 				if (get_superproject_working_tree(&superproject))
-					print_path(superproject.buf, prefix, format, DEFAULT_UNMODIFIED);
+					print_path(superproject.buf, prefix, arg_path_format, PATH_FORMAT_UNMODIFIED);
 				strbuf_release(&superproject);
 				continue;
 			}
@@ -1028,18 +971,18 @@ int cmd_rev_parse(int argc,
 				const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
 				char *cwd;
 				int len;
-				enum format_type wanted = format;
+				enum path_format wanted = arg_path_format;
 				if (arg[2] == 'g') {	/* --git-dir */
 					if (gitdir) {
-						print_path(gitdir, prefix, format, DEFAULT_UNMODIFIED);
+						print_path(gitdir, prefix, arg_path_format, PATH_FORMAT_UNMODIFIED);
 						continue;
 					}
 					if (!prefix) {
-						print_path(".git", prefix, format, DEFAULT_UNMODIFIED);
+						print_path(".git", prefix, arg_path_format, PATH_FORMAT_UNMODIFIED);
 						continue;
 					}
 				} else {		/* --absolute-git-dir */
-					wanted = FORMAT_CANONICAL;
+					wanted = PATH_FORMAT_CANONICAL;
 					if (!gitdir && !prefix)
 						gitdir = ".git";
 					if (gitdir) {
@@ -1055,11 +998,11 @@ int cmd_rev_parse(int argc,
 				strbuf_reset(&buf);
 				strbuf_addf(&buf, "%s%s.git", cwd, len && cwd[len-1] != '/' ? "/" : "");
 				free(cwd);
-				print_path(buf.buf, prefix, wanted, DEFAULT_CANONICAL);
+				print_path(buf.buf, prefix, wanted, PATH_FORMAT_CANONICAL);
 				continue;
 			}
 			if (!strcmp(arg, "--git-common-dir")) {
-				print_path(repo_get_common_dir(the_repository), prefix, format, DEFAULT_RELATIVE_IF_SHARED);
+				print_path(repo_get_common_dir(the_repository), prefix, arg_path_format, PATH_FORMAT_RELATIVE_IF_SHARED);
 				continue;
 			}
 			if (!strcmp(arg, "--is-inside-git-dir")) {
@@ -1089,7 +1032,7 @@ int cmd_rev_parse(int argc,
 				if (the_repository->index->split_index) {
 					const struct object_id *oid = &the_repository->index->split_index->base_oid;
 					const char *path = repo_git_path_replace(the_repository, &buf, "sharedindex.%s", oid_to_hex(oid));
-					print_path(path, prefix, format, DEFAULT_RELATIVE);
+					print_path(path, prefix, arg_path_format, PATH_FORMAT_RELATIVE);
 				}
 				continue;
 			}
-- 
2.54.0


^ permalink raw reply related

* [GSoC Patch v3 1/4] path: introduce append_formatted_path() for shared path formatting
From: K Jayatheerth @ 2026-06-12 18:28 UTC (permalink / raw)
  To: jayatheerthkulkarni2005
  Cc: a3205153416, git, gitster, jltobler, kristofferhaugsbakk,
	kumarayushjha123, lucasseikioshiro, phillip.wood, sandals
In-Reply-To: <20260612182847.562816-1-jayatheerthkulkarni2005@gmail.com>

The path-formatting logic in builtin/rev-parse.c is tightly coupled
to that command and writes directly to stdout, making it impossible
for other builtins to reuse.

Extract the core algorithm into append_formatted_path() in path.c
and expose a path_format enum in path.h so that any builtin can
format paths consistently without duplicating logic.

Mentored-by: Justin Tobler <jltobler@gmail.com>
Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
---
 path.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 path.h | 36 ++++++++++++++++++++++++++++++
 2 files changed, 106 insertions(+)

diff --git a/path.c b/path.c
index d7e17bf174..5e83e3e4f6 100644
--- a/path.c
+++ b/path.c
@@ -1579,6 +1579,76 @@ char *xdg_cache_home(const char *filename)
 	return NULL;
 }
 
+void append_formatted_path(struct strbuf *dest, const char *path,
+			   const char *prefix, enum path_format format)
+{
+	switch (format) {
+	case PATH_FORMAT_DEFAULT:
+	case PATH_FORMAT_UNMODIFIED:
+		strbuf_addstr(dest, path);
+		break;
+
+	case PATH_FORMAT_RELATIVE: {
+		struct strbuf relative_buf = STRBUF_INIT;
+		struct strbuf real_path = STRBUF_INIT;
+		struct strbuf real_prefix = STRBUF_INIT;
+		char *cwd = NULL;
+
+		/*
+		 * We don't ever produce a relative path if prefix is NULL,
+		 * so set the prefix to the current directory so that we can
+		 * produce a relative path whenever possible.
+		 */
+		if (!prefix)
+			prefix = cwd = xgetcwd();
+
+		if (!is_absolute_path(path)) {
+			strbuf_realpath_forgiving(&real_path, path, 1);
+			path = real_path.buf;
+		}
+		if (!is_absolute_path(prefix)) {
+			strbuf_realpath_forgiving(&real_prefix, prefix, 1);
+			prefix = real_prefix.buf;
+		}
+
+		strbuf_addstr(dest, relative_path(path, prefix, &relative_buf));
+
+		strbuf_release(&relative_buf);
+		strbuf_release(&real_path);
+		strbuf_release(&real_prefix);
+		free(cwd);
+		break;
+	}
+
+	case PATH_FORMAT_RELATIVE_IF_SHARED: {
+		struct strbuf relative_buf = STRBUF_INIT;
+
+		/*
+		 * If we're using RELATIVE_IF_SHARED mode, then we want an
+		 * absolute path unless the two share a common prefix, so don't
+		 * default the prefix to the current working directory. Doing so
+		 * would cause a relative path to always be produced if possible.
+		 */
+		strbuf_addstr(dest, relative_path(path, prefix, &relative_buf));
+		strbuf_release(&relative_buf);
+		break;
+	}
+
+	case PATH_FORMAT_CANONICAL: {
+		struct strbuf canonical_buf = STRBUF_INIT;
+
+		strbuf_realpath_forgiving(&canonical_buf, path, 1);
+		strbuf_addbuf(dest, &canonical_buf);
+
+		strbuf_release(&canonical_buf);
+		break;
+	}
+
+	default:
+		BUG("unknown path_format value %d", format);
+	}
+}
+
 REPO_GIT_PATH_FUNC(squash_msg, "SQUASH_MSG")
 REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
 REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
diff --git a/path.h b/path.h
index 0434ba5e07..6aca53b100 100644
--- a/path.h
+++ b/path.h
@@ -262,6 +262,42 @@ enum scld_error safe_create_leading_directories_no_share(char *path);
 int safe_create_file_with_leading_directories(struct repository *repo,
 					      const char *path);
 
+/**
+ * The formatting strategy to apply when writing a path into a buffer.
+ */
+enum path_format {
+	/*
+	 * Represents the default formatting behavior. Treated as
+	 * PATH_FORMAT_UNMODIFIED by append_formatted_path().
+	 */
+	PATH_FORMAT_DEFAULT,
+
+	/* Output the path exactly as-is without any modifications. */
+	PATH_FORMAT_UNMODIFIED,
+
+	/* Output a path relative to the provided directory prefix. */
+	PATH_FORMAT_RELATIVE,
+
+	/* Output a relative path only if the path shares a root with the prefix. */
+	PATH_FORMAT_RELATIVE_IF_SHARED,
+
+	/* Output a fully resolved, absolute canonical path. */
+	PATH_FORMAT_CANONICAL
+};
+
+/**
+ * Format a path according to the specified formatting strategy and append
+ * the result to the given strbuf.
+ *
+ * `dest`   : The string buffer to append the formatted path to.
+ * `path`   : The path string that needs to be formatted.
+ * `prefix` : The directory prefix to calculate relative offsets against.
+ * Pass NULL to default to the current working directory where applicable.
+ * `format` : The formatting behavior rule to execute.
+ */
+void append_formatted_path(struct strbuf *dest, const char *path,
+			   const char *prefix, enum path_format format);
+
 # ifdef USE_THE_REPOSITORY_VARIABLE
 #  include "strbuf.h"
 #  include "repository.h"
-- 
2.54.0


^ permalink raw reply related

* [GSoC Patch v3 0/4] teach git repo info to handle path keys
From: K Jayatheerth @ 2026-06-12 18:28 UTC (permalink / raw)
  To: jayatheerthkulkarni2005
  Cc: a3205153416, git, gitster, jltobler, kristofferhaugsbakk,
	kumarayushjha123, lucasseikioshiro, phillip.wood, sandals
In-Reply-To: <20260601151950.30686-1-jayatheerthkulkarni2005@gmail.com>

Hi!

This series teaches `git repo info` to handle `path.*`
keys, allowing scripts to reliably discover core
repository paths without resorting to `git rev-parse`.

The patches are structured as follows:

1. path: Extract the localized path-formatting logic
   out of `rev-parse` and expose it globally via
   `path.h` using clear append semantics.

2. rev-parse: Refactor the command to leverage the
   newly shared path engine.

3. repo: Introduce `path.commondir.absolute` and
   `path.commondir.relative` alongside a robust,
   isolated test helper.

4. repo: Introduce `path.gitdir.absolute` and
   `path.gitdir.relative` using the same standardized
   formatting rules.

Since all the questions were answered
I have removed them from this cover letter.

Changes since v2:

* Renamed the shared helper from `format_path()` to
  `append_formatted_path()`, and renamed the `buf`
  parameter to `dest` to better reflect its
  append-style behavior (Lucas, Phillip).

* Introduced a dedicated `PATH_FORMAT_DEFAULT`
  enumerator. This removes the awkward `-1`
  sentinel in `print_path()` while preserving enum
  type safety (Phillip, Justin).

* Handled `PATH_FORMAT_DEFAULT` as
  `PATH_FORMAT_UNMODIFIED` inside
  `append_formatted_path()`, while intercepting it
  in `print_path()` for rev-parse-specific fallback
  behavior (Justin).

* Replaced the `else if` chain in `append_formatted_path()` with a 
  clean `switch` statement setup (Justin, Lucas).

* Reordered the `commondir` and `gitdir` patches so
  the parameterized test helper
  (`test_repo_info_path`) is introduced first,
  establishing the isolated test infrastructure up
  front (Justin).

* Reworked the test helper to accept a label,
  `repo_name`, and `path_suffix`; moved repository
  creation into the helper for isolation; and
  replaced `eval` by capturing `$PWD` before
  changing directories (Justin, Lucas).

* Corrected trailer ordering so `Signed-off-by`
  appears after `Mentored-by` (Kristoffer).

* Cleaned up minor trailing whitespace issues across
  the patch array declarations.

Tagging Justin Tobler, Lucas Seiki Oshiro, Junio,
Phillip Wood, brian m. carlson, and Ayush Jha.

Thanks for taking another look!


K Jayatheerth (4):
  path: introduce append_formatted_path() for shared path formatting
  rev-parse: use append_formatted_path() for path formatting
  repo: add path.commondir with absolute and relative suffix formatting
  repo: add path.gitdir with absolute and relative suffix formatting

 Documentation/git-repo.adoc |  15 ++++++
 builtin/repo.c              |  50 +++++++++++++++++
 builtin/rev-parse.c         | 103 ++++++++----------------------------
 path.c                      |  70 ++++++++++++++++++++++++
 path.h                      |  36 +++++++++++++
 t/t1900-repo-info.sh        |  68 ++++++++++++++++++++++++
 6 files changed, 262 insertions(+), 80 deletions(-)

Range-diff against v2:
1:  c1f1e87fe9 ! 1:  a396b4f8e6 path: introduce format_path() for centralized path formatting
    @@ Metadata
     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
     
      ## Commit message ##
    -    path: introduce format_path() for centralized path formatting
    +    path: introduce append_formatted_path() for shared path formatting
     
    -    The path-formatting logic inside `builtin/rev-parse.c` handles absolute,
    -    canonical, and relative formatting rules based on user-supplied options.
    -    However, this logic is tightly coupled to `rev-parse` and writes directly
    -    to stdout.
    +    The path-formatting logic in builtin/rev-parse.c is tightly coupled
    +    to that command and writes directly to stdout, making it impossible
    +    for other builtins to reuse.
     
    -    To allow other builtins (such as the upcoming `git repo` path keys) to
    -    re-use this logic, extract the core path-formatting algorithm into a centralized
    -    helper function, `format_path()`, in `path.c`.
    -
    -    Expose a single, streamlined `path_format` enum in `path.h` to let callers
    -    explicitly declare their formatting strategy (UNMODIFIED, RELATIVE,
    -    RELATIVE_IF_SHARED, or CANONICAL). This decouples the core algorithm from
    -    the localized fallback mechanics specific to `rev-parse`.
    +    Extract the core algorithm into append_formatted_path() in path.c
    +    and expose a path_format enum in path.h so that any builtin can
    +    format paths consistently without duplicating logic.
     
         Mentored-by: Justin Tobler <jltobler@gmail.com>
         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
    @@ path.c: char *xdg_cache_home(const char *filename)
      	return NULL;
      }
      
    -+void format_path(struct strbuf *buf, const char *path,
    -+		 const char *prefix, enum path_format format)
    ++void append_formatted_path(struct strbuf *dest, const char *path,
    ++			   const char *prefix, enum path_format format)
     +{
    -+	if (format == PATH_FORMAT_UNMODIFIED) {
    -+		strbuf_addstr(buf, path);
    -+		return;
    -+	}
    ++	switch (format) {
    ++	case PATH_FORMAT_DEFAULT:
    ++	case PATH_FORMAT_UNMODIFIED:
    ++		strbuf_addstr(dest, path);
    ++		break;
     +
    -+	if (format == PATH_FORMAT_RELATIVE) {
    ++	case PATH_FORMAT_RELATIVE: {
     +		struct strbuf relative_buf = STRBUF_INIT;
     +		struct strbuf real_path = STRBUF_INIT;
     +		struct strbuf real_prefix = STRBUF_INIT;
    @@ path.c: char *xdg_cache_home(const char *filename)
     +			prefix = real_prefix.buf;
     +		}
     +
    -+		strbuf_addstr(buf, relative_path(path, prefix, &relative_buf));
    ++		strbuf_addstr(dest, relative_path(path, prefix, &relative_buf));
     +
     +		strbuf_release(&relative_buf);
     +		strbuf_release(&real_path);
     +		strbuf_release(&real_prefix);
     +		free(cwd);
    -+	} else if (format == PATH_FORMAT_RELATIVE_IF_SHARED) {
    ++		break;
    ++	}
    ++
    ++	case PATH_FORMAT_RELATIVE_IF_SHARED: {
     +		struct strbuf relative_buf = STRBUF_INIT;
     +
     +		/*
    @@ path.c: char *xdg_cache_home(const char *filename)
     +		 * default the prefix to the current working directory. Doing so
     +		 * would cause a relative path to always be produced if possible.
     +		 */
    -+		strbuf_addstr(buf, relative_path(path, prefix, &relative_buf));
    ++		strbuf_addstr(dest, relative_path(path, prefix, &relative_buf));
     +		strbuf_release(&relative_buf);
    -+	} else if (format == PATH_FORMAT_CANONICAL) {
    ++		break;
    ++	}
    ++
    ++	case PATH_FORMAT_CANONICAL: {
     +		struct strbuf canonical_buf = STRBUF_INIT;
     +
     +		strbuf_realpath_forgiving(&canonical_buf, path, 1);
    -+		strbuf_addbuf(buf, &canonical_buf);
    ++		strbuf_addbuf(dest, &canonical_buf);
     +
     +		strbuf_release(&canonical_buf);
    ++		break;
    ++	}
    ++
    ++	default:
    ++		BUG("unknown path_format value %d", format);
     +	}
     +}
     +
    @@ path.h: enum scld_error safe_create_leading_directories_no_share(char *path);
     + * The formatting strategy to apply when writing a path into a buffer.
     + */
     +enum path_format {
    ++	/*
    ++	 * Represents the default formatting behavior. Treated as
    ++	 * PATH_FORMAT_UNMODIFIED by append_formatted_path().
    ++	 */
    ++	PATH_FORMAT_DEFAULT,
    ++
     +	/* Output the path exactly as-is without any modifications. */
     +	PATH_FORMAT_UNMODIFIED,
     +
    @@ path.h: enum scld_error safe_create_leading_directories_no_share(char *path);
     + * Format a path according to the specified formatting strategy and append
     + * the result to the given strbuf.
     + *
    -+ * `buf`    : The string buffer to append the formatted path to.
    ++ * `dest`   : The string buffer to append the formatted path to.
     + * `path`   : The path string that needs to be formatted.
     + * `prefix` : The directory prefix to calculate relative offsets against.
     + * Pass NULL to default to the current working directory where applicable.
     + * `format` : The formatting behavior rule to execute.
     + */
    -+void format_path(struct strbuf *buf, const char *path,
    -+		 const char *prefix, enum path_format format);
    ++void append_formatted_path(struct strbuf *dest, const char *path,
    ++			   const char *prefix, enum path_format format);
     +
      # ifdef USE_THE_REPOSITORY_VARIABLE
      #  include "strbuf.h"
2:  2cc3e671af ! 2:  16198f96d1 rev-parse: use format_path for path formatting
    @@ Metadata
     Author: K Jayatheerth <jayatheerthkulkarni2005@gmail.com>
     
      ## Commit message ##
    -    rev-parse: use format_path for path formatting
    +    rev-parse: use append_formatted_path() for path formatting
     
    -    Now that the core path-formatting logic has been abstracted into
    -    format_path() inside path.c, remove the localized duplicate formatting
    -    mechanics from builtin/rev-parse.c.
    +    Now that path formatting logic lives in a shared helper, keeping a
    +    duplicate implementation in rev-parse is unnecessary and risks the
    +    two diverging over time.
     
    -    Drop the usage of the old local format_type and default_type enums,
    -    and update print_path() to act as a light wrapper around the new shared
    -    engine. Resolve user-provided formatting flags directly within rev-parse
    -    to pass the final determined path_format to format_path().
    +    Replace the local format_type and default_type enums and the
    +    hand-rolled formatting logic with a call to append_formatted_path().
    +    Introduce PATH_FORMAT_DEFAULT as the initial value of arg_path_format
    +    so that per-path fallback behavior is resolved in print_path() rather
    +    than leaked into the shared helper.
     
         Mentored-by: Justin Tobler <jltobler@gmail.com>
         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
    @@ builtin/rev-parse.c: static void handle_ref_opt(const char *pattern, const char
     -
     -static void print_path(const char *path, const char *prefix, enum format_type format, enum default_type def)
     +static void print_path(const char *path, const char *prefix,
    -+		       int arg_path_format, enum path_format def_format)
    ++		       enum path_format arg_path_format, enum path_format def_format)
      {
     -	char *cwd = NULL;
     -	/*
    @@ builtin/rev-parse.c: static void handle_ref_opt(const char *pattern, const char
     -	}
     -	free(cwd);
     +	struct strbuf sb = STRBUF_INIT;
    -+	enum path_format fmt = (arg_path_format != -1) ? arg_path_format : def_format;
    ++	enum path_format fmt = (arg_path_format != PATH_FORMAT_DEFAULT) ? arg_path_format : def_format;
     +
    -+	format_path(&sb, path, prefix, fmt);
    ++	append_formatted_path(&sb, path, prefix, fmt);
     +	puts(sb.buf);
     +
     +	strbuf_release(&sb);
    @@ builtin/rev-parse.c: int cmd_rev_parse(int argc,
      	struct strbuf buf = STRBUF_INIT;
      	int seen_end_of_options = 0;
     -	enum format_type format = FORMAT_DEFAULT;
    -+	int arg_path_format = -1;
    ++	enum path_format arg_path_format = PATH_FORMAT_DEFAULT;
      
      	show_usage_if_asked(argc, argv, builtin_rev_parse_usage);
      
    @@ builtin/rev-parse.c: int cmd_rev_parse(int argc,
      				char *cwd;
      				int len;
     -				enum format_type wanted = format;
    -+				int wanted = arg_path_format;
    ++				enum path_format wanted = arg_path_format;
      				if (arg[2] == 'g') {	/* --git-dir */
      					if (gitdir) {
     -						print_path(gitdir, prefix, format, DEFAULT_UNMODIFIED);
4:  61b5d69306 ! 3:  7de41faa04 repo: add path.commondir with absolute and relative suffix formatting
    @@ Metadata
      ## Commit message ##
         repo: add path.commondir with absolute and relative suffix formatting
     
    -    In standard Git repositories, the Git directory and the common directory
    -    are identical. However, in environments utilizing multiple worktrees, the
    -    local working state ($GIT_DIR) is separated from the shared central data
    -    ($GIT_COMMON_DIR). Scripts require a reliable way to discover this shared
    -    path.
    +    Scripts working with worktree setups need a reliable way to discover
    +    the common directory, which diverges from the git directory when
    +    multiple worktrees are in use. There is no way to retrieve this path
    +    from git repo info today.
     
    -    Introduce `path.commondir.absolute` and `path.commondir.relative` keys
    -    to `git repo info`. Similar to the `path.gitdir` keys, exposing explicit
    -    format variants removes the ambiguity of default fallbacks. Both keys are
    -    evaluated via the `format_path()` engine.
    +    Introduce path.commondir.absolute and path.commondir.relative keys.
    +    Exposing explicit format variants rather than a single key with a
    +    default avoids ambiguity for scripts that require predictable output.
     
    -    Insert the new keys into the `repo_info_field` array in lexicographical
    -    order to maintain the integrity of binary search lookups.
    -
    -    Utilize the parameterized `test_repo_info_path` helper to validate the
    -    worktree edge cases. This ensures that path resolution correctly respects
    -    $GIT_COMMON_DIR when defined and safely falls back to $GIT_DIR otherwise.
    +    Add a test helper test_repo_info_path that creates isolated
    +    repositories per test case to prevent state leaks, captures the repo
    +    root before changing directories to avoid eval, and accepts an optional
    +    init_command to cover environment variable overrides such as
    +    GIT_COMMON_DIR and GIT_DIR.
     
         Mentored-by: Justin Tobler <jltobler@gmail.com>
         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
    @@ Documentation/git-repo.adoc: values that they return:
     +	The path to the Git repository's common directory relative to
     +	the current working directory.
     +
    - `path.gitdir.absolute`::
    - 	The canonical absolute path to the Git repository directory (the `.git` directory).
    - 
    + `references.format`::
    + 	The reference storage format. The valid values are:
    + +
     
      ## builtin/repo.c ##
    +@@
    + #include "hex.h"
    + #include "odb.h"
    + #include "parse-options.h"
    ++#include "path.h"
    + #include "path-walk.h"
    + #include "progress.h"
    + #include "quote.h"
    + #include "ref-filter.h"
    + #include "refs.h"
    + #include "revision.h"
    ++#include "setup.h"
    + #include "strbuf.h"
    + #include "string-list.h"
    + #include "shallow.h"
     @@ builtin/repo.c: static int get_object_format(struct repository *repo, struct strbuf *buf)
      	return 0;
      }
    @@ builtin/repo.c: static int get_object_format(struct repository *repo, struct str
     +	if (!common_dir)
     +		return error(_("unable to get common directory"));
     +
    -+	format_path(buf, common_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
    ++	append_formatted_path(buf, common_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
     +	return 0;
     +}
     +
    @@ builtin/repo.c: static int get_object_format(struct repository *repo, struct str
     +	if (!common_dir)
     +		return error(_("unable to get common directory"));
     +
    -+	format_path(buf, common_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);
    ++	append_formatted_path(buf, common_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);
     +	return 0;
     +}
     +
    - static int get_path_gitdir_absolute(struct repository *repo, struct strbuf *buf)
    + static int get_references_format(struct repository *repo, struct strbuf *buf)
      {
    - 	const char *git_dir = repo_get_git_dir(repo);
    + 	strbuf_addstr(buf,
     @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {
      	{ "layout.bare", get_layout_bare },
      	{ "layout.shallow", get_layout_shallow },
      	{ "object.format", get_object_format },
     +	{ "path.commondir.absolute", get_path_commondir_absolute },
     +	{ "path.commondir.relative", get_path_commondir_relative },
    - 	{ "path.gitdir.absolute", get_path_gitdir_absolute },
    - 	{ "path.gitdir.relative", get_path_gitdir_relative },
      	{ "references.format", get_references_format },
    + };
    + 
     
      ## t/t1900-repo-info.sh ##
    -@@ t/t1900-repo-info.sh: test_expect_success 'setup test repository layout for path fields' '
    - 	mkdir -p test-repo/sub
    +@@ t/t1900-repo-info.sh: test_expect_success 'git repo info -h shows only repo info usage' '
    + 	test_grep ! "git repo structure" actual
      '
      
    -+test_expect_success 'setup custom-common for commondir tests' '
    -+	git init --bare test-repo/custom-common
    -+'
    -+
    -+test_repo_info_path 'commondir' 'echo "$(cd .. && pwd)/.git"' '../.git'
    -+test_repo_info_path 'commondir' 'echo "$(cd .. && pwd)/custom-common"' '../custom-common' 'GIT_COMMON_DIR="$(cd .. && pwd)/custom-common" GIT_DIR=../.git'
    -+test_repo_info_path 'commondir' 'echo "$(cd .. && pwd)/.git"' '../.git' 'GIT_DIR=../.git'
    - test_repo_info_path 'gitdir' 'echo "$(cd .. && pwd)/.git"' '../.git'
    - 
    ++# Helper function to test path keys in both absolute and relative formats.
    ++# $1: label for the test
    ++# $2: field_name (e.g., commondir)
    ++# $3: unique repo name for isolation
    ++# $4: expect_absolute (suffix appended to repo root)
    ++# $5: expect_relative (the relative path string expected)
    ++# $6: init_command (extra setup like exporting env vars)
    ++test_repo_info_path () {
    ++	label=$1
    ++	field_name=$2
    ++	repo_name=$3
    ++	expect_absolute_suffix=$4
    ++	expect_relative=$5
    ++	init_command=$6
    ++
    ++	absolute_root="$repo_name-absolute"
    ++	relative_root="$repo_name-relative"
    ++
    ++	test_expect_success "setup: $label" '
    ++		git init "$absolute_root" &&
    ++		git init "$relative_root" &&
    ++		mkdir -p "$absolute_root/sub" "$relative_root/sub"
    ++	'
    ++
    ++	test_expect_success "absolute: $label" '
    ++		(
    ++			cd "$absolute_root/sub" &&
    ++			ROOT="$(test-tool path-utils real_path "..")" && export ROOT &&
    ++			eval "$init_command" &&
    ++			expect_path="$ROOT${expect_absolute_suffix:+/$expect_absolute_suffix}" &&
    ++			echo "path.$field_name.absolute=$expect_path" >expect &&
    ++			git repo info "path.$field_name.absolute" >actual &&
    ++			test_cmp expect actual
    ++		)
    ++	'
    ++
    ++	test_expect_success "relative: $label" '
    ++		(
    ++			cd "$relative_root/sub" &&
    ++			ROOT="$(test-tool path-utils real_path "..")" && export ROOT &&
    ++			eval "$init_command" &&
    ++			echo "path.$field_name.relative=$expect_relative" >expect &&
    ++			git repo info "path.$field_name.relative" >actual &&
    ++			test_cmp expect actual
    ++		)
    ++	'
    ++}
    ++
    ++test_repo_info_path 'commondir standard' 'commondir' 'commondir-std' \
    ++	'.git' '../.git'
    ++
    ++test_repo_info_path 'commondir with GIT_COMMON_DIR and GIT_DIR' 'commondir' \
    ++	'commondir-envs' 'custom-common' '../custom-common' \
    ++	'GIT_COMMON_DIR="$ROOT/custom-common" && export GIT_COMMON_DIR &&
    ++	 GIT_DIR="../.git" && export GIT_DIR &&
    ++	 git init --bare "$ROOT/custom-common"'
    ++
    ++test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
    ++	'commondir-only-gitdir' '.git' '../.git' \
    ++	'GIT_DIR="../.git" && export GIT_DIR'
    ++
      test_done
3:  ca95d51f6e ! 4:  ffd6a5bb16 repo: add path.gitdir with absolute and relative suffix formatting
    @@ Metadata
      ## Commit message ##
         repo: add path.gitdir with absolute and relative suffix formatting
     
    -    Scripts often need to locate the `.git` directory. While `git rev-parse`
    -    provides this, it relies on command-line flags to dictate path formatting.
    +    Scripts need a stable way to locate the git directory without
    +    parsing rev-parse output or relying on its flag-driven path format
    +    selection. There is no way to retrieve this path from git repo info
    +    today.
     
    -    Introduce `path.gitdir.absolute` and `path.gitdir.relative` keys to
    -    `git repo info`. Exposing separate format-specific keys instead of a base
    -    `path.gitdir` key avoids default fallbacks and requires callers to state
    -    their format requirements explicitly. Both keys use `format_path()` to
    -    resolve paths.
    -
    -    To test these keys, introduce the `test_repo_info_path` helper in
    -    `t/t1900-repo-info.sh`. The helper evaluates paths dynamically and accepts
    -    environment variable prefixes. This prepares the test suite for future path
    -    keys that depend on environment overrides, such as `commondir`.
    +    Introduce path.gitdir.absolute and path.gitdir.relative keys,
    +    consistent with the path.commondir keys added in the previous patch.
    +    Reuse the test_repo_info_path helper introduced there to validate
    +    both variants.
     
         Mentored-by: Justin Tobler <jltobler@gmail.com>
         Mentored-by: Lucas Seiki Oshiro <lucasseikioshiro@gmail.com>
    @@ Commit message
     
      ## Documentation/git-repo.adoc ##
     @@ Documentation/git-repo.adoc: values that they return:
    - `object.format`::
    - 	The object format (hash algorithm) used in the repository.
    + 	The path to the Git repository's common directory relative to
    + 	the current working directory.
      
     +`path.gitdir.absolute`::
     +	The canonical absolute path to the Git repository directory (the `.git` directory).
    @@ Documentation/git-repo.adoc: values that they return:
      +
     
      ## builtin/repo.c ##
    -@@
    - #include "hex.h"
    - #include "odb.h"
    - #include "parse-options.h"
    -+#include "path.h"
    - #include "path-walk.h"
    - #include "progress.h"
    - #include "quote.h"
    - #include "ref-filter.h"
    - #include "refs.h"
    - #include "revision.h"
    -+#include "setup.h"
    - #include "strbuf.h"
    - #include "string-list.h"
    - #include "shallow.h"
    -@@ builtin/repo.c: static int get_object_format(struct repository *repo, struct strbuf *buf)
    +@@ builtin/repo.c: static int get_path_commondir_relative(struct repository *repo, struct strbuf *b
      	return 0;
      }
      
    @@ builtin/repo.c: static int get_object_format(struct repository *repo, struct str
     +	if (!git_dir)
     +		return error(_("unable to get git directory"));
     +
    -+	format_path(buf, git_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
    ++	append_formatted_path(buf, git_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
     +	return 0;
     +}
     +
    @@ builtin/repo.c: static int get_object_format(struct repository *repo, struct str
     +	if (!git_dir)
     +		return error(_("unable to get git directory"));
     +
    -+	format_path(buf, git_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);
    ++	append_formatted_path(buf, git_dir, startup_info->prefix, PATH_FORMAT_RELATIVE);
     +	return 0;
     +}
     +
    @@ builtin/repo.c: static int get_object_format(struct repository *repo, struct str
      {
      	strbuf_addstr(buf,
     @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {
    - 	{ "layout.bare", get_layout_bare },
    - 	{ "layout.shallow", get_layout_shallow },
      	{ "object.format", get_object_format },
    + 	{ "path.commondir.absolute", get_path_commondir_absolute },
    + 	{ "path.commondir.relative", get_path_commondir_relative },
     +	{ "path.gitdir.absolute", get_path_gitdir_absolute },
     +	{ "path.gitdir.relative", get_path_gitdir_relative },
      	{ "references.format", get_references_format },
    @@ builtin/repo.c: static const struct repo_info_field repo_info_field[] = {
      
     
      ## t/t1900-repo-info.sh ##
    -@@ t/t1900-repo-info.sh: test_expect_success 'git repo info -h shows only repo info usage' '
    - 	test_grep ! "git repo structure" actual
    - '
    +@@ t/t1900-repo-info.sh: test_repo_info_path 'commondir with only GIT_DIR' 'commondir' \
    + 	'commondir-only-gitdir' '.git' '../.git' \
    + 	'GIT_DIR="../.git" && export GIT_DIR'
      
    -+test_repo_info_path () {
    -+	field_name=$1
    -+	expect_absolute_eval=$2
    -+	expect_relative=$3
    -+	env_prefix=$4
    -+
    -+	test_expect_success "query individual key: path.$field_name.absolute${env_prefix:+ ($env_prefix)}" '
    -+		(
    -+			cd test-repo/sub &&
    -+			expect_absolute=$(eval "$expect_absolute_eval") &&
    -+			echo "path.$field_name.absolute=$expect_absolute" >expect &&
    -+			eval "${env_prefix:+$env_prefix }git repo info \"path.$field_name.absolute\"" >actual &&
    -+			test_cmp expect actual
    -+		)
    -+	'
    -+
    -+	test_expect_success "query individual key: path.$field_name.relative${env_prefix:+ ($env_prefix)}" '
    -+		(
    -+			cd test-repo/sub &&
    -+			echo "path.$field_name.relative=$expect_relative" >expect &&
    -+			eval "${env_prefix:+$env_prefix }git repo info \"path.$field_name.relative\"" >actual &&
    -+			test_cmp expect actual
    -+		)
    -+	'
    -+}
    -+
    -+test_expect_success 'setup test repository layout for path fields' '
    -+	git init test-repo &&
    -+	mkdir -p test-repo/sub
    -+'
    ++test_repo_info_path 'gitdir standard' 'gitdir' 'gitdir-std' \
    ++	'.git' '../.git'
     +
    -+test_repo_info_path 'gitdir' 'echo "$(cd .. && pwd)/.git"' '../.git'
    ++test_repo_info_path 'gitdir with explicit GIT_DIR' 'gitdir' \
    ++	'gitdir-env' '.git' '../.git' \
    ++	'GIT_DIR="../.git" && export GIT_DIR'
     +
      test_done
-- 
2.54.0

^ permalink raw reply

* Re: [PATCH v4 06/16] midx: support custom `--base` for incremental MIDX writes
From: Taylor Blau @ 2026-06-12 18:18 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: SZEDER Gábor, git, Jeff King, Elijah Newren,
	Patrick Steinhardt
In-Reply-To: <xmqqqzmbj3mb.fsf@gitster.g>

On Fri, Jun 12, 2026 at 06:21:48AM -0700, Junio C Hamano wrote:
> SZEDER Gábor <szeder.dev@gmail.com> writes:
>
> >> +	layer="$(git multi-pack-index write --bitmap --incremental \
> >> +		--no-write-chain-file --base="$(nth_line 1 "$midx_chain")")" &&
> >
> > There is no 'nth_line' helper function in this test script.
>
> Good eyes.  It has been there in the file next door t5335 since
> February, but not available here in t5334.

Good spotting indeed. Fortunately or unfortunately for us, pulling on
this thread revealed a bit of a rabbit hole. Patches forthcoming..

Thanks,
Taylor

^ permalink raw reply

* Re: t5563-simple-http-auth failures with v2.55.0-rc0
From: Todd Zullinger @ 2026-06-12 18:02 UTC (permalink / raw)
  To: Matthew John Cheetham; +Cc: git@vger.kernel.org
In-Reply-To: <VI0PR03MB1163416D5C66FAB25AECAAE21C0182@VI0PR03MB11634.eurprd03.prod.outlook.com>

Hi,

Matthew John Cheetham wrote:
> On 2026-06-11 22:04, Todd Zullinger wrote:
>> I notice that Fedora 44 (where the tests all pass) has
>> curl-8.18.0 while Fedora 45 has curl-8.21.0-rc2.  The
>> version of httpd is the same between them, FWIW.  I didn't
>> compare other package differences; it could be something
>> else entirely.
> 
> Thanks for the report. The failure is not in Git, it is a libcurl
> behaviour change, and there is already an open upstream issue:
> 
>   https://github.com/curl/curl/issues/21943
>   "Negotiate ignored with --anyauth" (Dan Fandrich, 2026-06-10)
> 
> Dan also bisected it to the same commit I had locally,
> `8f71d0fde515` ("creds: hold credentials", curl PR #21548).
[...]
> Daniel Stenberg has acknowledged the curl issue but has not yet
> posted a fix. I will follow curl#21943 and, if the upstream answer
> is "the new behaviour is intended", come back here with a proposal
> for what Git should do about `http.emptyAuth` and test 18.

Excellent.  This is it good hands all around.

With luck, curl is updated and the canary of distributions
like Fedora's Rawhide will have served a useful purpose in
flushing out issues before they affect most people.  With
the help of git's excellent and thorough test suite, of
course. :)

If there is a curl update, I imagine it will be picked up
reasonably quickly in Fedora (and elsewhere that was testing
8.21.0 release candidates) and there will hopefully be no
strong need to make any changes on the git side.

Thanks!

-- 
Todd

^ permalink raw reply

* Re: [PATCH v2 1/1] environment.c: move 'protect_hfs' and 'protect_ntfs' into 'repo_config_values'
From: Junio C Hamano @ 2026-06-12 16:51 UTC (permalink / raw)
  To: Christian Couder
  Cc: Tian Yuchen, git, phillip.wood123, Ayush Chandekar,
	Olamide Caleb Bello
In-Reply-To: <CAP8UFD1UbsXu_7DK2keGLUO3Yh06-YHieZP+On-yjY3SmV2Xmg@mail.gmail.com>

Christian Couder <christian.couder@gmail.com> writes:

> On Wed, Jun 10, 2026 at 6:41 PM Junio C Hamano <gitster@pobox.com> wrote:
>>
>> Tian Yuchen <cat@malon.dev> writes:
>>
>> > +int repo_protect_ntfs(struct repository *repo)
>> > +{
>> > +     return repo->gitdir ?
>> ...
>> Shall we declare victory and mark the topic for 'next' now?
>
> I would have preferred the commit subject to start with "environment:"
> rather than "environment.c:" but it's a small nit and maybe you can
> fix it while merging.

If I remember, perhaps I'll try.  But you know what happens when you
add more stuff that are not something only the maintainer can do on
my plate ;-)

^ permalink raw reply

* Re: [PATCH v2] update-ref: add --rename option
From: Junio C Hamano @ 2026-06-12 16:31 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git
In-Reply-To: <xmqqwlw4nccr.fsf@gitster.g>

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

> [Appendix]
> ...  Ideally, these cherry-pickable commits
> that are stored under refs/merge-fix hierarchies SHOULD be indexable
> by a pair of topic (i.e. "when topic A and topic B first meets, apply
> this evil merge"), but this computation is cumbersome to write.

One scheme might be to use "refs/merge-fix/$A--$B" to store the
interaction between topic $A and topic $B, with the convention that
no topic is named with double-dash in its name.

We sequencially merge these in-flight topic into the integration
branch (e.g., 'seen').  When merging topic X, we roughly would need
to do the following.

 (0) Skip if X is already in the integration branch.

 (1) See what topic Y that would also be merged for the first time
     to the integration branch.  This is because a complex topic X
     often is done by merging in-flight Y into then-current master
     and applyng patches on top, and depending on the state of the
     integration branch, such topic Y may or may not have already
     been mergeed there.  Enumerate all these topic Ys that would
     be pulled into the integration branch as a side effect of
     merging X.

 (2) Enumerate all merge-fix refs that has any of the topic Ys or X.
     For each such refs/merge-fix/$A--$B (where either $A or $B is X
     or one of Ys), call the other side of "--" Z.  If Z is already
     in the integration branch, then we found the merge-fix we need
     to apply.

The "topic X might pull other topics that haven't been merged
together with it when it gets merged" is what makes it cumbersome to
write.  If we do not have to worry about it, it would be fairly
straight forward, but then the bulk-merge driver probably needs to
learn a safety check to make sure at the point of merging each topic
that the merge is not pulling another topic (base) into as a side
effect.  It would mean that you prepare a new topic X on top of a
merge of Y into 'master', and X can never be merged into 'seen'
before Y is.  Which may or may not be what we really want, and I
need to think about it a bit.


^ permalink raw reply

* Re: [PATCH v2] commit-reach: remove get_reachable_subset()
From: Weijie Yuan @ 2026-06-12 16:21 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Derrick Stolee, Kristofer Karlsson via GitGitGadget, git,
	Kristofer Karlsson, Patrick Steinhardt
In-Reply-To: <aiww2oXXDQXk0dgu@wyuan.org>

On Sat, Jun 13, 2026 at 12:16:34AM +0800, Weijie Yuan wrote:
> On Fri, Jun 12, 2026 at 07:41:48AM -0700, Junio C Hamano wrote:
> > Weijie Yuan <wy@wyuan.org> writes:
> > 
> > > On Thu, Jun 11, 2026 at 10:48:18AM -0700, Junio C Hamano wrote:
> > >> I wonder if we should talk about it in the SubmittingPatches and/or
> > >> MyFirstContribution document?
> > >
> > > Hi, I think it might be a good idea to cover these details in
> > > MyFirstContribution, then cross-reference them from the part of
> > > SubmittingPatches that discusses sending a new version.
> > 
> > Sorry to be nitpicky, but the above is omitting too much from your
> > quote.  "it" in "talk about it" is totally unclear to a reader who
> > haven't seen the message you are replying to.
> 
> Oops, so sorry! You are not nitpicky at all, this is totally my
> carelessness and fault. Sorry readers!
> 
> Thank you for catching this! It shows that I still need to really
> understand the previous patch I wrote, and put it into real practice:
> 
> > It is usually helpful to trim away unrelated context, such as large
> > portions of the patch that are not being discussed, while _keeping
> > enough quoted text_ for readers to understand *what* you are
> > responding to.
> 
> Thank you! I'll immediately set a solid "pre-reply" hook in my .git
> folder ;-)

Sorry, here I mean setting a pre-reply hook to remind me how to do a
good quote when writting a reply mail :-)

Sorry for the unnecessary noise, and thank you.

^ permalink raw reply

* Re: [PATCH v2] commit-reach: remove get_reachable_subset()
From: Weijie Yuan @ 2026-06-12 16:16 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Derrick Stolee, Kristofer Karlsson via GitGitGadget, git,
	Kristofer Karlsson, Patrick Steinhardt
In-Reply-To: <xmqqecibizwz.fsf@gitster.g>

On Fri, Jun 12, 2026 at 07:41:48AM -0700, Junio C Hamano wrote:
> Weijie Yuan <wy@wyuan.org> writes:
> 
> > On Thu, Jun 11, 2026 at 10:48:18AM -0700, Junio C Hamano wrote:
> >> I wonder if we should talk about it in the SubmittingPatches and/or
> >> MyFirstContribution document?
> >
> > Hi, I think it might be a good idea to cover these details in
> > MyFirstContribution, then cross-reference them from the part of
> > SubmittingPatches that discusses sending a new version.
> 
> Sorry to be nitpicky, but the above is omitting too much from your
> quote.  "it" in "talk about it" is totally unclear to a reader who
> haven't seen the message you are replying to.

Oops, so sorry! You are not nitpicky at all, this is totally my
carelessness and fault. Sorry readers!

Thank you for catching this! It shows that I still need to really
understand the previous patch I wrote, and put it into real practice:

> It is usually helpful to trim away unrelated context, such as large
> portions of the patch that are not being discussed, while _keeping
> enough quoted text_ for readers to understand *what* you are
> responding to.

Thank you! I'll immediately set a solid "pre-reply" hook in my .git
folder ;-)

^ permalink raw reply

* [PATCH v3 3/3] environment: move trust_executable_bit into repo_config_values
From: Tian Yuchen @ 2026-06-12 16:05 UTC (permalink / raw)
  To: git; +Cc: ps, Tian Yuchen, Christian Couder, Ayush Chandekar,
	Olamide Caleb Bello
In-Reply-To: <20260612160527.167203-1-cat@malon.dev>

Move the global 'trust_executable_bit' configuration
into the repository-specific 'repo_config_values'
struct. To ensure code readability, the getter function
'repo_trust_executable_bit()' has been introduced.

For now, associated functions access this configuration by
explicitly falling back to 'the_repository'.

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       |  2 +-
 environment.c | 11 +++++++++--
 environment.h |  9 ++++++++-
 read-cache.c  |  8 ++++----
 4 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/apply.c b/apply.c
index 249248d4f2..fbb907d3c0 100644
--- a/apply.c
+++ b/apply.c
@@ -3893,7 +3893,7 @@ static int check_preimage(struct apply_state *state,
 		if (*ce && !(*ce)->ce_mode)
 			BUG("ce_mode == 0 for path '%s'", old_name);
 
-		if (trust_executable_bit || !S_ISREG(st->st_mode))
+		if (repo_trust_executable_bit(the_repository) || !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/environment.c b/environment.c
index fc3ed8bb1c..75069a884d 100644
--- a/environment.c
+++ b/environment.c
@@ -41,7 +41,6 @@
 static int pack_compression_seen;
 static int zlib_compression_seen;
 
-int trust_executable_bit = 1;
 int trust_ctime = 1;
 int check_stat = 1;
 int has_symlinks = 1;
@@ -142,6 +141,13 @@ int is_bare_repository(void)
 	return is_bare_repository_cfg && !repo_get_work_tree(the_repository);
 }
 
+int repo_trust_executable_bit(struct repository *repo)
+{
+	return repo->gitdir?
+		repo_config_values(repo)->trust_executable_bit :
+		1;
+}
+
 int have_git_dir(void)
 {
 	return startup_info->have_repository
@@ -305,7 +311,7 @@ int git_default_core_config(const char *var, const char *value,
 
 	/* This needs a better name */
 	if (!strcmp(var, "core.filemode")) {
-		trust_executable_bit = git_config_bool(var, value);
+		cfg->trust_executable_bit = git_config_bool(var, value);
 		return 0;
 	}
 	if (!strcmp(var, "core.trustctime")) {
@@ -720,5 +726,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;
 	cfg->branch_track = BRANCH_TRACK_REMOTE;
 }
diff --git a/environment.h b/environment.h
index 123a71cdc8..d602d2cd95 100644
--- a/environment.h
+++ b/environment.h
@@ -91,6 +91,7 @@ struct repo_config_values {
 	/* section "core" config values */
 	char *attributes_file;
 	int apply_sparse_checkout;
+	int trust_executable_bit;
 
 	/* section "branch" config values */
 	enum branch_track branch_track;
@@ -123,6 +124,13 @@ int git_default_config(const char *, const char *,
 int git_default_core_config(const char *var, const char *value,
 			    const struct config_context *ctx, void *cb);
 
+/*
+ * Getter for the `trust_executable_bit` field of `struct repo_config_values`.
+ * It checks `repo->gitdir` to prevent calling repo_config_values()
+ * before the configuration is loaded or in bare environments.
+ */
+int repo_trust_executable_bit(struct repository *repo);
+
 void repo_config_values_init(struct repo_config_values *cfg);
 
 /*
@@ -160,7 +168,6 @@ int is_bare_repository(void);
 extern char *git_work_tree_cfg;
 
 /* Environment bits from configuration mechanism */
-extern int trust_executable_bit;
 extern int trust_ctime;
 extern int check_stat;
 extern int has_symlinks;
diff --git a/read-cache.c b/read-cache.c
index 54150fe756..757249a449 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -207,7 +207,7 @@ unsigned int ce_mode_from_stat(const struct cache_entry *ce, unsigned int mode)
 	if (!has_symlinks && S_ISREG(mode) &&
 	    ce && S_ISLNK(ce->ce_mode))
 		return ce->ce_mode;
-	if (!trust_executable_bit && S_ISREG(mode)) {
+	if (!repo_trust_executable_bit(the_repository) && S_ISREG(mode)) {
 		if (ce && S_ISREG(ce->ce_mode))
 			return ce->ce_mode;
 		return create_ce_mode(0666);
@@ -221,7 +221,7 @@ static unsigned int st_mode_from_ce(const struct cache_entry *ce)
 	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 & (repo_trust_executable_bit(the_repository) ? 0755 : 0644)) | S_IFREG;
 	case S_IFGITLINK:
 		return S_IFDIR | 0755;
 	case S_IFDIR:
@@ -331,7 +331,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 (repo_trust_executable_bit(the_repository) &&
 		    (0100 & (ce->ce_mode ^ st->st_mode)))
 			changed |= MODE_CHANGED;
 		break;
@@ -752,7 +752,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 (repo_trust_executable_bit(the_repository) && has_symlinks) {
 		ce->ce_mode = create_ce_mode(st_mode);
 	} else {
 		/* If there is an existing entry, pick the mode bits and type
-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 1/3] read-cache: remove redundant extern declarations
From: Tian Yuchen @ 2026-06-12 16:05 UTC (permalink / raw)
  To: git; +Cc: ps, Tian Yuchen, Christian Couder, Ayush Chandekar,
	Olamide Caleb Bello
In-Reply-To: <20260612160527.167203-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 v3 2/3] read-cache: move 'ce_mode_from_stat()' to 'read-cache.c'
From: Tian Yuchen @ 2026-06-12 16:05 UTC (permalink / raw)
  To: git; +Cc: ps, Tian Yuchen, Christian Couder, Ayush Chandekar,
	Olamide Caleb Bello
In-Reply-To: <20260612160527.167203-1-cat@malon.dev>

The ce_mode_from_stat() function is declared as a static inline function
in 'read-cache.h'. As we want to migrate configuration variables, this
helper function will need access to corresponding repository-specific
configuration logic. Move the implementation to 'read-cache.c' to
cleanly encapsulate its dependencies.

Note that the 'extern int trust_executable_bit, has_symlinks;' line is
discarded because it's not necessary when the function lives in
"read-cache.c".

At present, this change has no visible impact, but it is crucial
for our future plans to pass in the repo context. Comment
has been added whilst we are at it.

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 | 13 +++++++++++++
 read-cache.h | 23 +++++++++--------------
 2 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/read-cache.c b/read-cache.c
index c44e4d128f..54150fe756 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -202,6 +202,19 @@ 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)
+{
+	if (!has_symlinks && S_ISREG(mode) &&
+	    ce && S_ISLNK(ce->ce_mode))
+		return ce->ce_mode;
+	if (!trust_executable_bit && S_ISREG(mode)) {
+		if (ce && S_ISREG(ce->ce_mode))
+			return ce->ce_mode;
+		return create_ce_mode(0666);
+	}
+	return create_ce_mode(mode);
+}
+
 static unsigned int st_mode_from_ce(const struct cache_entry *ce)
 {
 	switch (ce->ce_mode & S_IFMT) {
diff --git a/read-cache.h b/read-cache.h
index 043da1f1aa..9088a0724a 100644
--- a/read-cache.h
+++ b/read-cache.h
@@ -5,20 +5,15 @@
 #include "object.h"
 #include "pathspec.h"
 
-static inline unsigned int ce_mode_from_stat(const struct cache_entry *ce,
-					     unsigned int mode)
-{
-	extern int trust_executable_bit, has_symlinks;
-	if (!has_symlinks && S_ISREG(mode) &&
-	    ce && S_ISLNK(ce->ce_mode))
-		return ce->ce_mode;
-	if (!trust_executable_bit && S_ISREG(mode)) {
-		if (ce && S_ISREG(ce->ce_mode))
-			return ce->ce_mode;
-		return create_ce_mode(0666);
-	}
-	return create_ce_mode(mode);
-}
+/*
+ * Determine the appropriate index mode for a file based on its stat()
+ * information and the existing cache entry (if any).
+ *
+ * This function handles degradation for filesystems that lack
+ * symlink support or reliable executable bits.
+ */
+unsigned int ce_mode_from_stat(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

* [PATCH v3 0/3] environment: migrate 'trust_executable_bit' into 'repo_config_values'
From: Tian Yuchen @ 2026-06-12 16:05 UTC (permalink / raw)
  To: git; +Cc: ps, Tian Yuchen, Christian Couder, Ayush Chandekar,
	Olamide Caleb Bello
In-Reply-To: <20260610093635.139719-1-cat@malon.dev>

The 'core.filemode' (stored as 'trust_executable_bit') configuration
act as a core filesystem capability flag.

This series moves it into 'struct 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.

Note: 'repo_config_values()' still does not support any struct
repository other than the_repository due to how deeply these flags
are accessed. 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 [1]
[PATCH] Refactor 'trust_executable_bit' to repository-scoped setting [2]
(This previous attempt 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: [3])

Changes since V2:

Fixed a whole bunch of typos;

Moved the comment for 'ce_mode_from_stat()' from 'read-cache.c' to
'read-cache.h'.

Thanks!

[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 (3):
  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

 apply.c       |  2 +-
 environment.c | 11 +++++++++--
 environment.h |  9 ++++++++-
 read-cache.c  | 21 ++++++++++++++++-----
 read-cache.h  | 23 +++++++++--------------
 5 files changed, 43 insertions(+), 23 deletions(-)

-- 
2.43.0


^ permalink raw reply

* Re: [RFC] commit-reach: terminate merge-base walk when one paint side is exhausted
From: Derrick Stolee @ 2026-06-12 15:48 UTC (permalink / raw)
  To: Kristofer Karlsson; +Cc: git
In-Reply-To: <CAL71e4MFb3UUKBr1P4ZwtK3o1gvUHMs+siCpLTXKkW6Vx=BxRg@mail.gmail.com>

On 6/12/2026 11:21 AM, Kristofer Karlsson wrote:
> On Fri, 12 Jun 2026 at 17:04, Derrick Stolee <stolee@gmail.com> wrote:
>>> So the actual halt condition would be:
>>>
>>>     no non-stale P1|P2 candidates in the queue
>>>     AND (no pure-P1 OR no pure-P2)
>>
>> And since STALE is added only after both P1 and P2 bits, the two
>> conditions are identical to how queue_has_nonstale() terminates the
>> loop.
> 
> No, I think this part is different. I can demonstrate with an example queue
> state: [P1, stale, P1, stale, stale]
> With the old code, the non-stale tracker would consider this to be non-stale
> since it still has two P1 commits to process.
> My new approach would instead consider that a valid halt state - we
> can't find any new merge-bases at that point.

Ah. this is indeed the detail I missed. For any i in {1, 2}, if Pi
only appears alongside the STALE bit, then we can stop the walk. This
tracks because the other bit can't contribute any new information.

A data shape that makes this particularly helpful is the "release
branch" data shape that I used to justify the --negotiation-include
option [1].

[1] https://lore.kernel.org/git/62e5ef1a4b800cb18b2e934f45303095d545613b.1779207896.git.gitgitgadget@gmail.com/

Suppose developers are merging into 'main' frequently. On occasion,
the tip of 'main' is merged into a new 'release' branch. Thus, the
first-parent history of 'release' is long and completely separate
from the commit history of 'main'. To reach the queue_has_nonstale()
exit condition, we'd need to walk the entire history.

However, if we focus on the single-side condition you are proposing,
we can stop walking once everything in the queue that is reachable
form 'main' is also reachable from that top merge-base.
>>> If this reasoning is correct, then the walk only terminates after
>>> merge-base candidates have either been processed or marked STALE,
>>> and the counterexample should produce [B] rather than [B, C].
>> That's the correct distinction: we need the set [B] and not [B,C]
>> but we need to discover that B can reach C to remove it from the
>> result set.
> 
> Yes, and I think that part works since we visit them in generational order,
> so B can invalidate C before C is reached.
> 
>> I think there is potential merit in "switching walk modes" to DFS
>> when all queued commits have both P1 and P2, but it comes with a
>> lot of complications. So tread carefully if you go down this road.
>>
> 
> On the DFS point: I may be misunderstanding the suggestion, but my current
> approach depends quite heavily on generation ordering. The reason the
> STALE propagation is safe is that, in the finite-generation region,
> descendants are processed before ancestors. If we switch to DFS, I think we
> would lose that ordering property unless the DFS is constrained in some
> additional way.
My thought was focused on the case of "all queued commits have P1 and P2"
and then we could determine which should be non-stale using DFS focused
only on the current queued set.

But I think your single-sided approach is a better way to get the gains
that you want. I think that case is much more likely to occur.

Thanks for your persistence in working on this through my
misunderstanding.

Thanks,
-Stolee


^ permalink raw reply

* Re: t5563-simple-http-auth failures with v2.55.0-rc0
From: Matthew John Cheetham @ 2026-06-12 15:42 UTC (permalink / raw)
  To: Todd Zullinger, git@vger.kernel.org
In-Reply-To: <20260611210456.XYfhytSL@teonanacatl.net>

On 2026-06-11 22:04, Todd Zullinger wrote:

> Hi,
> 
> I tested the freshly-tagged 2.55.0-rc0 and noticed some new
> failures on the in-progress Fedora 45 (AKA Rawhide) for
> t5563.18 (http.emptyAuth=auto attempts Negotiate before
> credential_fill) which was added in 9b1630b972 (t5563: add
> tests for http.emptyAuth with Negotiate, 2026-04-16).
> 
> I notice that Fedora 44 (where the tests all pass) has
> curl-8.18.0 while Fedora 45 has curl-8.21.0-rc2.  The
> version of httpd is the same between them, FWIW.  I didn't
> compare other package differences; it could be something
> else entirely.

Thanks for the report. The failure is not in Git, it is a libcurl
behaviour change, and there is already an open upstream issue:

   https://github.com/curl/curl/issues/21943
   "Negotiate ignored with --anyauth" (Dan Fandrich, 2026-06-10)

Dan also bisected it to the same commit I had locally,
`8f71d0fde515` ("creds: hold credentials", curl PR #21548).

His report describes the regression at the `curl(1)` level (`curl
--anyauth -u : ...` no longer attempts Negotiate); t5563 test 18 is
the same regression observed through `http.emptyAuth=auto`, which
under the hood is the same `CURLOPT_USERPWD=":"` pattern.

Dan also notes a workaround: replacing `-u :` with `-u
literally:anything` (any non-blank username, real or fake) puts
libcurl back on the Negotiate path. That suggests a small Git-side
escape hatch is possible if we want to unblock people running
against current libcurl while we wait for an upstream fix; I have
not tried it yet and would want to be sure it does not have side
effects on other auth schemes before proposing it.

Daniel Stenberg has acknowledged the curl issue but has not yet
posted a fix. I will follow curl#21943 and, if the upstream answer
is "the new behaviour is intended", come back here with a proposal
for what Git should do about `http.emptyAuth` and test 18.

Thanks,
Matthew


^ permalink raw reply

* Re: [PATCH v3] update-ref: add --rename option
From: Junio C Hamano @ 2026-06-12 15:41 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git
In-Reply-To: <aiugat0gvprSX5yr@pks.im>

Patrick Steinhardt <ps@pks.im> writes:

> A slight tangent: this is part of why I really don't like commands that
> determine their mode via flags: you now have to worry about every
> combination of flags and whether they even make sense. With subcommands
> we at least only have to worry about the set of flags that directly
> apply to that given subcommand.
>
> Makes me wonder whether I should have a look at extending git-refs(1)
> further:
>
>     git refs delete <ref> [<oldvalue>]
>     git refs update <ref> <newvalue> [<oldvalue>]
>     git refs rename <ref> <oldname> <newname>
>
> I always wanted to do this eventually so that we have one top-level
> command that knows how to do "everything refs".

That may indeed be a better direction to go, but isn't update-ref
the "everything refs" command already?


^ permalink raw reply

* Re: [PATCH v2 3/3] environment: move trust_executable_bit into repo_config_values
From: Junio C Hamano @ 2026-06-12 15:21 UTC (permalink / raw)
  To: Christian Couder
  Cc: Tian Yuchen, git, ps, Ayush Chandekar, Olamide Caleb Bello
In-Reply-To: <CAP8UFD0X48BJcjLrr8mY0x3A03NSEN35G7jrvdvvp7Qm5PYAdw@mail.gmail.com>

Christian Couder <christian.couder@gmail.com> writes:

> On Wed, Jun 10, 2026 at 11:37 AM Tian Yuchen <cat@malon.dev> wrote:
>
>> +/*
>> + * Getters for the `repo_trust_executable_bit` fields of `struct repo_config_values`.
>
> s/Getters/Getter/
> s/fields/field/
> s/repo_trust_executable_bit/trust_executable_bit/
>
>> + * They check `repo->gitdir` to prevent calling repo_config_values()
>> + * before the configuration is loaded or in bare environments.
>> + */
>> +int repo_trust_executable_bit(struct repository *repo);
>
> Thanks.

Thanks.  

A hopefully small and final reroll is in order, and
<21f74852-2209-4d77-94f4-b2b9412eb8e0@malon.dev> has already
promised one.


^ permalink raw reply

* Re: [RFC] commit-reach: terminate merge-base walk when one paint side is exhausted
From: Kristofer Karlsson @ 2026-06-12 15:21 UTC (permalink / raw)
  To: Derrick Stolee; +Cc: git
In-Reply-To: <8d0902ca-98b7-44a4-a23b-51de44ab6daa@gmail.com>

On Fri, 12 Jun 2026 at 17:04, Derrick Stolee <stolee@gmail.com> wrote:
> > So the actual halt condition would be:
> >
> >     no non-stale P1|P2 candidates in the queue
> >     AND (no pure-P1 OR no pure-P2)
>
> And since STALE is added only after both P1 and P2 bits, the two
> conditions are identical to how queue_has_nonstale() terminates the
> loop.

No, I think this part is different. I can demonstrate with an example queue
state: [P1, stale, P1, stale, stale]
With the old code, the non-stale tracker would consider this to be non-stale
since it still has two P1 commits to process.
My new approach would instead consider that a valid halt state - we
can't find any new merge-bases at that point.

> > If this reasoning is correct, then the walk only terminates after
> > merge-base candidates have either been processed or marked STALE,
> > and the counterexample should produce [B] rather than [B, C].
> That's the correct distinction: we need the set [B] and not [B,C]
> but we need to discover that B can reach C to remove it from the
> result set.

Yes, and I think that part works since we visit them in generational order,
so B can invalidate C before C is reached.

> I think there is potential merit in "switching walk modes" to DFS
> when all queued commits have both P1 and P2, but it comes with a
> lot of complications. So tread carefully if you go down this road.
>

On the DFS point: I may be misunderstanding the suggestion, but my current
approach depends quite heavily on generation ordering. The reason the
STALE propagation is safe is that, in the finite-generation region,
descendants are processed before ancestors. If we switch to DFS, I think we
would lose that ordering property unless the DFS is constrained in some
additional way.

So I think I may not fully understand the DFS idea, and I am not sure if
that type of optimization would be orthogonal to tweaking the halt condition
or not.

Thanks,
Kristofer

^ permalink raw reply

* Re: [ANNOUNCE] Git v2.55.0-rc0
From: Junio C Hamano @ 2026-06-12 15:14 UTC (permalink / raw)
  To: rsbecker; +Cc: git
In-Reply-To: <065e01dcfa75$ade00690$09a013b0$@nexbridge.com>

<rsbecker@nexbridge.com> writes:

> On June 11, 2026 11:32 AM, Junio wrote:
>> An early preview release Git v2.55.0-rc0 is now available for testing at the usual
>> places.  It is comprised of 397 non-merge commits since v2.54.0, contributed by
>> 70 people, 22 of which are new faces [*].
>
> Cargo is not available everywhere. Build is not possible on NonStop.
>
> cargo build  --release
> /usr/coreutils/bin/bash: cargo: command not found
> Makefile:3021: recipe for target 'target/release/libgitcore.a' failed
> make: *** [target/release/libgitcore.a] Error 127
>
> Is there a way around this?

I see this in the Makefile that you may or may not have read.  Does
it work?

# Define NO_RUST if you want to disable features and subsystems written in Rust
# from being compiled into Git. For now, Rust is still an optional feature of
# the build process. With Git 3.0 though, Rust will always be enabled.

^ permalink raw reply

* Re: [PATCH v3 0/3] doc: config: fix AsciiDoc glitches
From: Junio C Hamano @ 2026-06-12 15:05 UTC (permalink / raw)
  To: Jeff King; +Cc: Tuomas Ahola, git, Kristoffer Haugsbakk, Jean-Noël Avila
In-Reply-To: <20260612045329.GA593075@coredump.intra.peff.net>

Jeff King <peff@peff.net> writes:

> On Thu, Jun 11, 2026 at 07:19:43PM +0300, Tuomas Ahola wrote:
>
>> Tuomas Ahola (3):
>>   doc: config: terminate runaway lists
>>   doc: config/sideband: fix description list delimiter
>>   doc: git-config: escape erroneous highlight markup
>
> Thanks, this v3 looks good to me.

Yup this one nicely sidesteps the yucky \# thing, which is very
good.

Thanks.

^ permalink raw reply

* Re: [PATCH v3 0/3] doc: config: fix AsciiDoc glitches
From: Junio C Hamano @ 2026-06-12 15:05 UTC (permalink / raw)
  To: Jeff King; +Cc: Tuomas Ahola, git, Kristoffer Haugsbakk, Jean-Noël Avila
In-Reply-To: <20260612045329.GA593075@coredump.intra.peff.net>

Jeff King <peff@peff.net> writes:

> On Thu, Jun 11, 2026 at 07:19:43PM +0300, Tuomas Ahola wrote:
>
>> Tuomas Ahola (3):
>>   doc: config: terminate runaway lists
>>   doc: config/sideband: fix description list delimiter
>>   doc: git-config: escape erroneous highlight markup
>
> Thanks, this v3 looks good to me.

Yup this one nicely sidesteps the yucky \# thing, which is very
good.

Thanks.

^ permalink raw reply

* Re: [PATCH v3 0/3] doc: config: fix AsciiDoc glitches
From: Junio C Hamano @ 2026-06-12 15:05 UTC (permalink / raw)
  To: Jeff King; +Cc: Tuomas Ahola, git, Kristoffer Haugsbakk, Jean-Noël Avila
In-Reply-To: <20260612045329.GA593075@coredump.intra.peff.net>

Jeff King <peff@peff.net> writes:

> On Thu, Jun 11, 2026 at 07:19:43PM +0300, Tuomas Ahola wrote:
>
>> Tuomas Ahola (3):
>>   doc: config: terminate runaway lists
>>   doc: config/sideband: fix description list delimiter
>>   doc: git-config: escape erroneous highlight markup
>
> Thanks, this v3 looks good to me.

Yup this one nicely sidesteps the yucky \# thing, which is very
good.

Thanks.

^ permalink raw reply


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