mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-27 00:23:41 +09:00 
			
		
		
		
	Refactor git module, make Gitea use internal git config (#19732)
* Refactor git module, make Gitea use internal git config, add safe.directory config * introduce git.InitSimple and git.InitWithConfigSync, make serv cmd use gitconfig * use HOME instead of GIT_CONFIG_GLOBAL, because git always needs a correct HOME * fix cmd env in cmd/serv.go * fine tune error message * Fix a incorrect test case * fix configAddNonExist * fix configAddNonExist logic, add `--fixed-value` flag, add tests * add configSetNonExist function in case it's needed. * use configSetNonExist for `user.name` and `user.email` * add some comments * Update cmd/serv.go Co-authored-by: zeripath <art27@cantab.net> * Update cmd/serv.go Co-authored-by: zeripath <art27@cantab.net> * Update modules/git/git.go Co-authored-by: zeripath <art27@cantab.net> * Update modules/setting/setting.go Co-authored-by: zeripath <art27@cantab.net> * Update modules/git/repo_attribute.go Co-authored-by: zeripath <art27@cantab.net> * fix spaces in messages * use `configSet("core.protectNTFS", ...)` instead of `globalCommandArgs` * remove GIT_CONFIG_NOSYSTEM, continue to use system's git config * Update cmd/serv.go Co-authored-by: zeripath <art27@cantab.net> * fix merge * remove code for safe.directory * separate git.CommonEnvs to CommonGitCmdEnvs and CommonCmdServEnvs * avoid Golang's data race error Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
		
							
								
								
									
										60
									
								
								cmd/serv.go
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								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) | ||||
| 	} | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
| } | ||||
|   | ||||
| @@ -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", | ||||
| 	} | ||||
|   | ||||
| @@ -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 { | ||||
| 		if err := checkAndSetConfig("core.protectntfs", "false", true); err != nil { | ||||
| 			err = configSet("core.protectNTFS", "false") | ||||
| 		} else { | ||||
| 			err = configUnsetAll("core.protectNTFS", "false") | ||||
| 		} | ||||
| 		if 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) | ||||
| 			} | ||||
| 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) | ||||
| 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 err.IsExitCode(1) { | ||||
| 		// not exist, add new config | ||||
| 		_, _, err = NewCommand(DefaultContext, "config", "--global", "--add", key, value).RunStdString(nil) | ||||
| 		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) | ||||
| 			return fmt.Errorf("failed to add git global config %s, err: %w", key, err) | ||||
| 		} | ||||
| 		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 | ||||
| 	} | ||||
| 	} | ||||
| 	return fmt.Errorf("failed to get git config %s, err: %w", key, err) | ||||
| } | ||||
|  | ||||
| 	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) | ||||
| 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 | ||||
|   | ||||
| @@ -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")) | ||||
| } | ||||
|   | ||||
| @@ -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") | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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"} | ||||
|  | ||||
|   | ||||
| @@ -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()) | ||||
| 	} | ||||
|   | ||||
| @@ -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 | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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") | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user