mirror of
https://github.com/go-gitea/gitea.git
synced 2026-01-21 20:30:48 +00:00
Refactor git command stderr handling (#36402)
And clean up legacy fragile & incorrect logic
This commit is contained in:
@@ -76,13 +76,11 @@ func NewBatchChecker(repo *git.Repository, treeish string, attributes []string)
|
||||
_ = stdinReader.Close()
|
||||
_ = lw.Close()
|
||||
}()
|
||||
stdErr := new(bytes.Buffer)
|
||||
err := cmd.WithEnv(envs).
|
||||
WithDir(repo.Path).
|
||||
WithStdin(stdinReader).
|
||||
WithStdout(lw).
|
||||
WithStderr(stdErr).
|
||||
Run(ctx)
|
||||
RunWithStderr(ctx)
|
||||
|
||||
if err != nil && !git.IsErrCanceledOrKilled(err) {
|
||||
log.Error("Attribute checker for commit %s exits with error: %v", treeish, err)
|
||||
|
||||
@@ -69,14 +69,11 @@ func CheckAttributes(ctx context.Context, gitRepo *git.Repository, treeish strin
|
||||
defer cancel()
|
||||
|
||||
stdOut := new(bytes.Buffer)
|
||||
stdErr := new(bytes.Buffer)
|
||||
|
||||
if err := cmd.WithEnv(append(os.Environ(), envs...)).
|
||||
WithDir(gitRepo.Path).
|
||||
WithStdout(stdOut).
|
||||
WithStderr(stdErr).
|
||||
Run(ctx); err != nil {
|
||||
return nil, fmt.Errorf("failed to run check-attr: %w\n%s\n%s", err, stdOut.String(), stdErr.String())
|
||||
RunWithStderr(ctx); err != nil {
|
||||
return nil, fmt.Errorf("failed to run check-attr: %w", err)
|
||||
}
|
||||
|
||||
fields := bytes.Split(stdOut.Bytes(), []byte{'\000'})
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"strconv"
|
||||
@@ -40,15 +41,13 @@ func newCatFileBatch(ctx context.Context, repoPath string, cmdCatFile *gitcmd.Co
|
||||
|
||||
var batchStdinWriter io.WriteCloser
|
||||
var batchStdoutReader io.ReadCloser
|
||||
stderr := strings.Builder{}
|
||||
cmdCatFile = cmdCatFile.
|
||||
WithDir(repoPath).
|
||||
WithStdinWriter(&batchStdinWriter).
|
||||
WithStdoutReader(&batchStdoutReader).
|
||||
WithStderr(&stderr).
|
||||
WithUseContextTimeout(true)
|
||||
|
||||
err := cmdCatFile.Start(ctx)
|
||||
err := cmdCatFile.StartWithStderr(ctx)
|
||||
if err != nil {
|
||||
log.Error("Unable to start git command %v: %v", cmdCatFile.LogString(), err)
|
||||
// ideally here it should return the error, but it would require refactoring all callers
|
||||
@@ -63,9 +62,9 @@ func newCatFileBatch(ctx context.Context, repoPath string, cmdCatFile *gitcmd.Co
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := cmdCatFile.Wait()
|
||||
if err != nil {
|
||||
log.Error("cat-file --batch command failed in repo %s: %v - stderr: %s", repoPath, err, stderr.String())
|
||||
err := cmdCatFile.WaitWithStderr()
|
||||
if err != nil && !errors.Is(err, context.Canceled) {
|
||||
log.Error("cat-file --batch command failed in repo %s, error: %v", repoPath, err)
|
||||
}
|
||||
ctxCancel(err)
|
||||
}()
|
||||
|
||||
@@ -120,7 +120,7 @@ func CommitChanges(ctx context.Context, repoPath string, opts CommitChangesOptio
|
||||
|
||||
_, _, err := cmd.WithDir(repoPath).RunStdString(ctx)
|
||||
// No stderr but exit status 1 means nothing to commit.
|
||||
if err != nil && err.Error() == "exit status 1" {
|
||||
if gitcmd.IsErrorExitCode(err, 1) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
@@ -315,7 +315,7 @@ func GetFullCommitID(ctx context.Context, repoPath, shortID string) (string, err
|
||||
WithDir(repoPath).
|
||||
RunStdString(ctx)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "exit status 128") {
|
||||
if gitcmd.IsErrorExitCode(err, 128) {
|
||||
return "", ErrNotExist{shortID, ""}
|
||||
}
|
||||
return "", err
|
||||
|
||||
@@ -5,7 +5,6 @@ package git
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -80,14 +79,9 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
|
||||
return fmt.Errorf("invalid diffType: %s", diffType)
|
||||
}
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
if err = cmd.WithDir(repo.Path).
|
||||
return cmd.WithDir(repo.Path).
|
||||
WithStdout(writer).
|
||||
WithStderr(stderr).
|
||||
Run(repo.Ctx); err != nil {
|
||||
return fmt.Errorf("Run: %w - %s", err, stderr)
|
||||
}
|
||||
return nil
|
||||
RunWithStderr(repo.Ctx)
|
||||
}
|
||||
|
||||
// ParseDiffHunkString parse the diff hunk content and return
|
||||
|
||||
@@ -40,6 +40,7 @@ const DefaultLocale = "C"
|
||||
|
||||
// Command represents a command with its subcommands or arguments.
|
||||
type Command struct {
|
||||
callerInfo string
|
||||
prog string
|
||||
args []string
|
||||
preErrors []error
|
||||
@@ -52,9 +53,10 @@ type Command struct {
|
||||
cmdFinished context.CancelFunc
|
||||
cmdStartTime time.Time
|
||||
|
||||
cmdStdinWriter *io.WriteCloser
|
||||
cmdStdoutReader *io.ReadCloser
|
||||
cmdStderrReader *io.ReadCloser
|
||||
cmdStdinWriter *io.WriteCloser
|
||||
cmdStdoutReader *io.ReadCloser
|
||||
cmdStderrReader *io.ReadCloser
|
||||
cmdManagedStderr *bytes.Buffer
|
||||
}
|
||||
|
||||
func logArgSanitize(arg string) string {
|
||||
@@ -221,7 +223,7 @@ type runOpts struct {
|
||||
// The correct approach is to use `--git-dir" global argument
|
||||
Dir string
|
||||
|
||||
Stdout, Stderr io.Writer
|
||||
Stdout io.Writer
|
||||
|
||||
// Stdin is used for passing input to the command
|
||||
// The caller must make sure the Stdin writer is closed properly to finish the Run function.
|
||||
@@ -235,8 +237,6 @@ type runOpts struct {
|
||||
Stdin io.Reader
|
||||
|
||||
PipelineFunc func(context.Context, context.CancelFunc) error
|
||||
|
||||
callerInfo string
|
||||
}
|
||||
|
||||
func commonBaseEnvs() []string {
|
||||
@@ -310,12 +310,6 @@ func (c *Command) WithStderrReader(r *io.ReadCloser) *Command {
|
||||
return c
|
||||
}
|
||||
|
||||
// WithStderr is deprecated, use WithStderrReader instead
|
||||
func (c *Command) WithStderr(stderr io.Writer) *Command {
|
||||
c.opts.Stderr = stderr
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Command) WithStdinWriter(w *io.WriteCloser) *Command {
|
||||
c.cmdStdinWriter = w
|
||||
return c
|
||||
@@ -343,11 +337,11 @@ func (c *Command) WithUseContextTimeout(useContextTimeout bool) *Command {
|
||||
// then you can to call this function in GeneralWrapperFunc to set the caller info of FeatureFunc.
|
||||
// The caller info can only be set once.
|
||||
func (c *Command) WithParentCallerInfo(optInfo ...string) *Command {
|
||||
if c.opts.callerInfo != "" {
|
||||
if c.callerInfo != "" {
|
||||
return c
|
||||
}
|
||||
if len(optInfo) > 0 {
|
||||
c.opts.callerInfo = optInfo[0]
|
||||
c.callerInfo = optInfo[0]
|
||||
return c
|
||||
}
|
||||
skip := 1 /*parent "wrap/run" functions*/ + 1 /*this function*/
|
||||
@@ -356,7 +350,7 @@ func (c *Command) WithParentCallerInfo(optInfo ...string) *Command {
|
||||
if pos := strings.LastIndex(callerInfo, "/"); pos >= 0 {
|
||||
callerInfo = callerInfo[pos+1:]
|
||||
}
|
||||
c.opts.callerInfo = callerInfo
|
||||
c.callerInfo = callerInfo
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -372,7 +366,7 @@ func (c *Command) Start(ctx context.Context) (retErr error) {
|
||||
safeClosePtrCloser(c.cmdStdoutReader)
|
||||
safeClosePtrCloser(c.cmdStderrReader)
|
||||
safeClosePtrCloser(c.cmdStdinWriter)
|
||||
// if no error, cmdFinished will be called in "Wait" function
|
||||
// if error occurs, we must also finish the task, otherwise, cmdFinished will be called in "Wait" function
|
||||
if c.cmdFinished != nil {
|
||||
c.cmdFinished()
|
||||
}
|
||||
@@ -393,16 +387,16 @@ func (c *Command) Start(ctx context.Context) (retErr error) {
|
||||
}
|
||||
|
||||
cmdLogString := c.LogString()
|
||||
if c.opts.callerInfo == "" {
|
||||
if c.callerInfo == "" {
|
||||
c.WithParentCallerInfo()
|
||||
}
|
||||
// these logs are for debugging purposes only, so no guarantee of correctness or stability
|
||||
desc := fmt.Sprintf("git.Run(by:%s, repo:%s): %s", c.opts.callerInfo, logArgSanitize(c.opts.Dir), cmdLogString)
|
||||
desc := fmt.Sprintf("git.Run(by:%s, repo:%s): %s", c.callerInfo, logArgSanitize(c.opts.Dir), cmdLogString)
|
||||
log.Debug("git.Command: %s", desc)
|
||||
|
||||
_, span := gtprof.GetTracer().Start(ctx, gtprof.TraceSpanGitRun)
|
||||
defer span.End()
|
||||
span.SetAttributeString(gtprof.TraceAttrFuncCaller, c.opts.callerInfo)
|
||||
span.SetAttributeString(gtprof.TraceAttrFuncCaller, c.callerInfo)
|
||||
span.SetAttributeString(gtprof.TraceAttrGitCommand, cmdLogString)
|
||||
|
||||
if c.opts.UseContextTimeout {
|
||||
@@ -425,7 +419,6 @@ func (c *Command) Start(ctx context.Context) (retErr error) {
|
||||
cmd.Env = append(cmd.Env, CommonGitCmdEnvs()...)
|
||||
cmd.Dir = c.opts.Dir
|
||||
cmd.Stdout = c.opts.Stdout
|
||||
cmd.Stderr = c.opts.Stderr
|
||||
cmd.Stdin = c.opts.Stdin
|
||||
|
||||
if _, err := safeAssignPipe(c.cmdStdinWriter, cmd.StdinPipe); err != nil {
|
||||
@@ -437,19 +430,32 @@ func (c *Command) Start(ctx context.Context) (retErr error) {
|
||||
if _, err := safeAssignPipe(c.cmdStderrReader, cmd.StderrPipe); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.cmdManagedStderr != nil {
|
||||
if cmd.Stderr != nil {
|
||||
panic("CombineStderr needs managed (but not caller-provided) stderr pipe")
|
||||
}
|
||||
cmd.Stderr = c.cmdManagedStderr
|
||||
}
|
||||
return cmd.Start()
|
||||
}
|
||||
|
||||
func (c *Command) Wait() error {
|
||||
defer c.cmdFinished()
|
||||
defer func() {
|
||||
safeClosePtrCloser(c.cmdStdoutReader)
|
||||
safeClosePtrCloser(c.cmdStderrReader)
|
||||
safeClosePtrCloser(c.cmdStdinWriter)
|
||||
c.cmdFinished()
|
||||
}()
|
||||
|
||||
cmd, ctx, cancel := c.cmd, c.cmdCtx, c.cmdCancel
|
||||
|
||||
if c.opts.PipelineFunc != nil {
|
||||
err := c.opts.PipelineFunc(ctx, cancel)
|
||||
if err != nil {
|
||||
cancel()
|
||||
_ = cmd.Wait()
|
||||
return err
|
||||
errWait := cmd.Wait()
|
||||
return errors.Join(err, errWait)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,6 +478,34 @@ func (c *Command) Wait() error {
|
||||
return errCause
|
||||
}
|
||||
|
||||
func (c *Command) StartWithStderr(ctx context.Context) RunStdError {
|
||||
c.cmdManagedStderr = &bytes.Buffer{}
|
||||
err := c.Start(ctx)
|
||||
if err != nil {
|
||||
return &runStdError{err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Command) WaitWithStderr() RunStdError {
|
||||
if c.cmdManagedStderr == nil {
|
||||
panic("CombineStderr needs managed (but not caller-provided) stderr pipe")
|
||||
}
|
||||
errWait := c.Wait()
|
||||
if errWait == nil {
|
||||
// if no exec error but only stderr output, the stderr output is still saved in "c.cmdManagedStderr" and can be read later
|
||||
return nil
|
||||
}
|
||||
return &runStdError{err: errWait, stderr: util.UnsafeBytesToString(c.cmdManagedStderr.Bytes())}
|
||||
}
|
||||
|
||||
func (c *Command) RunWithStderr(ctx context.Context) RunStdError {
|
||||
if err := c.StartWithStderr(ctx); err != nil {
|
||||
return &runStdError{err: err}
|
||||
}
|
||||
return c.WaitWithStderr()
|
||||
}
|
||||
|
||||
func (c *Command) Run(ctx context.Context) (err error) {
|
||||
if err = c.Start(ctx); err != nil {
|
||||
return err
|
||||
@@ -493,9 +527,9 @@ type runStdError struct {
|
||||
|
||||
func (r *runStdError) Error() string {
|
||||
// FIXME: GIT-CMD-STDERR: it is a bad design, the stderr should not be put in the error message
|
||||
// But a lof of code only checks `strings.Contains(err.Error(), "git error")`
|
||||
// But a lot of code only checks `strings.Contains(err.Error(), "git error")`
|
||||
if r.errMsg == "" {
|
||||
r.errMsg = ConcatenateError(r.err, r.stderr).Error()
|
||||
r.errMsg = fmt.Sprintf("%s - %s", r.err.Error(), strings.TrimSpace(r.stderr))
|
||||
}
|
||||
return r.errMsg
|
||||
}
|
||||
@@ -543,24 +577,16 @@ func (c *Command) RunStdBytes(ctx context.Context) (stdout, stderr []byte, runEr
|
||||
return c.WithParentCallerInfo().runStdBytes(ctx)
|
||||
}
|
||||
|
||||
func (c *Command) runStdBytes(ctx context.Context) ( /*stdout*/ []byte /*stderr*/, []byte /*runErr*/, RunStdError) {
|
||||
if c.opts.Stdout != nil || c.opts.Stderr != nil {
|
||||
func (c *Command) runStdBytes(ctx context.Context) ([]byte, []byte, RunStdError) {
|
||||
if c.opts.Stdout != nil || c.cmdStdoutReader != nil || c.cmdStderrReader != 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 RunStdBytes")
|
||||
}
|
||||
stdoutBuf := &bytes.Buffer{}
|
||||
stderrBuf := &bytes.Buffer{}
|
||||
err := c.WithParentCallerInfo().
|
||||
WithStdout(stdoutBuf).
|
||||
WithStderr(stderrBuf).
|
||||
Run(ctx)
|
||||
if err != nil {
|
||||
// FIXME: GIT-CMD-STDERR: it is a bad design, the stderr should not be put in the error message
|
||||
// But a lot of code depends on it, so we have to keep this behavior
|
||||
return nil, stderrBuf.Bytes(), &runStdError{err: err, stderr: util.UnsafeBytesToString(stderrBuf.Bytes())}
|
||||
}
|
||||
// even if there is no err, there could still be some stderr output
|
||||
return stdoutBuf.Bytes(), stderrBuf.Bytes(), nil
|
||||
RunWithStderr(ctx)
|
||||
return stdoutBuf.Bytes(), c.cmdManagedStderr.Bytes(), err
|
||||
}
|
||||
|
||||
func (c *Command) DebugKill() {
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestRunWithContextStd(t *testing.T) {
|
||||
assert.Equal(t, stderr, err.Stderr())
|
||||
assert.Equal(t, "fatal: Not a valid object name no-such\n", err.Stderr())
|
||||
// FIXME: GIT-CMD-STDERR: it is a bad design, the stderr should not be put in the error message
|
||||
assert.Equal(t, "exit status 128 - fatal: Not a valid object name no-such\n", err.Error())
|
||||
assert.Equal(t, "exit status 128 - fatal: Not a valid object name no-such", err.Error())
|
||||
assert.Empty(t, stdout)
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ func TestRunWithContextStd(t *testing.T) {
|
||||
assert.Equal(t, string(stderr), err.Stderr())
|
||||
assert.Equal(t, "fatal: Not a valid object name no-such\n", err.Stderr())
|
||||
// FIXME: GIT-CMD-STDERR: it is a bad design, the stderr should not be put in the error message
|
||||
assert.Equal(t, "exit status 128 - fatal: Not a valid object name no-such\n", err.Error())
|
||||
assert.Equal(t, "exit status 128 - fatal: Not a valid object name no-such", err.Error())
|
||||
assert.Empty(t, stdout)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,22 +4,9 @@
|
||||
package gitcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// ConcatenateError concatenates an error with stderr string
|
||||
// FIXME: use RunStdError instead
|
||||
func ConcatenateError(err error, stderr string) error {
|
||||
if len(stderr) == 0 {
|
||||
return err
|
||||
}
|
||||
errMsg := fmt.Sprintf("%s - %s", err.Error(), stderr)
|
||||
return util.ErrorWrap(&runStdError{err: err, stderr: stderr, errMsg: errMsg}, "%s", errMsg)
|
||||
}
|
||||
|
||||
func safeClosePtrCloser[T *io.ReadCloser | *io.WriteCloser](c T) {
|
||||
switch v := any(c).(type) {
|
||||
case *io.ReadCloser:
|
||||
|
||||
@@ -5,11 +5,10 @@ package git
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"io"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -42,15 +41,6 @@ type GrepOptions struct {
|
||||
}
|
||||
|
||||
func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepOptions) ([]*GrepResult, error) {
|
||||
stdoutReader, stdoutWriter, err := os.Pipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create os pipe to grep: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = stdoutReader.Close()
|
||||
_ = stdoutWriter.Close()
|
||||
}()
|
||||
|
||||
/*
|
||||
The output is like this ( "^@" means \x00):
|
||||
|
||||
@@ -83,12 +73,11 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
|
||||
cmd.AddDynamicArguments(util.IfZero(opts.RefName, "HEAD"))
|
||||
cmd.AddDashesAndList(opts.PathspecList...)
|
||||
opts.MaxResultLimit = util.IfZero(opts.MaxResultLimit, 50)
|
||||
stderr := bytes.Buffer{}
|
||||
err = cmd.WithDir(repo.Path).
|
||||
WithStdout(stdoutWriter).
|
||||
WithStderr(&stderr).
|
||||
|
||||
var stdoutReader io.ReadCloser
|
||||
err := cmd.WithDir(repo.Path).
|
||||
WithStdoutReader(&stdoutReader).
|
||||
WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
|
||||
_ = stdoutWriter.Close()
|
||||
defer stdoutReader.Close()
|
||||
|
||||
isInBlock := false
|
||||
@@ -133,17 +122,17 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
|
||||
}
|
||||
return nil
|
||||
}).
|
||||
Run(ctx)
|
||||
RunWithStderr(ctx)
|
||||
// git grep exits by cancel (killed), usually it is caused by the limit of results
|
||||
if gitcmd.IsErrorExitCode(err, -1) && stderr.Len() == 0 {
|
||||
if gitcmd.IsErrorExitCode(err, -1) && err.Stderr() == "" {
|
||||
return results, nil
|
||||
}
|
||||
// git grep exits with 1 if no results are found
|
||||
if gitcmd.IsErrorExitCode(err, 1) && stderr.Len() == 0 {
|
||||
if gitcmd.IsErrorExitCode(err, 1) && err.Stderr() == "" {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil && !errors.Is(err, context.Canceled) {
|
||||
return nil, fmt.Errorf("unable to run git grep: %w, stderr: %s", err, stderr.String())
|
||||
return nil, fmt.Errorf("unable to run git grep: %w", err)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
@@ -64,20 +64,12 @@ func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, p
|
||||
cmd.AddDashesAndList(files...)
|
||||
|
||||
go func() {
|
||||
stderr := strings.Builder{}
|
||||
err := cmd.WithDir(repository).
|
||||
WithStdout(stdoutWriter).
|
||||
WithStderr(&stderr).
|
||||
Run(ctx)
|
||||
if err != nil {
|
||||
_ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
|
||||
return
|
||||
}
|
||||
|
||||
_ = stdoutWriter.Close()
|
||||
RunWithStderr(ctx)
|
||||
_ = stdoutWriter.CloseWithError(err)
|
||||
}()
|
||||
|
||||
// For simplicities sake we'll us a buffered reader to read from the cat-file --batch
|
||||
bufReader := bufio.NewReaderSize(stdoutReader, 32*1024)
|
||||
|
||||
return bufReader, cancel
|
||||
|
||||
@@ -5,7 +5,6 @@ package pipeline
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -14,7 +13,6 @@ import (
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// CatFileBatchCheck runs cat-file with --batch-check
|
||||
@@ -23,15 +21,12 @@ func CatFileBatchCheck(ctx context.Context, shasToCheckReader *io.PipeReader, ca
|
||||
defer shasToCheckReader.Close()
|
||||
defer catFileCheckWriter.Close()
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
var errbuf strings.Builder
|
||||
cmd := gitcmd.NewCommand("cat-file", "--batch-check")
|
||||
if err := cmd.WithDir(tmpBasePath).
|
||||
WithStdin(shasToCheckReader).
|
||||
WithStdout(catFileCheckWriter).
|
||||
WithStderr(stderr).
|
||||
Run(ctx); err != nil {
|
||||
_ = catFileCheckWriter.CloseWithError(fmt.Errorf("git cat-file --batch-check [%s]: %w - %s", tmpBasePath, err, errbuf.String()))
|
||||
RunWithStderr(ctx); err != nil {
|
||||
_ = catFileCheckWriter.CloseWithError(fmt.Errorf("git cat-file --batch-check [%s]: %w", tmpBasePath, err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,16 +35,11 @@ func CatFileBatchCheckAllObjects(ctx context.Context, catFileCheckWriter *io.Pip
|
||||
defer wg.Done()
|
||||
defer catFileCheckWriter.Close()
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
var errbuf strings.Builder
|
||||
cmd := gitcmd.NewCommand("cat-file", "--batch-check", "--batch-all-objects")
|
||||
if err := cmd.WithDir(tmpBasePath).
|
||||
WithStdout(catFileCheckWriter).
|
||||
WithStderr(stderr).
|
||||
Run(ctx); err != nil {
|
||||
log.Error("git cat-file --batch-check --batch-all-object [%s]: %v - %s", tmpBasePath, err, errbuf.String())
|
||||
err = fmt.Errorf("git cat-file --batch-check --batch-all-object [%s]: %w - %s", tmpBasePath, err, errbuf.String())
|
||||
_ = catFileCheckWriter.CloseWithError(err)
|
||||
RunWithStderr(ctx); err != nil {
|
||||
_ = catFileCheckWriter.CloseWithError(fmt.Errorf("git cat-file --batch-check --batch-all-object [%s]: %w", tmpBasePath, err))
|
||||
errChan <- err
|
||||
}
|
||||
}
|
||||
@@ -60,15 +50,12 @@ func CatFileBatch(ctx context.Context, shasToBatchReader *io.PipeReader, catFile
|
||||
defer shasToBatchReader.Close()
|
||||
defer catFileBatchWriter.Close()
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
var errbuf strings.Builder
|
||||
if err := gitcmd.NewCommand("cat-file", "--batch").
|
||||
WithDir(tmpBasePath).
|
||||
WithStdin(shasToBatchReader).
|
||||
WithStdout(catFileBatchWriter).
|
||||
WithStderr(stderr).
|
||||
Run(ctx); err != nil {
|
||||
_ = shasToBatchReader.CloseWithError(fmt.Errorf("git rev-list [%s]: %w - %s", tmpBasePath, err, errbuf.String()))
|
||||
RunWithStderr(ctx); err != nil {
|
||||
_ = shasToBatchReader.CloseWithError(fmt.Errorf("git rev-list [%s]: %w", tmpBasePath, err))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,17 +33,11 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
|
||||
}()
|
||||
|
||||
go func() {
|
||||
stderr := strings.Builder{}
|
||||
err := gitcmd.NewCommand("rev-list", "--all").
|
||||
WithDir(repo.Path).
|
||||
WithStdout(revListWriter).
|
||||
WithStderr(&stderr).
|
||||
Run(repo.Ctx)
|
||||
if err != nil {
|
||||
_ = revListWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
|
||||
} else {
|
||||
_ = revListWriter.Close()
|
||||
}
|
||||
RunWithStderr(repo.Ctx)
|
||||
_ = revListWriter.CloseWithError(err)
|
||||
}()
|
||||
|
||||
// Next feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary.
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
@@ -20,14 +18,11 @@ func NameRevStdin(ctx context.Context, shasToNameReader *io.PipeReader, nameRevS
|
||||
defer shasToNameReader.Close()
|
||||
defer nameRevStdinWriter.Close()
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
var errbuf strings.Builder
|
||||
if err := gitcmd.NewCommand("name-rev", "--stdin", "--name-only", "--always").
|
||||
WithDir(tmpBasePath).
|
||||
WithStdin(shasToNameReader).
|
||||
WithStdout(nameRevStdinWriter).
|
||||
WithStderr(stderr).
|
||||
Run(ctx); err != nil {
|
||||
_ = shasToNameReader.CloseWithError(fmt.Errorf("git name-rev [%s]: %w - %s", tmpBasePath, err, errbuf.String()))
|
||||
RunWithStderr(ctx); err != nil {
|
||||
_ = shasToNameReader.CloseWithError(fmt.Errorf("git name-rev [%s]: %w", tmpBasePath, err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ package pipeline
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -13,7 +12,6 @@ import (
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// RevListAllObjects runs rev-list --objects --all and writes to a pipewriter
|
||||
@@ -21,16 +19,11 @@ func RevListAllObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sy
|
||||
defer wg.Done()
|
||||
defer revListWriter.Close()
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
var errbuf strings.Builder
|
||||
cmd := gitcmd.NewCommand("rev-list", "--objects", "--all")
|
||||
if err := cmd.WithDir(basePath).
|
||||
WithStdout(revListWriter).
|
||||
WithStderr(stderr).
|
||||
Run(ctx); err != nil {
|
||||
log.Error("git rev-list --objects --all [%s]: %v - %s", basePath, err, errbuf.String())
|
||||
err = fmt.Errorf("git rev-list --objects --all [%s]: %w - %s", basePath, err, errbuf.String())
|
||||
_ = revListWriter.CloseWithError(err)
|
||||
RunWithStderr(ctx); err != nil {
|
||||
_ = revListWriter.CloseWithError(fmt.Errorf("git rev-list --objects --all [%s]: %w", basePath, err))
|
||||
errChan <- err
|
||||
}
|
||||
}
|
||||
@@ -39,18 +32,15 @@ func RevListAllObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sy
|
||||
func RevListObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.WaitGroup, tmpBasePath, headSHA, baseSHA string, errChan chan<- error) {
|
||||
defer wg.Done()
|
||||
defer revListWriter.Close()
|
||||
stderr := new(bytes.Buffer)
|
||||
var errbuf strings.Builder
|
||||
|
||||
cmd := gitcmd.NewCommand("rev-list", "--objects").AddDynamicArguments(headSHA)
|
||||
if baseSHA != "" {
|
||||
cmd = cmd.AddArguments("--not").AddDynamicArguments(baseSHA)
|
||||
}
|
||||
if err := cmd.WithDir(tmpBasePath).
|
||||
WithStdout(revListWriter).
|
||||
WithStderr(stderr).
|
||||
Run(ctx); err != nil {
|
||||
log.Error("git rev-list [%s]: %v - %s", tmpBasePath, err, errbuf.String())
|
||||
errChan <- fmt.Errorf("git rev-list [%s]: %w - %s", tmpBasePath, err, errbuf.String())
|
||||
RunWithStderr(ctx); err != nil {
|
||||
errChan <- fmt.Errorf("git rev-list [%s]: %w", tmpBasePath, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
@@ -83,22 +82,19 @@ func InitRepository(ctx context.Context, repoPath string, bare bool, objectForma
|
||||
|
||||
// IsEmpty Check if repository is empty.
|
||||
func (repo *Repository) IsEmpty() (bool, error) {
|
||||
var errbuf, output strings.Builder
|
||||
if err := gitcmd.NewCommand().
|
||||
stdout, _, err := gitcmd.NewCommand().
|
||||
AddOptionFormat("--git-dir=%s", repo.Path).
|
||||
AddArguments("rev-list", "-n", "1", "--all").
|
||||
WithDir(repo.Path).
|
||||
WithStdout(&output).
|
||||
WithStderr(&errbuf).
|
||||
Run(repo.Ctx); err != nil {
|
||||
if (err.Error() == "exit status 1" && strings.TrimSpace(errbuf.String()) == "") || err.Error() == "exit status 129" {
|
||||
RunStdString(repo.Ctx)
|
||||
if err != nil {
|
||||
if (gitcmd.IsErrorExitCode(err, 1) && err.Stderr() == "") || gitcmd.IsErrorExitCode(err, 129) {
|
||||
// git 2.11 exits with 129 if the repo is empty
|
||||
return true, nil
|
||||
}
|
||||
return true, fmt.Errorf("check empty: %w - %s", err, errbuf.String())
|
||||
return true, fmt.Errorf("check empty: %w", err)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(output.String()) == "", nil
|
||||
return strings.TrimSpace(stdout) == "", nil
|
||||
}
|
||||
|
||||
// CloneRepoOptions options when clone a repository
|
||||
@@ -171,16 +167,10 @@ func Clone(ctx context.Context, from, to string, opts CloneRepoOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
if err := cmd.
|
||||
return cmd.
|
||||
WithTimeout(opts.Timeout).
|
||||
WithEnv(envs).
|
||||
WithStdout(io.Discard).
|
||||
WithStderr(stderr).
|
||||
Run(ctx); err != nil {
|
||||
return gitcmd.ConcatenateError(err, stderr.String())
|
||||
}
|
||||
return nil
|
||||
RunWithStderr(ctx)
|
||||
}
|
||||
|
||||
// PushOptions options when push to remote
|
||||
|
||||
@@ -62,13 +62,7 @@ func (repo *Repository) CreateArchive(ctx context.Context, format ArchiveType, t
|
||||
cmd.AddOptionFormat("--format=%s", format.String())
|
||||
cmd.AddDynamicArguments(commitID)
|
||||
|
||||
var stderr strings.Builder
|
||||
err := cmd.WithDir(repo.Path).
|
||||
return cmd.WithDir(repo.Path).
|
||||
WithStdout(target).
|
||||
WithStderr(&stderr).
|
||||
Run(ctx)
|
||||
if err != nil {
|
||||
return gitcmd.ConcatenateError(err, stderr.String())
|
||||
}
|
||||
return nil
|
||||
RunWithStderr(ctx)
|
||||
}
|
||||
|
||||
@@ -101,23 +101,13 @@ func WalkShowRef(ctx context.Context, repoPath string, extraArgs gitcmd.TrustedC
|
||||
}()
|
||||
|
||||
go func() {
|
||||
stderrBuilder := &strings.Builder{}
|
||||
args := gitcmd.TrustedCmdArgs{"for-each-ref", "--format=%(objectname) %(refname)"}
|
||||
args = append(args, extraArgs...)
|
||||
err := gitcmd.NewCommand(args...).
|
||||
WithDir(repoPath).
|
||||
WithStdout(stdoutWriter).
|
||||
WithStderr(stderrBuilder).
|
||||
Run(ctx)
|
||||
if err != nil {
|
||||
if stderrBuilder.Len() == 0 {
|
||||
_ = stdoutWriter.Close()
|
||||
return
|
||||
}
|
||||
_ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, stderrBuilder.String()))
|
||||
} else {
|
||||
_ = stdoutWriter.Close()
|
||||
}
|
||||
RunWithStderr(ctx)
|
||||
_ = stdoutWriter.CloseWithError(err)
|
||||
}()
|
||||
|
||||
i := 0
|
||||
|
||||
@@ -232,7 +232,6 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
|
||||
_ = stdoutWriter.Close()
|
||||
}()
|
||||
go func() {
|
||||
stderr := strings.Builder{}
|
||||
gitCmd := gitcmd.NewCommand("rev-list").
|
||||
AddOptionFormat("--max-count=%d", setting.Git.CommitsRangeSize).
|
||||
AddOptionFormat("--skip=%d", (opts.Page-1)*setting.Git.CommitsRangeSize)
|
||||
@@ -251,13 +250,8 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
|
||||
gitCmd.AddDashesAndList(opts.File)
|
||||
err := gitCmd.WithDir(repo.Path).
|
||||
WithStdout(stdoutWriter).
|
||||
WithStderr(&stderr).
|
||||
Run(repo.Ctx)
|
||||
if err != nil {
|
||||
_ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
|
||||
} else {
|
||||
_ = stdoutWriter.Close()
|
||||
}
|
||||
RunWithStderr(repo.Ctx)
|
||||
_ = stdoutWriter.CloseWithError(err)
|
||||
}()
|
||||
|
||||
objectFormat, err := repo.GetObjectFormat()
|
||||
|
||||
@@ -34,7 +34,6 @@ func (l *lineCountWriter) Write(p []byte) (n int, err error) {
|
||||
func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparison bool) (int, error) {
|
||||
// Now there is git diff --shortstat but this appears to be slower than simply iterating with --nameonly
|
||||
w := &lineCountWriter{}
|
||||
stderr := new(bytes.Buffer)
|
||||
|
||||
separator := "..."
|
||||
if directComparison {
|
||||
@@ -47,24 +46,21 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
|
||||
AddArguments("--").
|
||||
WithDir(repo.Path).
|
||||
WithStdout(w).
|
||||
WithStderr(stderr).
|
||||
Run(repo.Ctx); err != nil {
|
||||
if strings.Contains(stderr.String(), "no merge base") {
|
||||
RunWithStderr(repo.Ctx); err != nil {
|
||||
if strings.Contains(err.Stderr(), "no merge base") {
|
||||
// git >= 2.28 now returns an error if base and head have become unrelated.
|
||||
// previously it would return the results of git diff -z --name-only base head so let's try that...
|
||||
w = &lineCountWriter{}
|
||||
stderr.Reset()
|
||||
if err = gitcmd.NewCommand("diff", "-z", "--name-only").
|
||||
AddDynamicArguments(base, head).
|
||||
AddArguments("--").
|
||||
WithDir(repo.Path).
|
||||
WithStdout(w).
|
||||
WithStderr(stderr).
|
||||
Run(repo.Ctx); err == nil {
|
||||
RunWithStderr(repo.Ctx); err == nil {
|
||||
return w.numLines, nil
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("%w: Stderr: %s", err, stderr)
|
||||
return 0, err
|
||||
}
|
||||
return w.numLines, nil
|
||||
}
|
||||
@@ -73,11 +69,9 @@ var patchCommits = regexp.MustCompile(`^From\s(\w+)\s`)
|
||||
|
||||
// GetDiff generates and returns patch data between given revisions, optimized for human readability
|
||||
func (repo *Repository) GetDiff(compareArg string, w io.Writer) error {
|
||||
stderr := new(bytes.Buffer)
|
||||
return gitcmd.NewCommand("diff", "-p").AddDynamicArguments(compareArg).
|
||||
WithDir(repo.Path).
|
||||
WithStdout(w).
|
||||
WithStderr(stderr).
|
||||
Run(repo.Ctx)
|
||||
}
|
||||
|
||||
@@ -92,11 +86,9 @@ func (repo *Repository) GetDiffBinary(compareArg string, w io.Writer) error {
|
||||
|
||||
// GetPatch generates and returns format-patch data between given revisions, able to be used with `git apply`
|
||||
func (repo *Repository) GetPatch(compareArg string, w io.Writer) error {
|
||||
stderr := new(bytes.Buffer)
|
||||
return gitcmd.NewCommand("format-patch", "--binary", "--stdout").AddDynamicArguments(compareArg).
|
||||
WithDir(repo.Path).
|
||||
WithStdout(w).
|
||||
WithStderr(stderr).
|
||||
Run(repo.Ctx)
|
||||
}
|
||||
|
||||
|
||||
@@ -101,21 +101,17 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
|
||||
return err
|
||||
}
|
||||
cmd := gitcmd.NewCommand("update-index", "--remove", "-z", "--index-info")
|
||||
stdout := new(bytes.Buffer)
|
||||
stderr := new(bytes.Buffer)
|
||||
buffer := new(bytes.Buffer)
|
||||
input := new(bytes.Buffer)
|
||||
for _, file := range filenames {
|
||||
if file != "" {
|
||||
// using format: mode SP type SP sha1 TAB path
|
||||
buffer.WriteString("0 blob " + objectFormat.EmptyObjectID().String() + "\t" + file + "\000")
|
||||
input.WriteString("0 blob " + objectFormat.EmptyObjectID().String() + "\t" + file + "\000")
|
||||
}
|
||||
}
|
||||
return cmd.
|
||||
WithDir(repo.Path).
|
||||
WithStdin(bytes.NewReader(buffer.Bytes())).
|
||||
WithStdout(stdout).
|
||||
WithStderr(stderr).
|
||||
Run(repo.Ctx)
|
||||
WithStdin(bytes.NewReader(input.Bytes())).
|
||||
RunWithStderr(repo.Ctx)
|
||||
}
|
||||
|
||||
type IndexObjectInfo struct {
|
||||
@@ -127,19 +123,15 @@ type IndexObjectInfo struct {
|
||||
// AddObjectsToIndex adds the provided object hashes to the index at the provided filenames
|
||||
func (repo *Repository) AddObjectsToIndex(objects ...IndexObjectInfo) error {
|
||||
cmd := gitcmd.NewCommand("update-index", "--add", "--replace", "-z", "--index-info")
|
||||
stdout := new(bytes.Buffer)
|
||||
stderr := new(bytes.Buffer)
|
||||
buffer := new(bytes.Buffer)
|
||||
input := new(bytes.Buffer)
|
||||
for _, object := range objects {
|
||||
// using format: mode SP type SP sha1 TAB path
|
||||
buffer.WriteString(object.Mode + " blob " + object.Object.String() + "\t" + object.Filename + "\000")
|
||||
input.WriteString(object.Mode + " blob " + object.Object.String() + "\t" + object.Filename + "\000")
|
||||
}
|
||||
return cmd.
|
||||
WithDir(repo.Path).
|
||||
WithStdin(bytes.NewReader(buffer.Bytes())).
|
||||
WithStdout(stdout).
|
||||
WithStderr(stderr).
|
||||
Run(repo.Ctx)
|
||||
WithStdin(bytes.NewReader(input.Bytes())).
|
||||
RunWithStderr(repo.Ctx)
|
||||
}
|
||||
|
||||
// AddObjectToIndex adds the provided object hash to the index at the provided filename
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
@@ -74,16 +73,12 @@ func (repo *Repository) hashObject(reader io.Reader, save bool) (string, error)
|
||||
} else {
|
||||
cmd = gitcmd.NewCommand("hash-object", "--stdin")
|
||||
}
|
||||
stdout := new(bytes.Buffer)
|
||||
stderr := new(bytes.Buffer)
|
||||
err := cmd.
|
||||
stdout, _, err := cmd.
|
||||
WithDir(repo.Path).
|
||||
WithStdin(reader).
|
||||
WithStdout(stdout).
|
||||
WithStderr(stderr).
|
||||
Run(repo.Ctx)
|
||||
RunStdString(repo.Ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(stdout.String()), nil
|
||||
return strings.TrimSpace(stdout), nil
|
||||
}
|
||||
|
||||
@@ -22,17 +22,11 @@ func (repo *Repository) GetRefsFiltered(pattern string) ([]*Reference, error) {
|
||||
}()
|
||||
|
||||
go func() {
|
||||
stderrBuilder := &strings.Builder{}
|
||||
err := gitcmd.NewCommand("for-each-ref").
|
||||
WithDir(repo.Path).
|
||||
WithStdout(stdoutWriter).
|
||||
WithStderr(stderrBuilder).
|
||||
Run(repo.Ctx)
|
||||
if err != nil {
|
||||
_ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, stderrBuilder.String()))
|
||||
} else {
|
||||
_ = stdoutWriter.Close()
|
||||
}
|
||||
_ = stdoutWriter.CloseWithError(err)
|
||||
}()
|
||||
|
||||
refs := make([]*Reference, 0)
|
||||
|
||||
@@ -72,11 +72,9 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
|
||||
gitCmd.AddArguments("--first-parent").AddDynamicArguments(branch)
|
||||
}
|
||||
|
||||
stderr := new(strings.Builder)
|
||||
err = gitCmd.
|
||||
WithDir(repo.Path).
|
||||
WithStdout(stdoutWriter).
|
||||
WithStderr(stderr).
|
||||
WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
|
||||
_ = stdoutWriter.Close()
|
||||
scanner := bufio.NewScanner(stdoutReader)
|
||||
@@ -146,9 +144,9 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
|
||||
_ = stdoutReader.Close()
|
||||
return nil
|
||||
}).
|
||||
Run(repo.Ctx)
|
||||
RunWithStderr(repo.Ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to get GetCodeActivityStats for repository.\nError: %w\nStderr: %s", err, stderr)
|
||||
return nil, fmt.Errorf("GetCodeActivityStats: %w", err)
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
|
||||
@@ -118,7 +118,6 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
|
||||
stdoutReader, stdoutWriter := io.Pipe()
|
||||
defer stdoutReader.Close()
|
||||
defer stdoutWriter.Close()
|
||||
stderr := strings.Builder{}
|
||||
|
||||
go func() {
|
||||
err := gitcmd.NewCommand("for-each-ref").
|
||||
@@ -126,13 +125,8 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
|
||||
AddArguments("--sort", "-*creatordate", "refs/tags").
|
||||
WithDir(repo.Path).
|
||||
WithStdout(stdoutWriter).
|
||||
WithStderr(&stderr).
|
||||
Run(repo.Ctx)
|
||||
if err != nil {
|
||||
_ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, stderr.String()))
|
||||
} else {
|
||||
_ = stdoutWriter.Close()
|
||||
}
|
||||
RunWithStderr(repo.Ctx)
|
||||
_ = stdoutWriter.CloseWithError(err)
|
||||
}()
|
||||
|
||||
var tags []*Tag
|
||||
|
||||
@@ -58,16 +58,12 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
|
||||
cmd.AddArguments("--no-gpg-sign")
|
||||
}
|
||||
|
||||
stdout := new(bytes.Buffer)
|
||||
stderr := new(bytes.Buffer)
|
||||
err := cmd.WithEnv(env).
|
||||
stdout, _, err := cmd.WithEnv(env).
|
||||
WithDir(repo.Path).
|
||||
WithStdin(messageBytes).
|
||||
WithStdout(stdout).
|
||||
WithStderr(stderr).
|
||||
Run(repo.Ctx)
|
||||
RunStdString(repo.Ctx)
|
||||
if err != nil {
|
||||
return nil, gitcmd.ConcatenateError(err, stderr.String())
|
||||
return nil, err
|
||||
}
|
||||
return NewIDFromString(strings.TrimSpace(stdout.String()))
|
||||
return NewIDFromString(strings.TrimSpace(stdout))
|
||||
}
|
||||
|
||||
@@ -36,12 +36,7 @@ func CreateArchive(ctx context.Context, repo Repository, format string, target i
|
||||
paths[i] = path.Clean(paths[i])
|
||||
}
|
||||
cmd.AddDynamicArguments(paths...)
|
||||
|
||||
var stderr strings.Builder
|
||||
if err := RunCmd(ctx, repo, cmd.WithStdout(target).WithStderr(&stderr)); err != nil {
|
||||
return gitcmd.ConcatenateError(err, stderr.String())
|
||||
}
|
||||
return nil
|
||||
return RunCmdWithStderr(ctx, repo, cmd.WithStdout(target))
|
||||
}
|
||||
|
||||
// CreateBundle create bundle content to the target path
|
||||
|
||||
@@ -12,16 +12,16 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
func LineBlame(ctx context.Context, repo Repository, revision, file string, line uint) (string, error) {
|
||||
return RunCmdString(ctx, repo,
|
||||
stdout, _, err := RunCmdString(ctx, repo,
|
||||
gitcmd.NewCommand("blame").
|
||||
AddOptionFormat("-L %d,%d", line, line).
|
||||
AddOptionValues("-p", revision).
|
||||
AddDashesAndList(file))
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
// BlamePart represents block of blame - continuous lines with one sha
|
||||
@@ -173,17 +173,10 @@ func CreateBlameReader(ctx context.Context, objectFormat git.ObjectFormat, repo
|
||||
return nil, err
|
||||
}
|
||||
go func() {
|
||||
stderr := bytes.Buffer{}
|
||||
// TODO: it doesn't work for directories (the directories shouldn't be "blamed"), and the "err" should be returned by "Read" but not by "Close"
|
||||
err := RunCmd(ctx, repo, cmd.WithUseContextTimeout(true).
|
||||
WithStdout(stdout).
|
||||
WithStderr(&stderr),
|
||||
)
|
||||
err := RunCmdWithStderr(ctx, repo, cmd.WithUseContextTimeout(true).WithStdout(stdout))
|
||||
done <- err
|
||||
_ = stdout.Close()
|
||||
if err != nil {
|
||||
log.Error("Error running git blame (dir: %v): %v, stderr: %v", repoPath, err, stderr.String())
|
||||
}
|
||||
}()
|
||||
|
||||
bufferedReader := bufio.NewReader(reader)
|
||||
|
||||
@@ -36,14 +36,14 @@ func GetBranchCommitID(ctx context.Context, repo Repository, branch string) (str
|
||||
|
||||
// SetDefaultBranch sets default branch of repository.
|
||||
func SetDefaultBranch(ctx context.Context, repo Repository, name string) error {
|
||||
_, err := RunCmdString(ctx, repo, gitcmd.NewCommand("symbolic-ref", "HEAD").
|
||||
_, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("symbolic-ref", "HEAD").
|
||||
AddDynamicArguments(git.BranchPrefix+name))
|
||||
return err
|
||||
}
|
||||
|
||||
// GetDefaultBranch gets default branch of repository.
|
||||
func GetDefaultBranch(ctx context.Context, repo Repository) (string, error) {
|
||||
stdout, err := RunCmdString(ctx, repo, gitcmd.NewCommand("symbolic-ref", "HEAD"))
|
||||
stdout, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("symbolic-ref", "HEAD"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func GetDefaultBranch(ctx context.Context, repo Repository) (string, error) {
|
||||
|
||||
// IsReferenceExist returns true if given reference exists in the repository.
|
||||
func IsReferenceExist(ctx context.Context, repo Repository, name string) bool {
|
||||
_, err := RunCmdString(ctx, repo, gitcmd.NewCommand("show-ref", "--verify").AddDashesAndList(name))
|
||||
_, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("show-ref", "--verify").AddDashesAndList(name))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func DeleteBranch(ctx context.Context, repo Repository, name string, force bool)
|
||||
}
|
||||
|
||||
cmd.AddDashesAndList(name)
|
||||
_, err := RunCmdString(ctx, repo, cmd)
|
||||
_, _, err := RunCmdString(ctx, repo, cmd)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -85,12 +85,12 @@ func CreateBranch(ctx context.Context, repo Repository, branch, oldbranchOrCommi
|
||||
cmd := gitcmd.NewCommand("branch")
|
||||
cmd.AddDashesAndList(branch, oldbranchOrCommit)
|
||||
|
||||
_, err := RunCmdString(ctx, repo, cmd)
|
||||
_, _, err := RunCmdString(ctx, repo, cmd)
|
||||
return err
|
||||
}
|
||||
|
||||
// RenameBranch rename a branch
|
||||
func RenameBranch(ctx context.Context, repo Repository, from, to string) error {
|
||||
_, err := RunCmdString(ctx, repo, gitcmd.NewCommand("branch", "-m").AddDynamicArguments(from, to))
|
||||
_, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("branch", "-m").AddDynamicArguments(from, to))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -13,11 +13,14 @@ func RunCmd(ctx context.Context, repo Repository, cmd *gitcmd.Command) error {
|
||||
return cmd.WithDir(repoPath(repo)).WithParentCallerInfo().Run(ctx)
|
||||
}
|
||||
|
||||
func RunCmdString(ctx context.Context, repo Repository, cmd *gitcmd.Command) (string, error) {
|
||||
res, _, err := cmd.WithDir(repoPath(repo)).WithParentCallerInfo().RunStdString(ctx)
|
||||
return res, err
|
||||
func RunCmdString(ctx context.Context, repo Repository, cmd *gitcmd.Command) (string, string, gitcmd.RunStdError) {
|
||||
return cmd.WithDir(repoPath(repo)).WithParentCallerInfo().RunStdString(ctx)
|
||||
}
|
||||
|
||||
func RunCmdBytes(ctx context.Context, repo Repository, cmd *gitcmd.Command) ([]byte, []byte, error) {
|
||||
func RunCmdBytes(ctx context.Context, repo Repository, cmd *gitcmd.Command) ([]byte, []byte, gitcmd.RunStdError) {
|
||||
return cmd.WithDir(repoPath(repo)).WithParentCallerInfo().RunStdBytes(ctx)
|
||||
}
|
||||
|
||||
func RunCmdWithStderr(ctx context.Context, repo Repository, cmd *gitcmd.Command) gitcmd.RunStdError {
|
||||
return cmd.WithDir(repoPath(repo)).WithParentCallerInfo().RunWithStderr(ctx)
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ func AllCommitsCount(ctx context.Context, repo Repository, hidePRRefs bool, file
|
||||
cmd.AddDashesAndList(files...)
|
||||
}
|
||||
|
||||
stdout, err := RunCmdString(ctx, repo, cmd)
|
||||
stdout, _, err := RunCmdString(ctx, repo, cmd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -102,7 +102,7 @@ func GetFullCommitID(ctx context.Context, repo Repository, shortID string) (stri
|
||||
|
||||
// GetLatestCommitTime returns time for latest commit in repository (across all branches)
|
||||
func GetLatestCommitTime(ctx context.Context, repo Repository) (time.Time, error) {
|
||||
stdout, err := RunCmdString(ctx, repo,
|
||||
stdout, _, err := RunCmdString(ctx, repo,
|
||||
gitcmd.NewCommand("for-each-ref", "--sort=-committerdate", git.BranchPrefix, "--count", "1", "--format=%(committerdate)"))
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
|
||||
@@ -5,7 +5,6 @@ package gitrepo
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
|
||||
@@ -76,16 +75,14 @@ func GetCommitFileStatus(ctx context.Context, repo Repository, commitID string)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
err := gitcmd.NewCommand("log", "--name-status", "-m", "--pretty=format:", "--first-parent", "--no-renames", "-z", "-1").
|
||||
AddDynamicArguments(commitID).
|
||||
WithDir(repoPath(repo)).
|
||||
WithStdout(w).
|
||||
WithStderr(stderr).
|
||||
Run(ctx)
|
||||
w.Close() // Close writer to exit parsing goroutine
|
||||
RunWithStderr(ctx)
|
||||
_ = w.Close() // Close writer to exit parsing goroutine
|
||||
if err != nil {
|
||||
return nil, gitcmd.ConcatenateError(err, stderr.String())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
<-done
|
||||
|
||||
@@ -22,7 +22,7 @@ type DivergeObject struct {
|
||||
func GetDivergingCommits(ctx context.Context, repo Repository, baseBranch, targetBranch string) (*DivergeObject, error) {
|
||||
cmd := gitcmd.NewCommand("rev-list", "--count", "--left-right").
|
||||
AddDynamicArguments(baseBranch + "..." + targetBranch).AddArguments("--")
|
||||
stdout, err1 := RunCmdString(ctx, repo, cmd)
|
||||
stdout, _, err1 := RunCmdString(ctx, repo, cmd)
|
||||
if err1 != nil {
|
||||
return nil, err1
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func GitConfigGet(ctx context.Context, repo Repository, key string) (string, error) {
|
||||
result, err := RunCmdString(ctx, repo, gitcmd.NewCommand("config", "--get").
|
||||
result, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("config", "--get").
|
||||
AddDynamicArguments(key))
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -27,7 +27,7 @@ func getRepoConfigLockKey(repoStoragePath string) string {
|
||||
// GitConfigAdd add a git configuration key to a specific value for the given repository.
|
||||
func GitConfigAdd(ctx context.Context, repo Repository, key, value string) error {
|
||||
return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error {
|
||||
_, err := RunCmdString(ctx, repo, gitcmd.NewCommand("config", "--add").
|
||||
_, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("config", "--add").
|
||||
AddDynamicArguments(key, value))
|
||||
return err
|
||||
})
|
||||
@@ -38,7 +38,7 @@ func GitConfigAdd(ctx context.Context, repo Repository, key, value string) error
|
||||
// If the key exists, it will be updated to the new value.
|
||||
func GitConfigSet(ctx context.Context, repo Repository, key, value string) error {
|
||||
return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error {
|
||||
_, err := RunCmdString(ctx, repo, gitcmd.NewCommand("config").
|
||||
_, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("config").
|
||||
AddDynamicArguments(key, value))
|
||||
return err
|
||||
})
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
package gitrepo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -22,7 +21,7 @@ func GetDiffShortStatByCmdArgs(ctx context.Context, repo Repository, trustedArgs
|
||||
// we get:
|
||||
// " 9902 files changed, 2034198 insertions(+), 298800 deletions(-)\n"
|
||||
cmd := gitcmd.NewCommand("diff", "--shortstat").AddArguments(trustedArgs...).AddDynamicArguments(dynamicArgs...)
|
||||
stdout, err := RunCmdString(ctx, repo, cmd)
|
||||
stdout, _, err := RunCmdString(ctx, repo, cmd)
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
@@ -65,12 +64,8 @@ func parseDiffStat(stdout string) (numFiles, totalAdditions, totalDeletions int,
|
||||
|
||||
// GetReverseRawDiff dumps the reverse diff results of repository in given commit ID to io.Writer.
|
||||
func GetReverseRawDiff(ctx context.Context, repo Repository, commitID string, writer io.Writer) error {
|
||||
stderr := new(bytes.Buffer)
|
||||
if err := RunCmd(ctx, repo, gitcmd.NewCommand("show", "--pretty=format:revert %H%n", "-R").
|
||||
return RunCmdWithStderr(ctx, repo, gitcmd.NewCommand("show", "--pretty=format:revert %H%n", "-R").
|
||||
AddDynamicArguments(commitID).
|
||||
WithStdout(writer).
|
||||
WithStderr(stderr)); err != nil {
|
||||
return fmt.Errorf("GetReverseRawDiff: %w - %s", err, stderr)
|
||||
}
|
||||
return nil
|
||||
WithStdout(writer),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
// MergeBase checks and returns merge base of two commits.
|
||||
func MergeBase(ctx context.Context, repo Repository, baseCommitID, headCommitID string) (string, error) {
|
||||
mergeBase, err := RunCmdString(ctx, repo, gitcmd.NewCommand("merge-base").
|
||||
mergeBase, _, err := RunCmdString(ctx, repo, gitcmd.NewCommand("merge-base").
|
||||
AddDashesAndList(baseCommitID, headCommitID))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get merge-base of %s and %s failed: %w", baseCommitID, headCommitID, err)
|
||||
|
||||
@@ -6,8 +6,6 @@ package gitrepo
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
@@ -36,7 +34,7 @@ func GitRemoteAdd(ctx context.Context, repo Repository, remoteName, remoteURL st
|
||||
return errors.New("unknown remote option: " + string(options[0]))
|
||||
}
|
||||
}
|
||||
_, err := RunCmdString(ctx, repo, cmd.AddDynamicArguments(remoteName, remoteURL))
|
||||
_, _, err := RunCmdString(ctx, repo, cmd.AddDynamicArguments(remoteName, remoteURL))
|
||||
return err
|
||||
})
|
||||
}
|
||||
@@ -44,7 +42,7 @@ func GitRemoteAdd(ctx context.Context, repo Repository, remoteName, remoteURL st
|
||||
func GitRemoteRemove(ctx context.Context, repo Repository, remoteName string) error {
|
||||
return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error {
|
||||
cmd := gitcmd.NewCommand("remote", "rm").AddDynamicArguments(remoteName)
|
||||
_, err := RunCmdString(ctx, repo, cmd)
|
||||
_, _, err := RunCmdString(ctx, repo, cmd)
|
||||
return err
|
||||
})
|
||||
}
|
||||
@@ -60,21 +58,3 @@ func GitRemoteGetURL(ctx context.Context, repo Repository, remoteName string) (*
|
||||
}
|
||||
return giturl.ParseGitURL(addr)
|
||||
}
|
||||
|
||||
// GitRemotePrune prunes the remote branches that no longer exist in the remote repository.
|
||||
func GitRemotePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error {
|
||||
return RunCmd(ctx, repo, gitcmd.NewCommand("remote", "prune").
|
||||
AddDynamicArguments(remoteName).
|
||||
WithTimeout(timeout).
|
||||
WithStdout(stdout).
|
||||
WithStderr(stderr))
|
||||
}
|
||||
|
||||
// GitRemoteUpdatePrune updates the remote branches and prunes the ones that no longer exist in the remote repository.
|
||||
func GitRemoteUpdatePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error {
|
||||
return RunCmd(ctx, repo, gitcmd.NewCommand("remote", "update", "--prune").
|
||||
AddDynamicArguments(remoteName).
|
||||
WithTimeout(timeout).
|
||||
WithStdout(stdout).
|
||||
WithStderr(stderr))
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ func (b *Indexer) addUpdate(ctx context.Context, catFileBatch git.CatFileBatch,
|
||||
var err error
|
||||
if !update.Sized {
|
||||
var stdout string
|
||||
stdout, err = gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("cat-file", "-s").AddDynamicArguments(update.BlobSha))
|
||||
stdout, _, err = gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("cat-file", "-s").AddDynamicArguments(update.BlobSha))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ func (b *Indexer) addUpdate(ctx context.Context, catFileBatch git.CatFileBatch,
|
||||
var err error
|
||||
if !update.Sized {
|
||||
var stdout string
|
||||
stdout, err = gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("cat-file", "-s").AddDynamicArguments(update.BlobSha))
|
||||
stdout, _, err = gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("cat-file", "-s").AddDynamicArguments(update.BlobSha))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
func getDefaultBranchSha(ctx context.Context, repo *repo_model.Repository) (string, error) {
|
||||
stdout, err := gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("show-ref", "-s").AddDynamicArguments(git.BranchPrefix+repo.DefaultBranch))
|
||||
stdout, _, err := gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("show-ref", "-s").AddDynamicArguments(git.BranchPrefix+repo.DefaultBranch))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -35,7 +35,7 @@ func getRepoChanges(ctx context.Context, repo *repo_model.Repository, revision s
|
||||
needGenesis := len(status.CommitSha) == 0
|
||||
if !needGenesis {
|
||||
hasAncestorCmd := gitcmd.NewCommand("merge-base").AddDynamicArguments(status.CommitSha, revision)
|
||||
stdout, _ := gitrepo.RunCmdString(ctx, repo, hasAncestorCmd)
|
||||
stdout, _, _ := gitrepo.RunCmdString(ctx, repo, hasAncestorCmd) // FIXME: error is not handled
|
||||
needGenesis = len(stdout) == 0
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision s
|
||||
// nonGenesisChanges get changes since the previous indexer update
|
||||
func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*internal.RepoChanges, error) {
|
||||
diffCmd := gitcmd.NewCommand("diff", "--name-status").AddDynamicArguments(repo.CodeIndexerStatus.CommitSha, revision)
|
||||
stdout, runErr := gitrepo.RunCmdString(ctx, repo, diffCmd)
|
||||
stdout, _, runErr := gitrepo.RunCmdString(ctx, repo, diffCmd)
|
||||
if runErr != nil {
|
||||
// previous commit sha may have been removed by a force push, so
|
||||
// try rebuilding from scratch
|
||||
|
||||
Reference in New Issue
Block a user