Refactor git command stderr handling (#36402)

And clean up legacy fragile & incorrect logic
This commit is contained in:
wxiaoguang
2026-01-19 07:10:33 +08:00
committed by GitHub
parent fafd1db19e
commit 72be55f7d3
69 changed files with 345 additions and 627 deletions

View File

@@ -88,7 +88,7 @@ func GitGcRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Du
command := gitcmd.NewCommand("gc").AddArguments(args...)
var stdout string
var err error
stdout, err = gitrepo.RunCmdString(ctx, repo, command)
stdout, _, err = gitrepo.RunCmdString(ctx, repo, command)
if err != nil {
log.Error("Repository garbage collection failed for %-v. Stdout: %s\nError: %v", repo, stdout, err)
desc := fmt.Sprintf("Repository garbage collection failed for %s. Stdout: %s\nError: %v", repo.RelativePath(), stdout, err)

View File

@@ -131,10 +131,8 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int
gitCmd.AddDynamicArguments(baseCommit.ID.String())
var extendedCommitStats []*ExtendedCommitStats
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)
@@ -191,9 +189,9 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int
_ = stdoutReader.Close()
return nil
}).
Run(repo.Ctx)
RunWithStderr(repo.Ctx)
if err != nil {
return nil, fmt.Errorf("Failed to get ContributorsCommitStats for repository.\nError: %w\nStderr: %s", err, stderr)
return nil, fmt.Errorf("ContributorsCommitStats: %w", err)
}
return extendedCommitStats, nil

View File

