Thread (43 messages) 43 messages, 4 authors, 15h ago

Re: [PATCH v3 1/7] repo: add path.toplevel with absolute and relative suffix formatting

From: K Jayatheerth <hidden>
Date: 2026-07-29 16:36:50

Hey Justin,

Thanks for the detailed review.

On Tue, Jul 28, 2026 at 10:06 PM Justin Tobler [off-list ref] wrote:
On 26/07/26 04:13PM, K Jayatheerth wrote:
quoted
Scripts frequently need to find the root directory of a repository's
working tree. Currently, this requires using `git rev-parse --show-toplevel`
or inferring it from other path components.

Introduce `path.toplevel.absolute` and `path.toplevel.relative` keys
to `git repo info`. This allows scripts to retrieve the top-level
working tree path in a predictable, strictly formatted manner without
relying on `rev-parse`.
Ok, this seems like suitable information to also look up under
git-repo-info.
quoted
If requested in a bare repository where no working tree exists, the
command returns an empty string.
This matches the existing behavior in git-rev-parse(1). Makes sense.

Yes, that is correct.

quoted
+static int get_path_toplevel_absolute(struct repository *repo, struct strbuf *buf)
+{
+     const char *work_tree = repo_get_work_tree(repo);
+
+     if (!work_tree) {
+             strbuf_addstr(buf, "");
The strbuf here is already NULL-terminated when its initialized. I don't
think this should be necessary.

Very nice point indeed
I will change that everywhere I have added that.

quoted
+     if (!work_tree) {
+             strbuf_addstr(buf, "");
Same here.
quoted
+test_expect_success 'path.toplevel returns empty in a bare repository' '
+     test_when_finished "rm -rf bare.git" &&
+     git init --bare bare.git &&
+     (
+             cd bare.git &&
+             echo "path.toplevel.absolute=" >expect &&
+             git repo info path.toplevel.absolute >actual &&
+             test_cmp expect actual
In this test we are only checking the absolute path. It probably
wouldn't hurt to also check the relative path too.

Good catch. I'll add relative-path tests in both places.

quoted
Introduce `path.superproject-working-tree.absolute` and
`path.superproject-working-tree.relative` keys to `git repo info`.
This exposes the core submodule context via a scriptable config-like key
using standard format rules.
Ok, this also seems like a good fit to include as a key in
git-repo-info, but "superproject-working-tree" is a bit of a mouthful
IMO. An alternative could potentially be "superproject-root"? Maybe its
best to just be consistent with the option name in git-rev-parse(1) and
keep it the same though.

show-superproject-working-tree is what git rev parse uses.
I am a bit unclear about this.
Since it is meant to be a scripting command, I don't know if it being
a long name matters much.
At the same time, I don't think we can keep the exact
"show-superproject-working-tree" as the key's name.
As the "show" is indirectly covered by "path." so the "show" is just
redundant if you ask me (I might be wrong here).
That is actually why I kept the rest of the name similar and omitted
the prefix from the flag name.

In other words, I am fine with the name being either one.
That said, I am more inclined with the "superproject-root".
I think the name explains exactly what it does.
Whereas "superproject-working-tree" was not that clear to me when I
first read it (Might be subjective to me).

quoted
+static int get_path_superproject_absolute(struct repository *repo UNUSED, struct strbuf *buf)
+{
+     struct strbuf superproject = STRBUF_INIT;
+
+     if (!get_superproject_working_tree(&superproject)) {
+             strbuf_release(&superproject);
+             strbuf_addstr(buf, "");
Same comment here as in the previous patch...
quoted
+     struct strbuf superproject = STRBUF_INIT;
+
+     if (!get_superproject_working_tree(&superproject)) {
+             strbuf_release(&superproject);
+             strbuf_addstr(buf, "");
...and here as well...
quoted
+             return 0;
+     }
+
+     format_path(buf, superproject.buf, startup_info->prefix, PATH_FORMAT_RELATIVE);
+     strbuf_release(&superproject);
+     return 0;
+}
+
+test_expect_success 'path.superproject-working-tree returns empty when not in a submodule' '
+     test_when_finished "rm -rf repo" &&
+     git init repo &&
+     (
+             cd repo &&
+             echo "path.superproject-working-tree.absolute=" >expect &&
+             git repo info path.superproject-working-tree.absolute >actual &&
+             test_cmp expect actual
... and here as well. :)

Good catch. I'll add a relative-path test there as well.

quoted
Tools and deployment hooks frequently query the location of the object
database directory. Currently, this relies on legacy parsing methods or
manually inspecting `git rev-parse --git-path objects`.
"Tools and deployment hooks" seems a bit overly specific. Maybe instead
we could just say "Scripts operating on a repository may need to query
the location of the object database directory"?

I think your version is consistent with other patches as well.
I wanted to ask if it is not a good practice to write commit messages
which are very specific.
I also wanted to know what constitutes being overly specific so that I
can avoid doing it again.

Also, I'm not entirely sure what is meant by "legacy parsing methods"
here.

Something scripts have historically done: parse git rev-parse
--git-dir (or read .git/config by hand)
and manually append /objects, rather than asking for the object
directory directly
which breaks if GIT_OBJECT_DIRECTORY is set.
I'll reword the commit message to just say that explicitly instead of
the vague "legacy parsing methods" phrase.

quoted
Introduce `path.objects.absolute` and `path.objects.relative` keys to
`git repo info`. This allows tools to discover the object database
location safely while natively adhering to active `GIT_OBJECT_DIRECTORY`
environment variable overrides.
In the context of pluggable ODBs, this proposed key is a little more
interesting because a non-"files" ODB source in the future may not even
have a filesystem path to an objects directory. When this becomes more
relevant, we could just adapt these keys to return an empty string in
such cases, but it does also make me question whether it is information
that we should further expose in the first place if it does eventually
becomes an internal detail of a specific ODB source.

It probably doesn't matter too much, but I've CC'd Patrick for his
thoughts too.

Thanks for looping in Patrick!

That's a fair concern.
My thinking is that the "files" backend is going to be the reality for
the vast majority of
repositories for a long time yet, and scripts asking for path.objects
today are almost certainly
assuming a filesystem-backed ODB anyway (that's the whole reason
they'd want the path).
So I don't think we're locking ourselves into a bad abstraction by
adding it now.

If/when a non-filesystem ODB source becomes real,
we'd have two reasonable options:
return an empty string for that key (same pattern we already use for
path.toplevel/superproject-working-tree when the concept doesn't
apply),
or deprecate the key outright if it turns out to be meaningless in that world.
Either way it doesn't block adding it now, worst case we're removing
or narrowing a key later, not stuck with something actively wrong.


quoted
+static int get_path_objects_absolute(struct repository *repo, struct strbuf *buf)
+{
+     const char *obj_dir = repo_get_object_directory(repo);
+
+     if (!obj_dir)
+             return error(_("unable to get object directory"));
+
+     format_path(buf, obj_dir, startup_info->prefix, PATH_FORMAT_CANONICAL);
For the absolute path, do we actually need to provide the prefix? It
might make it more clear that its the aboslute path if we just pass ""
instead?

This point was raised in the foundational patch as well.
It won't change the output for sure, so I will pass "" instead.


On Wed, Jul 29, 2026 at 12:30 AM Justin Tobler [off-list ref] wrote:
On 26/07/26 04:13PM, K Jayatheerth wrote:
quoted
External tool integrations and validation systems need a stable way to
identify where the repository hooks are stored. Currently, this involves
relying on `git rev-parse --git-path hooks` or querying `core.hooksPath`
manually.
Similar to the comment in the previous patch, "External tool
intergations and validation systems" seems overly specific IMO. Also,
"need" is a bit strongly worded as I'm sure its not a requirement for
every external script/tool.

Got it, I will try it to be not overly specific as well.
Maybe something like:
Scripts operating on a repository may want a stable way to identify
where the repository hooks are stored. Currently, this involves
relying on `git rev-parse --git-path hooks` or querying
`core.hooksPath` manually.

Introduce `path.hooks.absolute` and `path.hooks.relative` keys to
`git repo info`. This allows scripts to discover the active hooks
location, ensuring proper resolution regardless of whether Git is
using the standard `.git/hooks` structure or a custom
`core.hooksPath` setup.

quoted
Introduce `path.hooks.absolute` and `path.hooks.relative` keys to
`git repo info`. This allows tools to discover the active hooks location
natively, ensuring proper resolution regardless of whether Git is using
the standard `.git/hooks` structure or a custom `core.hooksPath` setup.
Per hooks path documentation:

  The path can be either absolute or relative. A relative path is taken
  as relative to the directory where the hooks are run.

  ...

  You  can also disable all hooks entirely by setting core.hooksPath to
  /dev/null.

Should we handle this /dev/null case specially? It looks like:

  $ git -c core.hooksPath=/dev/null rev-parse --git-path hooks

just prints '/dev/null'. I do wonder if this makes much sense though in
context of the relative path version of this key. From some quick
testing, it appears the git-rev-parse(1) version of this option always
prints the absolute path if that is what is configured (it appears to
ignore --path-format). Maybe we should just special case /dev/null and
return an empty string? I'm not entirely sure what the best route is
here though.

After thinking about it
I would lean towards not special-casing it. A few reasons:

/dev/null is just a valid (if unusual) value of core.hooksPath as far
as Git is concerned, the field is documented to reflect
whatever core.hooksPath resolves to, and rev-parse --git-path hooks
already treats it as an ordinary path.
If we special-case it to an empty string, we'd be the only place in
Git that treats /dev/null as meaningfully different from any other
hooksPath value.

Empty string is already used by other keys in this series
to mean "this concept doesn't apply here" (no working tree, not a submodule).
Using empty string for "hooks are disabled" would overload that
meaning with something different, "the path exists but you shouldn't
use it"
and a script can't tell the two apart without also knowing the
hooksPath convention, which defeats the purpose of adding a structured
key in the first place.

Any script that's checking for /dev/null already has to know that
convention when
using the existing rev-parse interface, so we're not creating new
surprises, just carrying forward a pre-existing quirk of
core.hooksPath semantics.

For the relative version:
I'll let format_path/relative_path do whatever it already does for a
path that isn't under the prefix
(I'd guess it falls back to printing the absolute path, similar to how
rev-parse seems to ignore --path-format for this case).
That keeps behavior consistent with the existing tool rather than
introducing new special-casing for path.hooks.relative specifically.

I'll add a test covering core.hooksPath=/dev/null to lock in whichever
behavior we land on,
and I can add a doc note under path.hooks.absolute/path.hooks.relative
mentioning that /dev/null is passed through unchanged if that's the
value in use,
similar to how the existing hooksPath docs describe it.
Let me know if you still think empty-string is preferable despite the
above,  happy to go either way once we agree on the reasoning.

Thanks again for the detailed review!

Regards,
- K Jayatheerth
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help