threads / discuss / 64145

safe.directory does not work at all (git 2.39.5, 2.51.0)

Subject: safe.directory does not work at all (git 2.39.5, 2.51.0)

## tl;dr

6 messages between Sep 13, 2025 and Sep 17, 2025.

replies: 5people: 3as markdown or json

Marc-Jano Knopp· Sep 13, 2025, 17:38 UTC · lore
Hi everyone!

git seems to have freshly implemented some security measure, and the documented solution / workaround / whatever does not seem to work at all. For the first time in years, git does not work for me anymore, I cannot push my changes to the remote repository.

See below for what was created using "git bugreport":

===================================================================== Thank you for filling out a Git bug report! Please answer the following questions to help us understand your issue.

- What did you do before the bug happened? (Steps to reproduce your issue)
  I created a shared bare repo on my.server, permissions for everything
  are 2770 (rwxrws---) for dirs and 660 (rw-rw----) for files in that
  remote repository, and all dirs and files belong to root:git. I have
  an account "myuser:git" on that server.
  
  Then I tried to clone it to my local PC, which failed to some new
  security measure git seems to have introduced recently:

--------- snip --------- $ git clone myuser@my.server:/git/main/test.git Cloning into 'test'... fatal: detected dubious ownership in repository at '/git/main/test.git' To add an exception for this directory, call:

        git config --global --add safe.directory /git/main/test.git
fatal: Could not read from remote repository.

Please make sure you have the correct access rights and the repository exists. $ --------- snip ---------

  I did execute the suggested command, so that my ~/.gitconfig now
  (only) contains:
--------- snip ---------
[safe]
        directory = /git/main/test.git
--------- snip ---------
          
  but the error still occurs. Using "git -c safe.directory='....'"
  did not help, either.
  
- What did you expect to happen? (Expected behavior)
  I expected the disabling of the above security measure to work.
  Actually, I want safe.directory to be set to "*", but that does not
  work, either.
  
- What happened instead? (Actual behavior)
  See above.
- What's different between what you expected and what actually happened?
  See above.
- Anything else you want to add:
  Can we please make suddenly occurring security measures and other
  breaking changes opt-in?

Please review the rest of the bug report below. You can delete any lines you don't wish to share.

[System Info] git version: git version 2.39.5 (same error with 2.51.0 on a different PC) cpu: x86_64 no commit associated with this build sizeof-long: 8 sizeof-size_t: 8 shell-path: /bin/sh uname: Linux 6.12.38+deb12-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.38-1~bpo12+1 (2025-07-27) x86_64 compiler info: gnuc: 12.2 libc info: glibc: 2.36 $SHELL (typically, interactive shell): /usr/bin/zsh

[Enabled Hooks] not run from a git repository - no hooks to show =====================================================================

Any help is appreciated!

If all else fails, I would downgrade to the last git version without that security feature, if someone knows the version number that introduced this feature ...

MJK
Carlo Marcelo Arenas Belón· Sep 13, 2025, 22:13 UTC · re: Marc-Jano Knopp · lore

Re: safe.directory does not work at all (git 2.39.5, 2.51.0)

On Sat, Sep 13, 2025 at 07:38:53PM -0800, Marc-Jano Knopp wrote:
Show 7 quoted lines
> $ git clone myuser@my.server:/git/main/test.git
> Cloning into 'test'...
> fatal: detected dubious ownership in repository at '/git/main/test.git'
> To add an exception for this directory, call:
> 
>         git config --global --add safe.directory /git/main/test.git
> fatal: Could not read from remote repository.

it is a little confusing, but the message comes from the git command running in "my.server".

doest it work if you run the same command after first doing ssh with "myuser" account into "my.server"?

Carlo
Marc-Jano Knopp· Sep 14, 2025, 18:26 UTC · re: Carlo Marcelo Arenas Belón · lore

[SOLVED] Re: safe.directory does not work at all (git 2.39.5, 2.51.0)