@@ -315,7 +315,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, owner *user_model.User,
licenses = append(licenses, opts.License)
var stdout string
stdout, err = gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("rev-parse", "HEAD"))
stdout, _, err = gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("rev-parse", "HEAD"))
if err != nil {
log.Error("CreateRepository(git rev-parse HEAD) in %v: Stdout: %s\nError: %v", repo, stdout, err)
return nil, fmt.Errorf("CreateRepository(git rev-parse HEAD): %w", err)
@@ -476,7 +476,7 @@ func updateGitRepoAfterCreate(ctx context.Context, repo *repo_model.Repository)
return fmt.Errorf("checkDaemonExportOK: %w", err)
}
if stdout, err := gitrepo.RunCmdString(ctx, repo,
if stdout, _, err := gitrepo.RunCmdString(ctx, repo,
gitcmd.NewCommand("update-server-info")); err != nil {
log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
return fmt.Errorf("CreateRepository(git update-server-info): %w", err)

View File

@@ -164,20 +164,15 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
}
}
stdout := &strings.Builder{}
stderr := &strings.Builder{}
cmdApply := gitcmd.NewCommand("apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary")
if git.DefaultFeatures().CheckVersionAtLeast("2.32") {
cmdApply.AddArguments("-3")
}
if err := cmdApply.WithDir(t.basePath).
WithStdout(stdout).
WithStderr(stderr).
WithStdin(strings.NewReader(opts.Content)).
Run(ctx); err != nil {
return nil, fmt.Errorf("Error: Stdout: %s\nStderr: %s\nErr: %w", stdout.String(), stderr.String(), err)
RunWithStderr(ctx); err != nil {
return nil, fmt.Errorf("git apply error: %w", err)
}
// Now write the tree

View File

@@ -98,7 +98,7 @@ func (t *TemporaryUploadRepository) Init(ctx context.Context, objectFormatName s
// SetDefaultIndex sets the git index to our HEAD
func (t *TemporaryUploadRepository) SetDefaultIndex(ctx context.Context) error {
if _, _, err := gitcmd.NewCommand("read-tree", "HEAD").WithDir(t.basePath).RunStdString(ctx); err != nil {
if err := gitcmd.NewCommand("read-tree", "HEAD").WithDir(t.basePath).RunWithStderr(ctx); err != nil {
return fmt.Errorf("SetDefaultIndex: %w", err)
}
return nil
@@ -106,7 +106,7 @@ func (t *TemporaryUploadRepository) SetDefaultIndex(ctx context.Context) error {
// RefreshIndex looks at the current index and checks to see if merges or updates are needed by checking stat() information.
func (t *TemporaryUploadRepository) RefreshIndex(ctx context.Context) error {
if _, _, err := gitcmd.NewCommand("update-index", "--refresh").WithDir(t.basePath).RunStdString(ctx); err != nil {
if err := gitcmd.NewCommand("update-index", "--refresh").WithDir(t.basePath).RunWithStderr(ctx); err != nil {
return fmt.Errorf("RefreshIndex: %w", err)
}
return nil
@@ -115,16 +115,11 @@ func (t *TemporaryUploadRepository) RefreshIndex(ctx context.Context) error {
// LsFiles checks if the given filename arguments are in the index
func (t *TemporaryUploadRepository) LsFiles(ctx context.Context, filenames ...string) ([]string, error) {
stdOut := new(bytes.Buffer)
stdErr := new(bytes.Buffer)
if err := gitcmd.NewCommand("ls-files", "-z").AddDashesAndList(filenames...).
WithDir(t.basePath).
WithStdout(stdOut).
WithStderr(stdErr).
Run(ctx); err != nil {
log.Error("Unable to run git ls-files for temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String())
err = fmt.Errorf("Unable to run git ls-files for temporary repo of: %s Error: %w\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String())
return nil, err
RunWithStderr(ctx); err != nil {
return nil, fmt.Errorf("unable to run git ls-files for temporary repo of: %s, error: %w", t.repo.FullName(), err)
}
fileList := make([]string, 0, len(filenames))
@@ -149,8 +144,6 @@ func (t *TemporaryUploadRepository) RemoveFilesFromIndex(ctx context.Context, fi
if err != nil {
return fmt.Errorf("unable to get object format for temporary repo: %q, error: %w", t.repo.FullName(), err)
}
stdOut := new(bytes.Buffer)
stdErr := new(bytes.Buffer)
stdIn := new(bytes.Buffer)
for _, file := range filenames {
if file != "" {
@@ -162,11 +155,9 @@ func (t *TemporaryUploadRepository) RemoveFilesFromIndex(ctx context.Context, fi
if err := gitcmd.NewCommand("update-index", "--remove", "-z", "--index-info").
WithDir(t.basePath).
WithStdout(stdOut).
WithStderr(stdErr).
WithStdin(stdIn).
Run(ctx); err != nil {
return fmt.Errorf("unable to update-index for temporary repo: %q, error: %w\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String())
RunWithStderr(ctx); err != nil {
return fmt.Errorf("unable to update-index for temporary repo: %q, error: %w", t.repo.FullName(), err)
}
return nil
}
@@ -174,16 +165,12 @@ func (t *TemporaryUploadRepository) RemoveFilesFromIndex(ctx context.Context, fi
// HashObjectAndWrite writes the provided content to the object db and returns its hash
func (t *TemporaryUploadRepository) HashObjectAndWrite(ctx context.Context, content io.Reader) (string, error) {
stdOut := new(bytes.Buffer)
stdErr := new(bytes.Buffer)
if err := gitcmd.NewCommand("hash-object", "-w", "--stdin").
WithDir(t.basePath).
WithStdout(stdOut).
WithStderr(stdErr).
WithStdin(content).
Run(ctx); err != nil {
log.Error("Unable to hash-object to temporary repo: %s (%s) Error: %v\nstdout: %s\nstderr: %s", t.repo.FullName(), t.basePath, err, stdOut.String(), stdErr.String())
return "", fmt.Errorf("Unable to hash-object to temporary repo: %s Error: %w\nstdout: %s\nstderr: %s", t.repo.FullName(), err, stdOut.String(), stdErr.String())
RunWithStderr(ctx); err != nil {
return "", fmt.Errorf("unable to hash-object to temporary repo: %s, error: %w", t.repo.FullName(), err)
}
return strings.TrimSpace(stdOut.String()), nil
@@ -191,17 +178,15 @@ func (t *TemporaryUploadRepository) HashObjectAndWrite(ctx context.Context, cont
// AddObjectToIndex adds the provided object hash to the index with the provided mode and path
func (t *TemporaryUploadRepository) AddObjectToIndex(ctx context.Context, mode, objectHash, objectPath string) error {
if _, _, err := gitcmd.NewCommand("update-index", "--add", "--replace", "--cacheinfo").
AddDynamicArguments(mode, objectHash, objectPath).WithDir(t.basePath).RunStdString(ctx); err != nil {
stderr := err.Error()
if matched, _ := regexp.MatchString(".*Invalid path '.*", stderr); matched {
if err := gitcmd.NewCommand("update-index", "--add", "--replace", "--cacheinfo").
AddDynamicArguments(mode, objectHash, objectPath).WithDir(t.basePath).RunWithStderr(ctx); err != nil {
if matched, _ := regexp.MatchString(".*Invalid path '.*", err.Stderr()); matched {
return ErrFilePathInvalid{
Message: objectPath,
Path: objectPath,
}
}
log.Error("Unable to add object to index: %s %s %s in temporary repo %s(%s) Error: %v", mode, objectHash, objectPath, t.repo.FullName(), t.basePath, err)
return fmt.Errorf("Unable to add object to index at %s in temporary repo %s Error: %w", objectPath, t.repo.FullName(), err)
return fmt.Errorf("unable to add object to index at %s in temporary repo %s, error: %w", objectPath, t.repo.FullName(), err)
}
return nil
}
@@ -342,18 +327,13 @@ func (t *TemporaryUploadRepository) CommitTree(ctx context.Context, opts *Commit
)
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
if err := cmdCommitTree.
WithEnv(env).
WithDir(t.basePath).
WithStdout(stdout).
WithStderr(stderr).
WithStdin(messageBytes).
Run(ctx); err != nil {
log.Error("Unable to commit-tree in temporary repo: %s (%s) Error: %v\nStdout: %s\nStderr: %s",
t.repo.FullName(), t.basePath, err, stdout, stderr)
return "", fmt.Errorf("Unable to commit-tree in temporary repo: %s Error: %w\nStdout: %s\nStderr: %s",
t.repo.FullName(), err, stdout, stderr)
RunWithStderr(ctx); err != nil {
return "", fmt.Errorf("unable to commit-tree in temporary repo: %s Error: %w", t.repo.FullName(), err)
}
return strings.TrimSpace(stdout.String()), nil
}
@@ -390,13 +370,11 @@ func (t *TemporaryUploadRepository) DiffIndex(ctx context.Context) (*gitdiff.Dif
_ = stdoutReader.Close()
_ = stdoutWriter.Close()
}()
stderr := new(bytes.Buffer)
var diff *gitdiff.Diff
err = gitcmd.NewCommand("diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD").
WithTimeout(30 * time.Second).
WithDir(t.basePath).
WithStdout(stdoutWriter).
WithStderr(stderr).
WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close()
defer cancel()
@@ -409,9 +387,8 @@ func (t *TemporaryUploadRepository) DiffIndex(ctx context.Context) (*gitdiff.Dif
}
return nil
}).
Run(ctx)
RunWithStderr(ctx)
if err != nil && !git.IsErrCanceledOrKilled(err) {
log.Error("Unable to diff-index in temporary repo %s (%s). Error: %v\nStderr: %s", t.repo.FullName(), t.basePath, err, stderr)
return nil, fmt.Errorf("unable to run diff-index pipeline in temporary repo: %w", err)
}

View File

@@ -8,7 +8,6 @@ import (
"bytes"
"context"
"os"
"strings"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd"
@@ -45,7 +44,6 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
}
graph := NewGraph()
stderr := new(strings.Builder)
stdoutReader, stdoutWriter, err := os.Pipe()
if err != nil {
return nil, err
@@ -57,7 +55,6 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
if err := graphCmd.
WithDir(r.Path).
WithStdout(stdoutWriter).
WithStderr(stderr).
WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close()
defer stdoutReader.Close()
@@ -110,7 +107,7 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
}
return scanner.Err()
}).
Run(r.Ctx); err != nil {
RunWithStderr(r.Ctx); err != nil {
return graph, err
}
return graph, nil

View File

@@ -225,7 +225,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
// this is necessary for sync local tags from remote
configName := fmt.Sprintf("remote.%s.fetch", mirrorModel.GetRemoteName())
if stdout, err := gitrepo.RunCmdString(ctx, repo,
if stdout, _, err := gitrepo.RunCmdString(ctx, repo,
gitcmd.NewCommand("config").
AddOptionValues("--add", configName, `+refs/tags/*:refs/tags/*`)); err != nil {
log.Error("MigrateRepositoryGitData(git config --add <remote> +refs/tags/*:refs/tags/*) in %v: Stdout: %s\nError: %v", repo, stdout, err)