diff --git a/.drone.yml b/.drone.yml
index 1aac10a828..928e75d1ac 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -109,7 +109,7 @@ steps:
     depends_on: [test-frontend]
 
   - name: build-backend-no-gcc
-    image: golang:1.17 # this step is kept as the lowest version of golang that we support
+    image: golang:1.18 # this step is kept as the lowest version of golang that we support
     pull: always
     environment:
       GO111MODULE: on
diff --git a/README.md b/README.md
index 84c0524ed4..172e16cf56 100644
--- a/README.md
+++ b/README.md
@@ -73,7 +73,7 @@ or if SQLite support is required:
 
 The `build` target is split into two sub-targets:
 
-- `make backend` which requires [Go 1.17](https://go.dev/dl/) or greater.
+- `make backend` which requires [Go Stable](https://go.dev/dl/), required version is defined in [go.mod](/go.mod).
 - `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater and Internet connectivity to download npm dependencies.
 
 When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js and Internet connectivity.
diff --git a/cmd/serv.go b/cmd/serv.go
index 6ba3e9de01..6e067a48a5 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -6,6 +6,7 @@
 package cmd
 
 import (
+	"context"
 	"fmt"
 	"net/http"
 	"net/url"
@@ -65,6 +66,21 @@ func setup(logPath string, debug bool) {
 	if debug {
 		setting.RunMode = "dev"
 	}
+
+	// Check if setting.RepoRootPath exists. It could be the case that it doesn't exist, this can happen when
+	// `[repository]` `ROOT` is a relative path and $GITEA_WORK_DIR isn't passed to the SSH connection.
+	if _, err := os.Stat(setting.RepoRootPath); err != nil {
+		if os.IsNotExist(err) {
+			_ = fail("Incorrect configuration, no repository directory.", "Directory `[repository].ROOT` %q was not found, please check if $GITEA_WORK_DIR is passed to the SSH connection or make `[repository].ROOT` an absolute value.", setting.RepoRootPath)
+		} else {
+			_ = fail("Incorrect configuration, repository directory is inaccessible", "Directory `[repository].ROOT` %q is inaccessible. err: %v", setting.RepoRootPath, err)
+		}
+		return
+	}
+
+	if err := git.InitSimple(context.Background()); err != nil {
+		_ = fail("Failed to init git", "Failed to init git, err: %v", err)
+	}
 }
 
 var (
@@ -80,12 +96,12 @@ var (
 func fail(userMessage, logMessage string, args ...interface{}) error {
 	// There appears to be a chance to cause a zombie process and failure to read the Exit status
 	// if nothing is outputted on stdout.
-	fmt.Fprintln(os.Stdout, "")
-	fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
+	_, _ = fmt.Fprintln(os.Stdout, "")
+	_, _ = fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
 
 	if len(logMessage) > 0 {
 		if !setting.IsProd {
-			fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
+			_, _ = fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
 		}
 	}
 	ctx, cancel := installSignals()
@@ -237,17 +253,6 @@ func runServ(c *cli.Context) error {
 		}
 		return fail("Internal Server Error", "%s", err.Error())
 	}
-	os.Setenv(repo_module.EnvRepoIsWiki, strconv.FormatBool(results.IsWiki))
-	os.Setenv(repo_module.EnvRepoName, results.RepoName)
-	os.Setenv(repo_module.EnvRepoUsername, results.OwnerName)
-	os.Setenv(repo_module.EnvPusherName, results.UserName)
-	os.Setenv(repo_module.EnvPusherEmail, results.UserEmail)
-	os.Setenv(repo_module.EnvPusherID, strconv.FormatInt(results.UserID, 10))
-	os.Setenv(repo_module.EnvRepoID, strconv.FormatInt(results.RepoID, 10))
-	os.Setenv(repo_module.EnvPRID, fmt.Sprintf("%d", 0))
-	os.Setenv(repo_module.EnvDeployKeyID, fmt.Sprintf("%d", results.DeployKeyID))
-	os.Setenv(repo_module.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
-	os.Setenv(repo_module.EnvAppURL, setting.AppURL)
 
 	// LFS token authentication
 	if verb == lfsAuthenticateVerb {
@@ -298,20 +303,29 @@ func runServ(c *cli.Context) error {
 		gitcmd = exec.CommandContext(ctx, verb, repoPath)
 	}
 
-	// Check if setting.RepoRootPath exists. It could be the case that it doesn't exist, this can happen when
-	// `[repository]` `ROOT` is a relative path and $GITEA_WORK_DIR isn't passed to the SSH connection.
-	if _, err := os.Stat(setting.RepoRootPath); err != nil {
-		if os.IsNotExist(err) {
-			return fail("Incorrect configuration.",
-				"Directory `[repository]` `ROOT` %s was not found, please check if $GITEA_WORK_DIR is passed to the SSH connection or make `[repository]` `ROOT` an absolute value.", setting.RepoRootPath)
-		}
-	}
-
 	process.SetSysProcAttribute(gitcmd)
 	gitcmd.Dir = setting.RepoRootPath
 	gitcmd.Stdout = os.Stdout
 	gitcmd.Stdin = os.Stdin
 	gitcmd.Stderr = os.Stderr
+	gitcmd.Env = append(gitcmd.Env, os.Environ()...)
+	gitcmd.Env = append(gitcmd.Env,
+		repo_module.EnvRepoIsWiki+"="+strconv.FormatBool(results.IsWiki),
+		repo_module.EnvRepoName+"="+results.RepoName,
+		repo_module.EnvRepoUsername+"="+results.OwnerName,
+		repo_module.EnvPusherName+"="+results.UserName,
+		repo_module.EnvPusherEmail+"="+results.UserEmail,
+		repo_module.EnvPusherID+"="+strconv.FormatInt(results.UserID, 10),
+		repo_module.EnvRepoID+"="+strconv.FormatInt(results.RepoID, 10),
+		repo_module.EnvPRID+"="+fmt.Sprintf("%d", 0),
+		repo_module.EnvDeployKeyID+"="+fmt.Sprintf("%d", results.DeployKeyID),
+		repo_module.EnvKeyID+"="+fmt.Sprintf("%d", results.KeyID),
+		repo_module.EnvAppURL+"="+setting.AppURL,
+	)
+	// to avoid breaking, here only use the minimal environment variables for the "gitea serv" command.
+	// it could be re-considered whether to use the same git.CommonGitCmdEnvs() as "git" command later.
+	gitcmd.Env = append(gitcmd.Env, git.CommonCmdServEnvs()...)
+
 	if err = gitcmd.Run(); err != nil {
 		return fail("Internal error", "Failed to execute git command: %v", err)
 	}
diff --git a/docs/config.yaml b/docs/config.yaml
index 075fd32ef4..5a773662cf 100644
--- a/docs/config.yaml
+++ b/docs/config.yaml
@@ -19,7 +19,7 @@ params:
   author: The Gitea Authors
   website: https://docs.gitea.io
   version: 1.16.8
-  minGoVersion: 1.17
+  minGoVersion: 1.18
   goVersion: 1.18
   minNodeVersion: 14
 
diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
index edd23012fb..3981dc5fab 100644
--- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md
+++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
@@ -315,8 +315,8 @@ The following configuration set `Content-Type: application/vnd.android.package-a
 - `SSL_MAX_VERSION`: **\<empty\>**: Set the maximum version of ssl support.
 - `SSL_CURVE_PREFERENCES`: **X25519,P256**: Set the preferred curves,
 - `SSL_CIPHER_SUITES`: **ecdhe_ecdsa_with_aes_256_gcm_sha384,ecdhe_rsa_with_aes_256_gcm_sha384,ecdhe_ecdsa_with_aes_128_gcm_sha256,ecdhe_rsa_with_aes_128_gcm_sha256,ecdhe_ecdsa_with_chacha20_poly1305,ecdhe_rsa_with_chacha20_poly1305**: Set the preferred cipher suites.
-  - If there is not hardware support for AES suites by default the cha cha suites will be preferred over the AES suites
-  - supported suites as of go 1.17 are:
+  - If there is no hardware support for AES suites, by default the ChaCha suites will be preferred over the AES suites.
+  - supported suites as of Go 1.18 are:
     - TLS 1.0 - 1.2 cipher suites
       - "rsa_with_rc4_128_sha"
       - "rsa_with_3des_ede_cbc_sha"
diff --git a/go.mod b/go.mod
index ed26b60747..8e5871cfb0 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module code.gitea.io/gitea
 
-go 1.17
+go 1.18
 
 require (
 	code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b
diff --git a/integrations/integration_test.go b/integrations/integration_test.go
index 687591d5fa..0a41546554 100644
--- a/integrations/integration_test.go
+++ b/integrations/integration_test.go
@@ -274,8 +274,8 @@ func prepareTestEnv(t testing.TB, skip ...int) func() {
 	deferFn := PrintCurrentTest(t, ourSkip)
 	assert.NoError(t, unittest.LoadFixtures())
 	assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
-
 	assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
+	assert.NoError(t, git.InitWithConfigSync(context.Background()))
 	ownerDirs, err := os.ReadDir(setting.RepoRootPath)
 	if err != nil {
 		assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
@@ -576,6 +576,7 @@ func resetFixtures(t *testing.T) {
 	assert.NoError(t, unittest.LoadFixtures())
 	assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
 	assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
+	assert.NoError(t, git.InitWithConfigSync(context.Background()))
 	ownerDirs, err := os.ReadDir(setting.RepoRootPath)
 	if err != nil {
 		assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
diff --git a/integrations/migration-test/migration_test.go b/integrations/migration-test/migration_test.go
index 6a431504a0..45e31ff9ac 100644
--- a/integrations/migration-test/migration_test.go
+++ b/integrations/migration-test/migration_test.go
@@ -62,6 +62,7 @@ func initMigrationTest(t *testing.T) func() {
 	assert.True(t, len(setting.RepoRootPath) != 0)
 	assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
 	assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
+	assert.NoError(t, git.InitWithConfigSync(context.Background()))
 	ownerDirs, err := os.ReadDir(setting.RepoRootPath)
 	if err != nil {
 		assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
diff --git a/models/migrations/migrations_test.go b/models/migrations/migrations_test.go
index a1fd49a8b9..2c4d21d974 100644
--- a/models/migrations/migrations_test.go
+++ b/models/migrations/migrations_test.go
@@ -202,9 +202,8 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En
 	ourSkip += skip
 	deferFn := PrintCurrentTest(t, ourSkip)
 	assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
-
-	assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
-		setting.RepoRootPath))
+	assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
+	assert.NoError(t, git.InitWithConfigSync(context.Background()))
 	ownerDirs, err := os.ReadDir(setting.RepoRootPath)
 	if err != nil {
 		assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go
index 117614a7a4..2a366836fe 100644
--- a/models/unittest/testdb.go
+++ b/models/unittest/testdb.go
@@ -14,6 +14,7 @@ import (
 
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/modules/base"
+	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/storage"
 	"code.gitea.io/gitea/modules/util"
@@ -116,6 +117,9 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
 	if err = CopyDir(filepath.Join(testOpts.GiteaRootPath, "integrations", "gitea-repositories-meta"), setting.RepoRootPath); err != nil {
 		fatalTestError("util.CopyDir: %v\n", err)
 	}
+	if err = git.InitWithConfigSync(context.Background()); err != nil {
+		fatalTestError("git.Init: %v\n", err)
+	}
 
 	ownerDirs, err := os.ReadDir(setting.RepoRootPath)
 	if err != nil {
@@ -198,6 +202,7 @@ func PrepareTestEnv(t testing.TB) {
 	assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
 	metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
 	assert.NoError(t, CopyDir(metaPath, setting.RepoRootPath))
+	assert.NoError(t, git.InitWithConfigSync(context.Background()))
 
 	ownerDirs, err := os.ReadDir(setting.RepoRootPath)
 	assert.NoError(t, err)
diff --git a/modules/git/command.go b/modules/git/command.go
index f6344dbfd1..d71497f1d7 100644
--- a/modules/git/command.go
+++ b/modules/git/command.go
@@ -8,6 +8,7 @@ package git
 import (
 	"bytes"
 	"context"
+	"errors"
 	"fmt"
 	"io"
 	"os"
@@ -104,6 +105,25 @@ type RunOpts struct {
 	PipelineFunc   func(context.Context, context.CancelFunc) error
 }
 
+// CommonGitCmdEnvs returns the common environment variables for a "git" command.
+func CommonGitCmdEnvs() []string {
+	// at the moment, do not set "GIT_CONFIG_NOSYSTEM", users may have put some configs like "receive.certNonceSeed" in it
+	return []string{
+		fmt.Sprintf("LC_ALL=%s", DefaultLocale),
+		"GIT_TERMINAL_PROMPT=0",    // avoid prompting for credentials interactively, supported since git v2.3
+		"GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace)
+		"HOME=" + HomeDir(),        // make Gitea use internal git config only, to prevent conflicts with user's git config
+	}
+}
+
+// CommonCmdServEnvs is like CommonGitCmdEnvs but it only returns minimal required environment variables for the "gitea serv" command
+func CommonCmdServEnvs() []string {
+	return []string{
+		"GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace)
+		"HOME=" + HomeDir(),        // make Gitea use internal git config only, to prevent conflicts with user's git config
+	}
+}
+
 // Run runs the command with the RunOpts
 func (c *Command) Run(opts *RunOpts) error {
 	if opts == nil {
@@ -148,16 +168,8 @@ func (c *Command) Run(opts *RunOpts) error {
 		cmd.Env = opts.Env
 	}
 
-	cmd.Env = append(
-		cmd.Env,
-		fmt.Sprintf("LC_ALL=%s", DefaultLocale),
-		// avoid prompting for credentials interactively, supported since git v2.3
-		"GIT_TERMINAL_PROMPT=0",
-		// ignore replace references (https://git-scm.com/docs/git-replace)
-		"GIT_NO_REPLACE_OBJECTS=1",
-	)
-
 	process.SetSysProcAttribute(cmd)
+	cmd.Env = append(cmd.Env, CommonGitCmdEnvs()...)
 	cmd.Dir = opts.Dir
 	cmd.Stdout = opts.Stdout
 	cmd.Stderr = opts.Stderr
@@ -184,7 +196,9 @@ func (c *Command) Run(opts *RunOpts) error {
 
 type RunStdError interface {
 	error
+	Unwrap() error
 	Stderr() string
+	IsExitCode(code int) bool
 }
 
 type runStdError struct {
@@ -209,6 +223,14 @@ func (r *runStdError) Stderr() string {
 	return r.stderr
 }
 
+func (r *runStdError) IsExitCode(code int) bool {
+	var exitError *exec.ExitError
+	if errors.As(r.err, &exitError) {
+		return exitError.ExitCode() == code
+	}
+	return false
+}
+
 func bytesToString(b []byte) string {
 	return *(*string)(unsafe.Pointer(&b)) // that's what Golang's strings.Builder.String() does (go/src/strings/builder.go)
 }
diff --git a/modules/git/commit.go b/modules/git/commit.go
index 8c194ef502..99fbbd0cfc 100644
--- a/modules/git/commit.go
+++ b/modules/git/commit.go
@@ -398,11 +398,6 @@ func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
 
 // GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only')
 func (c *Commit) GetBranchName() (string, error) {
-	err := LoadGitVersion()
-	if err != nil {
-		return "", fmt.Errorf("Git version missing: %v", err)
-	}
-
 	args := []string{
 		"name-rev",
 	}
diff --git a/modules/git/git.go b/modules/git/git.go
index 8fad070330..5817bd2c7f 100644
--- a/modules/git/git.go
+++ b/modules/git/git.go
@@ -10,11 +10,13 @@ import (
 	"fmt"
 	"os"
 	"os/exec"
+	"path/filepath"
 	"runtime"
 	"strings"
+	"sync"
 	"time"
 
-	"code.gitea.io/gitea/modules/process"
+	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 
 	"github.com/hashicorp/go-version"
@@ -32,38 +34,33 @@ var (
 	GitExecutable = "git"
 
 	// DefaultContext is the default context to run git commands in
-	// will be overwritten by Init with HammerContext
+	// will be overwritten by InitWithConfigSync with HammerContext
 	DefaultContext = context.Background()
 
-	gitVersion *version.Version
-
 	// SupportProcReceive version >= 2.29.0
 	SupportProcReceive bool
+
+	// initMutex is used to avoid Golang's data race error. see the comments below.
+	initMutex sync.Mutex
+
+	gitVersion *version.Version
 )
 
-// LocalVersion returns current Git version from shell.
-func LocalVersion() (*version.Version, error) {
-	if err := LoadGitVersion(); err != nil {
-		return nil, err
-	}
-	return gitVersion, nil
-}
-
-// LoadGitVersion returns current Git version from shell.
-func LoadGitVersion() error {
+// loadGitVersion returns current Git version from shell. Internal usage only.
+func loadGitVersion() (*version.Version, error) {
 	// doesn't need RWMutex because its exec by Init()
 	if gitVersion != nil {
-		return nil
+		return gitVersion, nil
 	}
 
-	stdout, _, runErr := NewCommand(context.Background(), "version").RunStdString(nil)
+	stdout, _, runErr := NewCommand(DefaultContext, "version").RunStdString(nil)
 	if runErr != nil {
-		return runErr
+		return nil, runErr
 	}
 
 	fields := strings.Fields(stdout)
 	if len(fields) < 3 {
-		return fmt.Errorf("not enough output: %s", stdout)
+		return nil, fmt.Errorf("invalid git version output: %s", stdout)
 	}
 
 	var versionString string
@@ -78,7 +75,7 @@ func LoadGitVersion() error {
 
 	var err error
 	gitVersion, err = version.NewVersion(versionString)
-	return err
+	return gitVersion, err
 }
 
 // SetExecutablePath changes the path of git executable and checks the file permission and version.
@@ -93,7 +90,7 @@ func SetExecutablePath(path string) error {
 	}
 	GitExecutable = absPath
 
-	err = LoadGitVersion()
+	_, err = loadGitVersion()
 	if err != nil {
 		return fmt.Errorf("unable to load git version: %w", err)
 	}
@@ -120,7 +117,10 @@ func SetExecutablePath(path string) error {
 
 // VersionInfo returns git version information
 func VersionInfo() string {
-	format := "Git Version: %s"
+	if gitVersion == nil {
+		return "(git not found)"
+	}
+	format := "%s"
 	args := []interface{}{gitVersion.Original()}
 	// Since git wire protocol has been released from git v2.18
 	if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
@@ -131,8 +131,33 @@ func VersionInfo() string {
 	return fmt.Sprintf(format, args...)
 }
 
-// Init initializes git module
-func Init(ctx context.Context) error {
+// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
+// This method doesn't change anything to filesystem
+func InitSimple(ctx context.Context) error {
+	initMutex.Lock()
+	defer initMutex.Unlock()
+
+	return initSimpleInternal(ctx)
+}
+
+// HomeDir is the home dir for git to store the global config file used by Gitea internally
+func HomeDir() string {
+	if setting.RepoRootPath == "" {
+		// TODO: now, some unit test code call the git module directly without initialization, which is incorrect.
+		// at the moment, we just use a temp HomeDir to prevent from conflicting with user's git config
+		// in the future, the git module should be initialized first before use.
+		tmpHomeDir := filepath.Join(os.TempDir(), "gitea-temp-home")
+		log.Error("Git's HomeDir is empty (RepoRootPath is empty), the git module is not initialized correctly, using a temp HomeDir (%s) temporarily", tmpHomeDir)
+		return tmpHomeDir
+	}
+	return setting.RepoRootPath
+}
+
+func initSimpleInternal(ctx context.Context) error {
+	// at the moment, when running integration tests, the git.InitXxx would be called twice.
+	// one is called by the GlobalInitInstalled, one is called by TestMain.
+	// so the init functions should be protected by a mutex to avoid Golang's data race error.
+
 	DefaultContext = ctx
 
 	if setting.Git.Timeout.Default > 0 {
@@ -146,6 +171,23 @@ func Init(ctx context.Context) error {
 	// force cleanup args
 	globalCommandArgs = []string{}
 
+	return nil
+}
+
+// InitWithConfigSync initializes git module. This method may create directories or write files into filesystem
+func InitWithConfigSync(ctx context.Context) error {
+	initMutex.Lock()
+	defer initMutex.Unlock()
+
+	err := initSimpleInternal(ctx)
+	if err != nil {
+		return err
+	}
+
+	if err = os.MkdirAll(setting.RepoRootPath, os.ModePerm); err != nil {
+		return fmt.Errorf("unable to create directory %s, err: %w", setting.RepoRootPath, err)
+	}
+
 	if CheckGitVersionAtLeast("2.9") == nil {
 		// Explicitly disable credential helper, otherwise Git credentials might leak
 		globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=")
@@ -161,68 +203,71 @@ func Init(ctx context.Context) error {
 		globalCommandArgs = append(globalCommandArgs, "-c", "uploadpack.allowfilter=true", "-c", "uploadpack.allowAnySHA1InWant=true")
 	}
 
-	// Save current git version on init to gitVersion otherwise it would require an RWMutex
-	if err := LoadGitVersion(); err != nil {
-		return err
-	}
-
-	// Git requires setting user.name and user.email in order to commit changes - if they're not set just add some defaults
-	for configKey, defaultValue := range map[string]string{"user.name": "Gitea", "user.email": "gitea@fake.local"} {
-		if err := checkAndSetConfig(configKey, defaultValue, false); err != nil {
+	// 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.
+	// If these values are not really used, then they can be set (overwritten) directly without considering about existence.
+	for configKey, defaultValue := range map[string]string{
+		"user.name":  "Gitea",
+		"user.email": "gitea@fake.local",
+	} {
+		if err := configSetNonExist(configKey, defaultValue); err != nil {
 			return err
 		}
 	}
 
 	// Set git some configurations - these must be set to these values for gitea to work correctly
-	if err := checkAndSetConfig("core.quotePath", "false", true); err != nil {
+	if err := configSet("core.quotePath", "false"); err != nil {
 		return err
 	}
 
 	if CheckGitVersionAtLeast("2.10") == nil {
-		if err := checkAndSetConfig("receive.advertisePushOptions", "true", true); err != nil {
+		if err := configSet("receive.advertisePushOptions", "true"); err != nil {
 			return err
 		}
 	}
 
 	if CheckGitVersionAtLeast("2.18") == nil {
-		if err := checkAndSetConfig("core.commitGraph", "true", true); err != nil {
+		if err := configSet("core.commitGraph", "true"); err != nil {
 			return err
 		}
-		if err := checkAndSetConfig("gc.writeCommitGraph", "true", true); err != nil {
+		if err := configSet("gc.writeCommitGraph", "true"); err != nil {
 			return err
 		}
 	}
 
 	if CheckGitVersionAtLeast("2.29") == nil {
 		// set support for AGit flow
-		if err := checkAndAddConfig("receive.procReceiveRefs", "refs/for"); err != nil {
+		if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil {
 			return err
 		}
 		SupportProcReceive = true
 	} else {
-		if err := checkAndRemoveConfig("receive.procReceiveRefs", "refs/for"); err != nil {
+		if err := configUnsetAll("receive.procReceiveRefs", "refs/for"); err != nil {
 			return err
 		}
 		SupportProcReceive = false
 	}
 
 	if runtime.GOOS == "windows" {
-		if err := checkAndSetConfig("core.longpaths", "true", true); err != nil {
+		if err := configSet("core.longpaths", "true"); err != nil {
+			return err
+		}
+		if setting.Git.DisableCoreProtectNTFS {
+			err = configSet("core.protectNTFS", "false")
+		} else {
+			err = configUnsetAll("core.protectNTFS", "false")
+		}
+		if err != nil {
 			return err
 		}
 	}
-	if setting.Git.DisableCoreProtectNTFS {
-		if err := checkAndSetConfig("core.protectntfs", "false", true); err != nil {
-			return err
-		}
-		globalCommandArgs = append(globalCommandArgs, "-c", "core.protectntfs=false")
-	}
+
 	return nil
 }
 
 // CheckGitVersionAtLeast check git version is at least the constraint version
 func CheckGitVersionAtLeast(atLeast string) error {
-	if err := LoadGitVersion(); err != nil {
+	if _, err := loadGitVersion(); err != nil {
 		return err
 	}
 	atLeastVersion, err := version.NewVersion(atLeast)
@@ -235,75 +280,75 @@ func CheckGitVersionAtLeast(atLeast string) error {
 	return nil
 }
 
-func checkAndSetConfig(key, defaultValue string, forceToDefault bool) error {
-	stdout, stderr, err := process.GetManager().Exec("git.Init(get setting)", GitExecutable, "config", "--get", key)
-	if err != nil {
-		perr, ok := err.(*process.Error)
-		if !ok {
-			return fmt.Errorf("Failed to get git %s(%v) errType %T: %s", key, err, err, stderr)
-		}
-		eerr, ok := perr.Err.(*exec.ExitError)
-		if !ok || eerr.ExitCode() != 1 {
-			return fmt.Errorf("Failed to get git %s(%v) errType %T: %s", key, err, err, stderr)
-		}
+func configSet(key, value string) error {
+	stdout, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil)
+	if err != nil && !err.IsExitCode(1) {
+		return fmt.Errorf("failed to get git config %s, err: %w", key, err)
 	}
 
 	currValue := strings.TrimSpace(stdout)
-
-	if currValue == defaultValue || (!forceToDefault && len(currValue) > 0) {
+	if currValue == value {
 		return nil
 	}
 
-	if _, stderr, err = process.GetManager().Exec(fmt.Sprintf("git.Init(set %s)", key), "git", "config", "--global", key, defaultValue); err != nil {
-		return fmt.Errorf("Failed to set git %s(%s): %s", key, err, stderr)
+	_, _, err = NewCommand(DefaultContext, "config", "--global", key, value).RunStdString(nil)
+	if err != nil {
+		return fmt.Errorf("failed to set git global config %s, err: %w", key, err)
 	}
 
 	return nil
 }
 
-func checkAndAddConfig(key, value string) error {
-	_, stderr, err := process.GetManager().Exec("git.Init(get setting)", GitExecutable, "config", "--get", key, value)
-	if err != nil {
-		perr, ok := err.(*process.Error)
-		if !ok {
-			return fmt.Errorf("Failed to get git %s(%v) errType %T: %s", key, err, err, stderr)
-		}
-		eerr, ok := perr.Err.(*exec.ExitError)
-		if !ok || eerr.ExitCode() != 1 {
-			return fmt.Errorf("Failed to get git %s(%v) errType %T: %s", key, err, err, stderr)
-		}
-		if eerr.ExitCode() == 1 {
-			if _, stderr, err = process.GetManager().Exec(fmt.Sprintf("git.Init(set %s)", key), "git", "config", "--global", "--add", key, value); err != nil {
-				return fmt.Errorf("Failed to set git %s(%s): %s", key, err, stderr)
-			}
-			return nil
+func configSetNonExist(key, value string) error {
+	_, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil)
+	if err == nil {
+		// already exist
+		return nil
+	}
+	if err.IsExitCode(1) {
+		// not exist, set new config
+		_, _, err = NewCommand(DefaultContext, "config", "--global", key, value).RunStdString(nil)
+		if err != nil {
+			return fmt.Errorf("failed to set git global config %s, err: %w", key, err)
 		}
+		return nil
 	}
 
-	return nil
+	return fmt.Errorf("failed to get git config %s, err: %w", key, err)
 }
 
-func checkAndRemoveConfig(key, value string) error {
-	_, stderr, err := process.GetManager().Exec("git.Init(get setting)", GitExecutable, "config", "--get", key, value)
-	if err != nil {
-		perr, ok := err.(*process.Error)
-		if !ok {
-			return fmt.Errorf("Failed to get git %s(%v) errType %T: %s", key, err, err, stderr)
-		}
-		eerr, ok := perr.Err.(*exec.ExitError)
-		if !ok || eerr.ExitCode() != 1 {
-			return fmt.Errorf("Failed to get git %s(%v) errType %T: %s", key, err, err, stderr)
-		}
-		if eerr.ExitCode() == 1 {
-			return nil
-		}
+func configAddNonExist(key, value string) error {
+	_, _, err := NewCommand(DefaultContext, "config", "--fixed-value", "--get", key, value).RunStdString(nil)
+	if err == nil {
+		// already exist
+		return nil
 	}
-
-	if _, stderr, err = process.GetManager().Exec(fmt.Sprintf("git.Init(set %s)", key), "git", "config", "--global", "--unset-all", key, value); err != nil {
-		return fmt.Errorf("Failed to set git %s(%s): %s", key, err, stderr)
+	if err.IsExitCode(1) {
+		// not exist, add new config
+		_, _, err = NewCommand(DefaultContext, "config", "--global", "--add", key, value).RunStdString(nil)
+		if err != nil {
+			return fmt.Errorf("failed to add git global config %s, err: %w", key, err)
+		}
+		return nil
 	}
+	return fmt.Errorf("failed to get git config %s, err: %w", key, err)
+}
 
-	return nil
+func configUnsetAll(key, value string) error {
+	_, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil)
+	if err == nil {
+		// exist, need to remove
+		_, _, err = NewCommand(DefaultContext, "config", "--global", "--fixed-value", "--unset-all", key, value).RunStdString(nil)
+		if err != nil {
+			return fmt.Errorf("failed to unset git global config %s, err: %w", key, err)
+		}
+		return nil
+	}
+	if err.IsExitCode(1) {
+		// not exist
+		return nil
+	}
+	return fmt.Errorf("failed to get git config %s, err: %w", key, err)
 }
 
 // Fsck verifies the connectivity and validity of the objects in the database
diff --git a/modules/git/git_test.go b/modules/git/git_test.go
index c62a55badc..5b1cd820e8 100644
--- a/modules/git/git_test.go
+++ b/modules/git/git_test.go
@@ -8,23 +8,74 @@ import (
 	"context"
 	"fmt"
 	"os"
+	"strings"
 	"testing"
 
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/util"
+
+	"github.com/stretchr/testify/assert"
 )
 
-func fatalTestError(fmtStr string, args ...interface{}) {
-	fmt.Fprintf(os.Stderr, fmtStr, args...)
-	os.Exit(1)
+func testRun(m *testing.M) error {
+	_ = log.NewLogger(1000, "console", "console", `{"level":"trace","stacktracelevel":"NONE","stderr":true}`)
+
+	repoRootPath, err := os.MkdirTemp(os.TempDir(), "repos")
+	if err != nil {
+		return fmt.Errorf("unable to create temp dir: %w", err)
+	}
+	defer util.RemoveAll(repoRootPath)
+	setting.RepoRootPath = repoRootPath
+
+	if err = InitWithConfigSync(context.Background()); err != nil {
+		return fmt.Errorf("failed to call Init: %w", err)
+	}
+
+	exitCode := m.Run()
+	if exitCode != 0 {
+		return fmt.Errorf("run test failed, ExitCode=%d", exitCode)
+	}
+	return nil
 }
 
 func TestMain(m *testing.M) {
-	_ = log.NewLogger(1000, "console", "console", `{"level":"trace","stacktracelevel":"NONE","stderr":true}`)
+	if err := testRun(m); err != nil {
+		_, _ = fmt.Fprintf(os.Stderr, "Test failed: %v", err)
+		os.Exit(1)
+	}
+}
 
-	if err := Init(context.Background()); err != nil {
-		fatalTestError("Init failed: %v", err)
+func TestGitConfig(t *testing.T) {
+	gitConfigContains := func(sub string) bool {
+		if b, err := os.ReadFile(HomeDir() + "/.gitconfig"); err == nil {
+			return strings.Contains(string(b), sub)
+		}
+		return false
 	}
 
-	exitStatus := m.Run()
-	os.Exit(exitStatus)
+	assert.False(t, gitConfigContains("key-a"))
+
+	assert.NoError(t, configSetNonExist("test.key-a", "val-a"))
+	assert.True(t, gitConfigContains("key-a = val-a"))
+
+	assert.NoError(t, configSetNonExist("test.key-a", "val-a-changed"))
+	assert.False(t, gitConfigContains("key-a = val-a-changed"))
+
+	assert.NoError(t, configSet("test.key-a", "val-a-changed"))
+	assert.True(t, gitConfigContains("key-a = val-a-changed"))
+
+	assert.NoError(t, configAddNonExist("test.key-b", "val-b"))
+	assert.True(t, gitConfigContains("key-b = val-b"))
+
+	assert.NoError(t, configAddNonExist("test.key-b", "val-2b"))
+	assert.True(t, gitConfigContains("key-b = val-b"))
+	assert.True(t, gitConfigContains("key-b = val-2b"))
+
+	assert.NoError(t, configUnsetAll("test.key-b", "val-b"))
+	assert.False(t, gitConfigContains("key-b = val-b"))
+	assert.True(t, gitConfigContains("key-b = val-2b"))
+
+	assert.NoError(t, configUnsetAll("test.key-b", "val-2b"))
+	assert.False(t, gitConfigContains("key-b = val-2b"))
 }
diff --git a/modules/git/lfs.go b/modules/git/lfs.go
index cdd9d15b2e..c5d8354b6d 100644
--- a/modules/git/lfs.go
+++ b/modules/git/lfs.go
@@ -18,12 +18,6 @@ func CheckLFSVersion() {
 	if setting.LFS.StartServer {
 		// Disable LFS client hooks if installed for the current OS user
 		// Needs at least git v2.1.2
-
-		err := LoadGitVersion()
-		if err != nil {
-			logger.Fatal("Error retrieving git version: %v", err)
-		}
-
 		if CheckGitVersionAtLeast("2.1.2") != nil {
 			setting.LFS.StartServer = false
 			logger.Error("LFS server support needs at least Git v2.1.2")
diff --git a/modules/git/remote.go b/modules/git/remote.go
index 536b1681ce..b2a2e6d7ab 100644
--- a/modules/git/remote.go
+++ b/modules/git/remote.go
@@ -11,10 +11,6 @@ import (
 
 // GetRemoteAddress returns the url of a specific remote of the repository.
 func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (*url.URL, error) {
-	err := LoadGitVersion()
-	if err != nil {
-		return nil, err
-	}
 	var cmd *Command
 	if CheckGitVersionAtLeast("2.7") == nil {
 		cmd = NewCommand(ctx, "remote", "get-url", remoteName)
diff --git a/modules/git/repo_attribute.go b/modules/git/repo_attribute.go
index a18c80c3f1..38818788f3 100644
--- a/modules/git/repo_attribute.go
+++ b/modules/git/repo_attribute.go
@@ -28,11 +28,6 @@ type CheckAttributeOpts struct {
 
 // CheckAttribute return the Blame object of file
 func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[string]string, error) {
-	err := LoadGitVersion()
-	if err != nil {
-		return nil, fmt.Errorf("git version missing: %v", err)
-	}
-
 	env := []string{}
 
 	if len(opts.IndexFile) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
@@ -126,7 +121,7 @@ type CheckAttributeReader struct {
 	cancel      context.CancelFunc
 }
 
-// Init initializes the cmd
+// Init initializes the CheckAttributeReader
 func (c *CheckAttributeReader) Init(ctx context.Context) error {
 	cmdArgs := []string{"check-attr", "--stdin", "-z"}
 
diff --git a/modules/git/repo_tree.go b/modules/git/repo_tree.go
index 3e7a9c2cfb..2e139daddf 100644
--- a/modules/git/repo_tree.go
+++ b/modules/git/repo_tree.go
@@ -24,11 +24,6 @@ type CommitTreeOpts struct {
 
 // CommitTree creates a commit from a given tree id for the user with provided message
 func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opts CommitTreeOpts) (SHA1, error) {
-	err := LoadGitVersion()
-	if err != nil {
-		return SHA1{}, err
-	}
-
 	commitTimeStr := time.Now().Format(time.RFC3339)
 
 	// Because this may call hooks we should pass in the environment
@@ -60,14 +55,13 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
 
 	stdout := new(bytes.Buffer)
 	stderr := new(bytes.Buffer)
-	err = cmd.Run(&RunOpts{
+	err := cmd.Run(&RunOpts{
 		Env:    env,
 		Dir:    repo.Path,
 		Stdin:  messageBytes,
 		Stdout: stdout,
 		Stderr: stderr,
 	})
-
 	if err != nil {
 		return SHA1{}, ConcatenateError(err, stderr.String())
 	}
diff --git a/modules/indexer/stats/indexer_test.go b/modules/indexer/stats/indexer_test.go
index 9d9de5413c..a335972c21 100644
--- a/modules/indexer/stats/indexer_test.go
+++ b/modules/indexer/stats/indexer_test.go
@@ -30,7 +30,7 @@ func TestMain(m *testing.M) {
 }
 
 func TestRepoStatsIndex(t *testing.T) {
-	if err := git.Init(context.Background()); !assert.NoError(t, err) {
+	if err := git.InitWithConfigSync(context.Background()); !assert.NoError(t, err) {
 		return
 	}
 
diff --git a/modules/repository/init.go b/modules/repository/init.go
index f8c7a89552..285fe81dbe 100644
--- a/modules/repository/init.go
+++ b/modules/repository/init.go
@@ -317,11 +317,6 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
 		return fmt.Errorf("git add --all: %v", err)
 	}
 
-	err = git.LoadGitVersion()
-	if err != nil {
-		return fmt.Errorf("Unable to get git version: %v", err)
-	}
-
 	args := []string{
 		"commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
 		"-m", "Initial commit",
diff --git a/modules/util/path.go b/modules/util/path.go
index ed7cc62699..0ccc7a1dc2 100644
--- a/modules/util/path.go
+++ b/modules/util/path.go
@@ -182,6 +182,7 @@ func FileURLToPath(u *url.URL) (string, error) {
 // it returns error when the variable does not exist.
 func HomeDir() (home string, err error) {
 	// TODO: some users run Gitea with mismatched uid  and "HOME=xxx" (they set HOME=xxx by environment manually)
+	// TODO: when running gitea as a sub command inside git, the HOME directory is not the user's home directory
 	// so at the moment we can not use `user.Current().HomeDir`
 	if isOSWindows() {
 		home = os.Getenv("USERPROFILE")
diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini
index ab59766d6c..5dafbfdb16 100644
--- a/options/locale/locale_cs-CZ.ini
+++ b/options/locale/locale_cs-CZ.ini
@@ -694,7 +694,6 @@ generate_token_success=Váše nová poukázka byla vytvořena. Zkopírujte ji ny
 generate_token_name_duplicate=<strong>%s</strong> byl již použit jako název aplikace. Použijte prosím nový.
 delete_token=Smazat
 access_token_deletion=Odstranit přístupovou poukázku
-access_token_deletion_desc=Smazání poukázky zruší přístup k vašemu účtu pro aplikace, které ji používají. Pokračovat?
 delete_token_success=Poukázka byla odstraněna. Aplikace, které ji používají již nemají přístup k vašemu účtu.
 
 manage_oauth2_applications=Spravovat OAuth2 aplikace
diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini
index d15b2104b8..442168cf69 100644
--- a/options/locale/locale_de-DE.ini
+++ b/options/locale/locale_de-DE.ini
@@ -704,7 +704,6 @@ generate_token_success=Ein neuer Token wurde generiert. Kopiere diesen, da er ni
 generate_token_name_duplicate=<strong>%s</strong> wurde bereits als Anwendungsname verwendet. Bitte wähle einen neuen Namen.
 delete_token=Löschen
 access_token_deletion=Zugriffstoken löschen
-access_token_deletion_desc=Wenn du ein Token löschst, haben die Anwendungen, die es nutzen keinen Zugriff mehr auf deinen Account. Fortfahren?
 delete_token_success=Der Zugriffstoken wurde gelöscht. Anwendungen die diesen Token genutzt haben, haben nun keinen Zugriff mehr auf deinen Account.
 
 manage_oauth2_applications=OAuth2 Anwendungen verwalten
diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini
index 7459576914..0a5cbbd3e1 100644
--- a/options/locale/locale_el-GR.ini
+++ b/options/locale/locale_el-GR.ini
@@ -716,7 +716,6 @@ generate_token_success=Το νέο διακριτικό σας έχει δημι
 generate_token_name_duplicate=Το <strong>%s</strong> έχει ήδη χρησιμοποιηθεί ως όνομα εφαρμογής. Παρακαλούμε χρησιμοποιήστε ένα νέο.
 delete_token=Διαγραφή
 access_token_deletion=Διαγραφή Διακριτικού Πρόσβασης
-access_token_deletion_desc=Η διαγραφή ενός διακριτικού θα ανακαλέσει την πρόσβαση στο λογαριασμό σας για εφαρμογές που χρησιμοποιούν αυτό. Συνέχεια;
 delete_token_success=Το διακριτικό έχει διαγραφεί. Οι εφαρμογές που το χρησιμοποιούν δεν έχουν πλέον πρόσβαση στο λογαριασμό σας.
 
 manage_oauth2_applications=Διαχείριση Εφαρμογών Oauth2
diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini
index eba3a8c1f2..5c98da03b9 100644
--- a/options/locale/locale_es-ES.ini
+++ b/options/locale/locale_es-ES.ini
@@ -707,7 +707,6 @@ generate_token_success=Su nuevo token ha sido generado. Cópielo ahora, ya que n
 generate_token_name_duplicate=<strong>%s</strong> ya se ha utilizado como nombre de la aplicación. Por favor, utilice una nueva.
 delete_token=Eliminar
 access_token_deletion=Eliminar Token de Acceso
-access_token_deletion_desc=Eliminando un token revocará el acceso a su cuenta para todas las aplicaciones que lo usen. ¿Continuar?
 delete_token_success=El token ha sido eliminado. Las aplicaciones que lo usen ya no tienen acceso a su cuenta.
 
 manage_oauth2_applications=Administrar aplicaciones OAuth2
diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini
index ef34c0e842..a465c00145 100644
--- a/options/locale/locale_fa-IR.ini
+++ b/options/locale/locale_fa-IR.ini
@@ -661,7 +661,6 @@ generate_token_success=اکنون token جدید ساخته شد. همینک آ
 generate_token_name_duplicate=<strong>%s</strong> قبلا بعنوان نام یک برنامه استفاده شده است. لطفا از یک نام دیگر استفاده کنید.
 delete_token=حذف
 access_token_deletion=حذف توکن
-access_token_deletion_desc=حذف token باعث از کار افتادن تمامی برنامه‎هایی که در آنها به کار رفته می‎شود. آیا ادامه می‎دهید؟
 delete_token_success=token مورد نظر حذف شد. برنامه هایی که از آن استفاده می‎کنند به زودی دسترسی به حساب کاربری شما را از دست می‎دهند.
 
 manage_oauth2_applications=مدیریت برنامه‎های OAuth2 
diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini
index 66de56c3e6..4ee872901c 100644
--- a/options/locale/locale_fr-FR.ini
+++ b/options/locale/locale_fr-FR.ini
@@ -628,7 +628,6 @@ generate_token_success=Votre nouveau jeton a été généré. Copiez-le maintena
 generate_token_name_duplicate=<strong>%s</strong> a déjà été utilisé comme nom d'application. Veuillez en utiliser un nouveau.
 delete_token=Supprimer
 access_token_deletion=Suppression de jetons d'accès
-access_token_deletion_desc=Supprimer un jeton révoquera l'accès à votre compte pour toutes les applications l'utilisant. Continuer ?
 delete_token_success=Ce jeton a été supprimé. Les applications l'utilisant n'ont plus accès à votre compte.
 
 manage_oauth2_applications=Gérer les applications OAuth2
diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini
index a523e1e8cf..790f11faf5 100644
--- a/options/locale/locale_hu-HU.ini
+++ b/options/locale/locale_hu-HU.ini
@@ -514,7 +514,6 @@ generate_token_success=Új token létrehozva. Másold le most, mivel többször
 generate_token_name_duplicate=A <strong>%s</strong> nevet már használja egy alkalmazás. Válassz kérlek más nevet.
 delete_token=Törlés
 access_token_deletion=Hozzáférési Token Törlése
-access_token_deletion_desc=Egy token törlésével visszavonja a hozzáférést a fiókjához az ezt használó alkalmazásoktól. Folytatja?
 delete_token_success=A token törölve lett. Az ezt használó alkalmazásoknak többé nincs hozzáférése a fiókodhoz.
 
 manage_oauth2_applications=OAuth2 alkalmazások kezelése
diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini
index 77df83eb38..8d49ce2ac1 100644
--- a/options/locale/locale_id-ID.ini
+++ b/options/locale/locale_id-ID.ini
@@ -505,7 +505,6 @@ generate_token=Hasilkan Token
 generate_token_success=Token baru Anda telah dibuat. Salin sekarang oleh karena tidak akan ditampilkan lagi.
 delete_token=Hapus
 access_token_deletion=Hapus Token Akses
-access_token_deletion_desc=Menghapus token akan mencabut akses ke akun Anda bagi aplikasi yang menggunakannya. Lanjutkan?
 delete_token_success=Token telah dihapus. Aplikasi yang menggunakannya tidak lagi memiliki akses ke akun Anda.
 
 manage_oauth2_applications=Kelola Aplikasi OAuth2
diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini
index bda0e8e7b5..0335f10449 100644
--- a/options/locale/locale_it-IT.ini
+++ b/options/locale/locale_it-IT.ini
@@ -568,7 +568,6 @@ generate_token_success=Il nuovo token è stato generato. Copia ora in quanto non
 generate_token_name_duplicate=<strong>%s</strong> è già stato utilizzato come nome dell'applicazione. Si prega di usarne uno nuovo.
 delete_token=Elimina
 access_token_deletion=Elimina token di accesso
-access_token_deletion_desc=Eliminare un token revocherà l'accesso al tuo account alle applicazioni che lo utilizzano. Continuare?
 delete_token_success=Il token è stato eliminato. Le applicazioni che lo utilizzavano non hanno più accesso al tuo account.
 
 manage_oauth2_applications=Gestisci applicazioni OAuth2
diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index de8f989b6e..3699ac37b3 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -716,7 +716,6 @@ generate_token_success=あなたの新しいトークンを生成しました。
 generate_token_name_duplicate=アプリケーション名 <strong>%s</strong> は既に使われています。他の名前を使用してください。
 delete_token=削除
 access_token_deletion=アクセストークンの削除
-access_token_deletion_desc=トークンを削除すると、それを使用しているアプリケーションは、アカウントへのアクセスができなくなります。 続行しますか?
 delete_token_success=トークンを削除しました。 削除したトークンを使用しているアプリケーションは、今後あなたのアカウントにアクセスできません。
 
 manage_oauth2_applications=OAuth2アプリケーションの管理
diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini
index f5d32e7793..625c31a5c7 100644
--- a/options/locale/locale_ko-KR.ini
+++ b/options/locale/locale_ko-KR.ini
@@ -487,7 +487,6 @@ generate_token=토큰 생성
 generate_token_success=새로운 토큰이 생성되었습니다. 이 토큰은 다시 보이지 않으니 지금 복사하십시오.
 delete_token=삭제
 access_token_deletion=액세스 토큰 삭제
-access_token_deletion_desc=토큰을 삭제하면 해당 토큰을 이용하는 어플리케이션으로부터 귀하의 계정에 대한 접근권한이 사라지게 됩니다. 계속하시겠습니까?
 delete_token_success=토큰이 삭제되었습니다. 해당 토큰을 사용하는 어플리케이션은 더 이상 이 계정으로 접근할 수 없습니다.
 
 manage_oauth2_applications=OAuth2 애플리케이션 관리
diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini
index 41b55f66de..537c04d3d8 100644
--- a/options/locale/locale_lv-LV.ini
+++ b/options/locale/locale_lv-LV.ini
@@ -695,7 +695,6 @@ generate_token_success=Piekļuves talons tika veiksmīgi uzģenerēts! Nokopēji
 generate_token_name_duplicate=Jau eksistē lietotne ar nosaukumu <strong>%s</strong>. Izmantojiet citu nosaukumu.
 delete_token=Dzēst
 access_token_deletion=Dzēst piekļuves talonu
-access_token_deletion_desc=Dzēšot piekļuves talonu tiks liegta piekļuve visām aplikācijām, kas to izmanto. Vai turpināt?
 delete_token_success=Piekļuves talons tika noņemts. Neaizmirstiet atjaunot informāciju lietojumprogrammās, kas izmantoja šo talonu.
 
 manage_oauth2_applications=Pārvaldīt OAuth2 lietotnes
diff --git a/options/locale/locale_ml-IN.ini b/options/locale/locale_ml-IN.ini
index 64638f5073..ece816f891 100644
--- a/options/locale/locale_ml-IN.ini
+++ b/options/locale/locale_ml-IN.ini
@@ -475,7 +475,6 @@ generate_token=ടോക്കൺ സൃഷ്‌ടിക്കുക
 generate_token_success=നിങ്ങളുടെ പുതിയ ടോക്കൺ ജനറേറ്റുചെയ്‌തു. ഇത് വീണ്ടും കാണിക്കാത്തതിനാൽ ഇപ്പോൾ തന്നെ പകർത്തുക.
 delete_token=നീക്കം ചെയ്യുക
 access_token_deletion=ആക്‌സസ്സ് ടോക്കണ്‍ നീക്കം ചെയ്യുക
-access_token_deletion_desc=ഒരു ടോക്കൺ ഇല്ലാതാക്കുന്നത് നിങ്ങളുടെ അക്കൗണ്ട് ഉപയോഗിക്കുന്ന അപ്ലിക്കേഷനുകൾക്കുള്ള പ്രവേശനം അസാധുവാക്കും. തുടരട്ടേ?
 delete_token_success=ടോക്കൺ ഇല്ലാതാക്കി. ഇനി ഇത് ഉപയോഗിക്കുന്ന അപ്ലിക്കേഷനുകൾക്ക് നിങ്ങളുടെ അക്കൌണ്ടിലേക്ക് പ്രവേശനം ഉണ്ടാകില്ല.
 
 manage_oauth2_applications=OAuth2 അപ്ലിക്കേഷനുകൾ നിയന്ത്രിക്കുക
diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini
index 516d31bee1..efdd4e632b 100644
--- a/options/locale/locale_nl-NL.ini
+++ b/options/locale/locale_nl-NL.ini
@@ -590,7 +590,6 @@ generate_token_success=Je nieuwe token is gegenereerd. Kopieer hem nu, want hij
 generate_token_name_duplicate=<strong>%s</strong> is al gebruikt als een applicatienaam. Gebruik een nieuwe.
 delete_token=Verwijderen
 access_token_deletion=Verwijder toegangstoken
-access_token_deletion_desc=Als je een token verwijdert, heeft de applicatie die hem gebruikt geen toegang meer tot je account. Doorgaan?
 delete_token_success=De token is verwijderd. Applicaties die hem gebruiken, verliezen toegang tot je account.
 
 manage_oauth2_applications=Beheer OAuth2-applicaties
diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini
index 0c1ca297cb..db96b31126 100644
--- a/options/locale/locale_pl-PL.ini
+++ b/options/locale/locale_pl-PL.ini
@@ -675,7 +675,6 @@ generate_token_success=Twój nowy token został wygenerowany. Skopiuj go teraz,
 generate_token_name_duplicate=<strong>%s</strong> istnieje już jako nazwa aplikacji. Użyj nowej.
 delete_token=Usuń
 access_token_deletion=Usuń token dostępu
-access_token_deletion_desc=Usunięcie tokenu unieważni dostęp do Twojego konta aplikacjom, które z niego korzystały. Kontynuować?
 delete_token_success=Token został usunięty. Aplikacje używające go nie będą miały już dostępu do Twojego konta.
 
 manage_oauth2_applications=Zarządzaj aplikacjami OAuth2
diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini
index 20a4478b75..7094e07347 100644
--- a/options/locale/locale_pt-BR.ini
+++ b/options/locale/locale_pt-BR.ini
@@ -716,7 +716,6 @@ generate_token_success=Seu novo token foi gerado. Copie-o agora, pois ele não s
 generate_token_name_duplicate=<strong>%s</strong> já foi usado como um nome de aplicativo. Por favor, use outro.
 delete_token=Excluir
 access_token_deletion=Excluir token de acesso
-access_token_deletion_desc=A exclusão de um token revoga o acesso à sua conta para aplicativos que o usam. Continuar?
 delete_token_success=O token foi excluído. Os aplicativos que o utilizam já não têm acesso à sua conta.
 
 manage_oauth2_applications=Gerenciar aplicativos OAuth2
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 301b596f5b..e675de4bae 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -716,7 +716,9 @@ generate_token_success=O seu novo código foi gerado. Copie-o agora porque não
 generate_token_name_duplicate=<strong>%s</strong> já foi usado como nome de uma aplicação. Tente outro.
 delete_token=Eliminar
 access_token_deletion=Eliminar código de acesso
-access_token_deletion_desc=Eliminar um código revoga o acesso à sua conta nas aplicações que o usem. Quer continuar?
+access_token_deletion_cancel_action=Cancelar
+access_token_deletion_confirm_action=Eliminar
+access_token_deletion_desc=Eliminar um código revoga o acesso à sua conta nas aplicações que o usem. Esta operação não poderá ser revertida. Quer continuar?
 delete_token_success=O código foi eliminado. Aplicações que o usavam deixaram de ter acesso à sua conta.
 
 manage_oauth2_applications=Gerir aplicações OAuth2
diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini
index 62399104dd..2d99b043f9 100644
--- a/options/locale/locale_ru-RU.ini
+++ b/options/locale/locale_ru-RU.ini
@@ -710,7 +710,6 @@ generate_token_success=Новый токен создан. Скопируйте
 generate_token_name_duplicate=<strong>%s</strong> уже используется в качестве имени приложения. Пожалуйста, используйте другое имя.
 delete_token=Удалить
 access_token_deletion=Удалить токен доступа
-access_token_deletion_desc=Удаление токена отменит доступ к вашей учетной записи для приложений, использующих его. Продолжить?
 delete_token_success=Токен удалён. Приложения, использующие его, больше не имеют доступа к вашему аккаунту.
 
 manage_oauth2_applications=Управление приложениями OAuth2
diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini
index 70463997d3..460d61c7c4 100644
--- a/options/locale/locale_si-LK.ini
+++ b/options/locale/locale_si-LK.ini
@@ -643,7 +643,6 @@ generate_token_success=ඔබේ නව ටෝකනය ජනනය කර ඇ
 generate_token_name_duplicate=<strong>%s</strong> දැනටමත් යෙදුම් නාමයක් ලෙස භාවිතා කර ඇත. කරුණාකර අලුත් එකක් භාවිතා කරන්න.
 delete_token=මකන්න
 access_token_deletion=ප්රවේශ ටෝකනය මකන්න
-access_token_deletion_desc=ටෝකනයක් මකා දැමීමෙන් එය භාවිතා කරන යෙදුම් සඳහා ඔබගේ ගිණුමට ප්රවේශය අවලංගු වේ. දිගටම?
 delete_token_success=ටෝකනය මකා දමා ඇත. එය භාවිතා කරන යෙදුම් තවදුරටත් ඔබගේ ගිණුමට ප්රවේශය නොමැත.
 
 manage_oauth2_applications=OUTU2 යෙදුම් කළමනාකරණය කරන්න
diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini
index 831607e80f..1afca6b778 100644
--- a/options/locale/locale_sv-SE.ini
+++ b/options/locale/locale_sv-SE.ini
@@ -547,7 +547,6 @@ generate_token_success=Din nya token har genererats. Kopiera nu då den inte kom
 generate_token_name_duplicate=<strong>%s</strong> finns redan som programnamn. Välj ett annat.
 delete_token=Radera
 access_token_deletion=Ta bort åtkomst token
-access_token_deletion_desc=Borttagning utav en token kommer återkalla åtkomst till ditt konto för de applikationer som använder den. Vill du fortsätta?
 delete_token_success=Token har tagits bort. Applikationer som använder den kommer inte längre ha åtkomst till ditt konto.
 
 manage_oauth2_applications=Hantera OAuth2-applikationer
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index 7950b3b63f..6cabad7a69 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -649,7 +649,6 @@ generate_token_success=Yeni bir jeton oluşturuldu. Tekrar gösterilmeyeceği i
 generate_token_name_duplicate=<strong>%s</strong> zaten bir uygulama adı olarak kullanılmış. Lütfen yeni bir tane kullanın.
 delete_token=Sil
 access_token_deletion=Erişim Jetonunu Sil
-access_token_deletion_desc=Bir jetonu silmek, onu kullanan uygulamalar için hesabınıza erişimi iptal eder. Devam edilsin mi?
 delete_token_success=Jeton silindi. Onu kullanan uygulamalar artık hesabınıza erişemez.
 
 manage_oauth2_applications=OAuth2 Uygulamalarını Yönet
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index e7b21a02bf..791a3bc105 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -669,7 +669,6 @@ generate_token_success=Ваш новий токен був створений. 
 generate_token_name_duplicate=Назва програми <strong>%s</strong> вже використовується. Будь ласка, використайте нову.
 delete_token=Видалити
 access_token_deletion=Видалити токен доступу
-access_token_deletion_desc=Видалення токена скасовує доступ до вашого облікового запису для програм, що використовують його. Продовжити?
 delete_token_success=Токен був знищений. Програми, що використовують його, більше не мають доступу до вашого облікового запису.
 
 manage_oauth2_applications=Керування програмами OAuth2
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 65eb3b2c56..0f28fa8f34 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -717,7 +717,6 @@ generate_token_success=新令牌生成成功。请拷贝因为令牌将只会显
 generate_token_name_duplicate=<strong>%s</strong> 已被用作应用程序名称。请使用一个新的名称。
 delete_token=删除令牌
 access_token_deletion=删除 Access Token
-access_token_deletion_desc=删除 Access Token 将会阻止其他应用程序通过它访问你账号。是否继续?
 delete_token_success=令牌已经被删除。使用该令牌的应用将不再能够访问你的账号。
 
 manage_oauth2_applications=管理 OAuth2 应用程序
diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini
index f51ea14712..ef478990f5 100644
--- a/options/locale/locale_zh-TW.ini
+++ b/options/locale/locale_zh-TW.ini
@@ -715,7 +715,6 @@ generate_token_success=已經產生新的 Token。請立刻複製它,因為他
 generate_token_name_duplicate=應用程式名稱 <strong>%s</strong> 已被使用,請換一個試試。
 delete_token=刪除
 access_token_deletion=刪除 Access Token
-access_token_deletion_desc=刪除 Token 後,使用此 Token 的應用程式將無法再存取您的帳戶。是否繼續?
 delete_token_success=已刪除 Token。使用此 Token 的應用程式無法再存取您的帳戶。
 
 manage_oauth2_applications=管理 OAuth2 應用程式
diff --git a/routers/init.go b/routers/init.go
index 6036499362..9b6a770f27 100644
--- a/routers/init.go
+++ b/routers/init.go
@@ -102,8 +102,8 @@ func GlobalInitInstalled(ctx context.Context) {
 		log.Fatal("Gitea is not installed")
 	}
 
-	mustInitCtx(ctx, git.Init)
-	log.Info(git.VersionInfo())
+	mustInitCtx(ctx, git.InitWithConfigSync)
+	log.Info("Git Version: %s (home: %s)", git.VersionInfo(), git.HomeDir())
 
 	git.CheckLFSVersion()
 	log.Info("AppPath: %s", setting.AppPath)
diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go
index 78347e67c4..24c07b5c1c 100644
--- a/routers/web/admin/admin.go
+++ b/routers/web/admin/admin.go
@@ -247,9 +247,8 @@ func Config(ctx *context.Context) {
 	ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
 	ctx.Data["RunUser"] = setting.RunUser
 	ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode)
-	if version, err := git.LocalVersion(); err == nil {
-		ctx.Data["GitVersion"] = version.Original()
-	}
+	ctx.Data["GitVersion"] = git.VersionInfo()
+
 	ctx.Data["RepoRootPath"] = setting.RepoRootPath
 	ctx.Data["CustomRootPath"] = setting.CustomPath
 	ctx.Data["StaticRootPath"] = setting.StaticRootPath
diff --git a/routers/web/repo/lfs.go b/routers/web/repo/lfs.go
index 7c2ff1cfae..e2421f1389 100644
--- a/routers/web/repo/lfs.go
+++ b/routers/web/repo/lfs.go
@@ -421,12 +421,9 @@ func LFSPointerFiles(ctx *context.Context) {
 		return
 	}
 	ctx.Data["PageIsSettingsLFS"] = true
-	err := git.LoadGitVersion()
-	if err != nil {
-		log.Fatal("Error retrieving git version: %v", err)
-	}
 	ctx.Data["LFSFilesLink"] = ctx.Repo.RepoLink + "/settings/lfs"
 
+	var err error
 	err = func() error {
 		pointerChan := make(chan lfs.PointerBlob)
 		errChan := make(chan error, 1)
diff --git a/services/pull/merge.go b/services/pull/merge.go
index fcced65cdf..76798d73ed 100644
--- a/services/pull/merge.go
+++ b/services/pull/merge.go
@@ -223,12 +223,6 @@ func Merge(ctx context.Context, pr *models.PullRequest, doer *user_model.User, b
 
 // rawMerge perform the merge operation without changing any pull information in database
 func rawMerge(ctx context.Context, pr *models.PullRequest, doer *user_model.User, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string) (string, error) {
-	err := git.LoadGitVersion()
-	if err != nil {
-		log.Error("git.LoadGitVersion: %v", err)
-		return "", fmt.Errorf("Unable to get git version: %v", err)
-	}
-
 	// Clone base repo.
 	tmpBasePath, err := createTemporaryRepo(ctx, pr)
 	if err != nil {
diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go
index 9c7d9aafec..97a80a96bd 100644
--- a/services/repository/files/temp_repo.go
+++ b/services/repository/files/temp_repo.go
@@ -229,11 +229,6 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, co
 	authorSig := author.NewGitSig()
 	committerSig := committer.NewGitSig()
 
-	err := git.LoadGitVersion()
-	if err != nil {
-		return "", fmt.Errorf("Unable to get git version: %v", err)
-	}
-
 	// Because this may call hooks we should pass in the environment
 	env := append(os.Environ(),
 		"GIT_AUTHOR_NAME="+authorSig.Name,