Support changing git config through `app.ini`, use `diff.algorithm=histogram` by default (#24860)
Close #13454 , Close #23255, Close #14697 (and maybe more related issues) Many users have the requirement to customize the git config. This PR introduces an easy way: put the options in Gitea's app.ini `[git.config]`, then the config options will be applied to git config. And it can support more flexible default config values, eg: now `diff.algorithm=histogram` by default. According to: https://stackoverflow.com/a/32367597/4754037 , `histogram diff` is efficient and doesn't like to cause server-side problems. --------- Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: KN4CK3R <admin@oldschoolhack.me> Co-authored-by: Giteabot <teabot@gitea.io>
This commit is contained in:
parent
910bf31546
commit
8080ace6fc
|
@ -682,6 +682,28 @@ LEVEL = Info
|
||||||
;; Disable the usage of using partial clones for git.
|
;; Disable the usage of using partial clones for git.
|
||||||
;DISABLE_PARTIAL_CLONE = false
|
;DISABLE_PARTIAL_CLONE = false
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Git Operation timeout in seconds
|
||||||
|
;[git.timeout]
|
||||||
|
;DEFAULT = 360
|
||||||
|
;MIGRATE = 600
|
||||||
|
;MIRROR = 300
|
||||||
|
;CLONE = 300
|
||||||
|
;PULL = 300
|
||||||
|
;GC = 60
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Git Reflog timeout in days
|
||||||
|
;[git.reflog]
|
||||||
|
;ENABLED = true
|
||||||
|
;EXPIRATION = 90
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Git config options
|
||||||
|
;; This section only does "set" config, a removed config key from this section won't be removed from git config automatically. The format is `some.configKey = value`.
|
||||||
|
;[git.config]
|
||||||
|
;diff.algorithm = histogram
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
[service]
|
[service]
|
||||||
|
@ -2176,32 +2198,6 @@ LEVEL = Info
|
||||||
;Check at least this proportion of LFSMetaObjects per repo. (This may cause all stale LFSMetaObjects to be checked.)
|
;Check at least this proportion of LFSMetaObjects per repo. (This may cause all stale LFSMetaObjects to be checked.)
|
||||||
;PROPORTION_TO_CHECK_PER_REPO = 0.6
|
;PROPORTION_TO_CHECK_PER_REPO = 0.6
|
||||||
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;; Git Operation timeout in seconds
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;[git.timeout]
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;DEFAULT = 360
|
|
||||||
;MIGRATE = 600
|
|
||||||
;MIRROR = 300
|
|
||||||
;CLONE = 300
|
|
||||||
;PULL = 300
|
|
||||||
;GC = 60
|
|
||||||
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;; Git Reflog timeout in days
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;[git.reflog]
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;ENABLED = true
|
|
||||||
;EXPIRATION = 90
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;[mirror]
|
;[mirror]
|
||||||
|
|
|
@ -1054,12 +1054,7 @@ Default templates for project boards:
|
||||||
- `DISABLE_CORE_PROTECT_NTFS`: **false** Set to true to forcibly set `core.protectNTFS` to false.
|
- `DISABLE_CORE_PROTECT_NTFS`: **false** Set to true to forcibly set `core.protectNTFS` to false.
|
||||||
- `DISABLE_PARTIAL_CLONE`: **false** Disable the usage of using partial clones for git.
|
- `DISABLE_PARTIAL_CLONE`: **false** Disable the usage of using partial clones for git.
|
||||||
|
|
||||||
## Git - Reflog settings (`git.reflog`)
|
### Git - Timeout settings (`git.timeout`)
|
||||||
|
|
||||||
- `ENABLED`: **true** Set to true to enable Git to write changes to reflogs in each repo.
|
|
||||||
- `EXPIRATION`: **90** Reflog entry lifetime, in days. Entries are removed opportunistically by Git.
|
|
||||||
|
|
||||||
## Git - Timeout settings (`git.timeout`)
|
|
||||||
|
|
||||||
- `DEFAULT`: **360**: Git operations default timeout seconds.
|
- `DEFAULT`: **360**: Git operations default timeout seconds.
|
||||||
- `MIGRATE`: **600**: Migrate external repositories timeout seconds.
|
- `MIGRATE`: **600**: Migrate external repositories timeout seconds.
|
||||||
|
@ -1068,6 +1063,18 @@ Default templates for project boards:
|
||||||
- `PULL`: **300**: Git pull from internal repositories timeout seconds.
|
- `PULL`: **300**: Git pull from internal repositories timeout seconds.
|
||||||
- `GC`: **60**: Git repository GC timeout seconds.
|
- `GC`: **60**: Git repository GC timeout seconds.
|
||||||
|
|
||||||
|
### Git - Reflog settings (`git.reflog`)
|
||||||
|
|
||||||
|
- `ENABLED`: **true** Set to true to enable Git to write changes to reflogs in each repo.
|
||||||
|
- `EXPIRATION`: **90** Reflog entry lifetime, in days. Entries are removed opportunistically by Git.
|
||||||
|
|
||||||
|
### Git - Config options (`git.config`)
|
||||||
|
|
||||||
|
The key/value pairs in this section will be used as git config.
|
||||||
|
This section only does "set" config, a removed config key from this section won't be removed from git config automatically. The format is `some.configKey = value`.
|
||||||
|
|
||||||
|
- `diff.algorithm`: **histogram**
|
||||||
|
|
||||||
## Metrics (`metrics`)
|
## Metrics (`metrics`)
|
||||||
|
|
||||||
- `ENABLED`: **false**: Enables /metrics endpoint for prometheus.
|
- `ENABLED`: **false**: Enables /metrics endpoint for prometheus.
|
||||||
|
|
|
@ -282,6 +282,22 @@ Place custom files in corresponding sub-folder under `custom/options`.
|
||||||
|
|
||||||
To add custom .gitignore, add a file with existing [.gitignore rules](https://git-scm.com/docs/gitignore) in it to `$GITEA_CUSTOM/options/gitignore`
|
To add custom .gitignore, add a file with existing [.gitignore rules](https://git-scm.com/docs/gitignore) in it to `$GITEA_CUSTOM/options/gitignore`
|
||||||
|
|
||||||
|
## Customizing the git configuration
|
||||||
|
|
||||||
|
Starting with Gitea 1.20, you can customize the git configuration via the `git.config` section.
|
||||||
|
|
||||||
|
### Enabling signed git pushes
|
||||||
|
|
||||||
|
To enable signed git pushes, set these two options:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[git.config]
|
||||||
|
receive.advertisePushOptions = true
|
||||||
|
receive.certNonceSeed = <randomstring>
|
||||||
|
```
|
||||||
|
|
||||||
|
`certNonceSeed` should be set to a random string and be kept secret.
|
||||||
|
|
||||||
### Labels
|
### Labels
|
||||||
|
|
||||||
Starting with Gitea 1.19, you can add a file that follows the [YAML label format](https://github.com/go-gitea/gitea/blob/main/options/label/Advanced.yaml) to `$GITEA_CUSTOM/options/label`:
|
Starting with Gitea 1.19, you can add a file that follows the [YAML label format](https://github.com/go-gitea/gitea/blob/main/options/label/Advanced.yaml) to `$GITEA_CUSTOM/options/label`:
|
||||||
|
|
|
@ -224,6 +224,14 @@ func syncGitConfig() (err error) {
|
||||||
return fmt.Errorf("unable to prepare git home directory %s, err: %w", HomeDir(), err)
|
return fmt.Errorf("unable to prepare git home directory %s, err: %w", HomeDir(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// first, write user's git config options to git config file
|
||||||
|
// user config options could be overwritten by builtin values later, because if a value is builtin, it must have some special purposes
|
||||||
|
for k, v := range setting.GitConfig.Options {
|
||||||
|
if err = configSet(strings.ToLower(k), v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Git requires setting user.name and user.email in order to commit changes - old comment: "if they're not set just add some defaults"
|
// Git requires setting user.name and user.email in order to commit changes - old comment: "if they're not set just add some defaults"
|
||||||
// TODO: need to confirm whether users really need to change these values manually. It seems that these values are dummy only and not really used.
|
// TODO: need to confirm whether users really need to change these values manually. It seems that these values are dummy only and not really used.
|
||||||
// If these values are not really used, then they can be set (overwritten) directly without considering about existence.
|
// If these values are not really used, then they can be set (overwritten) directly without considering about existence.
|
||||||
|
|
|
@ -42,14 +42,14 @@ func TestMain(m *testing.M) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGitConfig(t *testing.T) {
|
func gitConfigContains(sub string) bool {
|
||||||
gitConfigContains := func(sub string) bool {
|
|
||||||
if b, err := os.ReadFile(HomeDir() + "/.gitconfig"); err == nil {
|
if b, err := os.ReadFile(HomeDir() + "/.gitconfig"); err == nil {
|
||||||
return strings.Contains(string(b), sub)
|
return strings.Contains(string(b), sub)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGitConfig(t *testing.T) {
|
||||||
assert.False(t, gitConfigContains("key-a"))
|
assert.False(t, gitConfigContains("key-a"))
|
||||||
|
|
||||||
assert.NoError(t, configSetNonExist("test.key-a", "val-a"))
|
assert.NoError(t, configSetNonExist("test.key-a", "val-a"))
|
||||||
|
@ -81,3 +81,15 @@ func TestGitConfig(t *testing.T) {
|
||||||
assert.NoError(t, configUnsetAll("test.key-x", "*"))
|
assert.NoError(t, configUnsetAll("test.key-x", "*"))
|
||||||
assert.False(t, gitConfigContains("key-x = *"))
|
assert.False(t, gitConfigContains("key-x = *"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSyncConfig(t *testing.T) {
|
||||||
|
oldGitConfig := setting.GitConfig
|
||||||
|
defer func() {
|
||||||
|
setting.GitConfig = oldGitConfig
|
||||||
|
}()
|
||||||
|
|
||||||
|
setting.GitConfig.Options["sync-test.cfg-key-a"] = "CfgValA"
|
||||||
|
assert.NoError(t, syncGitConfig())
|
||||||
|
assert.True(t, gitConfigContains("[sync-test]"))
|
||||||
|
assert.True(t, gitConfigContains("cfg-key-a = CfgValA"))
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
@ -78,12 +79,28 @@ var Git = struct {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var GitConfig = struct {
|
||||||
|
Options map[string]string
|
||||||
|
}{
|
||||||
|
Options: make(map[string]string),
|
||||||
|
}
|
||||||
|
|
||||||
func loadGitFrom(rootCfg ConfigProvider) {
|
func loadGitFrom(rootCfg ConfigProvider) {
|
||||||
sec := rootCfg.Section("git")
|
sec := rootCfg.Section("git")
|
||||||
if err := sec.MapTo(&Git); err != nil {
|
if err := sec.MapTo(&Git); err != nil {
|
||||||
log.Fatal("Failed to map Git settings: %v", err)
|
log.Fatal("Failed to map Git settings: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secGitConfig := rootCfg.Section("git.config")
|
||||||
|
GitConfig.Options = make(map[string]string)
|
||||||
|
for _, key := range secGitConfig.Keys() {
|
||||||
|
// git config key is case-insensitive, so always use lower-case
|
||||||
|
GitConfig.Options[strings.ToLower(key.Name())] = key.String()
|
||||||
|
}
|
||||||
|
if _, ok := GitConfig.Options["diff.algorithm"]; !ok {
|
||||||
|
GitConfig.Options["diff.algorithm"] = "histogram"
|
||||||
|
}
|
||||||
|
|
||||||
Git.HomePath = sec.Key("HOME_PATH").MustString("home")
|
Git.HomePath = sec.Key("HOME_PATH").MustString("home")
|
||||||
if !filepath.IsAbs(Git.HomePath) {
|
if !filepath.IsAbs(Git.HomePath) {
|
||||||
Git.HomePath = filepath.Join(AppDataPath, Git.HomePath)
|
Git.HomePath = filepath.Join(AppDataPath, Git.HomePath)
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGitConfig(t *testing.T) {
|
||||||
|
oldGit := Git
|
||||||
|
oldGitConfig := GitConfig
|
||||||
|
defer func() {
|
||||||
|
Git = oldGit
|
||||||
|
GitConfig = oldGitConfig
|
||||||
|
}()
|
||||||
|
|
||||||
|
cfg, err := NewConfigProviderFromData(`
|
||||||
|
[git.config]
|
||||||
|
a.b = 1
|
||||||
|
`)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
loadGitFrom(cfg)
|
||||||
|
|
||||||
|
assert.Len(t, GitConfig.Options, 2)
|
||||||
|
assert.EqualValues(t, "1", GitConfig.Options["a.b"])
|
||||||
|
assert.EqualValues(t, "histogram", GitConfig.Options["diff.algorithm"])
|
||||||
|
|
||||||
|
cfg, err = NewConfigProviderFromData(`
|
||||||
|
[git.config]
|
||||||
|
diff.algorithm = other
|
||||||
|
`)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
loadGitFrom(cfg)
|
||||||
|
|
||||||
|
assert.Len(t, GitConfig.Options, 1)
|
||||||
|
assert.EqualValues(t, "other", GitConfig.Options["diff.algorithm"])
|
||||||
|
}
|
Loading…
Reference in New Issue