threads / bug / 64714

bug report: git status -z doesn't respect status.relativePaths=true

Subject: bug report: git status -z doesn't respect status.relativePaths=true

## tl;dr

4 messages between Jan 3, 2026 and Jan 3, 2026.

replies: 3people: 3as markdown or json

Artur Pyrogovskyi· Jan 3, 2026, 10:47 UTC · lore

According to the man page of git-status: "-z Terminate entries with NUL, instead of LF."

However, it ignores status.relativePaths=true and always shows absolute paths.

Repro steps: $ mkdir test-repo && cd test-repo && git init . $ mkdir subdir && touch subdir/test-file.txt && cd subdir && git add test-file.txt $ git -c status.relativePaths=true status --porcelain=2 1 A. N... 000000 100644 100644 0000000000000000000000000000000000000000 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 test-file.txt $ git -c status.relativePaths=true status --porcelain=2 -z 1 A. N... 000000 100644 100644 0000000000000000000000000000000000000000 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 subdir/test-file.txt%

--porcelain=2 is for convenience here; the bug is present with or without it.
Here's the full bug report:
What did you do before the bug happened? (Steps to reproduce your issue)
$ git -c status.relativePaths=true status -z
What did you expect to happen? (Expected behavior)
I expect this to show relative paths, just as this shows relative paths:
$ git -c status.relativePaths=true status
What happened instead? (Actual behavior)
It shows absolute paths, not just terminating entries with NUL as manpage says.
What's different between what you expected and what actually happened?

I expect that when I use -z option with status, the only thing that changes is the line separator.

However, for some reason, -z also enforces absolute paths and ignores status.relativePaths option.

[System Info]
git version:
git version 2.52.0
cpu: arm64
no commit associated with this build
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/sh
rust: disabled
feature: fsmonitor--daemon
libcurl: 8.7.1
zlib: 1.2.12
SHA-1: SHA1_DC
SHA-256: SHA256_BLK
default-ref-format: files
default-hash: sha1
uname: Darwin 24.6.0 Darwin Kernel Version 24.6.0: Wed Nov  5 21:33:58
PST 2025; root:xnu-11417.140.69.705.2~1/RELEASE_ARM64_T6000 arm64
compiler info: clang: 17.0.0 (clang-1700.4.4.1)
libc info: no libc information available
$SHELL (typically, interactive shell): /bin/zsh
Artur
Pushkar Singh· Jan 3, 2026, 11:07 UTC · re: Artur Pyrogovskyi · lore

Re: bug report: git status -z doesn't respect status.relativePaths=true

Hi Artur,
Thanks for the report. I can reproduce this on my end.

The issue shows up when running `git status` from a subdirectory rather than the repository root. With `status.relativePaths=true`:

From inside `subdir/`:
    $ git -c status.relativePaths=true status --porcelain=2
    ... test-file.txt
but with `-z`:
    $ git -c status.relativePaths=true status --porcelain=2 -z
    ... subdir/test-file.txt

So `-z` appears to ignore `status.relativePaths` and forces paths relative to the repo root, even though the documentation suggests it should only affect record termination.

Tested with git 2.52.0 on Linux/WSL.

Thanks, Pushkar

