Fix locale-dependent git repo detection (#1099)

Fixes #1089

## Root cause
`tea clone` initializes the command context by probing the current directory with `git rev-parse --show-toplevel`. `classifyRepoError` recognizes the "not a git repository" case by matching the English stderr text. When git emits localized errors (for example with `LANG=de_DE.UTF-8`), that match fails, and the otherwise harmless "no repo here" error is returned as fatal before cloning.

## Fix
Pin the locale of git subprocesses to `C` by overriding `LC_ALL`, `LANG`, and `LANGUAGE` in `runGitCommand`. This keeps all git output stable for parsing while leaving the parent process locale untouched.

## Tests
Added a regression test using a fake git executable that emits a German "not a repository" error unless the locale is forced to `C`, and verifies `ErrRepositoryNotExists` is returned.

Reviewed-on: https://gitea.com/gitea/tea/pulls/1099
Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
This commit is contained in:
Lunny Xiao
2026-09-30 17:48:18 +00:00
parent 9c12138d62
commit 6daaa7c05e
2 changed files with 56 additions and 1 deletions
+16 -1
View File
@@ -258,6 +258,21 @@ func (e *gitCommandError) Unwrap() error {
return e.err
}
func gitCommandEnv(authEnv []string) []string {
baseEnv := os.Environ()
env := make([]string, 0, len(baseEnv)+len(authEnv)+3)
for _, value := range baseEnv {
if strings.HasPrefix(value, "LC_ALL=") ||
strings.HasPrefix(value, "LANG=") ||
strings.HasPrefix(value, "LANGUAGE=") {
continue
}
env = append(env, value)
}
env = append(env, authEnv...)
return append(env, "LC_ALL=C", "LANG=C", "LANGUAGE=C")
}
func runGitCommand(dir string, auth *AuthMethod, extraConfigs []string, args ...string) (string, error) {
authConfigs, authEnv, cleanup, err := prepareCLIAuth(auth)
if err != nil {
@@ -278,7 +293,7 @@ func runGitCommand(dir string, auth *AuthMethod, extraConfigs []string, args ...
if dir != "" {
cmd.Dir = dir
}
cmd.Env = append(os.Environ(), authEnv...)
cmd.Env = gitCommandEnv(authEnv)
var stdout bytes.Buffer
var stderr bytes.Buffer
+40
View File
@@ -0,0 +1,40 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
import (
"os"
"path/filepath"
"runtime"
"testing"
"github.com/stretchr/testify/require"
)
func TestOpenClassifiesLocalizedNotRepoError(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("test uses a POSIX shell script as fake git")
}
fakeGitDir := t.TempDir()
fakeGit := filepath.Join(fakeGitDir, "git")
script := `#!/bin/sh
locale="${LC_ALL:-${LANG:-C}}"
if [ "$locale" = "C" ]; then
echo "fatal: not a git repository (or any of the parent directories): .git" >&2
else
echo "fatal: Kein Git-Repository (oder irgendein Elternverzeichnis bis zum Einhangepunkt /)" >&2
fi
exit 128
`
require.NoError(t, os.WriteFile(fakeGit, []byte(script), 0o755))
t.Setenv("PATH", fakeGitDir+string(os.PathListSeparator)+os.Getenv("PATH"))
t.Setenv("LC_ALL", "de_DE.UTF-8")
t.Setenv("LANG", "de_DE.UTF-8")
t.Setenv("LANGUAGE", "de_DE:de")
_, err := (cliBackend{}).Open(t.TempDir())
require.ErrorIs(t, err, ErrRepositoryNotExists)
}