From 998a06627e83fad3f99e299c19180e54af36fb33 Mon Sep 17 00:00:00 2001 From: Giteabot Date: Sat, 10 Oct 2026 11:06:48 -0700 Subject: [PATCH] fix(git): match git config values exactly when syncing (#39728) (#39729) Backport #39728 by @silverwind Since https://github.com/go-gitea/gitea/pull/39703, Gitea fails to start when its gitconfig holds a `gc.reflogExpire` other than `90` (e.g. `30.days`), because `git config --unset-all` exits 5 when no value matches. Value patterns were also unanchored regexps, so `90` also removed `90.days`. - Match values exactly in `configUnsetAll` and `configAddNonExist` - Treat "nothing to unset" as success - Use `--replace-all` in `configSet` so a key with multiple values no longer fails startup Co-authored-by: silverwind --- modules/git/config.go | 21 ++++++--------------- modules/git/config_test.go | 4 ++++ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/modules/git/config.go b/modules/git/config.go index a44350a7761..90120aaed79 100644 --- a/modules/git/config.go +++ b/modules/git/config.go @@ -167,7 +167,7 @@ func configSet(ctx context.Context, key, value string) error { return nil } - if _, _, err = gitcmd.NewCommand("config", "--global"). + if _, _, err = gitcmd.NewCommand("config", "--global", "--replace-all"). AddDynamicArguments(key, value). RunStdString(ctx); err != nil { return fmt.Errorf("failed to set git global config %s, err: %w", key, err) @@ -195,7 +195,7 @@ func configSetNonExist(ctx context.Context, key, value string) error { } func configAddNonExist(ctx context.Context, key, value string) error { - _, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(ctx) + _, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key, "^"+regexp.QuoteMeta(value)+"$").RunStdString(ctx) if err == nil { // already exist return nil @@ -212,18 +212,9 @@ func configAddNonExist(ctx context.Context, key, value string) error { } func configUnsetAll(ctx context.Context, key, value string) error { - _, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx) - if err == nil { - // exist, need to remove - _, _, err = gitcmd.NewCommand("config", "--global", "--unset-all").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(ctx) - if err != nil { - return fmt.Errorf("failed to unset git global config %s, err: %w", key, err) - } - return nil + _, _, err := gitcmd.NewCommand("config", "--global", "--unset-all").AddDynamicArguments(key, "^"+regexp.QuoteMeta(value)+"$").RunStdString(ctx) + if err != nil && !gitcmd.IsErrorExitCode(err, 5) { // 5: no such key or no matching value + return fmt.Errorf("failed to unset git global config %s, err: %w", key, err) } - if gitcmd.IsErrorExitCode(err, 1) { - // not exist - return nil - } - return fmt.Errorf("failed to get git config %s, err: %w", key, err) + return nil } diff --git a/modules/git/config_test.go b/modules/git/config_test.go index 60e4f2c6479..206c99684e4 100644 --- a/modules/git/config_test.go +++ b/modules/git/config_test.go @@ -32,7 +32,9 @@ func TestGitConfig(t *testing.T) { assert.NoError(t, configSetNonExist(ctx, "test.key-a", "val-a-changed")) assert.False(t, gitConfigContains("key-a = val-a-changed")) + assert.NoError(t, configAddNonExist(ctx, "test.key-a", "val-a2")) assert.NoError(t, configSet(ctx, "test.key-a", "val-a-changed")) + assert.NoError(t, configUnsetAll(ctx, "test.key-a", "val-a")) assert.True(t, gitConfigContains("key-a = val-a-changed")) assert.NoError(t, configAddNonExist(ctx, "test.key-b", "val-b")) @@ -41,6 +43,8 @@ func TestGitConfig(t *testing.T) { assert.NoError(t, configAddNonExist(ctx, "test.key-b", "val-2b")) assert.True(t, gitConfigContains("key-b = val-b")) assert.True(t, gitConfigContains("key-b = val-2b")) + assert.NoError(t, configAddNonExist(ctx, "test.key-b", "val")) + assert.True(t, gitConfigContains("key-b = val\n")) assert.NoError(t, configUnsetAll(ctx, "test.key-b", "val-b")) assert.False(t, gitConfigContains("key-b = val-b"))