On Sat, Jan 3, 2026 at 4:17 PM Artur Pyrogovskyi <arp@letterty.com> wrote:
Show 69 quoted lines
>
> According to the man page of git-status: "-z Terminate entries with
> NUL, instead of LF."
>
> However, it ignores status.relativePaths=true and always shows absolute paths.
>
> Repro steps:
> $ mkdir test-repo && cd test-repo && git init .
> $ mkdir subdir && touch subdir/test-file.txt && cd subdir && git add
> test-file.txt
> $ git -c status.relativePaths=true status --porcelain=2
> 1 A. N... 000000 100644 100644
> 0000000000000000000000000000000000000000
> e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 test-file.txt
> $ git -c status.relativePaths=true status --porcelain=2 -z
> 1 A. N... 000000 100644 100644
> 0000000000000000000000000000000000000000
> e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 subdir/test-file.txt%
>
> --porcelain=2 is for convenience here; the bug is present with or without it.
>
> Here's the full bug report:
>
> What did you do before the bug happened? (Steps to reproduce your issue)
>
> $ git -c status.relativePaths=true status -z
>
> What did you expect to happen? (Expected behavior)
>
> I expect this to show relative paths, just as this shows relative paths:
>
> $ git -c status.relativePaths=true status
>
> What happened instead? (Actual behavior)
>
> It shows absolute paths, not just terminating entries with NUL as manpage says.
>
> What's different between what you expected and what actually happened?
>
> I expect that when I use -z option with status,
> the only thing that changes is the line separator.
>
> However, for some reason, -z also enforces absolute paths
> and ignores status.relativePaths option.
>
> [System Info]
> git version:
> git version 2.52.0
> cpu: arm64
> no commit associated with this build
> sizeof-long: 8
> sizeof-size_t: 8
> shell-path: /bin/sh
> rust: disabled
> feature: fsmonitor--daemon
> libcurl: 8.7.1
> zlib: 1.2.12
> SHA-1: SHA1_DC
> SHA-256: SHA256_BLK
> default-ref-format: files
> default-hash: sha1
> uname: Darwin 24.6.0 Darwin Kernel Version 24.6.0: Wed Nov  5 21:33:58
> PST 2025; root:xnu-11417.140.69.705.2~1/RELEASE_ARM64_T6000 arm64
> compiler info: clang: 17.0.0 (clang-1700.4.4.1)
> libc info: no libc information available
> $SHELL (typically, interactive shell): /bin/zsh
>
> Artur
>
Jeff King· Jan 3, 2026, 11:26 UTC · re: Artur Pyrogovskyi · lore

Re: bug report: git status -z doesn't respect status.relativePaths=true

On Sat, Jan 03, 2026 at 02:47:36AM -0800, Artur Pyrogovskyi wrote:
> According to the man page of git-status: "-z Terminate entries with
> NUL, instead of LF."
> 
> However, it ignores status.relativePaths=true and always shows absolute paths.

This matches the documented behavior. In --porcelain=v1 mode, we ignore most configuration options (like status.relativePaths). And -z puts us into v1 porcelain mode by default:

       -z
           Terminate entries with NUL, instead of LF. This implies the
           --porcelain=v1 output format if no other format is given.

I do think there is at least one bug here, though. I'd expect from that documentation to be able to do:

  git -c status.relativePaths=true status --short -z

and get relative paths. But it doesn't work. The --short output code that handles "-z" ignores the prefix entirely.

Something like this would fix it:
diff --git a/wt-status.c b/wt-status.c
index e12adb26b9..22797371a6 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -2005,6 +2005,13 @@ static void wt_shortstatus_unmerged(struct string_list_item *it,
 	}
 }
 
