mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 02:46:04 +01:00 
			
		
		
		
	Refactor git.Command.Run*, introduce RunWithContextString and RunWithContextBytes (#19266)
				
					
				
			This follows * https://github.com/go-gitea/gitea/issues/18553 Introduce `RunWithContextString` and `RunWithContextBytes` to help the refactoring. Add related unit tests. They keep the same behavior to save stderr into err.Error() as `RunInXxx` before. Remove `RunInDirTimeoutPipeline` `RunInDirTimeoutFullPipeline` `RunInDirTimeout` `RunInDirTimeoutEnv` `RunInDirPipeline` `RunInDirFullPipeline` `RunTimeout`, `RunInDirTimeoutEnvPipeline`, `RunInDirTimeoutEnvFullPipeline`, `RunInDirTimeoutEnvFullPipelineFunc`. Then remaining `RunInDir` `RunInDirBytes` `RunInDirWithEnv` can be easily refactored in next PR with a simple search & replace: * before: `stdout, err := RunInDir(path)` * next: `stdout, _, err := RunWithContextString(&git.RunContext{Dir:path})` Other changes: 1. When `timeout <= 0`, use default. Because `timeout==0` is meaningless and could cause bugs. And now many functions becomes more simple, eg: `GitGcRepos` 9 lines to 1 line. `Fsck` 6 lines to 1 line. 2. Only set defaultCommandExecutionTimeout when the option `setting.Git.Timeout.Default > 0`
This commit is contained in:
		| @@ -14,6 +14,7 @@ import ( | |||||||
| 	"os/exec" | 	"os/exec" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  | 	"unsafe" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/process" | 	"code.gitea.io/gitea/modules/process" | ||||||
| @@ -93,32 +94,6 @@ func (c *Command) AddArguments(args ...string) *Command { | |||||||
| 	return c | 	return c | ||||||
| } | } | ||||||
|  |  | ||||||
| // RunInDirTimeoutEnvPipeline executes the command in given directory with given timeout, |  | ||||||
| // it pipes stdout and stderr to given io.Writer. |  | ||||||
| func (c *Command) RunInDirTimeoutEnvPipeline(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer) error { |  | ||||||
| 	return c.RunInDirTimeoutEnvFullPipeline(env, timeout, dir, stdout, stderr, nil) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // RunInDirTimeoutEnvFullPipeline executes the command in given directory with given timeout, |  | ||||||
| // it pipes stdout and stderr to given io.Writer and passes in an io.Reader as stdin. |  | ||||||
| func (c *Command) RunInDirTimeoutEnvFullPipeline(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer, stdin io.Reader) error { |  | ||||||
| 	return c.RunInDirTimeoutEnvFullPipelineFunc(env, timeout, dir, stdout, stderr, stdin, nil) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // RunInDirTimeoutEnvFullPipelineFunc executes the command in given directory with given timeout, |  | ||||||
| // it pipes stdout and stderr to given io.Writer and passes in an io.Reader as stdin. Between cmd.Start and cmd.Wait the passed in function is run. |  | ||||||
| func (c *Command) RunInDirTimeoutEnvFullPipelineFunc(env []string, timeout time.Duration, dir string, stdout, stderr io.Writer, stdin io.Reader, fn func(context.Context, context.CancelFunc) error) error { |  | ||||||
| 	return c.RunWithContext(&RunContext{ |  | ||||||
| 		Env:          env, |  | ||||||
| 		Timeout:      timeout, |  | ||||||
| 		Dir:          dir, |  | ||||||
| 		Stdout:       stdout, |  | ||||||
| 		Stderr:       stderr, |  | ||||||
| 		Stdin:        stdin, |  | ||||||
| 		PipelineFunc: fn, |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // RunContext represents parameters to run the command | // RunContext represents parameters to run the command | ||||||
| type RunContext struct { | type RunContext struct { | ||||||
| 	Env            []string | 	Env            []string | ||||||
| @@ -131,7 +106,7 @@ type RunContext struct { | |||||||
|  |  | ||||||
| // RunWithContext run the command with context | // RunWithContext run the command with context | ||||||
| func (c *Command) RunWithContext(rc *RunContext) error { | func (c *Command) RunWithContext(rc *RunContext) error { | ||||||
| 	if rc.Timeout == -1 { | 	if rc.Timeout <= 0 { | ||||||
| 		rc.Timeout = defaultCommandExecutionTimeout | 		rc.Timeout = defaultCommandExecutionTimeout | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -203,58 +178,73 @@ func (c *Command) RunWithContext(rc *RunContext) error { | |||||||
| 	return ctx.Err() | 	return ctx.Err() | ||||||
| } | } | ||||||
|  |  | ||||||
| // RunInDirTimeoutPipeline executes the command in given directory with given timeout, | type RunError interface { | ||||||
| // it pipes stdout and stderr to given io.Writer. | 	error | ||||||
| func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error { | 	Stderr() string | ||||||
| 	return c.RunInDirTimeoutEnvPipeline(nil, timeout, dir, stdout, stderr) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // RunInDirTimeoutFullPipeline executes the command in given directory with given timeout, | type runError struct { | ||||||
| // it pipes stdout and stderr to given io.Writer, and stdin from the given io.Reader | 	err    error | ||||||
| func (c *Command) RunInDirTimeoutFullPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer, stdin io.Reader) error { | 	stderr string | ||||||
| 	return c.RunInDirTimeoutEnvFullPipeline(nil, timeout, dir, stdout, stderr, stdin) | 	errMsg string | ||||||
| } | } | ||||||
|  |  | ||||||
| // RunInDirTimeout executes the command in given directory with given timeout, | func (r *runError) Error() string { | ||||||
| // and returns stdout in []byte and error (combined with stderr). | 	// the stderr must be in the returned error text, some code only checks `strings.Contains(err.Error(), "git error")` | ||||||
| func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, error) { | 	if r.errMsg == "" { | ||||||
| 	return c.RunInDirTimeoutEnv(nil, timeout, dir) | 		r.errMsg = ConcatenateError(r.err, r.stderr).Error() | ||||||
| } |  | ||||||
|  |  | ||||||
| // RunInDirTimeoutEnv executes the command in given directory with given timeout, |  | ||||||
| // and returns stdout in []byte and error (combined with stderr). |  | ||||||
| func (c *Command) RunInDirTimeoutEnv(env []string, timeout time.Duration, dir string) ([]byte, error) { |  | ||||||
| 	stdout := new(bytes.Buffer) |  | ||||||
| 	stderr := new(bytes.Buffer) |  | ||||||
| 	if err := c.RunInDirTimeoutEnvPipeline(env, timeout, dir, stdout, stderr); err != nil { |  | ||||||
| 		return nil, ConcatenateError(err, stderr.String()) |  | ||||||
| 	} | 	} | ||||||
| 	if stdout.Len() > 0 && log.IsTrace() { | 	return r.errMsg | ||||||
| 		tracelen := stdout.Len() |  | ||||||
| 		if tracelen > 1024 { |  | ||||||
| 			tracelen = 1024 |  | ||||||
| 		} |  | ||||||
| 		log.Trace("Stdout:\n %s", stdout.Bytes()[:tracelen]) |  | ||||||
| 	} |  | ||||||
| 	return stdout.Bytes(), nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // RunInDirPipeline executes the command in given directory, | func (r *runError) Unwrap() error { | ||||||
| // it pipes stdout and stderr to given io.Writer. | 	return r.err | ||||||
| func (c *Command) RunInDirPipeline(dir string, stdout, stderr io.Writer) error { |  | ||||||
| 	return c.RunInDirFullPipeline(dir, stdout, stderr, nil) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // RunInDirFullPipeline executes the command in given directory, | func (r *runError) Stderr() string { | ||||||
| // it pipes stdout and stderr to given io.Writer. | 	return r.stderr | ||||||
| func (c *Command) RunInDirFullPipeline(dir string, stdout, stderr io.Writer, stdin io.Reader) error { | } | ||||||
| 	return c.RunInDirTimeoutFullPipeline(-1, dir, stdout, stderr, stdin) |  | ||||||
|  | func bytesToString(b []byte) string { | ||||||
|  | 	return *(*string)(unsafe.Pointer(&b)) // that's what Golang's strings.Builder.String() does (go/src/strings/builder.go) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RunWithContextString run the command with context and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr). | ||||||
|  | func (c *Command) RunWithContextString(rc *RunContext) (stdout, stderr string, runErr RunError) { | ||||||
|  | 	stdoutBytes, stderrBytes, err := c.RunWithContextBytes(rc) | ||||||
|  | 	stdout = bytesToString(stdoutBytes) | ||||||
|  | 	stderr = bytesToString(stderrBytes) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return stdout, stderr, &runError{err: err, stderr: stderr} | ||||||
|  | 	} | ||||||
|  | 	// even if there is no err, there could still be some stderr output, so we just return stdout/stderr as they are | ||||||
|  | 	return stdout, stderr, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RunWithContextBytes run the command with context and returns stdout/stderr as bytes. and store stderr to returned error (err combined with stderr). | ||||||
|  | func (c *Command) RunWithContextBytes(rc *RunContext) (stdout, stderr []byte, runErr RunError) { | ||||||
|  | 	if rc.Stdout != nil || rc.Stderr != nil { | ||||||
|  | 		// we must panic here, otherwise there would be bugs if developers set Stdin/Stderr by mistake, and it would be very difficult to debug | ||||||
|  | 		panic("stdout and stderr field must be nil when using RunWithContextBytes") | ||||||
|  | 	} | ||||||
|  | 	stdoutBuf := &bytes.Buffer{} | ||||||
|  | 	stderrBuf := &bytes.Buffer{} | ||||||
|  | 	rc.Stdout = stdoutBuf | ||||||
|  | 	rc.Stderr = stderrBuf | ||||||
|  | 	err := c.RunWithContext(rc) | ||||||
|  | 	stderr = stderrBuf.Bytes() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, stderr, &runError{err: err, stderr: string(stderr)} | ||||||
|  | 	} | ||||||
|  | 	// even if there is no err, there could still be some stderr output | ||||||
|  | 	return stdoutBuf.Bytes(), stderr, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // RunInDirBytes executes the command in given directory | // RunInDirBytes executes the command in given directory | ||||||
| // and returns stdout in []byte and error (combined with stderr). | // and returns stdout in []byte and error (combined with stderr). | ||||||
| func (c *Command) RunInDirBytes(dir string) ([]byte, error) { | func (c *Command) RunInDirBytes(dir string) ([]byte, error) { | ||||||
| 	return c.RunInDirTimeout(-1, dir) | 	stdout, _, err := c.RunWithContextBytes(&RunContext{Dir: dir}) | ||||||
|  | 	return stdout, err | ||||||
| } | } | ||||||
|  |  | ||||||
| // RunInDir executes the command in given directory | // RunInDir executes the command in given directory | ||||||
| @@ -266,27 +256,15 @@ func (c *Command) RunInDir(dir string) (string, error) { | |||||||
| // RunInDirWithEnv executes the command in given directory | // RunInDirWithEnv executes the command in given directory | ||||||
| // and returns stdout in string and error (combined with stderr). | // and returns stdout in string and error (combined with stderr). | ||||||
| func (c *Command) RunInDirWithEnv(dir string, env []string) (string, error) { | func (c *Command) RunInDirWithEnv(dir string, env []string) (string, error) { | ||||||
| 	stdout, err := c.RunInDirTimeoutEnv(env, -1, dir) | 	stdout, _, err := c.RunWithContextString(&RunContext{Env: env, Dir: dir}) | ||||||
| 	if err != nil { | 	return stdout, err | ||||||
| 		return "", err |  | ||||||
| 	} |  | ||||||
| 	return string(stdout), nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // RunTimeout executes the command in default working directory with given timeout, |  | ||||||
| // and returns stdout in string and error (combined with stderr). |  | ||||||
| func (c *Command) RunTimeout(timeout time.Duration) (string, error) { |  | ||||||
| 	stdout, err := c.RunInDirTimeout(timeout, "") |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "", err |  | ||||||
| 	} |  | ||||||
| 	return string(stdout), nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Run executes the command in default working directory | // Run executes the command in default working directory | ||||||
| // and returns stdout in string and error (combined with stderr). | // and returns stdout in string and error (combined with stderr). | ||||||
| func (c *Command) Run() (string, error) { | func (c *Command) Run() (string, error) { | ||||||
| 	return c.RunTimeout(-1) | 	stdout, _, err := c.RunWithContextString(&RunContext{}) | ||||||
|  | 	return stdout, err | ||||||
| } | } | ||||||
|  |  | ||||||
| // AllowLFSFiltersArgs return globalCommandArgs with lfs filter, it should only be used for tests | // AllowLFSFiltersArgs return globalCommandArgs with lfs filter, it should only be used for tests | ||||||
|   | |||||||
							
								
								
									
										40
									
								
								modules/git/command_race_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								modules/git/command_race_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | // Copyright 2017 The Gitea Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | //go:build race | ||||||
|  | // +build race | ||||||
|  |  | ||||||
|  | package git | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestRunWithContextNoTimeout(t *testing.T) { | ||||||
|  | 	maxLoops := 10 | ||||||
|  |  | ||||||
|  | 	// 'git --version' does not block so it must be finished before the timeout triggered. | ||||||
|  | 	cmd := NewCommand(context.Background(), "--version") | ||||||
|  | 	for i := 0; i < maxLoops; i++ { | ||||||
|  | 		if err := cmd.RunWithContext(&RunContext{}); err != nil { | ||||||
|  | 			t.Fatal(err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestRunWithContextTimeout(t *testing.T) { | ||||||
|  | 	maxLoops := 10 | ||||||
|  |  | ||||||
|  | 	// 'git hash-object --stdin' blocks on stdin so we can have the timeout triggered. | ||||||
|  | 	cmd := NewCommand(context.Background(), "hash-object", "--stdin") | ||||||
|  | 	for i := 0; i < maxLoops; i++ { | ||||||
|  | 		if err := cmd.RunWithContext(&RunContext{Timeout: 1 * time.Millisecond}); err != nil { | ||||||
|  | 			if err != context.DeadlineExceeded { | ||||||
|  | 				t.Fatalf("Testing %d/%d: %v", i, maxLoops, err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -1,40 +1,29 @@ | |||||||
| // Copyright 2017 The Gitea Authors. All rights reserved. | // Copyright 2022 The Gitea Authors. All rights reserved. | ||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| //go:build race |  | ||||||
| // +build race |  | ||||||
|  |  | ||||||
| package git | package git | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestRunInDirTimeoutPipelineNoTimeout(t *testing.T) { | func TestRunWithContextStd(t *testing.T) { | ||||||
| 	maxLoops := 1000 |  | ||||||
|  |  | ||||||
| 	// 'git --version' does not block so it must be finished before the timeout triggered. |  | ||||||
| 	cmd := NewCommand(context.Background(), "--version") | 	cmd := NewCommand(context.Background(), "--version") | ||||||
| 	for i := 0; i < maxLoops; i++ { | 	stdout, stderr, err := cmd.RunWithContextString(&RunContext{}) | ||||||
| 		if err := cmd.RunInDirTimeoutPipeline(-1, "", nil, nil); err != nil { | 	assert.NoError(t, err) | ||||||
| 			t.Fatal(err) | 	assert.Empty(t, stderr) | ||||||
| 		} | 	assert.Contains(t, stdout, "git version") | ||||||
| 	} |  | ||||||
| } | 	cmd = NewCommand(context.Background(), "--no-such-arg") | ||||||
|  | 	stdout, stderr, err = cmd.RunWithContextString(&RunContext{}) | ||||||
| func TestRunInDirTimeoutPipelineAlwaysTimeout(t *testing.T) { | 	if assert.Error(t, err) { | ||||||
| 	maxLoops := 1000 | 		assert.Equal(t, stderr, err.Stderr()) | ||||||
|  | 		assert.Contains(t, err.Stderr(), "unknown option:") | ||||||
| 	// 'git hash-object --stdin' blocks on stdin so we can have the timeout triggered. | 		assert.Contains(t, err.Error(), "exit status 129 - unknown option:") | ||||||
| 	cmd := NewCommand(context.Background(), "hash-object", "--stdin") | 		assert.Empty(t, stdout) | ||||||
| 	for i := 0; i < maxLoops; i++ { |  | ||||||
| 		if err := cmd.RunInDirTimeoutPipeline(1*time.Microsecond, "", nil, nil); err != nil { |  | ||||||
| 			if err != context.DeadlineExceeded { |  | ||||||
| 				t.Fatalf("Testing %d/%d: %v", i, maxLoops, err) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -124,7 +124,9 @@ func VersionInfo() string { | |||||||
| func Init(ctx context.Context) error { | func Init(ctx context.Context) error { | ||||||
| 	DefaultContext = ctx | 	DefaultContext = ctx | ||||||
|  |  | ||||||
|  | 	if setting.Git.Timeout.Default > 0 { | ||||||
| 		defaultCommandExecutionTimeout = time.Duration(setting.Git.Timeout.Default) * time.Second | 		defaultCommandExecutionTimeout = time.Duration(setting.Git.Timeout.Default) * time.Second | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if err := SetExecutablePath(setting.Git.Path); err != nil { | 	if err := SetExecutablePath(setting.Git.Path); err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -295,10 +297,5 @@ func checkAndRemoveConfig(key, value string) error { | |||||||
|  |  | ||||||
| // Fsck verifies the connectivity and validity of the objects in the database | // Fsck verifies the connectivity and validity of the objects in the database | ||||||
| func Fsck(ctx context.Context, repoPath string, timeout time.Duration, args ...string) error { | func Fsck(ctx context.Context, repoPath string, timeout time.Duration, args ...string) error { | ||||||
| 	// Make sure timeout makes sense. | 	return NewCommand(ctx, "fsck").AddArguments(args...).RunWithContext(&RunContext{Timeout: timeout, Dir: repoPath}) | ||||||
| 	if timeout <= 0 { |  | ||||||
| 		timeout = -1 |  | ||||||
| 	} |  | ||||||
| 	_, err := NewCommand(ctx, "fsck").AddArguments(args...).RunInDirTimeout(timeout, repoPath) |  | ||||||
| 	return err |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -86,6 +86,10 @@ func (pm *Manager) AddContext(parent context.Context, description string) (ctx c | |||||||
| // Most processes will not need to use the cancel function but there will be cases whereby you want to cancel the process but not immediately remove it from the | // Most processes will not need to use the cancel function but there will be cases whereby you want to cancel the process but not immediately remove it from the | ||||||
| // process table. | // process table. | ||||||
| func (pm *Manager) AddContextTimeout(parent context.Context, timeout time.Duration, description string) (ctx context.Context, cancel context.CancelFunc, finshed FinishedFunc) { | func (pm *Manager) AddContextTimeout(parent context.Context, timeout time.Duration, description string) (ctx context.Context, cancel context.CancelFunc, finshed FinishedFunc) { | ||||||
|  | 	if timeout <= 0 { | ||||||
|  | 		// it's meaningless to use timeout <= 0, and it must be a bug! so we must panic here to tell developers to make the timeout correct | ||||||
|  | 		panic("the timeout must be greater than zero, otherwise the context will be cancelled immediately") | ||||||
|  | 	} | ||||||
| 	ctx, cancel = context.WithTimeout(parent, timeout) | 	ctx, cancel = context.WithTimeout(parent, timeout) | ||||||
|  |  | ||||||
| 	ctx, pid, finshed := pm.Add(ctx, description, cancel) | 	ctx, pid, finshed := pm.Add(ctx, description, cancel) | ||||||
| @@ -239,7 +243,7 @@ func (pm *Manager) ExecDirEnv(ctx context.Context, timeout time.Duration, dir, d | |||||||
| // Returns its complete stdout and stderr | // Returns its complete stdout and stderr | ||||||
| // outputs and an error, if any (including timeout) | // outputs and an error, if any (including timeout) | ||||||
| func (pm *Manager) ExecDirEnvStdIn(ctx context.Context, timeout time.Duration, dir, desc string, env []string, stdIn io.Reader, cmdName string, args ...string) (string, string, error) { | func (pm *Manager) ExecDirEnvStdIn(ctx context.Context, timeout time.Duration, dir, desc string, env []string, stdIn io.Reader, cmdName string, args ...string) (string, string, error) { | ||||||
| 	if timeout == -1 { | 	if timeout <= 0 { | ||||||
| 		timeout = 60 * time.Second | 		timeout = 60 * time.Second | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -542,7 +542,7 @@ func GetInfoRefs(ctx *context.Context) { | |||||||
| 		} | 		} | ||||||
| 		h.environ = append(os.Environ(), h.environ...) | 		h.environ = append(os.Environ(), h.environ...) | ||||||
|  |  | ||||||
| 		refs, err := git.NewCommand(ctx, service, "--stateless-rpc", "--advertise-refs", ".").RunInDirTimeoutEnv(h.environ, -1, h.dir) | 		refs, _, err := git.NewCommand(ctx, service, "--stateless-rpc", "--advertise-refs", ".").RunWithContextBytes(&git.RunContext{Env: h.environ, Dir: h.dir}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error(fmt.Sprintf("%v - %s", err, string(refs))) | 			log.Error(fmt.Sprintf("%v - %s", err, string(refs))) | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -77,15 +77,7 @@ func GitGcRepos(ctx context.Context, timeout time.Duration, args ...string) erro | |||||||
| 				SetDescription(fmt.Sprintf("Repository Garbage Collection: %s", repo.FullName())) | 				SetDescription(fmt.Sprintf("Repository Garbage Collection: %s", repo.FullName())) | ||||||
| 			var stdout string | 			var stdout string | ||||||
| 			var err error | 			var err error | ||||||
| 			if timeout > 0 { | 			stdout, _, err = command.RunWithContextString(&git.RunContext{Timeout: timeout, Dir: repo.RepoPath()}) | ||||||
| 				var stdoutBytes []byte |  | ||||||
| 				stdoutBytes, err = command.RunInDirTimeout( |  | ||||||
| 					timeout, |  | ||||||
| 					repo.RepoPath()) |  | ||||||
| 				stdout = string(stdoutBytes) |  | ||||||
| 			} else { |  | ||||||
| 				stdout, err = command.RunInDir(repo.RepoPath()) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Error("Repository garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err) | 				log.Error("Repository garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err) | ||||||
|   | |||||||
| @@ -108,10 +108,10 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork | |||||||
| 		needsRollback = true | 		needsRollback = true | ||||||
|  |  | ||||||
| 		repoPath := repo_model.RepoPath(owner.Name, repo.Name) | 		repoPath := repo_model.RepoPath(owner.Name, repo.Name) | ||||||
| 		if stdout, err := git.NewCommand(txCtx, | 		if stdout, _, err := git.NewCommand(txCtx, | ||||||
| 			"clone", "--bare", oldRepoPath, repoPath). | 			"clone", "--bare", oldRepoPath, repoPath). | ||||||
| 			SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", opts.BaseRepo.FullName(), repo.FullName())). | 			SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", opts.BaseRepo.FullName(), repo.FullName())). | ||||||
| 			RunInDirTimeout(10*time.Minute, ""); err != nil { | 			RunWithContextBytes(&git.RunContext{Timeout: 10 * time.Minute}); err != nil { | ||||||
| 			log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, opts.BaseRepo, stdout, err) | 			log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, opts.BaseRepo, stdout, err) | ||||||
| 			return fmt.Errorf("git clone: %v", err) | 			return fmt.Errorf("git clone: %v", err) | ||||||
| 		} | 		} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user