On Sun, 2025-09-14, at 00:13:14 (+0200), Carlo Marcelo Arenas Belón wrote:
Show 11 quoted lines
> On Sat, Sep 13, 2025 at 07:38:53PM -0800, Marc-Jano Knopp wrote:
> > $ git clone myuser@my.server:/git/main/test.git
> > Cloning into 'test'...
> > fatal: detected dubious ownership in repository at '/git/main/test.git'
> > To add an exception for this directory, call:
> > 
> >         git config --global --add safe.directory /git/main/test.git
> > fatal: Could not read from remote repository.
> 
> it is a little confusing, but the message comes from the git command
> running in "my.server".

D'oh! Is there a way for the layman to see if a message comes from the client or the server?

> doest it work if you run the same command after first doing ssh with "myuser"
> account into "my.server"?
Yes, it does! Thanks a million! *smooch* <3  :)
MJK
Jeff King· Sep 15, 2025, 02:23 UTC · re: Marc-Jano Knopp · lore

Re: [SOLVED] Re: safe.directory does not work at all (git 2.39.5, 2.51.0)

On Sun, Sep 14, 2025 at 08:26:53PM +0200, Marc-Jano Knopp wrote:
Show 12 quoted lines
> > > Cloning into 'test'...
> > > fatal: detected dubious ownership in repository at '/git/main/test.git'
> > > To add an exception for this directory, call:
> > > 
> > >         git config --global --add safe.directory /git/main/test.git
> > > fatal: Could not read from remote repository.
> > 
> > it is a little confusing, but the message comes from the git command
> > running in "my.server".
> 
> D'oh! Is there a way for the layman to see if a message comes from the
> client or the server?

Usually we try to pass error messages from the server over the sideband channel, where the client prefixes them with "remote:" before showing them to the user. Or report them via ERR packets, in which case the client says something like "remote error: foo". Like:

  [this is an ERR packet; we are asking for a nonsense object id]
  $ git fetch origin 0000000000000000000000000000000000000001
  fatal: remote error: upload-pack: not our ref 0000000000000000000000000000000000000001
  [this is stderr from a server sub-process routed over the err
   sideband; I corrupted the server-side repo by removing one of
   its packfiles]
  $ git fetch $url
  remote: error: Could not read 576053ed5ad378490974fabe97e4bd59633d2d1e
  remote: fatal: Failed to traverse parents of commit a3287c454eb8f7b89d969e675768a6cfa258ad34
  remote: aborting due to possible repository corruption on the remote side.
  fatal: early EOF
  fatal: index-pack failed

But for the error you're seeing, it is happening within upload-pack itself (the server-side process handling the request), it happens before we have even established that the client can handle sideband data, and it is a die() call from within library code that does not know about ERR packets. So the message goes to upload-pack's stderr on the server side, and then ssh just passes it back. In fact, you are a little lucky to see it at all; for a clone over http, it would just go to the webserver's log (or maybe /dev/null).

I do agree it is not very friendly, so I'm laying this out to help brainstorm ideas to make it better. Some possible directions I can think of:

  - could upload-pack install a die() handler that prints the message in
    an ERR packet? I worry a little that older versions of Git would not
    handle this great, as I don't think they were always prepared to see
    an ERR packet at any point. OTOH, it is probably better than sending
    nothing, which is what we do now.
  - could the client-side process (git-clone or git-fetch) intercept
    stderr from processes it spawns (ssh in this case, but also
    git-upload-pack directly for local-system clones) and prefix it with
    "remote:" or similar? That might help ssh and local system cases,
    but other transports like http wouldn't benefit at all. Also, it
    would probably involve forking off another process to consume
    stderr.

I dunno. I don't love either of those that much. And while it could help things in general, I think the main clue in this case is just that the error message refers to '/git/main/test.git'. And that path is only meaningful on the server, since the url was my.server:/git/main/test.git. Knowing that the config advice is _also_ coming from the server is probably the key subtle bit, though.

-Peff
Jeff King· Sep 15, 2025, 02:46 UTC · re: Jeff King · lore