+static void print_with_nul(struct wt_status *s, const char *fn)
+{
+	struct strbuf scratch = STRBUF_INIT;
+	fprintf(s->fp, "%s%c", relative_path(fn, s->prefix, &scratch), 0);
+	strbuf_release(&scratch);
+}
+
 static void wt_shortstatus_status(struct string_list_item *it,
 			 struct wt_status *s)
 {
@@ -2020,9 +2027,9 @@ static void wt_shortstatus_status(struct string_list_item *it,
 		fputc(' ', s->fp);
 	fputc(' ', s->fp);
 	if (s->null_termination) {
-		fprintf(s->fp, "%s%c", it->string, 0);
+		print_with_nul(s, it->string);
 		if (d->rename_source)
-			fprintf(s->fp, "%s%c", d->rename_source, 0);
+			print_with_nul(s, d->rename_source);
 	} else {
 		struct strbuf onebuf = STRBUF_INIT;
 		const char *one;

but:

  1. it would need similar adjustments to a few other printing
     functions; and

  2. it's not clear to me if we want to change this behavior or keep
     it as a historical quirk and document it as such.

     I guess nobody noticed because using "-z" with "--short" is a bit
     of an odd thing to want to do.


There's another oddity, which is this:

> Repro steps:
> $ mkdir test-repo && cd test-repo && git init .
> $ mkdir subdir && touch subdir/test-file.txt && cd subdir && git add
> test-file.txt
> $ git -c status.relativePaths=true status --porcelain=2
> 1 A. N... 000000 100644 100644
> 0000000000000000000000000000000000000000
> e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 test-file.txt

I am surprised to see that the v2 porcelain respects that config option
at all. I don't know if that's a bug, or a subtle change from v1. I
don't see it mentioned in the documentation.

If it isn't a bug, and we expect v2 porcelain to respect the config,
then this:

> $ git -c status.relativePaths=true status --porcelain=2 -z
> 1 A. N... 000000 100644 100644
> 0000000000000000000000000000000000000000
> e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 subdir/test-file.txt%

is another bug similar to the --short one.

-Peff
Pushkar Singh· Jan 3, 2026, 11:34 UTC · re: Jeff King · lore

Re: bug report: git status -z doesn't respect status.relativePaths=true

Hi Jeff,

Thanks for the detailed explanation. That clarifies why `-z` implies `--porcelain=v1` and why `status.relativePaths` gets ignored in that case.

I agree that the `--short -z` behavior is surprising. From the documentation, I would also expect `status.relativePaths=true` to be honored there, so ignoring the prefix entirely does look like a real issue rather than just a quirk.

The `--porcelain=v2 -z` case is interesting as well. If v2 is intended to respect the configuration, then the inconsistency when using `-z` does seem like a related bug.

If it would be useful, I am happy to help investigate further or work on tests once there is consensus on the intended behavior.

Thanks, Pushkar

On Sat, Jan 3, 2026 at 4:56 PM Jeff King <peff@peff.net> wrote:
Show 96 quoted lines
>
> On Sat, Jan 03, 2026 at 02:47:36AM -0800, Artur Pyrogovskyi wrote:
>
> > According to the man page of git-status: "-z Terminate entries with
> > NUL, instead of LF."
> >
> > However, it ignores status.relativePaths=true and always shows absolute paths.
>
> This matches the documented behavior. In --porcelain=v1 mode, we ignore
> most configuration options (like status.relativePaths). And -z puts us
> into v1 porcelain mode by default:
>
>        -z
>            Terminate entries with NUL, instead of LF. This implies the
>            --porcelain=v1 output format if no other format is given.
>
> I do think there is at least one bug here, though. I'd expect from that
> documentation to be able to do:
>
>   git -c status.relativePaths=true status --short -z
>
> and get relative paths. But it doesn't work. The --short output code
> that handles "-z" ignores the prefix entirely.
>
> Something like this would fix it:
>
> diff --git a/wt-status.c b/wt-status.c
> index e12adb26b9..22797371a6 100644
> --- a/wt-status.c
> +++ b/wt-status.c
> @@ -2005,6 +2005,13 @@ static void wt_shortstatus_unmerged(struct string_list_item *it,
>         }
>  }
>
> +static void print_with_nul(struct wt_status *s, const char *fn)
> +{
> +       struct strbuf scratch = STRBUF_INIT;
> +       fprintf(s->fp, "%s%c", relative_path(fn, s->prefix, &scratch), 0);
> +       strbuf_release(&scratch);
> +}
> +
>  static void wt_shortstatus_status(struct string_list_item *it,
>                          struct wt_status *s)
>  {
> @@ -2020,9 +2027,9 @@ static void wt_shortstatus_status(struct string_list_item *it,
>                 fputc(' ', s->fp);
>         fputc(' ', s->fp);
>         if (s->null_termination) {
> -               fprintf(s->fp, "%s%c", it->string, 0);
> +               print_with_nul(s, it->string);
>                 if (d->rename_source)
> -                       fprintf(s->fp, "%s%c", d->rename_source, 0);
> +                       print_with_nul(s, d->rename_source);
>         } else {
>                 struct strbuf onebuf = STRBUF_INIT;
>                 const char *one;
>
> but:
>
>   1. it would need similar adjustments to a few other printing
>      functions; and
>
>   2. it's not clear to me if we want to change this behavior or keep
>      it as a historical quirk and document it as such.
>
>      I guess nobody noticed because using "-z" with "--short" is a bit
>      of an odd thing to want to do.
>
>
> There's another oddity, which is this:
>
> > Repro steps:
> > $ mkdir test-repo && cd test-repo && git init .
> > $ mkdir subdir && touch subdir/test-file.txt && cd subdir && git add
> > test-file.txt
> > $ git -c status.relativePaths=true status --porcelain=2
> > 1 A. N... 000000 100644 100644
> > 0000000000000000000000000000000000000000
> > e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 test-file.txt
>
> I am surprised to see that the v2 porcelain respects that config option
> at all. I don't know if that's a bug, or a subtle change from v1. I
> don't see it mentioned in the documentation.
>
> If it isn't a bug, and we expect v2 porcelain to respect the config,
> then this:
>
> > $ git -c status.relativePaths=true status --porcelain=2 -z
> > 1 A. N... 000000 100644 100644
> > 0000000000000000000000000000000000000000
> > e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 subdir/test-file.txt%
>
> is another bug similar to the --short one.
>
> -Peff
>

← back to recent threads