Re: [SOLVED] Re: safe.directory does not work at all (git 2.39.5, 2.51.0)

On Sun, Sep 14, 2025 at 10:23:01PM -0400, Jeff King wrote:
Show 5 quoted lines
>   - could upload-pack install a die() handler that prints the message in
>     an ERR packet? I worry a little that older versions of Git would not
>     handle this great, as I don't think they were always prepared to see
>     an ERR packet at any point. OTOH, it is probably better than sending
>     nothing, which is what we do now.
Just for fun, I tried the patch below on v2.39.5:
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
index f446ff04f6..ad40143beb 100644
--- a/builtin/upload-pack.c
+++ b/builtin/upload-pack.c
@@ -13,6 +13,21 @@ static const char * const upload_pack_usage[] = {
 	NULL
 };
 
+NORETURN
+static void send_err_pkt_on_die(const char *fmt, va_list ap)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	/* format into a buf since interfaces below do not handle va_list */
+	strbuf_vaddf(&buf, fmt, ap);
+
+	/* write our ERR packet */
+	packet_write_fmt_gently(1, "ERR %s", buf.buf);
+
+	/* and then do the usual die to stderr */
+	exit(die_message("%s", buf.buf));
+}
+
 int cmd_upload_pack(int argc, const char **argv, const char *prefix)
 {
 	const char *dir;
@@ -38,6 +53,8 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
 	/* TODO: This should use NO_LAZY_FETCH_ENVIRONMENT */
 	xsetenv("GIT_NO_LAZY_FETCH", "1", 0);
 
+	set_die_routine(send_err_pkt_on_die);
+
 	argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
 
 	if (argc != 1)


The results are...not great. You get every message twice, of course,
because we still print it to stderr. Though that could easily be fixed.
But for the multi-line message in question, the "remote error" part is
hard to see amidst the other lines:

  Cloning into 'foo'...
  fatal: detected dubious ownership in repository at '/tmp/foo.git'
  To add an exception for this directory, call:
  
  	git config --global --add safe.directory /tmp/foo.git
  fatal: remote error: detected dubious ownership in repository at '/tmp/foo.git'
  To add an exception for this directory, call:
  
  	git config --global --add safe.directory /tmp/foo.git

Probably it would help to look for newlines and prefix every line with
"remote: or similar. But an even bigger problem is that we die
immediately on seeing the remote ERR packet, so we miss out on any local
error messages. An obvious one is trying to clone something that doesn't
exist at all. We used to say:

  Cloning into 'does-not-exist'...
  fatal: '/tmp/does-not-exist.git' does not appear to be a git repository
  fatal: Could not read from remote repository.

  Please make sure you have the correct access rights
  and the repository exists.

Noting that we saw an error from the remote side and giving some hints.
And the stderr from the other side is enough to give us the more
specific message (though again, over http the user would not get that
stderr message; however, if we get a 404 we do show a useful message).

But with the patch above we just relay what the other side says:

  Cloning into 'does-not-exist'...
  fatal: '/tmp/does-not-exist.git' does not appear to be a git repository
  fatal: remote error: '/tmp/does-not-exist.git' does not appear to be a git repository

which seems worse to me.

So probably not a very productive direction. Oh well.

-Peff
Marc-Jano Knopp· Sep 17, 2025, 20:22 UTC · re: Jeff King · lore

Re: [SOLVED] Re: safe.directory does not work at all (git 2.39.5, 2.51.0)

On Mon, 2025-09-15, at 04:23:01 (+0200), Jeff King wrote: [...]

> I dunno. I don't love either of those that much. And while it could help
> things in general, I think the main clue in this case is just that the
> error message refers to '/git/main/test.git'. And that path is only
> meaningful on the server, since the url was my.server:/git/main/test.git.
Good point!
> Knowing that the config advice is _also_ coming from the server is
> probably the key subtle bit, though.

Yeah, the keyword "remote" would probably have been successfully caught by my brain's pattern matching algorithm ...

MJK

← back to recent threads