Move some functions to gitrepo package (#35543)

Refactor Git command functions to use WithXXX methods instead of
exposing RunOpts.
This change simplifies reuse across gitrepo and improves consistency,
encapsulation, and maintainability of command options.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Lunny Xiao
2025-10-07 02:06:51 -07:00
committed by GitHub
parent c9e7fde8b3
commit 69f5ee970c
114 changed files with 1188 additions and 919 deletions

View File

@@ -121,7 +121,7 @@ func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error {
} }
log.Trace("Processing next %d repos of %d", len(repos), count) log.Trace("Processing next %d repos of %d", len(repos), count)
for _, repo := range repos { for _, repo := range repos {
log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RepoPath()) log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RelativePath())
gitRepo, err := gitrepo.OpenRepository(ctx, repo) gitRepo, err := gitrepo.OpenRepository(ctx, repo)
if err != nil { if err != nil {
log.Warn("OpenRepository: %v", err) log.Warn("OpenRepository: %v", err)

View File

@@ -313,7 +313,7 @@ func runHookPostReceive(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
// First of all run update-server-info no matter what // First of all run update-server-info no matter what
if _, _, err := gitcmd.NewCommand("update-server-info").RunStdString(ctx, nil); err != nil { if _, _, err := gitcmd.NewCommand("update-server-info").RunStdString(ctx); err != nil {
return fmt.Errorf("failed to call 'git update-server-info': %w", err) return fmt.Errorf("failed to call 'git update-server-info': %w", err)
} }

View File

@@ -84,17 +84,17 @@ func FixMergeBase(ctx context.Context, x *xorm.Engine) error {
if !pr.HasMerged { if !pr.HasMerged {
var err error var err error
pr.MergeBase, _, err = gitcmd.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) pr.MergeBase, _, err = gitcmd.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).WithDir(repoPath).RunStdString(ctx)
if err != nil { if err != nil {
var err2 error var err2 error
pr.MergeBase, _, err2 = gitcmd.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix+pr.BaseBranch).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) pr.MergeBase, _, err2 = gitcmd.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).WithDir(repoPath).RunStdString(ctx)
if err2 != nil { if err2 != nil {
log.Error("Unable to get merge base for PR ID %d, Index %d in %s/%s. Error: %v & %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err, err2) log.Error("Unable to get merge base for PR ID %d, Index %d in %s/%s. Error: %v & %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err, err2)
continue continue
} }
} }
} else { } else {
parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).WithDir(repoPath).RunStdString(ctx)
if err != nil { if err != nil {
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue continue
@@ -108,7 +108,7 @@ func FixMergeBase(ctx context.Context, x *xorm.Engine) error {
refs = append(refs, gitRefName) refs = append(refs, gitRefName)
cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...) cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...)
pr.MergeBase, _, err = cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) pr.MergeBase, _, err = cmd.WithDir(repoPath).RunStdString(ctx)
if err != nil { if err != nil {
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue continue

View File

@@ -80,7 +80,7 @@ func RefixMergeBase(ctx context.Context, x *xorm.Engine) error {
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index) gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).WithDir(repoPath).RunStdString(ctx)
if err != nil { if err != nil {
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue continue
@@ -95,7 +95,7 @@ func RefixMergeBase(ctx context.Context, x *xorm.Engine) error {
refs = append(refs, gitRefName) refs = append(refs, gitRefName)
cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...) cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...)
pr.MergeBase, _, err = cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) pr.MergeBase, _, err = cmd.WithDir(repoPath).RunStdString(ctx)
if err != nil { if err != nil {
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue continue

View File

@@ -77,13 +77,12 @@ func NewBatchChecker(repo *git.Repository, treeish string, attributes []string)
_ = lw.Close() _ = lw.Close()
}() }()
stdErr := new(bytes.Buffer) stdErr := new(bytes.Buffer)
err := cmd.Run(ctx, &gitcmd.RunOpts{ err := cmd.WithEnv(envs).
Env: envs, WithDir(repo.Path).
Dir: repo.Path, WithStdin(stdinReader).
Stdin: stdinReader, WithStdout(lw).
Stdout: lw, WithStderr(stdErr).
Stderr: stdErr, Run(ctx)
})
if err != nil && !git.IsErrCanceledOrKilled(err) { if err != nil && !git.IsErrCanceledOrKilled(err) {
log.Error("Attribute checker for commit %s exits with error: %v", treeish, err) log.Error("Attribute checker for commit %s exits with error: %v", treeish, err)

View File

@@ -71,12 +71,11 @@ func CheckAttributes(ctx context.Context, gitRepo *git.Repository, treeish strin
stdOut := new(bytes.Buffer) stdOut := new(bytes.Buffer)
stdErr := new(bytes.Buffer) stdErr := new(bytes.Buffer)
if err := cmd.Run(ctx, &gitcmd.RunOpts{ if err := cmd.WithEnv(append(os.Environ(), envs...)).
Env: append(os.Environ(), envs...), WithDir(gitRepo.Path).
Dir: gitRepo.Path, WithStdout(stdOut).
Stdout: stdOut, WithStderr(stdErr).
Stderr: stdErr, Run(ctx); err != nil {
}); err != nil {
return nil, fmt.Errorf("failed to run check-attr: %w\n%s\n%s", err, stdOut.String(), stdErr.String()) return nil, fmt.Errorf("failed to run check-attr: %w\n%s\n%s", err, stdOut.String(), stdErr.String())
} }

View File

@@ -31,10 +31,9 @@ type WriteCloserError interface {
func ensureValidGitRepository(ctx context.Context, repoPath string) error { func ensureValidGitRepository(ctx context.Context, repoPath string) error {
stderr := strings.Builder{} stderr := strings.Builder{}
err := gitcmd.NewCommand("rev-parse"). err := gitcmd.NewCommand("rev-parse").
Run(ctx, &gitcmd.RunOpts{ WithDir(repoPath).
Dir: repoPath, WithStderr(&stderr).
Stderr: &stderr, Run(ctx)
})
if err != nil { if err != nil {
return gitcmd.ConcatenateError(err, (&stderr).String()) return gitcmd.ConcatenateError(err, (&stderr).String())
} }
@@ -63,14 +62,12 @@ func catFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError,
go func() { go func() {
stderr := strings.Builder{} stderr := strings.Builder{}
err := gitcmd.NewCommand("cat-file", "--batch-check"). err := gitcmd.NewCommand("cat-file", "--batch-check").
Run(ctx, &gitcmd.RunOpts{ WithDir(repoPath).
Dir: repoPath, WithStdin(batchStdinReader).
Stdin: batchStdinReader, WithStdout(batchStdoutWriter).
Stdout: batchStdoutWriter, WithStderr(&stderr).
Stderr: &stderr, WithUseContextTimeout(true).
Run(ctx)
UseContextTimeout: true,
})
if err != nil { if err != nil {
_ = batchStdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String())) _ = batchStdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
_ = batchStdinReader.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String())) _ = batchStdinReader.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
@@ -111,14 +108,12 @@ func catFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
go func() { go func() {
stderr := strings.Builder{} stderr := strings.Builder{}
err := gitcmd.NewCommand("cat-file", "--batch"). err := gitcmd.NewCommand("cat-file", "--batch").
Run(ctx, &gitcmd.RunOpts{ WithDir(repoPath).
Dir: repoPath, WithStdin(batchStdinReader).
Stdin: batchStdinReader, WithStdout(batchStdoutWriter).
Stdout: batchStdoutWriter, WithStderr(&stderr).
Stderr: &stderr, WithUseContextTimeout(true).
Run(ctx)
UseContextTimeout: true,
})
if err != nil { if err != nil {
_ = batchStdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String())) _ = batchStdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
_ = batchStdinReader.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String())) _ = batchStdinReader.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))

View File

@@ -166,12 +166,11 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath
go func() { go func() {
stderr := bytes.Buffer{} 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" // 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 := cmd.Run(ctx, &gitcmd.RunOpts{ err := cmd.WithDir(repoPath).
UseContextTimeout: true, WithUseContextTimeout(true).
Dir: repoPath, WithStdout(stdout).
Stdout: stdout, WithStderr(&stderr).
Stderr: &stderr, Run(ctx)
})
done <- err done <- err
_ = stdout.Close() _ = stdout.Close()
if err != nil { if err != nil {

View File

@@ -93,7 +93,7 @@ func AddChanges(ctx context.Context, repoPath string, all bool, files ...string)
cmd.AddArguments("--all") cmd.AddArguments("--all")
} }
cmd.AddDashesAndList(files...) cmd.AddDashesAndList(files...)
_, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) _, _, err := cmd.WithDir(repoPath).RunStdString(ctx)
return err return err
} }
@@ -122,7 +122,7 @@ func CommitChanges(ctx context.Context, repoPath string, opts CommitChangesOptio
} }
cmd.AddOptionFormat("--message=%s", opts.Message) cmd.AddOptionFormat("--message=%s", opts.Message)
_, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) _, _, err := cmd.WithDir(repoPath).RunStdString(ctx)
// No stderr but exit status 1 means nothing to commit. // No stderr but exit status 1 means nothing to commit.
if err != nil && err.Error() == "exit status 1" { if err != nil && err.Error() == "exit status 1" {
return nil return nil
@@ -141,7 +141,7 @@ func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, file
cmd.AddDashesAndList(files...) cmd.AddDashesAndList(files...)
} }
stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) stdout, _, err := cmd.WithDir(repoPath).RunStdString(ctx)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -173,7 +173,7 @@ func CommitsCount(ctx context.Context, opts CommitsCountOptions) (int64, error)
cmd.AddDashesAndList(opts.RelPath...) cmd.AddDashesAndList(opts.RelPath...)
} }
stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: opts.RepoPath}) stdout, _, err := cmd.WithDir(opts.RepoPath).RunStdString(ctx)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -208,7 +208,10 @@ func (c *Commit) HasPreviousCommit(objectID ObjectID) (bool, error) {
return false, nil return false, nil
} }
_, _, err := gitcmd.NewCommand("merge-base", "--is-ancestor").AddDynamicArguments(that, this).RunStdString(c.repo.Ctx, &gitcmd.RunOpts{Dir: c.repo.Path}) _, _, err := gitcmd.NewCommand("merge-base", "--is-ancestor").
AddDynamicArguments(that, this).
WithDir(c.repo.Path).
RunStdString(c.repo.Ctx)
if err == nil { if err == nil {
return true, nil return true, nil
} }
@@ -354,7 +357,7 @@ func (c *Commit) GetBranchName() (string, error) {
cmd.AddArguments("--exclude", "refs/tags/*") cmd.AddArguments("--exclude", "refs/tags/*")
} }
cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String()) cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String())
data, _, err := cmd.RunStdString(c.repo.Ctx, &gitcmd.RunOpts{Dir: c.repo.Path}) data, _, err := cmd.WithDir(c.repo.Path).RunStdString(c.repo.Ctx)
if err != nil { if err != nil {
// handle special case where git can not describe commit // handle special case where git can not describe commit
if strings.Contains(err.Error(), "cannot describe") { if strings.Contains(err.Error(), "cannot describe") {
@@ -432,11 +435,12 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi
}() }()
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
err := gitcmd.NewCommand("log", "--name-status", "-m", "--pretty=format:", "--first-parent", "--no-renames", "-z", "-1").AddDynamicArguments(commitID).Run(ctx, &gitcmd.RunOpts{ err := gitcmd.NewCommand("log", "--name-status", "-m", "--pretty=format:", "--first-parent", "--no-renames", "-z", "-1").
Dir: repoPath, AddDynamicArguments(commitID).
Stdout: w, WithDir(repoPath).
Stderr: stderr, WithStdout(w).
}) WithStderr(stderr).
Run(ctx)
w.Close() // Close writer to exit parsing goroutine w.Close() // Close writer to exit parsing goroutine
if err != nil { if err != nil {
return nil, gitcmd.ConcatenateError(err, stderr.String()) return nil, gitcmd.ConcatenateError(err, stderr.String())
@@ -448,7 +452,10 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi
// GetFullCommitID returns full length (40) of commit ID by given short SHA in a repository. // GetFullCommitID returns full length (40) of commit ID by given short SHA in a repository.
func GetFullCommitID(ctx context.Context, repoPath, shortID string) (string, error) { func GetFullCommitID(ctx context.Context, repoPath, shortID string) (string, error) {
commitID, _, err := gitcmd.NewCommand("rev-parse").AddDynamicArguments(shortID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) commitID, _, err := gitcmd.NewCommand("rev-parse").
AddDynamicArguments(shortID).
WithDir(repoPath).
RunStdString(ctx)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "exit status 128") { if strings.Contains(err.Error(), "exit status 128") {
return "", ErrNotExist{shortID, ""} return "", ErrNotExist{shortID, ""}

View File

@@ -118,7 +118,9 @@ func syncGitConfig(ctx context.Context) (err error) {
} }
func configSet(ctx context.Context, key, value string) error { func configSet(ctx context.Context, key, value string) error {
stdout, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil) stdout, _, err := gitcmd.NewCommand("config", "--global", "--get").
AddDynamicArguments(key).
RunStdString(ctx)
if err != nil && !gitcmd.IsErrorExitCode(err, 1) { if err != nil && !gitcmd.IsErrorExitCode(err, 1) {
return fmt.Errorf("failed to get git config %s, err: %w", key, err) return fmt.Errorf("failed to get git config %s, err: %w", key, err)
} }
@@ -128,8 +130,9 @@ func configSet(ctx context.Context, key, value string) error {
return nil return nil
} }
_, _, err = gitcmd.NewCommand("config", "--global").AddDynamicArguments(key, value).RunStdString(ctx, nil) if _, _, err = gitcmd.NewCommand("config", "--global").
if err != nil { AddDynamicArguments(key, value).
RunStdString(ctx); err != nil {
return fmt.Errorf("failed to set git global config %s, err: %w", key, err) return fmt.Errorf("failed to set git global config %s, err: %w", key, err)
} }
@@ -137,14 +140,14 @@ func configSet(ctx context.Context, key, value string) error {
} }
func configSetNonExist(ctx context.Context, key, value string) error { func configSetNonExist(ctx context.Context, key, value string) error {
_, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil) _, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx)
if err == nil { if err == nil {
// already exist // already exist
return nil return nil
} }
if gitcmd.IsErrorExitCode(err, 1) { if gitcmd.IsErrorExitCode(err, 1) {
// not exist, set new config // not exist, set new config
_, _, err = gitcmd.NewCommand("config", "--global").AddDynamicArguments(key, value).RunStdString(ctx, nil) _, _, err = gitcmd.NewCommand("config", "--global").AddDynamicArguments(key, value).RunStdString(ctx)
if err != nil { if err != nil {
return fmt.Errorf("failed to set git global config %s, err: %w", key, err) return fmt.Errorf("failed to set git global config %s, err: %w", key, err)
} }
@@ -155,14 +158,14 @@ func configSetNonExist(ctx context.Context, key, value string) error {
} }
func configAddNonExist(ctx context.Context, key, value string) error { func configAddNonExist(ctx context.Context, key, value string) error {
_, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(ctx, nil) _, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(ctx)
if err == nil { if err == nil {
// already exist // already exist
return nil return nil
} }
if gitcmd.IsErrorExitCode(err, 1) { if gitcmd.IsErrorExitCode(err, 1) {
// not exist, add new config // not exist, add new config
_, _, err = gitcmd.NewCommand("config", "--global", "--add").AddDynamicArguments(key, value).RunStdString(ctx, nil) _, _, err = gitcmd.NewCommand("config", "--global", "--add").AddDynamicArguments(key, value).RunStdString(ctx)
if err != nil { if err != nil {
return fmt.Errorf("failed to add git global config %s, err: %w", key, err) return fmt.Errorf("failed to add git global config %s, err: %w", key, err)
} }
@@ -172,10 +175,10 @@ func configAddNonExist(ctx context.Context, key, value string) error {
} }
func configUnsetAll(ctx context.Context, key, value string) error { func configUnsetAll(ctx context.Context, key, value string) error {
_, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil) _, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx)
if err == nil { if err == nil {
// exist, need to remove // exist, need to remove
_, _, err = gitcmd.NewCommand("config", "--global", "--unset-all").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(ctx, nil) _, _, err = gitcmd.NewCommand("config", "--global", "--unset-all").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(ctx)
if err != nil { if err != nil {
return fmt.Errorf("failed to unset git global config %s, err: %w", key, err) return fmt.Errorf("failed to unset git global config %s, err: %w", key, err)
} }

View File

@@ -35,12 +35,12 @@ func GetRawDiff(repo *Repository, commitID string, diffType RawDiffType, writer
// GetReverseRawDiff dumps the reverse diff results of repository in given commit ID to io.Writer. // GetReverseRawDiff dumps the reverse diff results of repository in given commit ID to io.Writer.
func GetReverseRawDiff(ctx context.Context, repoPath, commitID string, writer io.Writer) error { func GetReverseRawDiff(ctx context.Context, repoPath, commitID string, writer io.Writer) error {
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
cmd := gitcmd.NewCommand("show", "--pretty=format:revert %H%n", "-R").AddDynamicArguments(commitID) if err := gitcmd.NewCommand("show", "--pretty=format:revert %H%n", "-R").
if err := cmd.Run(ctx, &gitcmd.RunOpts{ AddDynamicArguments(commitID).
Dir: repoPath, WithDir(repoPath).
Stdout: writer, WithStdout(writer).
Stderr: stderr, WithStderr(stderr).
}); err != nil { Run(ctx); err != nil {
return fmt.Errorf("Run: %w - %s", err, stderr) return fmt.Errorf("Run: %w - %s", err, stderr)
} }
return nil return nil
@@ -90,11 +90,10 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
} }
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
if err = cmd.Run(repo.Ctx, &gitcmd.RunOpts{ if err = cmd.WithDir(repo.Path).
Dir: repo.Path, WithStdout(writer).
Stdout: writer, WithStderr(stderr).
Stderr: stderr, Run(repo.Ctx); err != nil {
}); err != nil {
return fmt.Errorf("Run: %w - %s", err, stderr) return fmt.Errorf("Run: %w - %s", err, stderr)
} }
return nil return nil
@@ -314,11 +313,10 @@ func GetAffectedFiles(repo *Repository, branchName, oldCommitID, newCommitID str
// Run `git diff --name-only` to get the names of the changed files // Run `git diff --name-only` to get the names of the changed files
err = gitcmd.NewCommand("diff", "--name-only").AddDynamicArguments(oldCommitID, newCommitID). err = gitcmd.NewCommand("diff", "--name-only").AddDynamicArguments(oldCommitID, newCommitID).
Run(repo.Ctx, &gitcmd.RunOpts{ WithEnv(env).
Env: env, WithDir(repo.Path).
Dir: repo.Path, WithStdout(stdoutWriter).
Stdout: stdoutWriter, WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
// Close the writer end of the pipe to begin processing // Close the writer end of the pipe to begin processing
_ = stdoutWriter.Close() _ = stdoutWriter.Close()
defer func() { defer func() {
@@ -335,8 +333,8 @@ func GetAffectedFiles(repo *Repository, branchName, oldCommitID, newCommitID str
affectedFiles = append(affectedFiles, path) affectedFiles = append(affectedFiles, path)
} }
return scanner.Err() return scanner.Err()
}, }).
}) Run(repo.Ctx)
if err != nil { if err != nil {
log.Error("Unable to get affected files for commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err) log.Error("Unable to get affected files for commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err)
} }

View File

@@ -57,7 +57,7 @@ func DefaultFeatures() *Features {
} }
func loadGitVersionFeatures() (*Features, error) { func loadGitVersionFeatures() (*Features, error) {
stdout, _, runErr := gitcmd.NewCommand("version").RunStdString(context.Background(), nil) stdout, _, runErr := gitcmd.NewCommand("version").RunStdString(context.Background())
if runErr != nil { if runErr != nil {
return nil, runErr return nil, runErr
} }

View File

@@ -46,6 +46,7 @@ type Command struct {
brokenArgs []string brokenArgs []string
cmd *exec.Cmd // for debug purpose only cmd *exec.Cmd // for debug purpose only
configArgs []string configArgs []string
opts runOpts
} }
func logArgSanitize(arg string) string { func logArgSanitize(arg string) string {
@@ -194,8 +195,8 @@ func ToTrustedCmdArgs(args []string) TrustedCmdArgs {
return ret return ret
} }
// RunOpts represents parameters to run the command. If UseContextTimeout is specified, then Timeout is ignored. // runOpts represents parameters to run the command. If UseContextTimeout is specified, then Timeout is ignored.
type RunOpts struct { type runOpts struct {
Env []string Env []string
Timeout time.Duration Timeout time.Duration
UseContextTimeout bool UseContextTimeout bool
@@ -221,6 +222,8 @@ type RunOpts struct {
Stdin io.Reader Stdin io.Reader
PipelineFunc func(context.Context, context.CancelFunc) error PipelineFunc func(context.Context, context.CancelFunc) error
callerInfo string
} }
func commonBaseEnvs() []string { func commonBaseEnvs() []string {
@@ -263,44 +266,99 @@ func CommonCmdServEnvs() []string {
var ErrBrokenCommand = errors.New("git command is broken") var ErrBrokenCommand = errors.New("git command is broken")
// Run runs the command with the RunOpts func (c *Command) WithDir(dir string) *Command {
func (c *Command) Run(ctx context.Context, opts *RunOpts) error { c.opts.Dir = dir
return c.run(ctx, 1, opts) return c
} }
func (c *Command) run(ctx context.Context, skip int, opts *RunOpts) error { func (c *Command) WithEnv(env []string) *Command {
c.opts.Env = env
return c
}
func (c *Command) WithTimeout(timeout time.Duration) *Command {
c.opts.Timeout = timeout
return c
}
func (c *Command) WithStdout(stdout io.Writer) *Command {
c.opts.Stdout = stdout
return c
}
func (c *Command) WithStderr(stderr io.Writer) *Command {
c.opts.Stderr = stderr
return c
}
func (c *Command) WithStdin(stdin io.Reader) *Command {
c.opts.Stdin = stdin
return c
}
func (c *Command) WithPipelineFunc(f func(context.Context, context.CancelFunc) error) *Command {
c.opts.PipelineFunc = f
return c
}
func (c *Command) WithUseContextTimeout(useContextTimeout bool) *Command {
c.opts.UseContextTimeout = useContextTimeout
return c
}
// WithParentCallerInfo can be used to set the caller info (usually function name) of the parent function of the caller.
// For most cases, "Run" family functions can get its caller info automatically
// But if you need to call "Run" family functions in a wrapper function: "FeatureFunc -> GeneralWrapperFunc -> RunXxx",
// 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 != "" {
return c
}
if len(optInfo) > 0 {
c.opts.callerInfo = optInfo[0]
return c
}
skip := 1 /*parent "wrap/run" functions*/ + 1 /*this function*/
callerFuncName := util.CallerFuncName(skip)
callerInfo := callerFuncName
if pos := strings.LastIndex(callerInfo, "/"); pos >= 0 {
callerInfo = callerInfo[pos+1:]
}
c.opts.callerInfo = callerInfo
return c
}
// Run runs the command
func (c *Command) Run(ctx context.Context) error {
if len(c.brokenArgs) != 0 { if len(c.brokenArgs) != 0 {
log.Error("git command is broken: %s, broken args: %s", c.LogString(), strings.Join(c.brokenArgs, " ")) log.Error("git command is broken: %s, broken args: %s", c.LogString(), strings.Join(c.brokenArgs, " "))
return ErrBrokenCommand return ErrBrokenCommand
} }
if opts == nil {
opts = &RunOpts{}
}
// We must not change the provided options // We must not change the provided options
timeout := opts.Timeout timeout := c.opts.Timeout
if timeout <= 0 { if timeout <= 0 {
timeout = defaultCommandExecutionTimeout timeout = defaultCommandExecutionTimeout
} }
cmdLogString := c.LogString() cmdLogString := c.LogString()
callerInfo := util.CallerFuncName(1 /* util */ + 1 /* this */ + skip /* parent */) if c.opts.callerInfo == "" {
if pos := strings.LastIndex(callerInfo, "/"); pos >= 0 { c.WithParentCallerInfo()
callerInfo = callerInfo[pos+1:]
} }
// these logs are for debugging purposes only, so no guarantee of correctness or stability // these logs are for debugging purposes only, so no guarantee of correctness or stability
desc := fmt.Sprintf("git.Run(by:%s, repo:%s): %s", callerInfo, logArgSanitize(opts.Dir), cmdLogString) desc := fmt.Sprintf("git.Run(by:%s, repo:%s): %s", c.opts.callerInfo, logArgSanitize(c.opts.Dir), cmdLogString)
log.Debug("git.Command: %s", desc) log.Debug("git.Command: %s", desc)
_, span := gtprof.GetTracer().Start(ctx, gtprof.TraceSpanGitRun) _, span := gtprof.GetTracer().Start(ctx, gtprof.TraceSpanGitRun)
defer span.End() defer span.End()
span.SetAttributeString(gtprof.TraceAttrFuncCaller, callerInfo) span.SetAttributeString(gtprof.TraceAttrFuncCaller, c.opts.callerInfo)
span.SetAttributeString(gtprof.TraceAttrGitCommand, cmdLogString) span.SetAttributeString(gtprof.TraceAttrGitCommand, cmdLogString)
var cancel context.CancelFunc var cancel context.CancelFunc
var finished context.CancelFunc var finished context.CancelFunc
if opts.UseContextTimeout { if c.opts.UseContextTimeout {
ctx, cancel, finished = process.GetManager().AddContext(ctx, desc) ctx, cancel, finished = process.GetManager().AddContext(ctx, desc)
} else { } else {
ctx, cancel, finished = process.GetManager().AddContextTimeout(ctx, timeout, desc) ctx, cancel, finished = process.GetManager().AddContextTimeout(ctx, timeout, desc)
@@ -311,24 +369,24 @@ func (c *Command) run(ctx context.Context, skip int, opts *RunOpts) error {
cmd := exec.CommandContext(ctx, c.prog, append(c.configArgs, c.args...)...) cmd := exec.CommandContext(ctx, c.prog, append(c.configArgs, c.args...)...)
c.cmd = cmd // for debug purpose only c.cmd = cmd // for debug purpose only
if opts.Env == nil { if c.opts.Env == nil {
cmd.Env = os.Environ() cmd.Env = os.Environ()
} else { } else {
cmd.Env = opts.Env cmd.Env = c.opts.Env
} }
process.SetSysProcAttribute(cmd) process.SetSysProcAttribute(cmd)
cmd.Env = append(cmd.Env, CommonGitCmdEnvs()...) cmd.Env = append(cmd.Env, CommonGitCmdEnvs()...)
cmd.Dir = opts.Dir cmd.Dir = c.opts.Dir
cmd.Stdout = opts.Stdout cmd.Stdout = c.opts.Stdout
cmd.Stderr = opts.Stderr cmd.Stderr = c.opts.Stderr
cmd.Stdin = opts.Stdin cmd.Stdin = c.opts.Stdin
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return err return err
} }
if opts.PipelineFunc != nil { if c.opts.PipelineFunc != nil {
err := opts.PipelineFunc(ctx, cancel) err := c.opts.PipelineFunc(ctx, cancel)
if err != nil { if err != nil {
cancel() cancel()
_ = cmd.Wait() _ = cmd.Wait()
@@ -374,7 +432,8 @@ type runStdError struct {
} }
func (r *runStdError) Error() string { func (r *runStdError) Error() string {
// the stderr must be in the returned error text, some code only checks `strings.Contains(err.Error(), "git error")` // 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")`
if r.errMsg == "" { if r.errMsg == "" {
r.errMsg = ConcatenateError(r.err, r.stderr).Error() r.errMsg = ConcatenateError(r.err, r.stderr).Error()
} }
@@ -397,51 +456,33 @@ func IsErrorExitCode(err error, code int) bool {
return false return false
} }
// RunStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr). // RunStdString runs the command and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr).
func (c *Command) RunStdString(ctx context.Context, opts *RunOpts) (stdout, stderr string, runErr RunStdError) { func (c *Command) RunStdString(ctx context.Context) (stdout, stderr string, runErr RunStdError) {
stdoutBytes, stderrBytes, err := c.runStdBytes(ctx, opts) stdoutBytes, stderrBytes, runErr := c.WithParentCallerInfo().runStdBytes(ctx)
stdout = util.UnsafeBytesToString(stdoutBytes) return util.UnsafeBytesToString(stdoutBytes), util.UnsafeBytesToString(stderrBytes), runErr
stderr = util.UnsafeBytesToString(stderrBytes)
if err != nil {
return stdout, stderr, &runStdError{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
} }
// RunStdBytes runs the command with options and returns stdout/stderr as bytes. and store stderr to returned error (err combined with stderr). // RunStdBytes runs the command and returns stdout/stderr as bytes. and store stderr to returned error (err combined with stderr).
func (c *Command) RunStdBytes(ctx context.Context, opts *RunOpts) (stdout, stderr []byte, runErr RunStdError) { func (c *Command) RunStdBytes(ctx context.Context) (stdout, stderr []byte, runErr RunStdError) {
return c.runStdBytes(ctx, opts) return c.WithParentCallerInfo().runStdBytes(ctx)
} }
func (c *Command) runStdBytes(ctx context.Context, opts *RunOpts) (stdout, stderr []byte, runErr RunStdError) { func (c *Command) runStdBytes(ctx context.Context) ( /*stdout*/ []byte /*stderr*/, []byte /*runErr*/, RunStdError) {
if opts == nil { if c.opts.Stdout != nil || c.opts.Stderr != nil {
opts = &RunOpts{}
}
if opts.Stdout != nil || opts.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 // 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") panic("stdout and stderr field must be nil when using RunStdBytes")
} }
stdoutBuf := &bytes.Buffer{} stdoutBuf := &bytes.Buffer{}
stderrBuf := &bytes.Buffer{} stderrBuf := &bytes.Buffer{}
err := c.WithParentCallerInfo().
// We must not change the provided options as it could break future calls - therefore make a copy. WithStdout(stdoutBuf).
newOpts := &RunOpts{ WithStderr(stderrBuf).
Env: opts.Env, Run(ctx)
Timeout: opts.Timeout,
UseContextTimeout: opts.UseContextTimeout,
Dir: opts.Dir,
Stdout: stdoutBuf,
Stderr: stderrBuf,
Stdin: opts.Stdin,
PipelineFunc: opts.PipelineFunc,
}
err := c.run(ctx, 2, newOpts)
stderr = stderrBuf.Bytes()
if err != nil { if err != nil {
return nil, stderr, &runStdError{err: err, stderr: util.UnsafeBytesToString(stderr)} // 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 // even if there is no err, there could still be some stderr output
return stdoutBuf.Bytes(), stderr, nil return stdoutBuf.Bytes(), stderrBuf.Bytes(), nil
} }

View File

@@ -17,7 +17,7 @@ func TestRunWithContextNoTimeout(t *testing.T) {
// 'git --version' does not block so it must be finished before the timeout triggered. // 'git --version' does not block so it must be finished before the timeout triggered.
cmd := NewCommand("--version") cmd := NewCommand("--version")
for i := 0; i < maxLoops; i++ { for i := 0; i < maxLoops; i++ {
if err := cmd.Run(t.Context(), &RunOpts{}); err != nil { if err := cmd.Run(t.Context()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@@ -29,7 +29,7 @@ func TestRunWithContextTimeout(t *testing.T) {
// 'git hash-object --stdin' blocks on stdin so we can have the timeout triggered. // 'git hash-object --stdin' blocks on stdin so we can have the timeout triggered.
cmd := NewCommand("hash-object", "--stdin") cmd := NewCommand("hash-object", "--stdin")
for i := 0; i < maxLoops; i++ { for i := 0; i < maxLoops; i++ {
if err := cmd.Run(t.Context(), &RunOpts{Timeout: 1 * time.Millisecond}); err != nil { if err := cmd.WithTimeout(1 * time.Millisecond).Run(t.Context()); err != nil {
if err != context.DeadlineExceeded { if err != context.DeadlineExceeded {
t.Fatalf("Testing %d/%d: %v", i, maxLoops, err) t.Fatalf("Testing %d/%d: %v", i, maxLoops, err)
} }

View File

@@ -23,39 +23,61 @@ func TestMain(m *testing.M) {
defer cleanup() defer cleanup()
setting.Git.HomePath = gitHomePath setting.Git.HomePath = gitHomePath
os.Exit(m.Run())
} }
func TestRunWithContextStd(t *testing.T) { func TestRunWithContextStd(t *testing.T) {
{
cmd := NewCommand("--version") cmd := NewCommand("--version")
stdout, stderr, err := cmd.RunStdString(t.Context(), &RunOpts{}) stdout, stderr, err := cmd.RunStdString(t.Context())
assert.NoError(t, err) assert.NoError(t, err)
assert.Empty(t, stderr) assert.Empty(t, stderr)
assert.Contains(t, stdout, "git version") assert.Contains(t, stdout, "git version")
cmd = NewCommand("--no-such-arg")
stdout, stderr, err = cmd.RunStdString(t.Context(), &RunOpts{})
if assert.Error(t, err) {
assert.Equal(t, stderr, err.Stderr())
assert.Contains(t, err.Stderr(), "unknown option:")
assert.Contains(t, err.Error(), "exit status 129 - unknown option:")
assert.Empty(t, stdout)
} }
cmd = NewCommand() {
cmd := NewCommand("ls-tree", "no-such")
stdout, stderr, err := cmd.RunStdString(t.Context())
if assert.Error(t, err) {
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.Empty(t, stdout)
}
}
{
cmd := NewCommand("ls-tree", "no-such")
stdout, stderr, err := cmd.RunStdBytes(t.Context())
if assert.Error(t, err) {
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.Empty(t, stdout)
}
}
{
cmd := NewCommand()
cmd.AddDynamicArguments("-test") cmd.AddDynamicArguments("-test")
assert.ErrorIs(t, cmd.Run(t.Context(), &RunOpts{}), ErrBrokenCommand) assert.ErrorIs(t, cmd.Run(t.Context()), ErrBrokenCommand)
cmd = NewCommand() cmd = NewCommand()
cmd.AddDynamicArguments("--test") cmd.AddDynamicArguments("--test")
assert.ErrorIs(t, cmd.Run(t.Context(), &RunOpts{}), ErrBrokenCommand) assert.ErrorIs(t, cmd.Run(t.Context()), ErrBrokenCommand)
}
{
subCmd := "version" subCmd := "version"
cmd = NewCommand().AddDynamicArguments(subCmd) // for test purpose only, the sub-command should never be dynamic for production cmd := NewCommand().AddDynamicArguments(subCmd) // for test purpose only, the sub-command should never be dynamic for production
stdout, stderr, err = cmd.RunStdString(t.Context(), &RunOpts{}) stdout, stderr, err := cmd.RunStdString(t.Context())
assert.NoError(t, err) assert.NoError(t, err)
assert.Empty(t, stderr) assert.Empty(t, stderr)
assert.Contains(t, stdout, "git version") assert.Contains(t, stdout, "git version")
} }
}
func TestGitArgument(t *testing.T) { func TestGitArgument(t *testing.T) {
assert.True(t, isValidArgumentOption("-x")) assert.True(t, isValidArgumentOption("-x"))

View File

@@ -84,11 +84,10 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
cmd.AddDashesAndList(opts.PathspecList...) cmd.AddDashesAndList(opts.PathspecList...)
opts.MaxResultLimit = util.IfZero(opts.MaxResultLimit, 50) opts.MaxResultLimit = util.IfZero(opts.MaxResultLimit, 50)
stderr := bytes.Buffer{} stderr := bytes.Buffer{}
err = cmd.Run(ctx, &gitcmd.RunOpts{ err = cmd.WithDir(repo.Path).
Dir: repo.Path, WithStdout(stdoutWriter).
Stdout: stdoutWriter, WithStderr(&stderr).
Stderr: &stderr, WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close() _ = stdoutWriter.Close()
defer stdoutReader.Close() defer stdoutReader.Close()
@@ -133,8 +132,8 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
} }
} }
return nil return nil
}, }).
}) Run(ctx)
// git grep exits by cancel (killed), usually it is caused by the limit of results // 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) && stderr.Len() == 0 {
return results, nil return results, nil

View File

@@ -65,11 +65,10 @@ func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, p
go func() { go func() {
stderr := strings.Builder{} stderr := strings.Builder{}
err := cmd.Run(ctx, &gitcmd.RunOpts{ err := cmd.WithDir(repository).
Dir: repository, WithStdout(stdoutWriter).
Stdout: stdoutWriter, WithStderr(&stderr).
Stderr: &stderr, Run(ctx)
})
if err != nil { if err != nil {
_ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String())) _ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
return return

View File

@@ -26,12 +26,11 @@ func CatFileBatchCheck(ctx context.Context, shasToCheckReader *io.PipeReader, ca
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
var errbuf strings.Builder var errbuf strings.Builder
cmd := gitcmd.NewCommand("cat-file", "--batch-check") cmd := gitcmd.NewCommand("cat-file", "--batch-check")
if err := cmd.Run(ctx, &gitcmd.RunOpts{ if err := cmd.WithDir(tmpBasePath).
Dir: tmpBasePath, WithStdin(shasToCheckReader).
Stdin: shasToCheckReader, WithStdout(catFileCheckWriter).
Stdout: catFileCheckWriter, WithStderr(stderr).
Stderr: stderr, Run(ctx); err != nil {
}); err != nil {
_ = catFileCheckWriter.CloseWithError(fmt.Errorf("git cat-file --batch-check [%s]: %w - %s", tmpBasePath, err, errbuf.String())) _ = catFileCheckWriter.CloseWithError(fmt.Errorf("git cat-file --batch-check [%s]: %w - %s", tmpBasePath, err, errbuf.String()))
} }
} }
@@ -44,11 +43,10 @@ func CatFileBatchCheckAllObjects(ctx context.Context, catFileCheckWriter *io.Pip
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
var errbuf strings.Builder var errbuf strings.Builder
cmd := gitcmd.NewCommand("cat-file", "--batch-check", "--batch-all-objects") cmd := gitcmd.NewCommand("cat-file", "--batch-check", "--batch-all-objects")
if err := cmd.Run(ctx, &gitcmd.RunOpts{ if err := cmd.WithDir(tmpBasePath).
Dir: tmpBasePath, WithStdout(catFileCheckWriter).
Stdout: catFileCheckWriter, WithStderr(stderr).
Stderr: stderr, Run(ctx); err != nil {
}); err != nil {
log.Error("git cat-file --batch-check --batch-all-object [%s]: %v - %s", tmpBasePath, err, errbuf.String()) 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()) err = fmt.Errorf("git cat-file --batch-check --batch-all-object [%s]: %w - %s", tmpBasePath, err, errbuf.String())
_ = catFileCheckWriter.CloseWithError(err) _ = catFileCheckWriter.CloseWithError(err)
@@ -64,12 +62,12 @@ func CatFileBatch(ctx context.Context, shasToBatchReader *io.PipeReader, catFile
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
var errbuf strings.Builder var errbuf strings.Builder
if err := gitcmd.NewCommand("cat-file", "--batch").Run(ctx, &gitcmd.RunOpts{ if err := gitcmd.NewCommand("cat-file", "--batch").
Dir: tmpBasePath, WithDir(tmpBasePath).
Stdout: catFileBatchWriter, WithStdin(shasToBatchReader).
Stdin: shasToBatchReader, WithStdout(catFileBatchWriter).
Stderr: stderr, WithStderr(stderr).
}); err != nil { Run(ctx); err != nil {
_ = shasToBatchReader.CloseWithError(fmt.Errorf("git rev-list [%s]: %w - %s", tmpBasePath, err, errbuf.String())) _ = shasToBatchReader.CloseWithError(fmt.Errorf("git rev-list [%s]: %w - %s", tmpBasePath, err, errbuf.String()))
} }
} }

View File

@@ -33,11 +33,11 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
go func() { go func() {
stderr := strings.Builder{} stderr := strings.Builder{}
err := gitcmd.NewCommand("rev-list", "--all").Run(repo.Ctx, &gitcmd.RunOpts{ err := gitcmd.NewCommand("rev-list", "--all").
Dir: repo.Path, WithDir(repo.Path).
Stdout: revListWriter, WithStdout(revListWriter).
Stderr: &stderr, WithStderr(&stderr).
}) Run(repo.Ctx)
if err != nil { if err != nil {
_ = revListWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String())) _ = revListWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
} else { } else {

View File

@@ -22,12 +22,12 @@ func NameRevStdin(ctx context.Context, shasToNameReader *io.PipeReader, nameRevS
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
var errbuf strings.Builder var errbuf strings.Builder
if err := gitcmd.NewCommand("name-rev", "--stdin", "--name-only", "--always").Run(ctx, &gitcmd.RunOpts{ if err := gitcmd.NewCommand("name-rev", "--stdin", "--name-only", "--always").
Dir: tmpBasePath, WithDir(tmpBasePath).
Stdout: nameRevStdinWriter, WithStdin(shasToNameReader).
Stdin: shasToNameReader, WithStdout(nameRevStdinWriter).
Stderr: stderr, WithStderr(stderr).
}); err != nil { Run(ctx); err != nil {
_ = shasToNameReader.CloseWithError(fmt.Errorf("git name-rev [%s]: %w - %s", tmpBasePath, err, errbuf.String())) _ = shasToNameReader.CloseWithError(fmt.Errorf("git name-rev [%s]: %w - %s", tmpBasePath, err, errbuf.String()))
} }
} }

View File

@@ -24,11 +24,10 @@ func RevListAllObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sy
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
var errbuf strings.Builder var errbuf strings.Builder
cmd := gitcmd.NewCommand("rev-list", "--objects", "--all") cmd := gitcmd.NewCommand("rev-list", "--objects", "--all")
if err := cmd.Run(ctx, &gitcmd.RunOpts{ if err := cmd.WithDir(basePath).
Dir: basePath, WithStdout(revListWriter).
Stdout: revListWriter, WithStderr(stderr).
Stderr: stderr, Run(ctx); err != nil {
}); err != nil {
log.Error("git rev-list --objects --all [%s]: %v - %s", basePath, err, errbuf.String()) 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()) err = fmt.Errorf("git rev-list --objects --all [%s]: %w - %s", basePath, err, errbuf.String())
_ = revListWriter.CloseWithError(err) _ = revListWriter.CloseWithError(err)
@@ -46,11 +45,10 @@ func RevListObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.
if baseSHA != "" { if baseSHA != "" {
cmd = cmd.AddArguments("--not").AddDynamicArguments(baseSHA) cmd = cmd.AddArguments("--not").AddDynamicArguments(baseSHA)
} }
if err := cmd.Run(ctx, &gitcmd.RunOpts{ if err := cmd.WithDir(tmpBasePath).
Dir: tmpBasePath, WithStdout(revListWriter).
Stdout: revListWriter, WithStderr(stderr).
Stderr: stderr, Run(ctx); err != nil {
}); err != nil {
log.Error("git rev-list [%s]: %v - %s", tmpBasePath, err, errbuf.String()) 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()) errChan <- fmt.Errorf("git rev-list [%s]: %w - %s", tmpBasePath, err, errbuf.String())
} }

View File

@@ -22,7 +22,7 @@ func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string,
cmd = gitcmd.NewCommand("config", "--get").AddDynamicArguments("remote." + remoteName + ".url") cmd = gitcmd.NewCommand("config", "--get").AddDynamicArguments("remote." + remoteName + ".url")
} }
result, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) result, _, err := cmd.WithDir(repoPath).RunStdString(ctx)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@@ -40,8 +40,8 @@ func (repo *Repository) GetAllCommitsCount() (int64, error) {
func (repo *Repository) ShowPrettyFormatLogToList(ctx context.Context, revisionRange string) ([]*Commit, error) { func (repo *Repository) ShowPrettyFormatLogToList(ctx context.Context, revisionRange string) ([]*Commit, error) {
// avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git <command> [<revision>...] -- [<file>...]' // avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git <command> [<revision>...] -- [<file>...]'
logs, _, err := gitcmd.NewCommand("log").AddArguments(prettyLogFormat). logs, _, err := gitcmd.NewCommand("log").AddArguments(prettyLogFormat).
AddDynamicArguments(revisionRange).AddArguments("--"). AddDynamicArguments(revisionRange).AddArguments("--").WithDir(repo.Path).
RunStdBytes(ctx, &gitcmd.RunOpts{Dir: repo.Path}) RunStdBytes(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -69,7 +69,7 @@ func (repo *Repository) parsePrettyFormatLogToList(logs []byte) ([]*Commit, erro
// IsRepoURLAccessible checks if given repository URL is accessible. // IsRepoURLAccessible checks if given repository URL is accessible.
func IsRepoURLAccessible(ctx context.Context, url string) bool { func IsRepoURLAccessible(ctx context.Context, url string) bool {
_, _, err := gitcmd.NewCommand("ls-remote", "-q", "-h").AddDynamicArguments(url, "HEAD").RunStdString(ctx, nil) _, _, err := gitcmd.NewCommand("ls-remote", "-q", "-h").AddDynamicArguments(url, "HEAD").RunStdString(ctx)
return err == nil return err == nil
} }
@@ -92,19 +92,20 @@ func InitRepository(ctx context.Context, repoPath string, bare bool, objectForma
if bare { if bare {
cmd.AddArguments("--bare") cmd.AddArguments("--bare")
} }
_, _, err = cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) _, _, err = cmd.WithDir(repoPath).RunStdString(ctx)
return err return err
} }
// IsEmpty Check if repository is empty. // IsEmpty Check if repository is empty.
func (repo *Repository) IsEmpty() (bool, error) { func (repo *Repository) IsEmpty() (bool, error) {
var errbuf, output strings.Builder var errbuf, output strings.Builder
if err := gitcmd.NewCommand().AddOptionFormat("--git-dir=%s", repo.Path).AddArguments("rev-list", "-n", "1", "--all"). if err := gitcmd.NewCommand().
Run(repo.Ctx, &gitcmd.RunOpts{ AddOptionFormat("--git-dir=%s", repo.Path).
Dir: repo.Path, AddArguments("rev-list", "-n", "1", "--all").
Stdout: &output, WithDir(repo.Path).
Stderr: &errbuf, WithStdout(&output).
}); err != nil { WithStderr(&errbuf).
Run(repo.Ctx); err != nil {
if (err.Error() == "exit status 1" && strings.TrimSpace(errbuf.String()) == "") || err.Error() == "exit status 129" { if (err.Error() == "exit status 1" && strings.TrimSpace(errbuf.String()) == "") || err.Error() == "exit status 129" {
// git 2.11 exits with 129 if the repo is empty // git 2.11 exits with 129 if the repo is empty
return true, nil return true, nil
@@ -177,12 +178,12 @@ func Clone(ctx context.Context, from, to string, opts CloneRepoOptions) error {
} }
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
if err = cmd.Run(ctx, &gitcmd.RunOpts{ if err = cmd.
Timeout: opts.Timeout, WithTimeout(opts.Timeout).
Env: envs, WithEnv(envs).
Stdout: io.Discard, WithStdout(io.Discard).
Stderr: stderr, WithStderr(stderr).
}); err != nil { Run(ctx); err != nil {
return gitcmd.ConcatenateError(err, stderr.String()) return gitcmd.ConcatenateError(err, stderr.String())
} }
return nil return nil
@@ -213,7 +214,7 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
} }
cmd.AddDashesAndList(remoteBranchArgs...) cmd.AddDashesAndList(remoteBranchArgs...)
stdout, stderr, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Env: opts.Env, Timeout: opts.Timeout, Dir: repoPath}) stdout, stderr, err := cmd.WithEnv(opts.Env).WithTimeout(opts.Timeout).WithDir(repoPath).RunStdString(ctx)
if err != nil { if err != nil {
if strings.Contains(stderr, "non-fast-forward") { if strings.Contains(stderr, "non-fast-forward") {
return &ErrPushOutOfDate{StdOut: stdout, StdErr: stderr, Err: err} return &ErrPushOutOfDate{StdOut: stdout, StdErr: stderr, Err: err}
@@ -233,7 +234,7 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
// GetLatestCommitTime returns time for latest commit in repository (across all branches) // GetLatestCommitTime returns time for latest commit in repository (across all branches)
func GetLatestCommitTime(ctx context.Context, repoPath string) (time.Time, error) { func GetLatestCommitTime(ctx context.Context, repoPath string) (time.Time, error) {
cmd := gitcmd.NewCommand("for-each-ref", "--sort=-committerdate", BranchPrefix, "--count", "1", "--format=%(committerdate)") cmd := gitcmd.NewCommand("for-each-ref", "--sort=-committerdate", BranchPrefix, "--count", "1", "--format=%(committerdate)")
stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) stdout, _, err := cmd.WithDir(repoPath).RunStdString(ctx)
if err != nil { if err != nil {
return time.Time{}, err return time.Time{}, err
} }

View File

@@ -0,0 +1,74 @@
// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
import (
"context"
"fmt"
"io"
"path/filepath"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
)
// ArchiveType archive types
type ArchiveType int
const (
ArchiveUnknown ArchiveType = iota
ArchiveZip // 1
ArchiveTarGz // 2
ArchiveBundle // 3
)
// String converts an ArchiveType to string: the extension of the archive file without prefix dot
func (a ArchiveType) String() string {
switch a {
case ArchiveZip:
return "zip"
case ArchiveTarGz:
return "tar.gz"
case ArchiveBundle:
return "bundle"
}
return "unknown"
}
func SplitArchiveNameType(s string) (string, ArchiveType) {
switch {
case strings.HasSuffix(s, ".zip"):
return strings.TrimSuffix(s, ".zip"), ArchiveZip
case strings.HasSuffix(s, ".tar.gz"):
return strings.TrimSuffix(s, ".tar.gz"), ArchiveTarGz
case strings.HasSuffix(s, ".bundle"):
return strings.TrimSuffix(s, ".bundle"), ArchiveBundle
}
return s, ArchiveUnknown
}
// CreateArchive create archive content to the target path
func (repo *Repository) CreateArchive(ctx context.Context, format ArchiveType, target io.Writer, usePrefix bool, commitID string) error {
if format.String() == "unknown" {
return fmt.Errorf("unknown format: %v", format)
}
cmd := gitcmd.NewCommand("archive")
if usePrefix {
cmd.AddOptionFormat("--prefix=%s", filepath.Base(strings.TrimSuffix(repo.Path, ".git"))+"/")
}
cmd.AddOptionFormat("--format=%s", format.String())
cmd.AddDynamicArguments(commitID)
var stderr strings.Builder
err := cmd.WithDir(repo.Path).
WithStdout(target).
WithStderr(&stderr).
Run(ctx)
if err != nil {
return gitcmd.ConcatenateError(err, stderr.String())
}
return nil
}

View File

@@ -17,8 +17,8 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
if fetch { if fetch {
cmd.AddArguments("-f") cmd.AddArguments("-f")
} }
cmd.AddDynamicArguments(name, url) _, _, err := cmd.AddDynamicArguments(name, url).
WithDir(repo.Path).
_, _, err := cmd.RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) RunStdString(repo.Ctx)
return err return err
} }

View File

@@ -110,11 +110,11 @@ func WalkShowRef(ctx context.Context, repoPath string, extraArgs gitcmd.TrustedC
stderrBuilder := &strings.Builder{} stderrBuilder := &strings.Builder{}
args := gitcmd.TrustedCmdArgs{"for-each-ref", "--format=%(objectname) %(refname)"} args := gitcmd.TrustedCmdArgs{"for-each-ref", "--format=%(objectname) %(refname)"}
args = append(args, extraArgs...) args = append(args, extraArgs...)
err := gitcmd.NewCommand(args...).Run(ctx, &gitcmd.RunOpts{ err := gitcmd.NewCommand(args...).
Dir: repoPath, WithDir(repoPath).
Stdout: stdoutWriter, WithStdout(stdoutWriter).
Stderr: stderrBuilder, WithStderr(stderrBuilder).
}) Run(ctx)
if err != nil { if err != nil {
if stderrBuilder.Len() == 0 { if stderrBuilder.Len() == 0 {
_ = stdoutWriter.Close() _ = stdoutWriter.Close()

View File

@@ -60,7 +60,11 @@ func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Com
relpath = `\` + relpath relpath = `\` + relpath
} }
stdout, _, runErr := gitcmd.NewCommand("log", "-1", prettyLogFormat).AddDynamicArguments(id.String()).AddDashesAndList(relpath).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, runErr := gitcmd.NewCommand("log", "-1", prettyLogFormat).
AddDynamicArguments(id.String()).
AddDashesAndList(relpath).
WithDir(repo.Path).
RunStdString(repo.Ctx)
if runErr != nil { if runErr != nil {
return nil, runErr return nil, runErr
} }
@@ -75,7 +79,10 @@ func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Com
// GetCommitByPath returns the last commit of relative path. // GetCommitByPath returns the last commit of relative path.
func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) { func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
stdout, _, runErr := gitcmd.NewCommand("log", "-1", prettyLogFormat).AddDashesAndList(relpath).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, runErr := gitcmd.NewCommand("log", "-1", prettyLogFormat).
AddDashesAndList(relpath).
WithDir(repo.Path).
RunStdBytes(repo.Ctx)
if runErr != nil { if runErr != nil {
return nil, runErr return nil, runErr
} }
@@ -108,7 +115,7 @@ func (repo *Repository) commitsByRangeWithTime(id ObjectID, page, pageSize int,
cmd.AddOptionFormat("--until=%s", until) cmd.AddOptionFormat("--until=%s", until)
} }
stdout, _, err := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err := cmd.WithDir(repo.Path).RunStdBytes(repo.Ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -162,7 +169,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([
// search for commits matching given constraints and keywords in commit msg // search for commits matching given constraints and keywords in commit msg
addCommonSearchArgs(cmd) addCommonSearchArgs(cmd)
stdout, _, err := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err := cmd.WithDir(repo.Path).RunStdBytes(repo.Ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -183,7 +190,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([
hashCmd.AddDynamicArguments(v) hashCmd.AddDynamicArguments(v)
// search with given constraints for commit matching sha hash of v // search with given constraints for commit matching sha hash of v
hashMatching, _, err := hashCmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) hashMatching, _, err := hashCmd.WithDir(repo.Path).RunStdBytes(repo.Ctx)
if err != nil || bytes.Contains(stdout, hashMatching) { if err != nil || bytes.Contains(stdout, hashMatching) {
continue continue
} }
@@ -198,7 +205,11 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([
// FileChangedBetweenCommits Returns true if the file changed between commit IDs id1 and id2 // FileChangedBetweenCommits Returns true if the file changed between commit IDs id1 and id2
// You must ensure that id1 and id2 are valid commit ids. // You must ensure that id1 and id2 are valid commit ids.
func (repo *Repository) FileChangedBetweenCommits(filename, id1, id2 string) (bool, error) { func (repo *Repository) FileChangedBetweenCommits(filename, id1, id2 string) (bool, error) {
stdout, _, err := gitcmd.NewCommand("diff", "--name-only", "-z").AddDynamicArguments(id1, id2).AddDashesAndList(filename).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err := gitcmd.NewCommand("diff", "--name-only", "-z").
AddDynamicArguments(id1, id2).
AddDashesAndList(filename).
WithDir(repo.Path).
RunStdBytes(repo.Ctx)
if err != nil { if err != nil {
return false, err return false, err
} }
@@ -249,11 +260,10 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
} }
gitCmd.AddDashesAndList(opts.File) gitCmd.AddDashesAndList(opts.File)
err := gitCmd.Run(repo.Ctx, &gitcmd.RunOpts{ err := gitCmd.WithDir(repo.Path).
Dir: repo.Path, WithStdout(stdoutWriter).
Stdout: stdoutWriter, WithStderr(&stderr).
Stderr: &stderr, Run(repo.Ctx)
})
if err != nil { if err != nil {
_ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String())) _ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
} else { } else {
@@ -291,11 +301,17 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
// FilesCountBetween return the number of files changed between two commits // FilesCountBetween return the number of files changed between two commits
func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) { func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) {
stdout, _, err := gitcmd.NewCommand("diff", "--name-only").AddDynamicArguments(startCommitID+"..."+endCommitID).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err := gitcmd.NewCommand("diff", "--name-only").
AddDynamicArguments(startCommitID + "..." + endCommitID).
WithDir(repo.Path).
RunStdString(repo.Ctx)
if err != nil && strings.Contains(err.Error(), "no merge base") { if err != nil && strings.Contains(err.Error(), "no merge base") {
// git >= 2.28 now returns an error if startCommitID and endCommitID have become unrelated. // git >= 2.28 now returns an error if startCommitID and endCommitID have become unrelated.
// previously it would return the results of git diff --name-only startCommitID endCommitID so let's try that... // previously it would return the results of git diff --name-only startCommitID endCommitID so let's try that...
stdout, _, err = gitcmd.NewCommand("diff", "--name-only").AddDynamicArguments(startCommitID, endCommitID).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err = gitcmd.NewCommand("diff", "--name-only").
AddDynamicArguments(startCommitID, endCommitID).
WithDir(repo.Path).
RunStdString(repo.Ctx)
} }
if err != nil { if err != nil {
return 0, err return 0, err
@@ -309,13 +325,22 @@ func (repo *Repository) CommitsBetween(last, before *Commit) ([]*Commit, error)
var stdout []byte var stdout []byte
var err error var err error
if before == nil { if before == nil {
stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err = gitcmd.NewCommand("rev-list").
AddDynamicArguments(last.ID.String()).
WithDir(repo.Path).
RunStdBytes(repo.Ctx)
} else { } else {
stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(before.ID.String()+".."+last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err = gitcmd.NewCommand("rev-list").
AddDynamicArguments(before.ID.String() + ".." + last.ID.String()).
WithDir(repo.Path).
RunStdBytes(repo.Ctx)
if err != nil && strings.Contains(err.Error(), "no merge base") { if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated. // future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list before last so let's try that... // previously it would return the results of git rev-list before last so let's try that...
stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err = gitcmd.NewCommand("rev-list").
AddDynamicArguments(before.ID.String(), last.ID.String()).
WithDir(repo.Path).
RunStdBytes(repo.Ctx)
} }
} }
if err != nil { if err != nil {
@@ -332,19 +357,25 @@ func (repo *Repository) CommitsBetweenLimit(last, before *Commit, limit, skip in
stdout, _, err = gitcmd.NewCommand("rev-list"). stdout, _, err = gitcmd.NewCommand("rev-list").
AddOptionValues("--max-count", strconv.Itoa(limit)). AddOptionValues("--max-count", strconv.Itoa(limit)).
AddOptionValues("--skip", strconv.Itoa(skip)). AddOptionValues("--skip", strconv.Itoa(skip)).
AddDynamicArguments(last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) AddDynamicArguments(last.ID.String()).
WithDir(repo.Path).
RunStdBytes(repo.Ctx)
} else { } else {
stdout, _, err = gitcmd.NewCommand("rev-list"). stdout, _, err = gitcmd.NewCommand("rev-list").
AddOptionValues("--max-count", strconv.Itoa(limit)). AddOptionValues("--max-count", strconv.Itoa(limit)).
AddOptionValues("--skip", strconv.Itoa(skip)). AddOptionValues("--skip", strconv.Itoa(skip)).
AddDynamicArguments(before.ID.String()+".."+last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) AddDynamicArguments(before.ID.String() + ".." + last.ID.String()).
WithDir(repo.Path).
RunStdBytes(repo.Ctx)
if err != nil && strings.Contains(err.Error(), "no merge base") { if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated. // future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list --max-count n before last so let's try that... // previously it would return the results of git rev-list --max-count n before last so let's try that...
stdout, _, err = gitcmd.NewCommand("rev-list"). stdout, _, err = gitcmd.NewCommand("rev-list").
AddOptionValues("--max-count", strconv.Itoa(limit)). AddOptionValues("--max-count", strconv.Itoa(limit)).
AddOptionValues("--skip", strconv.Itoa(skip)). AddOptionValues("--skip", strconv.Itoa(skip)).
AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) AddDynamicArguments(before.ID.String(), last.ID.String()).
WithDir(repo.Path).
RunStdBytes(repo.Ctx)
} }
} }
if err != nil { if err != nil {
@@ -359,13 +390,25 @@ func (repo *Repository) CommitsBetweenNotBase(last, before *Commit, baseBranch s
var stdout []byte var stdout []byte
var err error var err error
if before == nil { if before == nil {
stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err = gitcmd.NewCommand("rev-list").
AddDynamicArguments(last.ID.String()).
AddOptionValues("--not", baseBranch).
WithDir(repo.Path).
RunStdBytes(repo.Ctx)
} else { } else {
stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(before.ID.String()+".."+last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err = gitcmd.NewCommand("rev-list").
AddDynamicArguments(before.ID.String()+".."+last.ID.String()).
AddOptionValues("--not", baseBranch).
WithDir(repo.Path).
RunStdBytes(repo.Ctx)
if err != nil && strings.Contains(err.Error(), "no merge base") { if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated. // future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list before last so let's try that... // previously it would return the results of git rev-list before last so let's try that...
stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err = gitcmd.NewCommand("rev-list").
AddDynamicArguments(before.ID.String(), last.ID.String()).
AddOptionValues("--not", baseBranch).
WithDir(repo.Path).
RunStdBytes(repo.Ctx)
} }
} }
if err != nil { if err != nil {
@@ -417,7 +460,7 @@ func (repo *Repository) commitsBefore(id ObjectID, limit int) ([]*Commit, error)
} }
cmd.AddDynamicArguments(id.String()) cmd.AddDynamicArguments(id.String())
stdout, _, runErr := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, runErr := cmd.WithDir(repo.Path).RunStdBytes(repo.Ctx)
if runErr != nil { if runErr != nil {
return nil, runErr return nil, runErr
} }
@@ -457,10 +500,9 @@ func (repo *Repository) getBranches(env []string, commitID string, limit int) ([
stdout, _, err := gitcmd.NewCommand("for-each-ref", "--format=%(refname:strip=2)"). stdout, _, err := gitcmd.NewCommand("for-each-ref", "--format=%(refname:strip=2)").
AddOptionFormat("--count=%d", limit). AddOptionFormat("--count=%d", limit).
AddOptionValues("--contains", commitID, BranchPrefix). AddOptionValues("--contains", commitID, BranchPrefix).
RunStdString(repo.Ctx, &gitcmd.RunOpts{ WithDir(repo.Path).
Dir: repo.Path, WithEnv(env).
Env: env, RunStdString(repo.Ctx)
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -469,10 +511,11 @@ func (repo *Repository) getBranches(env []string, commitID string, limit int) ([
return branches, nil return branches, nil
} }
stdout, _, err := gitcmd.NewCommand("branch").AddOptionValues("--contains", commitID).RunStdString(repo.Ctx, &gitcmd.RunOpts{ stdout, _, err := gitcmd.NewCommand("branch").
Dir: repo.Path, AddOptionValues("--contains", commitID).
Env: env, WithDir(repo.Path).
}) WithEnv(env).
RunStdString(repo.Ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -511,7 +554,10 @@ func (repo *Repository) GetCommitsFromIDs(commitIDs []string) []*Commit {
// IsCommitInBranch check if the commit is on the branch // IsCommitInBranch check if the commit is on the branch
func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err error) { func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err error) {
stdout, _, err := gitcmd.NewCommand("branch", "--contains").AddDynamicArguments(commitID, branch).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err := gitcmd.NewCommand("branch", "--contains").
AddDynamicArguments(commitID, branch).
WithDir(repo.Path).
RunStdString(repo.Ctx)
if err != nil { if err != nil {
return false, err return false, err
} }
@@ -540,10 +586,9 @@ func (repo *Repository) GetCommitBranchStart(env []string, branch, endCommitID s
cmd := gitcmd.NewCommand("log", prettyLogFormat) cmd := gitcmd.NewCommand("log", prettyLogFormat)
cmd.AddDynamicArguments(endCommitID) cmd.AddDynamicArguments(endCommitID)
stdout, _, runErr := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{ stdout, _, runErr := cmd.WithDir(repo.Path).
Dir: repo.Path, WithEnv(env).
Env: env, RunStdBytes(repo.Ctx)
})
if runErr != nil { if runErr != nil {
return "", runErr return "", runErr
} }

View File

@@ -51,7 +51,10 @@ func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) {
} }
} }
actualCommitID, _, err := gitcmd.NewCommand("rev-parse", "--verify").AddDynamicArguments(commitID).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) actualCommitID, _, err := gitcmd.NewCommand("rev-parse", "--verify").
AddDynamicArguments(commitID).
WithDir(repo.Path).
RunStdString(repo.Ctx)
actualCommitID = strings.TrimSpace(actualCommitID) actualCommitID = strings.TrimSpace(actualCommitID)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "unknown revision or path") || if strings.Contains(err.Error(), "unknown revision or path") ||

View File

@@ -17,7 +17,10 @@ import (
// ResolveReference resolves a name to a reference // ResolveReference resolves a name to a reference
func (repo *Repository) ResolveReference(name string) (string, error) { func (repo *Repository) ResolveReference(name string) (string, error) {
stdout, _, err := gitcmd.NewCommand("show-ref", "--hash").AddDynamicArguments(name).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err := gitcmd.NewCommand("show-ref", "--hash").
AddDynamicArguments(name).
WithDir(repo.Path).
RunStdString(repo.Ctx)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "not a valid ref") { if strings.Contains(err.Error(), "not a valid ref") {
return "", ErrNotExist{name, ""} return "", ErrNotExist{name, ""}
@@ -57,7 +60,10 @@ func (repo *Repository) IsCommitExist(name string) bool {
log.Error("IsCommitExist: %v", err) log.Error("IsCommitExist: %v", err)
return false return false
} }
_, _, err := gitcmd.NewCommand("cat-file", "-e").AddDynamicArguments(name).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) _, _, err := gitcmd.NewCommand("cat-file", "-e").
AddDynamicArguments(name).
WithDir(repo.Path).
RunStdString(repo.Ctx)
return err == nil return err == nil
} }

View File

@@ -14,7 +14,7 @@ import (
// this requires git v2.18 to be installed // this requires git v2.18 to be installed
func WriteCommitGraph(ctx context.Context, repoPath string) error { func WriteCommitGraph(ctx context.Context, repoPath string) error {
if DefaultFeatures().CheckVersionAtLeast("2.18") { if DefaultFeatures().CheckVersionAtLeast("2.18") {
if _, _, err := gitcmd.NewCommand("commit-graph", "write").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}); err != nil { if _, _, err := gitcmd.NewCommand("commit-graph", "write").WithDir(repoPath).RunStdString(ctx); err != nil {
return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err) return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err)
} }
} }

View File

@@ -27,13 +27,20 @@ func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, stri
if tmpRemote != "origin" { if tmpRemote != "origin" {
tmpBaseName := RemotePrefix + tmpRemote + "/tmp_" + base tmpBaseName := RemotePrefix + tmpRemote + "/tmp_" + base
// Fetch commit into a temporary branch in order to be able to handle commits and tags // Fetch commit into a temporary branch in order to be able to handle commits and tags
_, _, err := gitcmd.NewCommand("fetch", "--no-tags").AddDynamicArguments(tmpRemote).AddDashesAndList(base+":"+tmpBaseName).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) _, _, err := gitcmd.NewCommand("fetch", "--no-tags").
AddDynamicArguments(tmpRemote).
AddDashesAndList(base + ":" + tmpBaseName).
WithDir(repo.Path).
RunStdString(repo.Ctx)
if err == nil { if err == nil {
base = tmpBaseName base = tmpBaseName
} }
} }
stdout, _, err := gitcmd.NewCommand("merge-base").AddDashesAndList(base, head).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err := gitcmd.NewCommand("merge-base").
AddDashesAndList(base, head).
WithDir(repo.Path).
RunStdString(repo.Ctx)
return strings.TrimSpace(stdout), base, err return strings.TrimSpace(stdout), base, err
} }
@@ -61,22 +68,25 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
} }
// avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git <command> [<revision>...] -- [<file>...]' // avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git <command> [<revision>...] -- [<file>...]'
if err := gitcmd.NewCommand("diff", "-z", "--name-only").AddDynamicArguments(base+separator+head).AddArguments("--"). if err := gitcmd.NewCommand("diff", "-z", "--name-only").
Run(repo.Ctx, &gitcmd.RunOpts{ AddDynamicArguments(base + separator + head).
Dir: repo.Path, AddArguments("--").
Stdout: w, WithDir(repo.Path).
Stderr: stderr, WithStdout(w).
}); err != nil { WithStderr(stderr).
Run(repo.Ctx); err != nil {
if strings.Contains(stderr.String(), "no merge base") { if strings.Contains(stderr.String(), "no merge base") {
// git >= 2.28 now returns an error if base and head have become unrelated. // 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... // previously it would return the results of git diff -z --name-only base head so let's try that...
w = &lineCountWriter{} w = &lineCountWriter{}
stderr.Reset() stderr.Reset()
if err = gitcmd.NewCommand("diff", "-z", "--name-only").AddDynamicArguments(base, head).AddArguments("--").Run(repo.Ctx, &gitcmd.RunOpts{ if err = gitcmd.NewCommand("diff", "-z", "--name-only").
Dir: repo.Path, AddDynamicArguments(base, head).
Stdout: w, AddArguments("--").
Stderr: stderr, WithDir(repo.Path).
}); err == nil { WithStdout(w).
WithStderr(stderr).
Run(repo.Ctx); err == nil {
return w.numLines, nil return w.numLines, nil
} }
} }
@@ -91,30 +101,29 @@ var patchCommits = regexp.MustCompile(`^From\s(\w+)\s`)
func (repo *Repository) GetDiff(compareArg string, w io.Writer) error { func (repo *Repository) GetDiff(compareArg string, w io.Writer) error {
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
return gitcmd.NewCommand("diff", "-p").AddDynamicArguments(compareArg). return gitcmd.NewCommand("diff", "-p").AddDynamicArguments(compareArg).
Run(repo.Ctx, &gitcmd.RunOpts{ WithDir(repo.Path).
Dir: repo.Path, WithStdout(w).
Stdout: w, WithStderr(stderr).
Stderr: stderr, Run(repo.Ctx)
})
} }
// GetDiffBinary generates and returns patch data between given revisions, including binary diffs. // GetDiffBinary generates and returns patch data between given revisions, including binary diffs.
func (repo *Repository) GetDiffBinary(compareArg string, w io.Writer) error { func (repo *Repository) GetDiffBinary(compareArg string, w io.Writer) error {
return gitcmd.NewCommand("diff", "-p", "--binary", "--histogram").AddDynamicArguments(compareArg).Run(repo.Ctx, &gitcmd.RunOpts{ return gitcmd.NewCommand("diff", "-p", "--binary", "--histogram").
Dir: repo.Path, AddDynamicArguments(compareArg).
Stdout: w, WithDir(repo.Path).
}) WithStdout(w).
Run(repo.Ctx)
} }
// GetPatch generates and returns format-patch data between given revisions, able to be used with `git apply` // 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 { func (repo *Repository) GetPatch(compareArg string, w io.Writer) error {
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
return gitcmd.NewCommand("format-patch", "--binary", "--stdout").AddDynamicArguments(compareArg). return gitcmd.NewCommand("format-patch", "--binary", "--stdout").AddDynamicArguments(compareArg).
Run(repo.Ctx, &gitcmd.RunOpts{ WithDir(repo.Path).
Dir: repo.Path, WithStdout(w).
Stdout: w, WithStderr(stderr).
Stderr: stderr, Run(repo.Ctx)
})
} }
// GetFilesChangedBetween returns a list of all files that have been changed between the given commits // GetFilesChangedBetween returns a list of all files that have been changed between the given commits
@@ -131,7 +140,7 @@ func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, err
} else { } else {
cmd.AddDynamicArguments(base, head) cmd.AddDynamicArguments(base, head)
} }
stdout, _, err := cmd.RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err := cmd.WithDir(repo.Path).RunStdString(repo.Ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -103,7 +103,8 @@ func TestReadWritePullHead(t *testing.T) {
newCommit := "feaf4ba6bc635fec442f46ddd4512416ec43c2c2" newCommit := "feaf4ba6bc635fec442f46ddd4512416ec43c2c2"
_, _, err = gitcmd.NewCommand("update-ref"). _, _, err = gitcmd.NewCommand("update-ref").
AddDynamicArguments(PullPrefix+"1/head", newCommit). AddDynamicArguments(PullPrefix+"1/head", newCommit).
RunStdString(t.Context(), &gitcmd.RunOpts{Dir: repo.Path}) WithDir(repo.Path).
RunStdString(t.Context())
if err != nil { if err != nil {
assert.NoError(t, err) assert.NoError(t, err)
return return
@@ -122,7 +123,8 @@ func TestReadWritePullHead(t *testing.T) {
// Remove file after the test // Remove file after the test
_, _, err = gitcmd.NewCommand("update-ref", "--no-deref", "-d"). _, _, err = gitcmd.NewCommand("update-ref", "--no-deref", "-d").
AddDynamicArguments(PullPrefix + "1/head"). AddDynamicArguments(PullPrefix + "1/head").
RunStdString(t.Context(), &gitcmd.RunOpts{Dir: repo.Path}) WithDir(repo.Path).
RunStdString(t.Context())
assert.NoError(t, err) assert.NoError(t, err)
} }

View File

@@ -43,7 +43,7 @@ func (repo *Repository) GetDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings,
Sign: true, Sign: true,
} }
value, _, _ := gitcmd.NewCommand("config", "--get", "commit.gpgsign").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) value, _, _ := gitcmd.NewCommand("config", "--get", "commit.gpgsign").WithDir(repo.Path).RunStdString(repo.Ctx)
sign, valid := ParseBool(strings.TrimSpace(value)) sign, valid := ParseBool(strings.TrimSpace(value))
if !sign || !valid { if !sign || !valid {
gpgSettings.Sign = false gpgSettings.Sign = false
@@ -51,16 +51,16 @@ func (repo *Repository) GetDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings,
return gpgSettings, nil return gpgSettings, nil
} }
signingKey, _, _ := gitcmd.NewCommand("config", "--get", "user.signingkey").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) signingKey, _, _ := gitcmd.NewCommand("config", "--get", "user.signingkey").WithDir(repo.Path).RunStdString(repo.Ctx)
gpgSettings.KeyID = strings.TrimSpace(signingKey) gpgSettings.KeyID = strings.TrimSpace(signingKey)
format, _, _ := gitcmd.NewCommand("config", "--default", SigningKeyFormatOpenPGP, "--get", "gpg.format").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) format, _, _ := gitcmd.NewCommand("config", "--default", SigningKeyFormatOpenPGP, "--get", "gpg.format").WithDir(repo.Path).RunStdString(repo.Ctx)
gpgSettings.Format = strings.TrimSpace(format) gpgSettings.Format = strings.TrimSpace(format)
defaultEmail, _, _ := gitcmd.NewCommand("config", "--get", "user.email").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) defaultEmail, _, _ := gitcmd.NewCommand("config", "--get", "user.email").WithDir(repo.Path).RunStdString(repo.Ctx)
gpgSettings.Email = strings.TrimSpace(defaultEmail) gpgSettings.Email = strings.TrimSpace(defaultEmail)
defaultName, _, _ := gitcmd.NewCommand("config", "--get", "user.name").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) defaultName, _, _ := gitcmd.NewCommand("config", "--get", "user.name").WithDir(repo.Path).RunStdString(repo.Ctx)
gpgSettings.Name = strings.TrimSpace(defaultName) gpgSettings.Name = strings.TrimSpace(defaultName)
if err := gpgSettings.LoadPublicKeyContent(); err != nil { if err := gpgSettings.LoadPublicKeyContent(); err != nil {

View File

@@ -22,7 +22,7 @@ func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string)
} }
if len(treeish) != objectFormat.FullLength() { if len(treeish) != objectFormat.FullLength() {
res, _, err := gitcmd.NewCommand("rev-parse", "--verify").AddDynamicArguments(treeish).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) res, _, err := gitcmd.NewCommand("rev-parse", "--verify").AddDynamicArguments(treeish).WithDir(repo.Path).RunStdString(repo.Ctx)
if err != nil { if err != nil {
return err return err
} }
@@ -42,7 +42,7 @@ func (repo *Repository) readTreeToIndex(id ObjectID, indexFilename ...string) er
if len(indexFilename) > 0 { if len(indexFilename) > 0 {
env = append(os.Environ(), "GIT_INDEX_FILE="+indexFilename[0]) env = append(os.Environ(), "GIT_INDEX_FILE="+indexFilename[0])
} }
_, _, err := gitcmd.NewCommand("read-tree").AddDynamicArguments(id.String()).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path, Env: env}) _, _, err := gitcmd.NewCommand("read-tree").AddDynamicArguments(id.String()).WithDir(repo.Path).WithEnv(env).RunStdString(repo.Ctx)
if err != nil { if err != nil {
return err return err
} }
@@ -75,14 +75,14 @@ func (repo *Repository) ReadTreeToTemporaryIndex(treeish string) (tmpIndexFilena
// EmptyIndex empties the index // EmptyIndex empties the index
func (repo *Repository) EmptyIndex() error { func (repo *Repository) EmptyIndex() error {
_, _, err := gitcmd.NewCommand("read-tree", "--empty").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) _, _, err := gitcmd.NewCommand("read-tree", "--empty").WithDir(repo.Path).RunStdString(repo.Ctx)
return err return err
} }
// LsFiles checks if the given filenames are in the index // LsFiles checks if the given filenames are in the index
func (repo *Repository) LsFiles(filenames ...string) ([]string, error) { func (repo *Repository) LsFiles(filenames ...string) ([]string, error) {
cmd := gitcmd.NewCommand("ls-files", "-z").AddDashesAndList(filenames...) cmd := gitcmd.NewCommand("ls-files", "-z").AddDashesAndList(filenames...)
res, _, err := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) res, _, err := cmd.WithDir(repo.Path).RunStdBytes(repo.Ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -110,12 +110,12 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
buffer.WriteString("0 blob " + objectFormat.EmptyObjectID().String() + "\t" + file + "\000") buffer.WriteString("0 blob " + objectFormat.EmptyObjectID().String() + "\t" + file + "\000")
} }
} }
return cmd.Run(repo.Ctx, &gitcmd.RunOpts{ return cmd.
Dir: repo.Path, WithDir(repo.Path).
Stdin: bytes.NewReader(buffer.Bytes()), WithStdin(bytes.NewReader(buffer.Bytes())).
Stdout: stdout, WithStdout(stdout).
Stderr: stderr, WithStderr(stderr).
}) Run(repo.Ctx)
} }
type IndexObjectInfo struct { type IndexObjectInfo struct {
@@ -134,12 +134,12 @@ func (repo *Repository) AddObjectsToIndex(objects ...IndexObjectInfo) error {
// using format: mode SP type SP sha1 TAB path // using format: mode SP type SP sha1 TAB path
buffer.WriteString(object.Mode + " blob " + object.Object.String() + "\t" + object.Filename + "\000") buffer.WriteString(object.Mode + " blob " + object.Object.String() + "\t" + object.Filename + "\000")
} }
return cmd.Run(repo.Ctx, &gitcmd.RunOpts{ return cmd.
Dir: repo.Path, WithDir(repo.Path).
Stdin: bytes.NewReader(buffer.Bytes()), WithStdin(bytes.NewReader(buffer.Bytes())).
Stdout: stdout, WithStdout(stdout).
Stderr: stderr, WithStderr(stderr).
}) Run(repo.Ctx)
} }
// AddObjectToIndex adds the provided object hash to the index at the provided filename // AddObjectToIndex adds the provided object hash to the index at the provided filename
@@ -149,7 +149,7 @@ func (repo *Repository) AddObjectToIndex(mode string, object ObjectID, filename
// WriteTree writes the current index as a tree to the object db and returns its hash // WriteTree writes the current index as a tree to the object db and returns its hash
func (repo *Repository) WriteTree() (*Tree, error) { func (repo *Repository) WriteTree() (*Tree, error) {
stdout, _, runErr := gitcmd.NewCommand("write-tree").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, runErr := gitcmd.NewCommand("write-tree").WithDir(repo.Path).RunStdString(repo.Ctx)
if runErr != nil { if runErr != nil {
return nil, runErr return nil, runErr
} }

View File

@@ -76,12 +76,12 @@ func (repo *Repository) hashObject(reader io.Reader, save bool) (string, error)
} }
stdout := new(bytes.Buffer) stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
err := cmd.Run(repo.Ctx, &gitcmd.RunOpts{ err := cmd.
Dir: repo.Path, WithDir(repo.Path).
Stdin: reader, WithStdin(reader).
Stdout: stdout, WithStdout(stdout).
Stderr: stderr, WithStderr(stderr).
}) Run(repo.Ctx)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@@ -28,7 +28,8 @@ func (repo *Repository) ListOccurrences(ctx context.Context, refType, commitSHA
default: default:
return nil, util.NewInvalidArgumentErrorf(`can only use "branch" or "tag" for refType, but got %q`, refType) return nil, util.NewInvalidArgumentErrorf(`can only use "branch" or "tag" for refType, but got %q`, refType)
} }
stdout, _, err := cmd.AddArguments("--no-color", "--sort=-creatordate", "--contains").AddDynamicArguments(commitSHA).RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err := cmd.AddArguments("--no-color", "--sort=-creatordate", "--contains").
AddDynamicArguments(commitSHA).WithDir(repo.Path).RunStdString(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -23,11 +23,11 @@ func (repo *Repository) GetRefsFiltered(pattern string) ([]*Reference, error) {
go func() { go func() {
stderrBuilder := &strings.Builder{} stderrBuilder := &strings.Builder{}
err := gitcmd.NewCommand("for-each-ref").Run(repo.Ctx, &gitcmd.RunOpts{ err := gitcmd.NewCommand("for-each-ref").
Dir: repo.Path, WithDir(repo.Path).
Stdout: stdoutWriter, WithStdout(stdoutWriter).
Stderr: stderrBuilder, WithStderr(stderrBuilder).
}) Run(repo.Ctx)
if err != nil { if err != nil {
_ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, stderrBuilder.String())) _ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, stderrBuilder.String()))
} else { } else {

View File

@@ -43,7 +43,8 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
stdout, _, runErr := gitcmd.NewCommand("rev-list", "--count", "--no-merges", "--branches=*", "--date=iso"). stdout, _, runErr := gitcmd.NewCommand("rev-list", "--count", "--no-merges", "--branches=*", "--date=iso").
AddOptionFormat("--since=%s", since). AddOptionFormat("--since=%s", since).
RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) WithDir(repo.Path).
RunStdString(repo.Ctx)
if runErr != nil { if runErr != nil {
return nil, runErr return nil, runErr
} }
@@ -72,12 +73,11 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
} }
stderr := new(strings.Builder) stderr := new(strings.Builder)
err = gitCmd.Run(repo.Ctx, &gitcmd.RunOpts{ err = gitCmd.
Env: []string{}, WithDir(repo.Path).
Dir: repo.Path, WithStdout(stdoutWriter).
Stdout: stdoutWriter, WithStderr(stderr).
Stderr: stderr, WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close() _ = stdoutWriter.Close()
scanner := bufio.NewScanner(stdoutReader) scanner := bufio.NewScanner(stdoutReader)
scanner.Split(bufio.ScanLines) scanner.Split(bufio.ScanLines)
@@ -145,8 +145,8 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
stats.Authors = a stats.Authors = a
_ = stdoutReader.Close() _ = stdoutReader.Close()
return nil return nil
}, }).
}) Run(repo.Ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to get GetCodeActivityStats for repository.\nError: %w\nStderr: %s", err, stderr) return nil, fmt.Errorf("Failed to get GetCodeActivityStats for repository.\nError: %w\nStderr: %s", err, stderr)
} }

View File

@@ -19,13 +19,17 @@ const TagPrefix = "refs/tags/"
// CreateTag create one tag in the repository // CreateTag create one tag in the repository
func (repo *Repository) CreateTag(name, revision string) error { func (repo *Repository) CreateTag(name, revision string) error {
_, _, err := gitcmd.NewCommand("tag").AddDashesAndList(name, revision).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) _, _, err := gitcmd.NewCommand("tag").AddDashesAndList(name, revision).WithDir(repo.Path).RunStdString(repo.Ctx)
return err return err
} }
// CreateAnnotatedTag create one annotated tag in the repository // CreateAnnotatedTag create one annotated tag in the repository
func (repo *Repository) CreateAnnotatedTag(name, message, revision string) error { func (repo *Repository) CreateAnnotatedTag(name, message, revision string) error {
_, _, err := gitcmd.NewCommand("tag", "-a", "-m").AddDynamicArguments(message).AddDashesAndList(name, revision).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) _, _, err := gitcmd.NewCommand("tag", "-a", "-m").
AddDynamicArguments(message).
AddDashesAndList(name, revision).
WithDir(repo.Path).
RunStdString(repo.Ctx)
return err return err
} }
@@ -35,7 +39,7 @@ func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
return "", fmt.Errorf("SHA is too short: %s", sha) return "", fmt.Errorf("SHA is too short: %s", sha)
} }
stdout, _, err := gitcmd.NewCommand("show-ref", "--tags", "-d").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err := gitcmd.NewCommand("show-ref", "--tags", "-d").WithDir(repo.Path).RunStdString(repo.Ctx)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -58,7 +62,7 @@ func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
// GetTagID returns the object ID for a tag (annotated tags have both an object SHA AND a commit SHA) // GetTagID returns the object ID for a tag (annotated tags have both an object SHA AND a commit SHA)
func (repo *Repository) GetTagID(name string) (string, error) { func (repo *Repository) GetTagID(name string) (string, error) {
stdout, _, err := gitcmd.NewCommand("show-ref", "--tags").AddDashesAndList(name).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) stdout, _, err := gitcmd.NewCommand("show-ref", "--tags").AddDashesAndList(name).WithDir(repo.Path).RunStdString(repo.Ctx)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -115,12 +119,15 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
defer stdoutReader.Close() defer stdoutReader.Close()
defer stdoutWriter.Close() defer stdoutWriter.Close()
stderr := strings.Builder{} stderr := strings.Builder{}
rc := &gitcmd.RunOpts{Dir: repo.Path, Stdout: stdoutWriter, Stderr: &stderr}
go func() { go func() {
err := gitcmd.NewCommand("for-each-ref"). err := gitcmd.NewCommand("for-each-ref").
AddOptionFormat("--format=%s", forEachRefFmt.Flag()). AddOptionFormat("--format=%s", forEachRefFmt.Flag()).
AddArguments("--sort", "-*creatordate", "refs/tags").Run(repo.Ctx, rc) AddArguments("--sort", "-*creatordate", "refs/tags").
WithDir(repo.Path).
WithStdout(stdoutWriter).
WithStderr(&stderr).
Run(repo.Ctx)
if err != nil { if err != nil {
_ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, stderr.String())) _ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, stderr.String()))
} else { } else {

View File

@@ -60,13 +60,12 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
stdout := new(bytes.Buffer) stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
err := cmd.Run(repo.Ctx, &gitcmd.RunOpts{ err := cmd.WithEnv(env).
Env: env, WithDir(repo.Path).
Dir: repo.Path, WithStdin(messageBytes).
Stdin: messageBytes, WithStdout(stdout).
Stdout: stdout, WithStderr(stderr).
Stderr: stderr, Run(repo.Ctx)
})
if err != nil { if err != nil {
return nil, gitcmd.ConcatenateError(err, stderr.String()) return nil, gitcmd.ConcatenateError(err, stderr.String())
} }

View File

@@ -38,7 +38,10 @@ func (repo *Repository) GetTree(idStr string) (*Tree, error) {
} }
if len(idStr) != objectFormat.FullLength() { if len(idStr) != objectFormat.FullLength() {
res, _, err := gitcmd.NewCommand("rev-parse", "--verify").AddDynamicArguments(idStr).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) res, _, err := gitcmd.NewCommand("rev-parse", "--verify").
AddDynamicArguments(idStr).
WithDir(repo.Path).
RunStdString(repo.Ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -25,10 +25,11 @@ func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submodul
if err != nil { if err != nil {
return nil, err return nil, err
} }
opts := &gitcmd.RunOpts{
Dir: repoPath, err = gitcmd.NewCommand("ls-tree", "-r", "--", "HEAD").
Stdout: stdoutWriter, WithDir(repoPath).
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { WithStdout(stdoutWriter).
WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close() _ = stdoutWriter.Close()
defer stdoutReader.Close() defer stdoutReader.Close()
@@ -44,9 +45,8 @@ func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submodul
} }
} }
return scanner.Err() return scanner.Err()
}, }).
} Run(ctx)
err = gitcmd.NewCommand("ls-tree", "-r", "--", "HEAD").Run(ctx, opts)
if err != nil { if err != nil {
return nil, fmt.Errorf("GetTemplateSubmoduleCommits: error running git ls-tree: %v", err) return nil, fmt.Errorf("GetTemplateSubmoduleCommits: error running git ls-tree: %v", err)
} }
@@ -58,7 +58,7 @@ func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submodul
func AddTemplateSubmoduleIndexes(ctx context.Context, repoPath string, submodules []TemplateSubmoduleCommit) error { func AddTemplateSubmoduleIndexes(ctx context.Context, repoPath string, submodules []TemplateSubmoduleCommit) error {
for _, submodule := range submodules { for _, submodule := range submodules {
cmd := gitcmd.NewCommand("update-index", "--add", "--cacheinfo", "160000").AddDynamicArguments(submodule.Commit, submodule.Path) cmd := gitcmd.NewCommand("update-index", "--add", "--cacheinfo", "160000").AddDynamicArguments(submodule.Commit, submodule.Path)
if stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}); err != nil { if stdout, _, err := cmd.WithDir(repoPath).RunStdString(ctx); err != nil {
log.Error("Unable to add %s as submodule to repo %s: stdout %s\nError: %v", submodule.Path, repoPath, stdout, err) log.Error("Unable to add %s as submodule to repo %s: stdout %s\nError: %v", submodule.Path, repoPath, stdout, err)
return err return err
} }

View File

@@ -32,14 +32,14 @@ func TestAddTemplateSubmoduleIndexes(t *testing.T) {
ctx := t.Context() ctx := t.Context()
tmpDir := t.TempDir() tmpDir := t.TempDir()
var err error var err error
_, _, err = gitcmd.NewCommand("init").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpDir}) _, _, err = gitcmd.NewCommand("init").WithDir(tmpDir).RunStdString(ctx)
require.NoError(t, err) require.NoError(t, err)
_ = os.Mkdir(filepath.Join(tmpDir, "new-dir"), 0o755) _ = os.Mkdir(filepath.Join(tmpDir, "new-dir"), 0o755)
err = AddTemplateSubmoduleIndexes(ctx, tmpDir, []TemplateSubmoduleCommit{{Path: "new-dir", Commit: "1234567890123456789012345678901234567890"}}) err = AddTemplateSubmoduleIndexes(ctx, tmpDir, []TemplateSubmoduleCommit{{Path: "new-dir", Commit: "1234567890123456789012345678901234567890"}})
require.NoError(t, err) require.NoError(t, err)
_, _, err = gitcmd.NewCommand("add", "--all").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpDir}) _, _, err = gitcmd.NewCommand("add", "--all").WithDir(tmpDir).RunStdString(ctx)
require.NoError(t, err) require.NoError(t, err)
_, _, err = gitcmd.NewCommand("-c", "user.name=a", "-c", "user.email=b", "commit", "-m=test").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpDir}) _, _, err = gitcmd.NewCommand("-c", "user.name=a", "-c", "user.email=b", "commit", "-m=test").WithDir(tmpDir).RunStdString(ctx)
require.NoError(t, err) require.NoError(t, err)
submodules, err := GetTemplateSubmoduleCommits(t.Context(), tmpDir) submodules, err := GetTemplateSubmoduleCommits(t.Context(), tmpDir)
require.NoError(t, err) require.NoError(t, err)

View File

@@ -53,7 +53,7 @@ func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error
cmd := gitcmd.NewCommand("ls-tree", "-z", "--name-only"). cmd := gitcmd.NewCommand("ls-tree", "-z", "--name-only").
AddDashesAndList(append([]string{ref}, filenames...)...) AddDashesAndList(append([]string{ref}, filenames...)...)
res, _, err := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) res, _, err := cmd.WithDir(repo.Path).RunStdBytes(repo.Ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -69,7 +69,8 @@ func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error
func (repo *Repository) GetTreePathLatestCommit(refName, treePath string) (*Commit, error) { func (repo *Repository) GetTreePathLatestCommit(refName, treePath string) (*Commit, error) {
stdout, _, err := gitcmd.NewCommand("rev-list", "-1"). stdout, _, err := gitcmd.NewCommand("rev-list", "-1").
AddDynamicArguments(refName).AddDashesAndList(treePath). AddDynamicArguments(refName).AddDashesAndList(treePath).
RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) WithDir(repo.Path).
RunStdString(repo.Ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -72,7 +72,7 @@ func (t *Tree) ListEntries() (Entries, error) {
} }
} }
stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-l").AddDynamicArguments(t.ID.String()).RunStdBytes(t.repo.Ctx, &gitcmd.RunOpts{Dir: t.repo.Path}) stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-l").AddDynamicArguments(t.ID.String()).WithDir(t.repo.Path).RunStdBytes(t.repo.Ctx)
if runErr != nil { if runErr != nil {
if strings.Contains(runErr.Error(), "fatal: Not a valid object name") || strings.Contains(runErr.Error(), "fatal: not a tree object") { if strings.Contains(runErr.Error(), "fatal: Not a valid object name") || strings.Contains(runErr.Error(), "fatal: not a tree object") {
return nil, ErrNotExist{ return nil, ErrNotExist{
@@ -101,7 +101,8 @@ func (t *Tree) listEntriesRecursive(extraArgs gitcmd.TrustedCmdArgs) (Entries, e
stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-t", "-r"). stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-t", "-r").
AddArguments(extraArgs...). AddArguments(extraArgs...).
AddDynamicArguments(t.ID.String()). AddDynamicArguments(t.ID.String()).
RunStdBytes(t.repo.Ctx, &gitcmd.RunOpts{Dir: t.repo.Path}) WithDir(t.repo.Path).
RunStdBytes(t.repo.Ctx)
if runErr != nil { if runErr != nil {
return nil, runErr return nil, runErr
} }

View File

@@ -29,12 +29,7 @@ func CreateArchive(ctx context.Context, repo Repository, format string, target i
cmd.AddDynamicArguments(commitID) cmd.AddDynamicArguments(commitID)
var stderr strings.Builder var stderr strings.Builder
err := cmd.Run(ctx, &gitcmd.RunOpts{ if err := RunCmd(ctx, repo, cmd.WithStdout(target).WithStderr(&stderr)); err != nil {
Dir: repoPath(repo),
Stdout: target,
Stderr: &stderr,
})
if err != nil {
return gitcmd.ConcatenateError(err, stderr.String()) return gitcmd.ConcatenateError(err, stderr.String())
} }
return nil return nil
@@ -49,23 +44,23 @@ func CreateBundle(ctx context.Context, repo Repository, commit string, out io.Wr
defer cleanup() defer cleanup()
env := append(os.Environ(), "GIT_OBJECT_DIRECTORY="+filepath.Join(repoPath(repo), "objects")) env := append(os.Environ(), "GIT_OBJECT_DIRECTORY="+filepath.Join(repoPath(repo), "objects"))
_, _, err = gitcmd.NewCommand("init", "--bare").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env}) _, _, err = gitcmd.NewCommand("init", "--bare").WithDir(tmp).WithEnv(env).RunStdString(ctx)
if err != nil { if err != nil {
return err return err
} }
_, _, err = gitcmd.NewCommand("reset", "--soft").AddDynamicArguments(commit).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env}) _, _, err = gitcmd.NewCommand("reset", "--soft").AddDynamicArguments(commit).WithDir(tmp).WithEnv(env).RunStdString(ctx)
if err != nil { if err != nil {
return err return err
} }
_, _, err = gitcmd.NewCommand("branch", "-m", "bundle").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env}) _, _, err = gitcmd.NewCommand("branch", "-m", "bundle").WithDir(tmp).WithEnv(env).RunStdString(ctx)
if err != nil { if err != nil {
return err return err
} }
tmpFile := filepath.Join(tmp, "bundle") tmpFile := filepath.Join(tmp, "bundle")
_, _, err = gitcmd.NewCommand("bundle", "create").AddDynamicArguments(tmpFile, "bundle", "HEAD").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env}) _, _, err = gitcmd.NewCommand("bundle", "create").AddDynamicArguments(tmpFile, "bundle", "HEAD").WithDir(tmp).WithEnv(env).RunStdString(ctx)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -10,7 +10,7 @@ import (
) )
func LineBlame(ctx context.Context, repo Repository, revision, file string, line uint) (string, error) { func LineBlame(ctx context.Context, repo Repository, revision, file string, line uint) (string, error) {
return runCmdString(ctx, repo, return RunCmdString(ctx, repo,
gitcmd.NewCommand("blame"). gitcmd.NewCommand("blame").
AddOptionFormat("-L %d,%d", line, line). AddOptionFormat("-L %d,%d", line, line).
AddOptionValues("-p", revision). AddOptionValues("-p", revision).

View File

@@ -36,14 +36,14 @@ func GetBranchCommitID(ctx context.Context, repo Repository, branch string) (str
// SetDefaultBranch sets default branch of repository. // SetDefaultBranch sets default branch of repository.
func SetDefaultBranch(ctx context.Context, repo Repository, name string) error { 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)) AddDynamicArguments(git.BranchPrefix+name))
return err return err
} }
// GetDefaultBranch gets default branch of repository. // GetDefaultBranch gets default branch of repository.
func GetDefaultBranch(ctx context.Context, repo Repository) (string, error) { 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 { if err != nil {
return "", err 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. // IsReferenceExist returns true if given reference exists in the repository.
func IsReferenceExist(ctx context.Context, repo Repository, name string) bool { 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 return err == nil
} }
@@ -76,7 +76,7 @@ func DeleteBranch(ctx context.Context, repo Repository, name string, force bool)
} }
cmd.AddDashesAndList(name) cmd.AddDashesAndList(name)
_, err := runCmdString(ctx, repo, cmd) _, err := RunCmdString(ctx, repo, cmd)
return err return err
} }
@@ -85,12 +85,12 @@ func CreateBranch(ctx context.Context, repo Repository, branch, oldbranchOrCommi
cmd := gitcmd.NewCommand("branch") cmd := gitcmd.NewCommand("branch")
cmd.AddDashesAndList(branch, oldbranchOrCommit) cmd.AddDashesAndList(branch, oldbranchOrCommit)
_, err := runCmdString(ctx, repo, cmd) _, err := RunCmdString(ctx, repo, cmd)
return err return err
} }
// RenameBranch rename a branch // RenameBranch rename a branch
func RenameBranch(ctx context.Context, repo Repository, from, to string) error { 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 return err
} }

View File

@@ -9,7 +9,15 @@ import (
"code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/git/gitcmd"
) )
func runCmdString(ctx context.Context, repo Repository, cmd *gitcmd.Command) (string, error) { func RunCmd(ctx context.Context, repo Repository, cmd *gitcmd.Command) error {
res, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath(repo)}) 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 return res, err
} }
func RunCmdBytes(ctx context.Context, repo Repository, cmd *gitcmd.Command) ([]byte, []byte, error) {
return cmd.WithDir(repoPath(repo)).WithParentCallerInfo().RunStdBytes(ctx)
}

View File

@@ -22,7 +22,7 @@ type DivergeObject struct {
func GetDivergingCommits(ctx context.Context, repo Repository, baseBranch, targetBranch string) (*DivergeObject, error) { func GetDivergingCommits(ctx context.Context, repo Repository, baseBranch, targetBranch string) (*DivergeObject, error) {
cmd := gitcmd.NewCommand("rev-list", "--count", "--left-right"). cmd := gitcmd.NewCommand("rev-list", "--count", "--left-right").
AddDynamicArguments(baseBranch + "..." + targetBranch).AddArguments("--") AddDynamicArguments(baseBranch + "..." + targetBranch).AddArguments("--")
stdout, _, err1 := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath(repo)}) stdout, err1 := RunCmdString(ctx, repo, cmd)
if err1 != nil { if err1 != nil {
return nil, err1 return nil, err1
} }

View File

@@ -12,7 +12,7 @@ import (
) )
func GitConfigGet(ctx context.Context, repo Repository, key string) (string, error) { 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)) AddDynamicArguments(key))
if err != nil { if err != nil {
return "", err 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. // 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 { func GitConfigAdd(ctx context.Context, repo Repository, key, value string) error {
return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) 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)) AddDynamicArguments(key, value))
return err 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. // If the key exists, it will be updated to the new value.
func GitConfigSet(ctx context.Context, repo Repository, key, value string) error { func GitConfigSet(ctx context.Context, repo Repository, key, value string) error {
return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) 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)) AddDynamicArguments(key, value))
return err return err
}) })

View File

@@ -20,7 +20,7 @@ func GetDiffShortStatByCmdArgs(ctx context.Context, repo Repository, trustedArgs
// we get: // we get:
// " 9902 files changed, 2034198 insertions(+), 298800 deletions(-)\n" // " 9902 files changed, 2034198 insertions(+), 298800 deletions(-)\n"
cmd := gitcmd.NewCommand("diff", "--shortstat").AddArguments(trustedArgs...).AddDynamicArguments(dynamicArgs...) cmd := gitcmd.NewCommand("diff", "--shortstat").AddArguments(trustedArgs...).AddDynamicArguments(dynamicArgs...)
stdout, err := runCmdString(ctx, repo, cmd) stdout, err := RunCmdString(ctx, repo, cmd)
if err != nil { if err != nil {
return 0, 0, 0, err return 0, 0, 0, err
} }

View File

@@ -12,5 +12,5 @@ import (
// 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, repo Repository, timeout time.Duration, args gitcmd.TrustedCmdArgs) error { func Fsck(ctx context.Context, repo Repository, timeout time.Duration, args gitcmd.TrustedCmdArgs) error {
return gitcmd.NewCommand("fsck").AddArguments(args...).Run(ctx, &gitcmd.RunOpts{Timeout: timeout, Dir: repoPath(repo)}) return RunCmd(ctx, repo, gitcmd.NewCommand("fsck").AddArguments(args...).WithTimeout(timeout))
} }

View File

@@ -10,12 +10,10 @@ import (
) )
func UpdateRef(ctx context.Context, repo Repository, refName, newCommitID string) error { func UpdateRef(ctx context.Context, repo Repository, refName, newCommitID string) error {
_, _, err := gitcmd.NewCommand("update-ref").AddDynamicArguments(refName, newCommitID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath(repo)}) return RunCmd(ctx, repo, gitcmd.NewCommand("update-ref").AddDynamicArguments(refName, newCommitID))
return err
} }
func RemoveRef(ctx context.Context, repo Repository, refName string) error { func RemoveRef(ctx context.Context, repo Repository, refName string) error {
_, _, err := gitcmd.NewCommand("update-ref", "--no-deref", "-d"). return RunCmd(ctx, repo, gitcmd.NewCommand("update-ref", "--no-deref", "-d").
AddDynamicArguments(refName).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath(repo)}) AddDynamicArguments(refName))
return err
} }

View File

@@ -36,7 +36,7 @@ func GitRemoteAdd(ctx context.Context, repo Repository, remoteName, remoteURL st
return errors.New("unknown remote option: " + string(options[0])) 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 return err
}) })
} }
@@ -44,7 +44,7 @@ func GitRemoteAdd(ctx context.Context, repo Repository, remoteName, remoteURL st
func GitRemoteRemove(ctx context.Context, repo Repository, remoteName string) error { func GitRemoteRemove(ctx context.Context, repo Repository, remoteName string) error {
return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error { return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error {
cmd := gitcmd.NewCommand("remote", "rm").AddDynamicArguments(remoteName) cmd := gitcmd.NewCommand("remote", "rm").AddDynamicArguments(remoteName)
_, err := runCmdString(ctx, repo, cmd) _, err := RunCmdString(ctx, repo, cmd)
return err return err
}) })
} }
@@ -63,22 +63,18 @@ func GitRemoteGetURL(ctx context.Context, repo Repository, remoteName string) (*
// GitRemotePrune prunes the remote branches that no longer exist in the remote repository. // 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 { func GitRemotePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error {
return gitcmd.NewCommand("remote", "prune").AddDynamicArguments(remoteName). return RunCmd(ctx, repo, gitcmd.NewCommand("remote", "prune").
Run(ctx, &gitcmd.RunOpts{ AddDynamicArguments(remoteName).
Timeout: timeout, WithTimeout(timeout).
Dir: repoPath(repo), WithStdout(stdout).
Stdout: stdout, WithStderr(stderr))
Stderr: stderr,
})
} }
// GitRemoteUpdatePrune updates the remote branches and prunes the ones that no longer exist in the remote repository. // 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 { func GitRemoteUpdatePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error {
return gitcmd.NewCommand("remote", "update", "--prune").AddDynamicArguments(remoteName). return RunCmd(ctx, repo, gitcmd.NewCommand("remote", "update", "--prune").
Run(ctx, &gitcmd.RunOpts{ AddDynamicArguments(remoteName).
Timeout: timeout, WithTimeout(timeout).
Dir: repoPath(repo), WithStdout(stdout).
Stdout: stdout, WithStderr(stderr))
Stderr: stderr,
})
} }

37
modules/gitrepo/size.go Normal file
View File

@@ -0,0 +1,37 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package gitrepo
import (
"os"
"path/filepath"
)
const notRegularFileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular
// CalcRepositorySize returns the disk consumption for a given path
func CalcRepositorySize(repo Repository) (int64, error) {
var size int64
err := filepath.WalkDir(repoPath(repo), func(_ string, entry os.DirEntry, err error) error {
if os.IsNotExist(err) { // ignore the error because some files (like temp/lock file) may be deleted during traversing.
return nil
} else if err != nil {
return err
}
if entry.IsDir() {
return nil
}
info, err := entry.Info()
if os.IsNotExist(err) { // ignore the error as above
return nil
} else if err != nil {
return err
}
if (info.Mode() & notRegularFileMode) == 0 {
size += info.Size()
}
return nil
})
return size, err
}

View File

@@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/indexer" "code.gitea.io/gitea/modules/indexer"
path_filter "code.gitea.io/gitea/modules/indexer/code/bleve/token/path" path_filter "code.gitea.io/gitea/modules/indexer/code/bleve/token/path"
"code.gitea.io/gitea/modules/indexer/code/internal" "code.gitea.io/gitea/modules/indexer/code/internal"
@@ -163,7 +164,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro
var err error var err error
if !update.Sized { if !update.Sized {
var stdout string var stdout string
stdout, _, err = gitcmd.NewCommand("cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) stdout, err = gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("cat-file", "-s").AddDynamicArguments(update.BlobSha))
if err != nil { if err != nil {
return err return err
} }

View File

@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/indexer" "code.gitea.io/gitea/modules/indexer"
"code.gitea.io/gitea/modules/indexer/code/internal" "code.gitea.io/gitea/modules/indexer/code/internal"
indexer_internal "code.gitea.io/gitea/modules/indexer/internal" indexer_internal "code.gitea.io/gitea/modules/indexer/internal"
@@ -148,7 +149,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro
var err error var err error
if !update.Sized { if !update.Sized {
var stdout string var stdout string
stdout, _, err = gitcmd.NewCommand("cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) stdout, err = gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("cat-file", "-s").AddDynamicArguments(update.BlobSha))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -11,13 +11,14 @@ import (
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/indexer/code/internal" "code.gitea.io/gitea/modules/indexer/code/internal"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
) )
func getDefaultBranchSha(ctx context.Context, repo *repo_model.Repository) (string, error) { func getDefaultBranchSha(ctx context.Context, repo *repo_model.Repository) (string, error) {
stdout, _, err := gitcmd.NewCommand("show-ref", "-s").AddDynamicArguments(git.BranchPrefix+repo.DefaultBranch).RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) stdout, err := gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("show-ref", "-s").AddDynamicArguments(git.BranchPrefix+repo.DefaultBranch))
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -34,7 +35,7 @@ func getRepoChanges(ctx context.Context, repo *repo_model.Repository, revision s
needGenesis := len(status.CommitSha) == 0 needGenesis := len(status.CommitSha) == 0
if !needGenesis { if !needGenesis {
hasAncestorCmd := gitcmd.NewCommand("merge-base").AddDynamicArguments(status.CommitSha, revision) hasAncestorCmd := gitcmd.NewCommand("merge-base").AddDynamicArguments(status.CommitSha, revision)
stdout, _, _ := hasAncestorCmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) stdout, _ := gitrepo.RunCmdString(ctx, repo, hasAncestorCmd)
needGenesis = len(stdout) == 0 needGenesis = len(stdout) == 0
} }
@@ -87,7 +88,7 @@ func parseGitLsTreeOutput(stdout []byte) ([]internal.FileUpdate, error) {
// genesisChanges get changes to add repo to the indexer for the first time // genesisChanges get changes to add repo to the indexer for the first time
func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*internal.RepoChanges, error) { func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*internal.RepoChanges, error) {
var changes internal.RepoChanges var changes internal.RepoChanges
stdout, _, runErr := gitcmd.NewCommand("ls-tree", "--full-tree", "-l", "-r").AddDynamicArguments(revision).RunStdBytes(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) stdout, _, runErr := gitrepo.RunCmdBytes(ctx, repo, gitcmd.NewCommand("ls-tree", "--full-tree", "-l", "-r").AddDynamicArguments(revision))
if runErr != nil { if runErr != nil {
return nil, runErr return nil, runErr
} }
@@ -100,7 +101,7 @@ func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision s
// nonGenesisChanges get changes since the previous indexer update // nonGenesisChanges get changes since the previous indexer update
func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*internal.RepoChanges, error) { 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) diffCmd := gitcmd.NewCommand("diff", "--name-status").AddDynamicArguments(repo.CodeIndexerStatus.CommitSha, revision)
stdout, _, runErr := diffCmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) stdout, runErr := gitrepo.RunCmdString(ctx, repo, diffCmd)
if runErr != nil { if runErr != nil {
// previous commit sha may have been removed by a force push, so // previous commit sha may have been removed by a force push, so
// try rebuilding from scratch // try rebuilding from scratch
@@ -118,7 +119,7 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio
updateChanges := func() error { updateChanges := func() error {
cmd := gitcmd.NewCommand("ls-tree", "--full-tree", "-l").AddDynamicArguments(revision). cmd := gitcmd.NewCommand("ls-tree", "--full-tree", "-l").AddDynamicArguments(revision).
AddDashesAndList(updatedFilenames...) AddDashesAndList(updatedFilenames...)
lsTreeStdout, _, err := cmd.RunStdBytes(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) lsTreeStdout, _, err := gitrepo.RunCmdBytes(ctx, repo, cmd)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -6,44 +6,15 @@ package repository
import ( import (
"context" "context"
"fmt" "fmt"
"os"
"path/filepath"
git_model "code.gitea.io/gitea/models/git" git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/gitrepo"
) )
const notRegularFileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular
// getDirectorySize returns the disk consumption for a given path
func getDirectorySize(path string) (int64, error) {
var size int64
err := filepath.WalkDir(path, func(_ string, entry os.DirEntry, err error) error {
if os.IsNotExist(err) { // ignore the error because some files (like temp/lock file) may be deleted during traversing.
return nil
} else if err != nil {
return err
}
if entry.IsDir() {
return nil
}
info, err := entry.Info()
if os.IsNotExist(err) { // ignore the error as above
return nil
} else if err != nil {
return err
}
if (info.Mode() & notRegularFileMode) == 0 {
size += info.Size()
}
return nil
})
return size, err
}
// UpdateRepoSize updates the repository size, calculating it using getDirectorySize // UpdateRepoSize updates the repository size, calculating it using getDirectorySize
func UpdateRepoSize(ctx context.Context, repo *repo_model.Repository) error { func UpdateRepoSize(ctx context.Context, repo *repo_model.Repository) error {
size, err := getDirectorySize(repo.RepoPath()) size, err := gitrepo.CalcRepositorySize(repo)
if err != nil { if err != nil {
return fmt.Errorf("updateSize: %w", err) return fmt.Errorf("updateSize: %w", err)
} }

View File

@@ -8,6 +8,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/gitrepo"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -16,7 +17,7 @@ func TestGetDirectorySize(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
repo, err := repo_model.GetRepositoryByID(t.Context(), 1) repo, err := repo_model.GetRepositoryByID(t.Context(), 1)
assert.NoError(t, err) assert.NoError(t, err)
size, err := getDirectorySize(repo.RepoPath()) size, err := gitrepo.CalcRepositorySize(repo)
assert.NoError(t, err) assert.NoError(t, err)
repo.Size = 8165 // real size on the disk repo.Size = 8165 // real size on the disk
assert.Equal(t, repo.Size, size) assert.Equal(t, repo.Size, size)

View File

@@ -5,9 +5,13 @@ package util
import "runtime" import "runtime"
func CallerFuncName(skip int) string { func CallerFuncName(optSkipParent ...int) string {
pc := make([]uintptr, 1) pc := make([]uintptr, 1)
runtime.Callers(skip+1, pc) skipParent := 0
if len(optSkipParent) > 0 {
skipParent = optSkipParent[0]
}
runtime.Callers(skipParent+1 /*this*/ +1 /*runtime*/, pc)
funcName := runtime.FuncForPC(pc[0]).Name() funcName := runtime.FuncForPC(pc[0]).Name()
return funcName return funcName
} }

View File

@@ -11,7 +11,7 @@ import (
) )
func TestCallerFuncName(t *testing.T) { func TestCallerFuncName(t *testing.T) {
s := CallerFuncName(1) s := CallerFuncName()
assert.Equal(t, "code.gitea.io/gitea/modules/util.TestCallerFuncName", s) assert.Equal(t, "code.gitea.io/gitea/modules/util.TestCallerFuncName", s)
} }
@@ -26,7 +26,7 @@ func BenchmarkCallerFuncName(b *testing.B) {
// It is almost as fast as fmt.Sprintf // It is almost as fast as fmt.Sprintf
b.Run("caller", func(b *testing.B) { b.Run("caller", func(b *testing.B) {
for b.Loop() { for b.Loop() {
CallerFuncName(1) CallerFuncName()
} }
}) })
} }

View File

@@ -189,7 +189,12 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
// 2. Disallow force pushes to protected branches // 2. Disallow force pushes to protected branches
if oldCommitID != objectFormat.EmptyObjectID().String() { if oldCommitID != objectFormat.EmptyObjectID().String() {
output, _, err := gitcmd.NewCommand("rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+newCommitID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath(), Env: ctx.env}) output, err := gitrepo.RunCmdString(ctx,
repo,
gitcmd.NewCommand("rev-list", "--max-count=1").
AddDynamicArguments(oldCommitID, "^"+newCommitID).
WithEnv(ctx.env),
)
if err != nil { if err != nil {
log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, newCommitID, repo, err) log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, newCommitID, repo, err)
ctx.JSON(http.StatusInternalServerError, private.Response{ ctx.JSON(http.StatusInternalServerError, private.Response{

View File

@@ -39,11 +39,10 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []
command = gitcmd.NewCommand("rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID) command = gitcmd.NewCommand("rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID)
} }
// This is safe as force pushes are already forbidden // This is safe as force pushes are already forbidden
err = command.Run(repo.Ctx, &gitcmd.RunOpts{ err = command.WithEnv(env).
Env: env, WithDir(repo.Path).
Dir: repo.Path, WithStdout(stdoutWriter).
Stdout: stdoutWriter, WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close() _ = stdoutWriter.Close()
err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env) err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env)
if err != nil { if err != nil {
@@ -52,8 +51,8 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []
} }
_ = stdoutReader.Close() _ = stdoutReader.Close()
return err return err
}, }).
}) Run(repo.Ctx)
if err != nil && !isErrUnverifiedCommit(err) { if err != nil && !isErrUnverifiedCommit(err) {
log.Error("Unable to check commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err) log.Error("Unable to check commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err)
} }
@@ -86,11 +85,10 @@ func readAndVerifyCommit(sha string, repo *git.Repository, env []string) error {
commitID := git.MustIDFromString(sha) commitID := git.MustIDFromString(sha)
return gitcmd.NewCommand("cat-file", "commit").AddDynamicArguments(sha). return gitcmd.NewCommand("cat-file", "commit").AddDynamicArguments(sha).
Run(repo.Ctx, &gitcmd.RunOpts{ WithEnv(env).
Env: env, WithDir(repo.Path).
Dir: repo.Path, WithStdout(stdoutWriter).
Stdout: stdoutWriter, WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close() _ = stdoutWriter.Close()
commit, err := git.CommitFromReader(repo, commitID, stdoutReader) commit, err := git.CommitFromReader(repo, commitID, stdoutReader)
if err != nil { if err != nil {
@@ -104,8 +102,8 @@ func readAndVerifyCommit(sha string, repo *git.Repository, env []string) error {
} }
} }
return nil return nil
}, }).
}) Run(repo.Ctx)
} }
type errUnverifiedCommit struct { type errUnverifiedCommit struct {

View File

@@ -316,7 +316,9 @@ func dummyInfoRefs(ctx *context.Context) {
return return
} }
refs, _, err := gitcmd.NewCommand("receive-pack", "--stateless-rpc", "--advertise-refs", ".").RunStdBytes(ctx, &gitcmd.RunOpts{Dir: tmpDir}) refs, _, err := gitcmd.NewCommand("receive-pack", "--stateless-rpc", "--advertise-refs", ".").
WithDir(tmpDir).
RunStdBytes(ctx)
if err != nil { if err != nil {
log.Error(fmt.Sprintf("%v - %s", err, string(refs))) log.Error(fmt.Sprintf("%v - %s", err, string(refs)))
} }
@@ -447,15 +449,15 @@ func serviceRPC(ctx *context.Context, h *serviceHandler, service string) {
} }
var stderr bytes.Buffer var stderr bytes.Buffer
cmd.AddArguments("--stateless-rpc").AddDynamicArguments(h.getRepoDir()) if err := cmd.AddArguments("--stateless-rpc").
if err := cmd.Run(ctx, &gitcmd.RunOpts{ AddDynamicArguments(h.getRepoDir()).
Dir: h.getRepoDir(), WithDir(h.getRepoDir()).
Env: append(os.Environ(), h.environ...), WithEnv(append(os.Environ(), h.environ...)).
Stdout: ctx.Resp, WithStderr(&stderr).
Stdin: reqBody, WithStdin(reqBody).
Stderr: &stderr, WithStdout(ctx.Resp).
UseContextTimeout: true, WithUseContextTimeout(true).
}); err != nil { Run(ctx); err != nil {
if !git.IsErrCanceledOrKilled(err) { if !git.IsErrCanceledOrKilled(err) {
log.Error("Fail to serve RPC(%s) in %s: %v - %s", service, h.getRepoDir(), err, stderr.String()) log.Error("Fail to serve RPC(%s) in %s: %v - %s", service, h.getRepoDir(), err, stderr.String())
} }
@@ -495,7 +497,7 @@ func getServiceType(ctx *context.Context) string {
} }
func updateServerInfo(ctx gocontext.Context, dir string) []byte { func updateServerInfo(ctx gocontext.Context, dir string) []byte {
out, _, err := gitcmd.NewCommand("update-server-info").RunStdBytes(ctx, &gitcmd.RunOpts{Dir: dir}) out, _, err := gitcmd.NewCommand("update-server-info").WithDir(dir).RunStdBytes(ctx)
if err != nil { if err != nil {
log.Error(fmt.Sprintf("%v - %s", err, string(out))) log.Error(fmt.Sprintf("%v - %s", err, string(out)))
} }
@@ -525,7 +527,10 @@ func GetInfoRefs(ctx *context.Context) {
} }
h.environ = append(os.Environ(), h.environ...) h.environ = append(os.Environ(), h.environ...)
refs, _, err := cmd.AddArguments("--stateless-rpc", "--advertise-refs", ".").RunStdBytes(ctx, &gitcmd.RunOpts{Env: h.environ, Dir: h.getRepoDir()}) refs, _, err := cmd.AddArguments("--stateless-rpc", "--advertise-refs", ".").
WithEnv(h.environ).
WithDir(h.getRepoDir()).
RunStdBytes(ctx)
if err != nil { if err != nil {
log.Error(fmt.Sprintf("%v - %s", err, string(refs))) log.Error(fmt.Sprintf("%v - %s", err, string(refs)))
} }

View File

@@ -234,7 +234,8 @@ func GetMergedBaseCommitID(ctx *context.Context, issue *issues_model.Issue) stri
} }
if commitSHA != "" { if commitSHA != "" {
// Get immediate parent of the first commit in the patch, grab history back // Get immediate parent of the first commit in the patch, grab history back
parentCommit, _, err = gitcmd.NewCommand("rev-list", "-1", "--skip=1").AddDynamicArguments(commitSHA).RunStdString(ctx, &gitcmd.RunOpts{Dir: ctx.Repo.GitRepo.Path}) parentCommit, err = gitrepo.RunCmdString(ctx, ctx.Repo.Repository,
gitcmd.NewCommand("rev-list", "-1", "--skip=1").AddDynamicArguments(commitSHA))
if err == nil { if err == nil {
parentCommit = strings.TrimSpace(parentCommit) parentCommit = strings.TrimSpace(parentCommit)
} }

View File

@@ -195,7 +195,7 @@ func notify(ctx context.Context, input *notifyInput) error {
} }
log.Trace("repo %s with commit %s event %s find %d workflows and %d schedules", log.Trace("repo %s with commit %s event %s find %d workflows and %d schedules",
input.Repo.RepoPath(), input.Repo.RelativePath(),
commit.ID, commit.ID,
input.Event, input.Event,
len(workflows), len(workflows),
@@ -204,7 +204,7 @@ func notify(ctx context.Context, input *notifyInput) error {
for _, wf := range workflows { for _, wf := range workflows {
if actionsConfig.IsWorkflowDisabled(wf.EntryName) { if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
log.Trace("repo %s has disable workflows %s", input.Repo.RepoPath(), wf.EntryName) log.Trace("repo %s has disable workflows %s", input.Repo.RelativePath(), wf.EntryName)
continue continue
} }
@@ -225,7 +225,7 @@ func notify(ctx context.Context, input *notifyInput) error {
return fmt.Errorf("DetectWorkflows: %w", err) return fmt.Errorf("DetectWorkflows: %w", err)
} }
if len(baseWorkflows) == 0 { if len(baseWorkflows) == 0 {
log.Trace("repo %s with commit %s couldn't find pull_request_target workflows", input.Repo.RepoPath(), baseCommit.ID) log.Trace("repo %s with commit %s couldn't find pull_request_target workflows", input.Repo.RelativePath(), baseCommit.ID)
} else { } else {
for _, wf := range baseWorkflows { for _, wf := range baseWorkflows {
if wf.TriggerEvent.Name == actions_module.GithubEventPullRequestTarget { if wf.TriggerEvent.Name == actions_module.GithubEventPullRequestTarget {
@@ -255,11 +255,11 @@ func skipWorkflows(ctx context.Context, input *notifyInput, commit *git.Commit)
if slices.Contains(skipWorkflowEvents, input.Event) { if slices.Contains(skipWorkflowEvents, input.Event) {
for _, s := range setting.Actions.SkipWorkflowStrings { for _, s := range setting.Actions.SkipWorkflowStrings {
if input.PullRequest != nil && strings.Contains(input.PullRequest.Issue.Title, s) { if input.PullRequest != nil && strings.Contains(input.PullRequest.Issue.Title, s) {
log.Debug("repo %s: skipped run for pr %v because of %s string", input.Repo.RepoPath(), input.PullRequest.Issue.ID, s) log.Debug("repo %s: skipped run for pr %v because of %s string", input.Repo.RelativePath(), input.PullRequest.Issue.ID, s)
return true return true
} }
if strings.Contains(commit.CommitMessage, s) { if strings.Contains(commit.CommitMessage, s) {
log.Debug("repo %s with commit %s: skipped run because of %s string", input.Repo.RepoPath(), commit.ID, s) log.Debug("repo %s with commit %s: skipped run because of %s string", input.Repo.RelativePath(), commit.ID, s)
return true return true
} }
} }
@@ -282,7 +282,7 @@ func skipWorkflows(ctx context.Context, input *notifyInput, commit *git.Commit)
} }
} }
// skip workflow runs events exceeding the maximum of 5 recursive events // skip workflow runs events exceeding the maximum of 5 recursive events
log.Debug("repo %s: skipped workflow_run because of recursive event of 5", input.Repo.RepoPath()) log.Debug("repo %s: skipped workflow_run because of recursive event of 5", input.Repo.RelativePath())
return true return true
} }
return false return false
@@ -296,7 +296,7 @@ func handleWorkflows(
ref string, ref string,
) error { ) error {
if len(detectedWorkflows) == 0 { if len(detectedWorkflows) == 0 {
log.Trace("repo %s with commit %s couldn't find workflows", input.Repo.RepoPath(), commit.ID) log.Trace("repo %s with commit %s couldn't find workflows", input.Repo.RelativePath(), commit.ID)
return nil return nil
} }
@@ -521,7 +521,7 @@ func handleSchedules(
} }
if len(detectedWorkflows) == 0 { if len(detectedWorkflows) == 0 {
log.Trace("repo %s with commit %s couldn't find schedules", input.Repo.RepoPath(), commit.ID) log.Trace("repo %s with commit %s couldn't find schedules", input.Repo.RelativePath(), commit.ID)
return nil return nil
} }

View File

@@ -7,7 +7,6 @@ import (
"context" "context"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"os"
"strings" "strings"
git_model "code.gitea.io/gitea/models/git" git_model "code.gitea.io/gitea/models/git"
@@ -199,9 +198,10 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
} }
if !forcePush.Value() { if !forcePush.Value() {
output, _, err := gitcmd.NewCommand("rev-list", "--max-count=1"). output, err := gitrepo.RunCmdString(ctx, repo,
AddDynamicArguments(oldCommitID, "^"+opts.NewCommitIDs[i]). gitcmd.NewCommand("rev-list", "--max-count=1").
RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath(), Env: os.Environ()}) AddDynamicArguments(oldCommitID, "^"+opts.NewCommitIDs[i]),
)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to detect force push: %w", err) return nil, fmt.Errorf("failed to detect force push: %w", err)
} else if len(output) > 0 { } else if len(output) > 0 {

View File

@@ -117,16 +117,16 @@ func SigningKey(ctx context.Context, repoPath string) (*git.SigningKey, *git.Sig
if setting.Repository.Signing.SigningKey == "default" || setting.Repository.Signing.SigningKey == "" { if setting.Repository.Signing.SigningKey == "default" || setting.Repository.Signing.SigningKey == "" {
// Can ignore the error here as it means that commit.gpgsign is not set // Can ignore the error here as it means that commit.gpgsign is not set
value, _, _ := gitcmd.NewCommand("config", "--get", "commit.gpgsign").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) value, _, _ := gitcmd.NewCommand("config", "--get", "commit.gpgsign").WithDir(repoPath).RunStdString(ctx)
sign, valid := git.ParseBool(strings.TrimSpace(value)) sign, valid := git.ParseBool(strings.TrimSpace(value))
if !sign || !valid { if !sign || !valid {
return nil, nil return nil, nil
} }
format, _, _ := gitcmd.NewCommand("config", "--default", git.SigningKeyFormatOpenPGP, "--get", "gpg.format").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) format, _, _ := gitcmd.NewCommand("config", "--default", git.SigningKeyFormatOpenPGP, "--get", "gpg.format").WithDir(repoPath).RunStdString(ctx)
signingKey, _, _ := gitcmd.NewCommand("config", "--get", "user.signingkey").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) signingKey, _, _ := gitcmd.NewCommand("config", "--get", "user.signingkey").WithDir(repoPath).RunStdString(ctx)
signingName, _, _ := gitcmd.NewCommand("config", "--get", "user.name").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) signingName, _, _ := gitcmd.NewCommand("config", "--get", "user.name").WithDir(repoPath).RunStdString(ctx)
signingEmail, _, _ := gitcmd.NewCommand("config", "--get", "user.email").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) signingEmail, _, _ := gitcmd.NewCommand("config", "--get", "user.email").WithDir(repoPath).RunStdString(ctx)
if strings.TrimSpace(signingKey) == "" { if strings.TrimSpace(signingKey) == "" {
return nil, nil return nil, nil

View File

@@ -621,7 +621,7 @@ func RepoAssignment(ctx *Context) {
ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo) ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, repo)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") { if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") {
log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err) log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RelativePath(), err)
ctx.Repo.Repository.MarkAsBrokenEmpty() ctx.Repo.Repository.MarkAsBrokenEmpty()
// Only allow access to base of repo or settings // Only allow access to base of repo or settings
if !isHomeOrSettings { if !isHomeOrSettings {

View File

@@ -146,7 +146,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
gitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo) gitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo)
if err != nil { if err != nil {
log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RepoPath(), err) log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RelativePath(), err)
return nil return nil
} }
defer gitRepo.Close() defer gitRepo.Close()
@@ -192,7 +192,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
headGitRepo, err := gitrepo.OpenRepository(ctx, pr.HeadRepo) headGitRepo, err := gitrepo.OpenRepository(ctx, pr.HeadRepo)
if err != nil { if err != nil {
log.Error("OpenRepository[%s]: %v", pr.HeadRepo.RepoPath(), err) log.Error("OpenRepository[%s]: %v", pr.HeadRepo.RelativePath(), err)
return nil return nil
} }
defer headGitRepo.Close() defer headGitRepo.Close()
@@ -248,7 +248,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
if len(apiPullRequest.Head.Sha) == 0 && len(apiPullRequest.Head.Ref) != 0 { if len(apiPullRequest.Head.Sha) == 0 && len(apiPullRequest.Head.Ref) != 0 {
baseGitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo) baseGitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo)
if err != nil { if err != nil {
log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RepoPath(), err) log.Error("OpenRepository[%s]: %v", pr.BaseRepo.RelativePath(), err)
return nil return nil
} }
defer baseGitRepo.Close() defer baseGitRepo.Close()

View File

@@ -9,6 +9,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
) )
@@ -19,9 +20,11 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool)
numReposUpdated := 0 numReposUpdated := 0
err := iterateRepositories(ctx, func(repo *repo_model.Repository) error { err := iterateRepositories(ctx, func(repo *repo_model.Repository) error {
numRepos++ numRepos++
_, _, defaultBranchErr := gitcmd.NewCommand("rev-parse").AddDashesAndList(repo.DefaultBranch).RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) _, defaultBranchErr := gitrepo.RunCmdString(ctx, repo,
gitcmd.NewCommand("rev-parse").AddDashesAndList(repo.DefaultBranch))
head, _, headErr := gitcmd.NewCommand("symbolic-ref", "--short", "HEAD").RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) head, headErr := gitrepo.RunCmdString(ctx, repo,
gitcmd.NewCommand("symbolic-ref", "--short", "HEAD"))
// what we expect: default branch is valid, and HEAD points to it // what we expect: default branch is valid, and HEAD points to it
if headErr == nil && defaultBranchErr == nil && head == repo.DefaultBranch { if headErr == nil && defaultBranchErr == nil && head == repo.DefaultBranch {
@@ -47,7 +50,7 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool)
} }
// otherwise, let's try fixing HEAD // otherwise, let's try fixing HEAD
err := gitcmd.NewCommand("symbolic-ref").AddDashesAndList("HEAD", git.BranchPrefix+repo.DefaultBranch).Run(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) err := gitrepo.RunCmd(ctx, repo, gitcmd.NewCommand("symbolic-ref").AddDashesAndList("HEAD", git.BranchPrefix+repo.DefaultBranch))
if err != nil { if err != nil {
logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err) logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err)
return nil return nil

View File

@@ -13,6 +13,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"xorm.io/builder" "xorm.io/builder"
@@ -37,23 +38,22 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro
return iteratePRs(ctx, repo, func(repo *repo_model.Repository, pr *issues_model.PullRequest) error { return iteratePRs(ctx, repo, func(repo *repo_model.Repository, pr *issues_model.PullRequest) error {
numPRs++ numPRs++
pr.BaseRepo = repo pr.BaseRepo = repo
repoPath := repo.RepoPath()
oldMergeBase := pr.MergeBase oldMergeBase := pr.MergeBase
if !pr.HasMerged { if !pr.HasMerged {
var err error var err error
pr.MergeBase, _, err = gitcmd.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, pr.GetGitHeadRefName()).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) pr.MergeBase, err = gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, pr.GetGitHeadRefName()))
if err != nil { if err != nil {
var err2 error var err2 error
pr.MergeBase, _, err2 = gitcmd.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix+pr.BaseBranch).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) pr.MergeBase, err2 = gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix+pr.BaseBranch))
if err2 != nil { if err2 != nil {
logger.Warn("Unable to get merge base for PR ID %d, #%d onto %s in %s/%s. Error: %v & %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err, err2) logger.Warn("Unable to get merge base for PR ID %d, #%d onto %s in %s/%s. Error: %v & %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err, err2)
return nil return nil
} }
} }
} else { } else {
parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) parentsString, err := gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID))
if err != nil { if err != nil {
logger.Warn("Unable to get parents for merged PR ID %d, #%d onto %s in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err) logger.Warn("Unable to get parents for merged PR ID %d, #%d onto %s in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err)
return nil return nil
@@ -66,7 +66,7 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro
refs := append([]string{}, parents[1:]...) refs := append([]string{}, parents[1:]...)
refs = append(refs, pr.GetGitHeadRefName()) refs = append(refs, pr.GetGitHeadRefName())
cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...) cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...)
pr.MergeBase, _, err = cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) pr.MergeBase, err = gitrepo.RunCmdString(ctx, repo, cmd)
if err != nil { if err != nil {
logger.Warn("Unable to get merge base for merged PR ID %d, #%d onto %s in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err) logger.Warn("Unable to get merge base for merged PR ID %d, #%d onto %s in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err)
return nil return nil

View File

@@ -61,7 +61,7 @@ func runGitDiffTree(ctx context.Context, gitRepo *git.Repository, useMergeBase b
cmd.AddArguments("--merge-base") cmd.AddArguments("--merge-base")
} }
cmd.AddDynamicArguments(baseCommitID, headCommitID) cmd.AddDynamicArguments(baseCommitID, headCommitID)
stdout, _, runErr := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: gitRepo.Path}) stdout, _, runErr := cmd.WithDir(gitRepo.Path).RunStdString(ctx)
if runErr != nil { if runErr != nil {
log.Warn("git diff-tree: %v", runErr) log.Warn("git diff-tree: %v", runErr)
return nil, runErr return nil, runErr

View File

@@ -1170,12 +1170,11 @@ func getDiffBasic(ctx context.Context, gitRepo *git.Repository, opts *DiffOption
go func() { go func() {
stderr := &bytes.Buffer{} stderr := &bytes.Buffer{}
if err := cmdDiff.Run(cmdCtx, &gitcmd.RunOpts{ if err := cmdDiff.WithTimeout(time.Duration(setting.Git.Timeout.Default) * time.Second).
Timeout: time.Duration(setting.Git.Timeout.Default) * time.Second, WithDir(repoPath).
Dir: repoPath, WithStdout(writer).
Stdout: writer, WithStderr(stderr).
Stderr: stderr, Run(cmdCtx); err != nil && !git.IsErrCanceledOrKilled(err) {
}); err != nil && !git.IsErrCanceledOrKilled(err) {
log.Error("error during GetDiff(git diff dir: %s): %v, stderr: %s", repoPath, err, stderr.String()) log.Error("error during GetDiff(git diff dir: %s): %v, stderr: %s", repoPath, err, stderr.String())
} }

View File

@@ -489,7 +489,7 @@ func (g *RepositoryDumper) handlePullRequest(ctx context.Context, pr *base.PullR
if pr.Head.CloneURL == "" || pr.Head.Ref == "" { if pr.Head.CloneURL == "" || pr.Head.Ref == "" {
// Set head information if pr.Head.SHA is available // Set head information if pr.Head.SHA is available
if pr.Head.SHA != "" { if pr.Head.SHA != "" {
_, _, err = gitcmd.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &gitcmd.RunOpts{Dir: g.gitPath()}) _, _, err = gitcmd.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).WithDir(g.gitPath()).RunStdString(ctx)
if err != nil { if err != nil {
log.Error("PR #%d in %s/%s unable to update-ref for pr HEAD: %v", pr.Number, g.repoOwner, g.repoName, err) log.Error("PR #%d in %s/%s unable to update-ref for pr HEAD: %v", pr.Number, g.repoOwner, g.repoName, err)
} }
@@ -519,7 +519,7 @@ func (g *RepositoryDumper) handlePullRequest(ctx context.Context, pr *base.PullR
if !ok { if !ok {
// Set head information if pr.Head.SHA is available // Set head information if pr.Head.SHA is available
if pr.Head.SHA != "" { if pr.Head.SHA != "" {
_, _, err = gitcmd.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &gitcmd.RunOpts{Dir: g.gitPath()}) _, _, err = gitcmd.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).WithDir(g.gitPath()).RunStdString(ctx)
if err != nil { if err != nil {
log.Error("PR #%d in %s/%s unable to update-ref for pr HEAD: %v", pr.Number, g.repoOwner, g.repoName, err) log.Error("PR #%d in %s/%s unable to update-ref for pr HEAD: %v", pr.Number, g.repoOwner, g.repoName, err)
} }
@@ -554,7 +554,7 @@ func (g *RepositoryDumper) handlePullRequest(ctx context.Context, pr *base.PullR
fetchArg = git.BranchPrefix + fetchArg fetchArg = git.BranchPrefix + fetchArg
} }
_, _, err = gitcmd.NewCommand("fetch", "--no-tags").AddDashesAndList(remote, fetchArg).RunStdString(ctx, &gitcmd.RunOpts{Dir: g.gitPath()}) _, _, err = gitcmd.NewCommand("fetch", "--no-tags").AddDashesAndList(remote, fetchArg).WithDir(g.gitPath()).RunStdString(ctx)
if err != nil { if err != nil {
log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err) log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err)
// We need to continue here so that the Head.Ref is reset and we attempt to set the gitref for the PR // We need to continue here so that the Head.Ref is reset and we attempt to set the gitref for the PR
@@ -578,7 +578,7 @@ func (g *RepositoryDumper) handlePullRequest(ctx context.Context, pr *base.PullR
pr.Head.SHA = headSha pr.Head.SHA = headSha
} }
if pr.Head.SHA != "" { if pr.Head.SHA != "" {
_, _, err = gitcmd.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &gitcmd.RunOpts{Dir: g.gitPath()}) _, _, err = gitcmd.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).WithDir(g.gitPath()).RunStdString(ctx)
if err != nil { if err != nil {
log.Error("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v", pr.Head.SHA, pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err) log.Error("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v", pr.Head.SHA, pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err)
} }

View File

@@ -663,7 +663,7 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(ctx context.Context, pr *ba
fetchArg = git.BranchPrefix + fetchArg fetchArg = git.BranchPrefix + fetchArg
} }
_, _, err = gitcmd.NewCommand("fetch", "--no-tags").AddDashesAndList(remote, fetchArg).RunStdString(ctx, &gitcmd.RunOpts{Dir: g.repo.RepoPath()}) _, err = gitrepo.RunCmdString(ctx, g.repo, gitcmd.NewCommand("fetch", "--no-tags").AddDashesAndList(remote, fetchArg))
if err != nil { if err != nil {
log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err) log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err)
return head, nil return head, nil
@@ -698,7 +698,7 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(ctx context.Context, pr *ba
// The SHA is empty // The SHA is empty
log.Warn("Empty reference, no pull head for PR #%d in %s/%s", pr.Number, g.repoOwner, g.repoName) log.Warn("Empty reference, no pull head for PR #%d in %s/%s", pr.Number, g.repoOwner, g.repoName)
} else { } else {
_, _, err = gitcmd.NewCommand("rev-list", "--quiet", "-1").AddDynamicArguments(pr.Head.SHA).RunStdString(ctx, &gitcmd.RunOpts{Dir: g.repo.RepoPath()}) _, err = gitrepo.RunCmdString(ctx, g.repo, gitcmd.NewCommand("rev-list", "--quiet", "-1").AddDynamicArguments(pr.Head.SHA))
if err != nil { if err != nil {
// Git update-ref remove bad references with a relative path // Git update-ref remove bad references with a relative path
log.Warn("Deprecated local head %s for PR #%d in %s/%s, removing %s", pr.Head.SHA, pr.Number, g.repoOwner, g.repoName, pr.GetGitHeadRefName()) log.Warn("Deprecated local head %s for PR #%d in %s/%s, removing %s", pr.Head.SHA, pr.Number, g.repoOwner, g.repoName, pr.GetGitHeadRefName())

View File

@@ -239,7 +239,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
baseRef := "master" baseRef := "master"
// this is very different from the real situation. It should be a bare repository for all the Gitea managed repositories // this is very different from the real situation. It should be a bare repository for all the Gitea managed repositories
assert.NoError(t, git.InitRepository(t.Context(), fromRepo.RepoPath(), false, fromRepo.ObjectFormatName)) assert.NoError(t, git.InitRepository(t.Context(), fromRepo.RepoPath(), false, fromRepo.ObjectFormatName))
err := gitcmd.NewCommand("symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseRef).Run(t.Context(), &gitcmd.RunOpts{Dir: fromRepo.RepoPath()}) err := gitrepo.RunCmd(t.Context(), fromRepo, gitcmd.NewCommand("symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseRef))
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte("# Testing Repository\n\nOriginally created in: "+fromRepo.RepoPath()), 0o644)) assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte("# Testing Repository\n\nOriginally created in: "+fromRepo.RepoPath()), 0o644))
assert.NoError(t, git.AddChanges(t.Context(), fromRepo.RepoPath(), true)) assert.NoError(t, git.AddChanges(t.Context(), fromRepo.RepoPath(), true))
@@ -263,7 +263,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
// fromRepo branch1 // fromRepo branch1
// //
headRef := "branch1" headRef := "branch1"
_, _, err = gitcmd.NewCommand("checkout", "-b").AddDynamicArguments(headRef).RunStdString(t.Context(), &gitcmd.RunOpts{Dir: fromRepo.RepoPath()}) _, err = gitrepo.RunCmdString(t.Context(), fromRepo, gitcmd.NewCommand("checkout", "-b").AddDynamicArguments(headRef))
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte("SOMETHING"), 0o644)) assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte("SOMETHING"), 0o644))
assert.NoError(t, git.AddChanges(t.Context(), fromRepo.RepoPath(), true)) assert.NoError(t, git.AddChanges(t.Context(), fromRepo.RepoPath(), true))
@@ -287,7 +287,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
assert.NoError(t, git.Clone(t.Context(), fromRepo.RepoPath(), forkRepo.RepoPath(), git.CloneRepoOptions{ assert.NoError(t, git.Clone(t.Context(), fromRepo.RepoPath(), forkRepo.RepoPath(), git.CloneRepoOptions{
Branch: headRef, Branch: headRef,
})) }))
_, _, err = gitcmd.NewCommand("checkout", "-b").AddDynamicArguments(forkHeadRef).RunStdString(t.Context(), &gitcmd.RunOpts{Dir: forkRepo.RepoPath()}) _, err = gitrepo.RunCmdString(t.Context(), forkRepo, gitcmd.NewCommand("checkout", "-b").AddDynamicArguments(forkHeadRef))
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, os.WriteFile(filepath.Join(forkRepo.RepoPath(), "README.md"), []byte("# branch2 "+forkRepo.RepoPath()), 0o644)) assert.NoError(t, os.WriteFile(filepath.Join(forkRepo.RepoPath(), "README.md"), []byte("# branch2 "+forkRepo.RepoPath()), 0o644))
assert.NoError(t, git.AddChanges(t.Context(), forkRepo.RepoPath(), true)) assert.NoError(t, git.AddChanges(t.Context(), forkRepo.RepoPath(), true))

View File

@@ -272,13 +272,10 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
stdoutBuilder := strings.Builder{} stdoutBuilder := strings.Builder{}
stderrBuilder := strings.Builder{} stderrBuilder := strings.Builder{}
if err := cmd.Run(ctx, &gitcmd.RunOpts{ if err := gitrepo.RunCmd(ctx, m.Repo, cmd.WithTimeout(timeout).
Timeout: timeout, WithEnv(envs).
Dir: repoPath, WithStdout(&stdoutBuilder).
Env: envs, WithStderr(&stderrBuilder)); err != nil {
Stdout: &stdoutBuilder,
Stderr: &stderrBuilder,
}); err != nil {
stdout := stdoutBuilder.String() stdout := stdoutBuilder.String()
stderr := stderrBuilder.String() stderr := stderrBuilder.String()
@@ -297,12 +294,9 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
// Successful prune - reattempt mirror // Successful prune - reattempt mirror
stderrBuilder.Reset() stderrBuilder.Reset()
stdoutBuilder.Reset() stdoutBuilder.Reset()
if err = cmd.Run(ctx, &gitcmd.RunOpts{ if err = gitrepo.RunCmd(ctx, m.Repo, cmd.WithTimeout(timeout).
Timeout: timeout, WithStdout(&stdoutBuilder).
Dir: repoPath, WithStderr(&stderrBuilder)); err != nil {
Stdout: &stdoutBuilder,
Stderr: &stderrBuilder,
}); err != nil {
stdout := stdoutBuilder.String() stdout := stdoutBuilder.String()
stderr := stderrBuilder.String() stderr := stderrBuilder.String()
@@ -640,7 +634,7 @@ func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, re
// Update the is empty and default_branch columns // Update the is empty and default_branch columns
if err := repo_model.UpdateRepositoryColsWithAutoTime(ctx, m.Repo, "default_branch", "is_empty"); err != nil { if err := repo_model.UpdateRepositoryColsWithAutoTime(ctx, m.Repo, "default_branch", "is_empty"); err != nil {
log.Error("Failed to update default branch of repository %-v. Error: %v", m.Repo, err) log.Error("Failed to update default branch of repository %-v. Error: %v", m.Repo, err)
desc := fmt.Sprintf("Failed to update default branch of repository '%s': %v", m.Repo.RepoPath(), err) desc := fmt.Sprintf("Failed to update default branch of repository '%s': %v", m.Repo.RelativePath(), err)
if err = system_model.CreateRepositoryNotice(desc); err != nil { if err = system_model.CreateRepositoryNotice(desc); err != nil {
log.Error("CreateRepositoryNotice: %v", err) log.Error("CreateRepositoryNotice: %v", err)
} }

View File

@@ -281,9 +281,9 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com
prHeadRef := pr.GetGitHeadRefName() prHeadRef := pr.GetGitHeadRefName()
// Check if the pull request is merged into BaseBranch // Check if the pull request is merged into BaseBranch
if _, _, err := gitcmd.NewCommand("merge-base", "--is-ancestor"). if _, err := gitrepo.RunCmdString(ctx, pr.BaseRepo,
AddDynamicArguments(prHeadRef, pr.BaseBranch). gitcmd.NewCommand("merge-base", "--is-ancestor").
RunStdString(ctx, &gitcmd.RunOpts{Dir: pr.BaseRepo.RepoPath()}); err != nil { AddDynamicArguments(prHeadRef, pr.BaseBranch)); err != nil {
if strings.Contains(err.Error(), "exit status 1") { if strings.Contains(err.Error(), "exit status 1") {
// prHeadRef is not an ancestor of the base branch // prHeadRef is not an ancestor of the base branch
return nil, nil return nil, nil
@@ -309,9 +309,9 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com
objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)
// Get the commit from BaseBranch where the pull request got merged // Get the commit from BaseBranch where the pull request got merged
mergeCommit, _, err := gitcmd.NewCommand("rev-list", "--ancestry-path", "--merges", "--reverse"). mergeCommit, err := gitrepo.RunCmdString(ctx, pr.BaseRepo,
AddDynamicArguments(prHeadCommitID+".."+pr.BaseBranch). gitcmd.NewCommand("rev-list", "--ancestry-path", "--merges", "--reverse").
RunStdString(ctx, &gitcmd.RunOpts{Dir: pr.BaseRepo.RepoPath()}) AddDynamicArguments(prHeadCommitID+".."+pr.BaseBranch))
if err != nil { if err != nil {
return nil, fmt.Errorf("git rev-list --ancestry-path --merges --reverse: %w", err) return nil, fmt.Errorf("git rev-list --ancestry-path --merges --reverse: %w", err)
} else if len(mergeCommit) < objectFormat.FullLength() { } else if len(mergeCommit) < objectFormat.FullLength() {

View File

@@ -406,7 +406,7 @@ func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *use
// Push back to upstream. // Push back to upstream.
// This cause an api call to "/api/internal/hook/post-receive/...", // This cause an api call to "/api/internal/hook/post-receive/...",
// If it's merge, all db transaction and operations should be there but not here to prevent deadlock. // If it's merge, all db transaction and operations should be there but not here to prevent deadlock.
if err := pushCmd.Run(ctx, mergeCtx.RunOpts()); err != nil { if err := mergeCtx.PrepareGitCmd(pushCmd).Run(ctx); err != nil {
if strings.Contains(mergeCtx.errbuf.String(), "non-fast-forward") { if strings.Contains(mergeCtx.errbuf.String(), "non-fast-forward") {
return "", &git.ErrPushOutOfDate{ return "", &git.ErrPushOutOfDate{
StdOut: mergeCtx.outbuf.String(), StdOut: mergeCtx.outbuf.String(),
@@ -440,7 +440,7 @@ func commitAndSignNoAuthor(ctx *mergeContext, message string) error {
} }
cmdCommit.AddOptionFormat("-S%s", ctx.signKey.KeyID) cmdCommit.AddOptionFormat("-S%s", ctx.signKey.KeyID)
} }
if err := cmdCommit.Run(ctx, ctx.RunOpts()); err != nil { if err := ctx.PrepareGitCmd(cmdCommit).Run(ctx); err != nil {
log.Error("git commit %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) log.Error("git commit %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("git commit %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) return fmt.Errorf("git commit %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
} }
@@ -501,7 +501,7 @@ func (err ErrMergeDivergingFastForwardOnly) Error() string {
} }
func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *gitcmd.Command) error { func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *gitcmd.Command) error {
if err := cmd.Run(ctx, ctx.RunOpts()); err != nil { if err := ctx.PrepareGitCmd(cmd).Run(ctx); err != nil {
// Merge will leave a MERGE_HEAD file in the .git folder if there is a conflict // Merge will leave a MERGE_HEAD file in the .git folder if there is a conflict
if _, statErr := os.Stat(filepath.Join(ctx.tmpBasePath, ".git", "MERGE_HEAD")); statErr == nil { if _, statErr := os.Stat(filepath.Join(ctx.tmpBasePath, ".git", "MERGE_HEAD")); statErr == nil {
// We have a merge conflict error // We have a merge conflict error

View File

@@ -32,15 +32,14 @@ type mergeContext struct {
env []string env []string
} }
func (ctx *mergeContext) RunOpts() *gitcmd.RunOpts { func (ctx *mergeContext) PrepareGitCmd(cmd *gitcmd.Command) *gitcmd.Command {
ctx.outbuf.Reset() ctx.outbuf.Reset()
ctx.errbuf.Reset() ctx.errbuf.Reset()
return &gitcmd.RunOpts{ return cmd.WithEnv(ctx.env).
Env: ctx.env, WithDir(ctx.tmpBasePath).
Dir: ctx.tmpBasePath, WithParentCallerInfo().
Stdout: ctx.outbuf, WithStdout(ctx.outbuf).
Stderr: ctx.errbuf, WithStderr(ctx.errbuf)
}
} }
// ErrSHADoesNotMatch represents a "SHADoesNotMatch" kind of error. // ErrSHADoesNotMatch represents a "SHADoesNotMatch" kind of error.
@@ -74,7 +73,7 @@ func createTemporaryRepoForMerge(ctx context.Context, pr *issues_model.PullReque
} }
if expectedHeadCommitID != "" { if expectedHeadCommitID != "" {
trackingCommitID, _, err := gitcmd.NewCommand("show-ref", "--hash").AddDynamicArguments(git.BranchPrefix+trackingBranch).RunStdString(ctx, &gitcmd.RunOpts{Dir: mergeCtx.tmpBasePath}) trackingCommitID, _, err := mergeCtx.PrepareGitCmd(gitcmd.NewCommand("show-ref", "--hash").AddDynamicArguments(git.BranchPrefix + trackingBranch)).RunStdString(ctx)
if err != nil { if err != nil {
defer cancel() defer cancel()
log.Error("failed to get sha of head branch in %-v: show-ref[%s] --hash refs/heads/tracking: %v", mergeCtx.pr, mergeCtx.tmpBasePath, err) log.Error("failed to get sha of head branch in %-v: show-ref[%s] --hash refs/heads/tracking: %v", mergeCtx.pr, mergeCtx.tmpBasePath, err)
@@ -152,8 +151,8 @@ func prepareTemporaryRepoForMerge(ctx *mergeContext) error {
} }
setConfig := func(key, value string) error { setConfig := func(key, value string) error {
if err := gitcmd.NewCommand("config", "--local").AddDynamicArguments(key, value). if err := ctx.PrepareGitCmd(gitcmd.NewCommand("config", "--local").AddDynamicArguments(key, value)).
Run(ctx, ctx.RunOpts()); err != nil { Run(ctx); err != nil {
log.Error("git config [%s -> %q]: %v\n%s\n%s", key, value, err, ctx.outbuf.String(), ctx.errbuf.String()) log.Error("git config [%s -> %q]: %v\n%s\n%s", key, value, err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("git config [%s -> %q]: %w\n%s\n%s", key, value, err, ctx.outbuf.String(), ctx.errbuf.String()) return fmt.Errorf("git config [%s -> %q]: %w\n%s\n%s", key, value, err, ctx.outbuf.String(), ctx.errbuf.String())
} }
@@ -185,8 +184,8 @@ func prepareTemporaryRepoForMerge(ctx *mergeContext) error {
} }
// Read base branch index // Read base branch index
if err := gitcmd.NewCommand("read-tree", "HEAD"). if err := ctx.PrepareGitCmd(gitcmd.NewCommand("read-tree", "HEAD")).
Run(ctx, ctx.RunOpts()); err != nil { Run(ctx); err != nil {
log.Error("git read-tree HEAD: %v\n%s\n%s", err, ctx.outbuf.String(), ctx.errbuf.String()) log.Error("git read-tree HEAD: %v\n%s\n%s", err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("Unable to read base branch in to the index: %w\n%s\n%s", err, ctx.outbuf.String(), ctx.errbuf.String()) return fmt.Errorf("Unable to read base branch in to the index: %w\n%s\n%s", err, ctx.outbuf.String(), ctx.errbuf.String())
} }
@@ -222,11 +221,11 @@ func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string, o
return 0, nil, nil return 0, nil, nil
} }
err = gitcmd.NewCommand("diff-tree", "--no-commit-id", "--name-only", "-r", "-r", "-z", "--root").AddDynamicArguments(baseBranch, headBranch). err = gitcmd.NewCommand("diff-tree", "--no-commit-id", "--name-only", "-r", "-r", "-z", "--root").
Run(ctx, &gitcmd.RunOpts{ AddDynamicArguments(baseBranch, headBranch).
Dir: repoPath, WithDir(repoPath).
Stdout: diffOutWriter, WithStdout(diffOutWriter).
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
// Close the writer end of the pipe to begin processing // Close the writer end of the pipe to begin processing
_ = diffOutWriter.Close() _ = diffOutWriter.Close()
defer func() { defer func() {
@@ -245,8 +244,8 @@ func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string, o
fmt.Fprintf(out, "/%s\n", filepath) fmt.Fprintf(out, "/%s\n", filepath)
} }
return scanner.Err() return scanner.Err()
}, }).
}) Run(ctx)
return err return err
} }
@@ -273,16 +272,16 @@ func (err ErrRebaseConflicts) Error() string {
// if there is a conflict it will return an ErrRebaseConflicts // if there is a conflict it will return an ErrRebaseConflicts
func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) error { func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) error {
// Checkout head branch // Checkout head branch
if err := gitcmd.NewCommand("checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch). if err := ctx.PrepareGitCmd(gitcmd.NewCommand("checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch)).
Run(ctx, ctx.RunOpts()); err != nil { Run(ctx); err != nil {
return fmt.Errorf("unable to git checkout tracking as staging in temp repo for %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) return fmt.Errorf("unable to git checkout tracking as staging in temp repo for %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
} }
ctx.outbuf.Reset() ctx.outbuf.Reset()
ctx.errbuf.Reset() ctx.errbuf.Reset()
// Rebase before merging // Rebase before merging
if err := gitcmd.NewCommand("rebase").AddDynamicArguments(baseBranch). if err := ctx.PrepareGitCmd(gitcmd.NewCommand("rebase").AddDynamicArguments(baseBranch)).
Run(ctx, ctx.RunOpts()); err != nil { Run(ctx); err != nil {
// Rebase will leave a REBASE_HEAD file in .git if there is a conflict // Rebase will leave a REBASE_HEAD file in .git if there is a conflict
if _, statErr := os.Stat(filepath.Join(ctx.tmpBasePath, ".git", "REBASE_HEAD")); statErr == nil { if _, statErr := os.Stat(filepath.Join(ctx.tmpBasePath, ".git", "REBASE_HEAD")); statErr == nil {
var commitSha string var commitSha string

View File

@@ -17,7 +17,7 @@ import (
// getRebaseAmendMessage composes the message to amend commits in rebase merge of a pull request. // getRebaseAmendMessage composes the message to amend commits in rebase merge of a pull request.
func getRebaseAmendMessage(ctx *mergeContext, baseGitRepo *git.Repository) (message string, err error) { func getRebaseAmendMessage(ctx *mergeContext, baseGitRepo *git.Repository) (message string, err error) {
// Get existing commit message. // Get existing commit message.
commitMessage, _, err := gitcmd.NewCommand("show", "--format=%B", "-s").RunStdString(ctx, &gitcmd.RunOpts{Dir: ctx.tmpBasePath}) commitMessage, _, err := gitcmd.NewCommand("show", "--format=%B", "-s").WithDir(ctx.tmpBasePath).RunStdString(ctx)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -74,7 +74,10 @@ func doMergeRebaseFastForward(ctx *mergeContext) error {
} }
if newMessage != "" { if newMessage != "" {
if err := gitcmd.NewCommand("commit", "--amend").AddOptionFormat("--message=%s", newMessage).Run(ctx, &gitcmd.RunOpts{Dir: ctx.tmpBasePath}); err != nil { if err := gitcmd.NewCommand("commit", "--amend").
AddOptionFormat("--message=%s", newMessage).
WithDir(ctx.tmpBasePath).
Run(ctx); err != nil {
log.Error("Unable to amend commit message: %v", err) log.Error("Unable to amend commit message: %v", err)
return err return err
} }
@@ -106,8 +109,8 @@ func doMergeStyleRebase(ctx *mergeContext, mergeStyle repo_model.MergeStyle, mes
} }
// Checkout base branch again // Checkout base branch again
if err := gitcmd.NewCommand("checkout").AddDynamicArguments(baseBranch). if err := ctx.PrepareGitCmd(gitcmd.NewCommand("checkout").AddDynamicArguments(baseBranch)).
Run(ctx, ctx.RunOpts()); err != nil { Run(ctx); err != nil {
log.Error("git checkout base prior to merge post staging rebase %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) log.Error("git checkout base prior to merge post staging rebase %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("git checkout base prior to merge post staging rebase %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) return fmt.Errorf("git checkout base prior to merge post staging rebase %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
} }

View File

@@ -80,7 +80,7 @@ func doMergeStyleSquash(ctx *mergeContext, message string) error {
} }
cmdCommit.AddOptionFormat("-S%s", ctx.signKey.KeyID) cmdCommit.AddOptionFormat("-S%s", ctx.signKey.KeyID)
} }
if err := cmdCommit.Run(ctx, ctx.RunOpts()); err != nil { if err := ctx.PrepareGitCmd(cmdCommit).Run(ctx); err != nil {
log.Error("git commit %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) log.Error("git commit %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String())
return fmt.Errorf("git commit [%s:%s -> %s:%s]: %w\n%s\n%s", ctx.pr.HeadRepo.FullName(), ctx.pr.HeadBranch, ctx.pr.BaseRepo.FullName(), ctx.pr.BaseBranch, err, ctx.outbuf.String(), ctx.errbuf.String()) return fmt.Errorf("git commit [%s:%s -> %s:%s]: %w\n%s\n%s", ctx.pr.HeadRepo.FullName(), ctx.pr.HeadBranch, ctx.pr.BaseRepo.FullName(), ctx.pr.BaseBranch, err, ctx.outbuf.String(), ctx.errbuf.String())
} }

View File

@@ -91,7 +91,7 @@ func testPullRequestTmpRepoBranchMergeable(ctx context.Context, prCtx *prTmpRepo
defer gitRepo.Close() defer gitRepo.Close()
// 1. update merge base // 1. update merge base
pr.MergeBase, _, err = gitcmd.NewCommand("merge-base", "--", "base", "tracking").RunStdString(ctx, &gitcmd.RunOpts{Dir: prCtx.tmpBasePath}) pr.MergeBase, _, err = gitcmd.NewCommand("merge-base", "--", "base", "tracking").WithDir(prCtx.tmpBasePath).RunStdString(ctx)
if err != nil { if err != nil {
var err2 error var err2 error
pr.MergeBase, err2 = gitRepo.GetRefCommitID(git.BranchPrefix + "base") pr.MergeBase, err2 = gitRepo.GetRefCommitID(git.BranchPrefix + "base")
@@ -191,7 +191,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
} }
// Need to get the objects from the object db to attempt to merge // Need to get the objects from the object db to attempt to merge
root, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage1.sha).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) root, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage1.sha).WithDir(tmpBasePath).RunStdString(ctx)
if err != nil { if err != nil {
return fmt.Errorf("unable to get root object: %s at path: %s for merging. Error: %w", file.stage1.sha, file.stage1.path, err) return fmt.Errorf("unable to get root object: %s at path: %s for merging. Error: %w", file.stage1.sha, file.stage1.path, err)
} }
@@ -200,7 +200,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
_ = util.Remove(filepath.Join(tmpBasePath, root)) _ = util.Remove(filepath.Join(tmpBasePath, root))
}() }()
base, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage2.sha).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) base, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage2.sha).WithDir(tmpBasePath).RunStdString(ctx)
if err != nil { if err != nil {
return fmt.Errorf("unable to get base object: %s at path: %s for merging. Error: %w", file.stage2.sha, file.stage2.path, err) return fmt.Errorf("unable to get base object: %s at path: %s for merging. Error: %w", file.stage2.sha, file.stage2.path, err)
} }
@@ -208,7 +208,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
defer func() { defer func() {
_ = util.Remove(base) _ = util.Remove(base)
}() }()
head, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage3.sha).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) head, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage3.sha).WithDir(tmpBasePath).RunStdString(ctx)
if err != nil { if err != nil {
return fmt.Errorf("unable to get head object:%s at path: %s for merging. Error: %w", file.stage3.sha, file.stage3.path, err) return fmt.Errorf("unable to get head object:%s at path: %s for merging. Error: %w", file.stage3.sha, file.stage3.path, err)
} }
@@ -218,13 +218,13 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f
}() }()
// now git merge-file annoyingly takes a different order to the merge-tree ... // now git merge-file annoyingly takes a different order to the merge-tree ...
_, _, conflictErr := gitcmd.NewCommand("merge-file").AddDynamicArguments(base, root, head).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) _, _, conflictErr := gitcmd.NewCommand("merge-file").AddDynamicArguments(base, root, head).WithDir(tmpBasePath).RunStdString(ctx)
if conflictErr != nil { if conflictErr != nil {
return &errMergeConflict{file.stage2.path} return &errMergeConflict{file.stage2.path}
} }
// base now contains the merged data // base now contains the merged data
hash, _, err := gitcmd.NewCommand("hash-object", "-w", "--path").AddDynamicArguments(file.stage2.path, base).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) hash, _, err := gitcmd.NewCommand("hash-object", "-w", "--path").AddDynamicArguments(file.stage2.path, base).WithDir(tmpBasePath).RunStdString(ctx)
if err != nil { if err != nil {
return err return err
} }
@@ -249,7 +249,7 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo
defer cancel() defer cancel()
// First we use read-tree to do a simple three-way merge // First we use read-tree to do a simple three-way merge
if _, _, err := gitcmd.NewCommand("read-tree", "-m").AddDynamicArguments(base, ours, theirs).RunStdString(ctx, &gitcmd.RunOpts{Dir: gitPath}); err != nil { if _, _, err := gitcmd.NewCommand("read-tree", "-m").AddDynamicArguments(base, ours, theirs).WithDir(gitPath).RunStdString(ctx); err != nil {
log.Error("Unable to run read-tree -m! Error: %v", err) log.Error("Unable to run read-tree -m! Error: %v", err)
return false, nil, fmt.Errorf("unable to run read-tree -m! Error: %w", err) return false, nil, fmt.Errorf("unable to run read-tree -m! Error: %w", err)
} }
@@ -323,9 +323,9 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
// No conflicts detected so we need to check if the patch is empty... // No conflicts detected so we need to check if the patch is empty...
// a. Write the newly merged tree and check the new tree-hash // a. Write the newly merged tree and check the new tree-hash
var treeHash string var treeHash string
treeHash, _, err = gitcmd.NewCommand("write-tree").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) treeHash, _, err = gitcmd.NewCommand("write-tree").WithDir(tmpBasePath).RunStdString(ctx)
if err != nil { if err != nil {
lsfiles, _, _ := gitcmd.NewCommand("ls-files", "-u").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) lsfiles, _, _ := gitcmd.NewCommand("ls-files", "-u").WithDir(tmpBasePath).RunStdString(ctx)
return false, fmt.Errorf("unable to write unconflicted tree: %w\n`git ls-files -u`:\n%s", err, lsfiles) return false, fmt.Errorf("unable to write unconflicted tree: %w\n`git ls-files -u`:\n%s", err, lsfiles)
} }
treeHash = strings.TrimSpace(treeHash) treeHash = strings.TrimSpace(treeHash)
@@ -382,7 +382,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
log.Trace("PullRequest[%d].testPullRequestTmpRepoBranchMergeable (patchPath): %s", pr.ID, patchPath) log.Trace("PullRequest[%d].testPullRequestTmpRepoBranchMergeable (patchPath): %s", pr.ID, patchPath)
// 4. Read the base branch in to the index of the temporary repository // 4. Read the base branch in to the index of the temporary repository
_, _, err = gitcmd.NewCommand("read-tree", "base").RunStdString(gitRepo.Ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) _, _, err = gitcmd.NewCommand("read-tree", "base").WithDir(tmpBasePath).RunStdString(ctx)
if err != nil { if err != nil {
return false, fmt.Errorf("git read-tree %s: %w", pr.BaseBranch, err) return false, fmt.Errorf("git read-tree %s: %w", pr.BaseBranch, err)
} }
@@ -426,10 +426,10 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
// 8. Run the check command // 8. Run the check command
conflict = false conflict = false
err = cmdApply.Run(gitRepo.Ctx, &gitcmd.RunOpts{ err = cmdApply.
Dir: tmpBasePath, WithDir(tmpBasePath).
Stderr: stderrWriter, WithStderr(stderrWriter).
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
// Close the writer end of the pipe to begin processing // Close the writer end of the pipe to begin processing
_ = stderrWriter.Close() _ = stderrWriter.Close()
defer func() { defer func() {
@@ -488,8 +488,8 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
} }
return nil return nil
}, }).
}) Run(gitRepo.Ctx)
// 9. Check if the found conflictedfiles is non-zero, "err" could be non-nil, so we should ignore it if we found conflicts. // 9. Check if the found conflictedfiles is non-zero, "err" could be non-nil, so we should ignore it if we found conflicts.
// Note: `"err" could be non-nil` is due that if enable 3-way merge, it doesn't return any error on found conflicts. // Note: `"err" could be non-nil` is due that if enable 3-way merge, it doesn't return any error on found conflicts.

View File

@@ -73,11 +73,10 @@ func readUnmergedLsFileLines(ctx context.Context, tmpBasePath string, outputChan
stderr := &strings.Builder{} stderr := &strings.Builder{}
err = gitcmd.NewCommand("ls-files", "-u", "-z"). err = gitcmd.NewCommand("ls-files", "-u", "-z").
Run(ctx, &gitcmd.RunOpts{ WithDir(tmpBasePath).
Dir: tmpBasePath, WithStdout(lsFilesWriter).
Stdout: lsFilesWriter, WithStderr(stderr).
Stderr: stderr, WithPipelineFunc(func(_ context.Context, _ context.CancelFunc) error {
PipelineFunc: func(_ context.Context, _ context.CancelFunc) error {
_ = lsFilesWriter.Close() _ = lsFilesWriter.Close()
defer func() { defer func() {
_ = lsFilesReader.Close() _ = lsFilesReader.Close()
@@ -113,8 +112,8 @@ func readUnmergedLsFileLines(ctx context.Context, tmpBasePath string, outputChan
toemit.path = split[2][2 : len(split[2])-1] toemit.path = split[2][2 : len(split[2])-1]
outputChan <- toemit outputChan <- toemit
} }
}, }).
}) Run(ctx)
if err != nil { if err != nil {
outputChan <- &lsFileLine{err: fmt.Errorf("git ls-files -u -z: %w", gitcmd.ConcatenateError(err, stderr.String()))} outputChan <- &lsFileLine{err: fmt.Errorf("git ls-files -u -z: %w", gitcmd.ConcatenateError(err, stderr.String()))}
} }

View File

@@ -516,18 +516,17 @@ func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest,
} }
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
if err := cmd.Run(ctx, &gitcmd.RunOpts{ if err := cmd.WithDir(prCtx.tmpBasePath).
Dir: prCtx.tmpBasePath, WithStdout(stdoutWriter).
Stdout: stdoutWriter, WithStderr(stderr).
Stderr: stderr, WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close() _ = stdoutWriter.Close()
defer func() { defer func() {
_ = stdoutReader.Close() _ = stdoutReader.Close()
}() }()
return util.IsEmptyReader(stdoutReader) return util.IsEmptyReader(stdoutReader)
}, }).
}); err != nil { Run(ctx); err != nil {
if err == util.ErrNotEmpty { if err == util.ErrNotEmpty {
return true, nil return true, nil
} }

View File

@@ -37,14 +37,12 @@ type prTmpRepoContext struct {
errbuf *strings.Builder // any use should be preceded by a Reset and preferably after use errbuf *strings.Builder // any use should be preceded by a Reset and preferably after use
} }
func (ctx *prTmpRepoContext) RunOpts() *gitcmd.RunOpts { func (ctx *prTmpRepoContext) PrepareGitCmd(cmd *gitcmd.Command) *gitcmd.Command {
ctx.outbuf.Reset() ctx.outbuf.Reset()
ctx.errbuf.Reset() ctx.errbuf.Reset()
return &gitcmd.RunOpts{ return cmd.WithDir(ctx.tmpBasePath).
Dir: ctx.tmpBasePath, WithStdout(ctx.outbuf).
Stdout: ctx.outbuf, WithStderr(ctx.errbuf)
Stderr: ctx.errbuf,
}
} }
// createTemporaryRepoForPR creates a temporary repo with "base" for pr.BaseBranch and "tracking" for pr.HeadBranch // createTemporaryRepoForPR creates a temporary repo with "base" for pr.BaseBranch and "tracking" for pr.HeadBranch
@@ -132,22 +130,23 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
return nil, nil, fmt.Errorf("Unable to add base repository to temporary repo [%s -> tmpBasePath]: %w", pr.BaseRepo.FullName(), err) return nil, nil, fmt.Errorf("Unable to add base repository to temporary repo [%s -> tmpBasePath]: %w", pr.BaseRepo.FullName(), err)
} }
if err := gitcmd.NewCommand("remote", "add", "-t").AddDynamicArguments(pr.BaseBranch).AddArguments("-m").AddDynamicArguments(pr.BaseBranch).AddDynamicArguments("origin", baseRepoPath). if err := prCtx.PrepareGitCmd(gitcmd.NewCommand("remote", "add", "-t").AddDynamicArguments(pr.BaseBranch).AddArguments("-m").AddDynamicArguments(pr.BaseBranch).AddDynamicArguments("origin", baseRepoPath)).
Run(ctx, prCtx.RunOpts()); err != nil { Run(ctx); err != nil {
log.Error("%-v Unable to add base repository as origin [%s -> %s]: %v\n%s\n%s", pr, pr.BaseRepo.FullName(), tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String()) log.Error("%-v Unable to add base repository as origin [%s -> %s]: %v\n%s\n%s", pr, pr.BaseRepo.FullName(), tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
cancel() cancel()
return nil, nil, fmt.Errorf("Unable to add base repository as origin [%s -> tmpBasePath]: %w\n%s\n%s", pr.BaseRepo.FullName(), err, prCtx.outbuf.String(), prCtx.errbuf.String()) return nil, nil, fmt.Errorf("Unable to add base repository as origin [%s -> tmpBasePath]: %w\n%s\n%s", pr.BaseRepo.FullName(), err, prCtx.outbuf.String(), prCtx.errbuf.String())
} }
if err := gitcmd.NewCommand("fetch", "origin").AddArguments(fetchArgs...).AddDashesAndList(git.BranchPrefix+pr.BaseBranch+":"+git.BranchPrefix+baseBranch, git.BranchPrefix+pr.BaseBranch+":"+git.BranchPrefix+"original_"+baseBranch). if err := prCtx.PrepareGitCmd(gitcmd.NewCommand("fetch", "origin").AddArguments(fetchArgs...).
Run(ctx, prCtx.RunOpts()); err != nil { AddDashesAndList(git.BranchPrefix+pr.BaseBranch+":"+git.BranchPrefix+baseBranch, git.BranchPrefix+pr.BaseBranch+":"+git.BranchPrefix+"original_"+baseBranch)).
Run(ctx); err != nil {
log.Error("%-v Unable to fetch origin base branch [%s:%s -> base, original_base in %s]: %v:\n%s\n%s", pr, pr.BaseRepo.FullName(), pr.BaseBranch, tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String()) log.Error("%-v Unable to fetch origin base branch [%s:%s -> base, original_base in %s]: %v:\n%s\n%s", pr, pr.BaseRepo.FullName(), pr.BaseBranch, tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
cancel() cancel()
return nil, nil, fmt.Errorf("Unable to fetch origin base branch [%s:%s -> base, original_base in tmpBasePath]: %w\n%s\n%s", pr.BaseRepo.FullName(), pr.BaseBranch, err, prCtx.outbuf.String(), prCtx.errbuf.String()) return nil, nil, fmt.Errorf("Unable to fetch origin base branch [%s:%s -> base, original_base in tmpBasePath]: %w\n%s\n%s", pr.BaseRepo.FullName(), pr.BaseBranch, err, prCtx.outbuf.String(), prCtx.errbuf.String())
} }
if err := gitcmd.NewCommand("symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseBranch). if err := prCtx.PrepareGitCmd(gitcmd.NewCommand("symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseBranch)).
Run(ctx, prCtx.RunOpts()); err != nil { Run(ctx); err != nil {
log.Error("%-v Unable to set HEAD as base branch in [%s]: %v\n%s\n%s", pr, tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String()) log.Error("%-v Unable to set HEAD as base branch in [%s]: %v\n%s\n%s", pr, tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
cancel() cancel()
return nil, nil, fmt.Errorf("Unable to set HEAD as base branch in tmpBasePath: %w\n%s\n%s", err, prCtx.outbuf.String(), prCtx.errbuf.String()) return nil, nil, fmt.Errorf("Unable to set HEAD as base branch in tmpBasePath: %w\n%s\n%s", err, prCtx.outbuf.String(), prCtx.errbuf.String())
@@ -159,8 +158,8 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
return nil, nil, fmt.Errorf("Unable to add head base repository to temporary repo [%s -> tmpBasePath]: %w", pr.HeadRepo.FullName(), err) return nil, nil, fmt.Errorf("Unable to add head base repository to temporary repo [%s -> tmpBasePath]: %w", pr.HeadRepo.FullName(), err)
} }
if err := gitcmd.NewCommand("remote", "add").AddDynamicArguments(remoteRepoName, headRepoPath). if err := prCtx.PrepareGitCmd(gitcmd.NewCommand("remote", "add").AddDynamicArguments(remoteRepoName, headRepoPath)).
Run(ctx, prCtx.RunOpts()); err != nil { Run(ctx); err != nil {
log.Error("%-v Unable to add head repository as head_repo [%s -> %s]: %v\n%s\n%s", pr, pr.HeadRepo.FullName(), tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String()) log.Error("%-v Unable to add head repository as head_repo [%s -> %s]: %v\n%s\n%s", pr, pr.HeadRepo.FullName(), tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String())
cancel() cancel()
return nil, nil, fmt.Errorf("Unable to add head repository as head_repo [%s -> tmpBasePath]: %w\n%s\n%s", pr.HeadRepo.FullName(), err, prCtx.outbuf.String(), prCtx.errbuf.String()) return nil, nil, fmt.Errorf("Unable to add head repository as head_repo [%s -> tmpBasePath]: %w\n%s\n%s", pr.HeadRepo.FullName(), err, prCtx.outbuf.String(), prCtx.errbuf.String())
@@ -177,8 +176,8 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
} else { } else {
headBranch = pr.GetGitHeadRefName() headBranch = pr.GetGitHeadRefName()
} }
if err := gitcmd.NewCommand("fetch").AddArguments(fetchArgs...).AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch). if err := prCtx.PrepareGitCmd(gitcmd.NewCommand("fetch").AddArguments(fetchArgs...).AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch)).
Run(ctx, prCtx.RunOpts()); err != nil { Run(ctx); err != nil {
cancel() cancel()
if !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) { if !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
return nil, nil, git_model.ErrBranchNotExist{ return nil, nil, git_model.ErrBranchNotExist{

View File

@@ -28,7 +28,8 @@ func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullReques
defer cancel() defer cancel()
// Determine the old merge-base before the rebase - we use this for LFS push later on // Determine the old merge-base before the rebase - we use this for LFS push later on
oldMergeBase, _, _ := gitcmd.NewCommand("merge-base").AddDashesAndList(baseBranch, trackingBranch).RunStdString(ctx, &gitcmd.RunOpts{Dir: mergeCtx.tmpBasePath}) oldMergeBase, _, _ := gitcmd.NewCommand("merge-base").AddDashesAndList(baseBranch, trackingBranch).
WithDir(mergeCtx.tmpBasePath).RunStdString(ctx)
oldMergeBase = strings.TrimSpace(oldMergeBase) oldMergeBase = strings.TrimSpace(oldMergeBase)
// Rebase the tracking branch on to the base as the staging branch // Rebase the tracking branch on to the base as the staging branch
@@ -72,18 +73,18 @@ func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullReques
mergeCtx.outbuf.Reset() mergeCtx.outbuf.Reset()
mergeCtx.errbuf.Reset() mergeCtx.errbuf.Reset()
if err := pushCmd.Run(ctx, &gitcmd.RunOpts{ if err := pushCmd.
Env: repo_module.FullPushingEnvironment( WithEnv(repo_module.FullPushingEnvironment(
headUser, headUser,
doer, doer,
pr.HeadRepo, pr.HeadRepo,
pr.HeadRepo.Name, pr.HeadRepo.Name,
pr.ID, pr.ID,
), )).
Dir: mergeCtx.tmpBasePath, WithDir(mergeCtx.tmpBasePath).
Stdout: mergeCtx.outbuf, WithStdout(mergeCtx.outbuf).
Stderr: mergeCtx.errbuf, WithStderr(mergeCtx.errbuf).
}); err != nil { Run(ctx); err != nil {
if strings.Contains(mergeCtx.errbuf.String(), "non-fast-forward") { if strings.Contains(mergeCtx.errbuf.String(), "non-fast-forward") {
return &git.ErrPushOutOfDate{ return &git.ErrPushOutOfDate{
StdOut: mergeCtx.outbuf.String(), StdOut: mergeCtx.outbuf.String(),

View File

@@ -371,8 +371,9 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re
} }
} }
if stdout, _, err := gitcmd.NewCommand("tag", "-d").AddDashesAndList(rel.TagName). if stdout, err := gitrepo.RunCmdString(ctx, repo,
RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}); err != nil && !strings.Contains(err.Error(), "not found") { gitcmd.NewCommand("tag", "-d").AddDashesAndList(rel.TagName),
); err != nil && !strings.Contains(err.Error(), "not found") {
log.Error("DeleteReleaseByID (git tag -d): %d in %v Failed:\nStdout: %s\nError: %v", rel.ID, repo, stdout, err) log.Error("DeleteReleaseByID (git tag -d): %d in %v Failed:\nStdout: %s\nError: %v", rel.ID, repo, stdout, err)
return fmt.Errorf("git tag -d: %w", err) return fmt.Errorf("git tag -d: %w", err)
} }

View File

@@ -43,7 +43,7 @@ func UploadAvatar(ctx context.Context, repo *repo_model.Repository, data []byte)
_, err := w.Write(avatarData) _, err := w.Write(avatarData)
return err return err
}); err != nil { }); err != nil {
return fmt.Errorf("UploadAvatar %s failed: Failed to remove old repo avatar %s: %w", repo.RepoPath(), newAvatar, err) return fmt.Errorf("UploadAvatar %s failed: Failed to remove old repo avatar %s: %w", repo.RelativePath(), newAvatar, err)
} }
if len(oldAvatarPath) > 0 { if len(oldAvatarPath) > 0 {

View File

@@ -17,7 +17,6 @@ import (
"code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository" repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder" "xorm.io/builder"
) )
@@ -89,24 +88,24 @@ func GitGcRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Du
command := gitcmd.NewCommand("gc").AddArguments(args...) command := gitcmd.NewCommand("gc").AddArguments(args...)
var stdout string var stdout string
var err error var err error
stdout, _, err = command.RunStdString(ctx, &gitcmd.RunOpts{Timeout: timeout, Dir: repo.RepoPath()}) stdout, err = gitrepo.RunCmdString(ctx, repo, command)
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)
desc := fmt.Sprintf("Repository garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err) desc := fmt.Sprintf("Repository garbage collection failed for %s. Stdout: %s\nError: %v", repo.RelativePath(), stdout, err)
if err := system_model.CreateRepositoryNotice(desc); err != nil { if err := system_model.CreateRepositoryNotice(desc); err != nil {
log.Error("CreateRepositoryNotice: %v", err) log.Error("CreateRepositoryNotice: %v", err)
} }
return fmt.Errorf("Repository garbage collection failed in repo: %s: Error: %w", repo.FullName(), err) return fmt.Errorf("Repository garbage collection failed in repo: %s: Error: %w", repo.RelativePath(), err)
} }
// Now update the size of the repository // Now update the size of the repository
if err := repo_module.UpdateRepoSize(ctx, repo); err != nil { if err := repo_module.UpdateRepoSize(ctx, repo); err != nil {
log.Error("Updating size as part of garbage collection failed for %-v. Stdout: %s\nError: %v", repo, stdout, err) log.Error("Updating size as part of garbage collection failed for %-v. Stdout: %s\nError: %v", repo, stdout, err)
desc := fmt.Sprintf("Updating size as part of garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err) desc := fmt.Sprintf("Updating size as part of garbage collection failed for %s. Stdout: %s\nError: %v", repo.RelativePath(), stdout, err)
if err := system_model.CreateRepositoryNotice(desc); err != nil { if err := system_model.CreateRepositoryNotice(desc); err != nil {
log.Error("CreateRepositoryNotice: %v", err) log.Error("CreateRepositoryNotice: %v", err)
} }
return fmt.Errorf("Updating size as part of garbage collection failed in repo: %s: Error: %w", repo.FullName(), err) return fmt.Errorf("Updating size as part of garbage collection failed in repo: %s: Error: %w", repo.RelativePath(), err)
} }
return nil return nil
@@ -123,11 +122,11 @@ func gatherMissingRepoRecords(ctx context.Context) (repo_model.RepositoryList, e
return db.ErrCancelledf("during gathering missing repo records before checking %s", repo.FullName()) return db.ErrCancelledf("during gathering missing repo records before checking %s", repo.FullName())
default: default:
} }
isDir, err := util.IsDir(repo.RepoPath()) exist, err := gitrepo.IsRepositoryExist(ctx, repo)
if err != nil { if err != nil {
return fmt.Errorf("Unable to check dir for %s. %w", repo.FullName(), err) return fmt.Errorf("Unable to check dir for %s. %w", repo.FullName(), err)
} }
if !isDir { if !exist {
repos = append(repos, repo) repos = append(repos, repo)
} }
return nil return nil
@@ -191,7 +190,7 @@ func ReinitMissingRepositories(ctx context.Context) error {
} }
log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID) log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID)
if err := gitrepo.InitRepository(ctx, repo, repo.ObjectFormatName); err != nil { if err := gitrepo.InitRepository(ctx, repo, repo.ObjectFormatName); err != nil {
log.Error("Unable (re)initialize repository %d at %s. Error: %v", repo.ID, repo.RepoPath(), err) log.Error("Unable (re)initialize repository %d at %s. Error: %v", repo.ID, repo.RelativePath(), err)
if err2 := system_model.CreateRepositoryNotice("InitRepository [%d]: %v", repo.ID, err); err2 != nil { if err2 := system_model.CreateRepositoryNotice("InitRepository [%d]: %v", repo.ID, err); err2 != nil {
log.Error("CreateRepositoryNotice: %v", err2) log.Error("CreateRepositoryNotice: %v", err2)
} }

View File

@@ -69,12 +69,10 @@ func deleteCommitStatusCache(repoID int64, branchName string) error {
// NOTE: All text-values will be trimmed from whitespaces. // NOTE: All text-values will be trimmed from whitespaces.
// Requires: Repo, Creator, SHA // Requires: Repo, Creator, SHA
func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creator *user_model.User, sha string, status *git_model.CommitStatus) error { func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creator *user_model.User, sha string, status *git_model.CommitStatus) error {
repoPath := repo.RepoPath()
// confirm that commit is exist // confirm that commit is exist
gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo) gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
if err != nil { if err != nil {
return fmt.Errorf("OpenRepository[%s]: %w", repoPath, err) return fmt.Errorf("OpenRepository[%s]: %w", repo.RelativePath(), err)
} }
defer closer.Close() defer closer.Close()

View File

@@ -132,11 +132,10 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int
var extendedCommitStats []*ExtendedCommitStats var extendedCommitStats []*ExtendedCommitStats
stderr := new(strings.Builder) stderr := new(strings.Builder)
err = gitCmd.Run(repo.Ctx, &gitcmd.RunOpts{ err = gitCmd.WithDir(repo.Path).
Dir: repo.Path, WithStdout(stdoutWriter).
Stdout: stdoutWriter, WithStderr(stderr).
Stderr: stderr, WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close() _ = stdoutWriter.Close()
scanner := bufio.NewScanner(stdoutReader) scanner := bufio.NewScanner(stdoutReader)
@@ -191,8 +190,8 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int
} }
_ = stdoutReader.Close() _ = stdoutReader.Close()
return nil return nil
}, }).
}) Run(repo.Ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to get ContributorsCommitStats for repository.\nError: %w\nStderr: %s", err, stderr) return nil, fmt.Errorf("Failed to get ContributorsCommitStats for repository.\nError: %w\nStderr: %s", err, stderr)
} }

View File

@@ -70,7 +70,7 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir
// Clone to temporary path and do the init commit. // Clone to temporary path and do the init commit.
if stdout, _, err := gitcmd.NewCommand("clone").AddDynamicArguments(repo.RepoPath(), tmpDir). if stdout, _, err := gitcmd.NewCommand("clone").AddDynamicArguments(repo.RepoPath(), tmpDir).
RunStdString(ctx, &gitcmd.RunOpts{Dir: "", Env: env}); err != nil { WithEnv(env).RunStdString(ctx); err != nil {
log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err) log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err)
return fmt.Errorf("git clone: %w", err) return fmt.Errorf("git clone: %w", err)
} }
@@ -314,7 +314,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, owner *user_model.User,
licenses = append(licenses, opts.License) licenses = append(licenses, opts.License)
var stdout string var stdout string
stdout, _, err = gitcmd.NewCommand("rev-parse", "HEAD").RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) stdout, err = gitrepo.RunCmdString(ctx, repo, gitcmd.NewCommand("rev-parse", "HEAD"))
if err != nil { if err != nil {
log.Error("CreateRepository(git rev-parse HEAD) in %v: Stdout: %s\nError: %v", repo, stdout, err) 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) return nil, fmt.Errorf("CreateRepository(git rev-parse HEAD): %w", err)
@@ -475,8 +475,8 @@ func updateGitRepoAfterCreate(ctx context.Context, repo *repo_model.Repository)
return fmt.Errorf("checkDaemonExportOK: %w", err) return fmt.Errorf("checkDaemonExportOK: %w", err)
} }
if stdout, _, err := gitcmd.NewCommand("update-server-info"). if stdout, err := gitrepo.RunCmdString(ctx, repo,
RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}); err != nil { gitcmd.NewCommand("update-server-info")); err != nil {
log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err) 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) return fmt.Errorf("CreateRepository(git update-server-info): %w", err)
} }

View File

@@ -165,12 +165,11 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
cmdApply.AddArguments("-3") cmdApply.AddArguments("-3")
} }
if err := cmdApply.Run(ctx, &gitcmd.RunOpts{ if err := cmdApply.WithDir(t.basePath).
Dir: t.basePath, WithStdout(stdout).
Stdout: stdout, WithStderr(stderr).
Stderr: stderr, WithStdin(strings.NewReader(opts.Content)).
Stdin: strings.NewReader(opts.Content), Run(ctx); err != nil {
}); err != nil {
return nil, fmt.Errorf("Error: Stdout: %s\nStderr: %s\nErr: %w", stdout.String(), stderr.String(), err) return nil, fmt.Errorf("Error: Stdout: %s\nStderr: %s\nErr: %w", stdout.String(), stderr.String(), err)
} }

View File

@@ -59,7 +59,7 @@ func (t *TemporaryUploadRepository) Clone(ctx context.Context, branch string, ba
cmd.AddArguments("--bare") cmd.AddArguments("--bare")
} }
if _, _, err := cmd.RunStdString(ctx, nil); err != nil { if _, _, err := cmd.RunStdString(ctx); err != nil {
stderr := err.Error() stderr := err.Error()
if matched, _ := regexp.MatchString(".*Remote branch .* not found in upstream origin.*", stderr); matched { if matched, _ := regexp.MatchString(".*Remote branch .* not found in upstream origin.*", stderr); matched {
return git.ErrBranchNotExist{ return git.ErrBranchNotExist{
@@ -98,7 +98,7 @@ func (t *TemporaryUploadRepository) Init(ctx context.Context, objectFormatName s
// SetDefaultIndex sets the git index to our HEAD // SetDefaultIndex sets the git index to our HEAD
func (t *TemporaryUploadRepository) SetDefaultIndex(ctx context.Context) error { func (t *TemporaryUploadRepository) SetDefaultIndex(ctx context.Context) error {
if _, _, err := gitcmd.NewCommand("read-tree", "HEAD").RunStdString(ctx, &gitcmd.RunOpts{Dir: t.basePath}); err != nil { if _, _, err := gitcmd.NewCommand("read-tree", "HEAD").WithDir(t.basePath).RunStdString(ctx); err != nil {
return fmt.Errorf("SetDefaultIndex: %w", err) return fmt.Errorf("SetDefaultIndex: %w", err)
} }
return nil 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. // 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 { func (t *TemporaryUploadRepository) RefreshIndex(ctx context.Context) error {
if _, _, err := gitcmd.NewCommand("update-index", "--refresh").RunStdString(ctx, &gitcmd.RunOpts{Dir: t.basePath}); err != nil { if _, _, err := gitcmd.NewCommand("update-index", "--refresh").WithDir(t.basePath).RunStdString(ctx); err != nil {
return fmt.Errorf("RefreshIndex: %w", err) return fmt.Errorf("RefreshIndex: %w", err)
} }
return nil return nil
@@ -118,11 +118,10 @@ func (t *TemporaryUploadRepository) LsFiles(ctx context.Context, filenames ...st
stdErr := new(bytes.Buffer) stdErr := new(bytes.Buffer)
if err := gitcmd.NewCommand("ls-files", "-z").AddDashesAndList(filenames...). if err := gitcmd.NewCommand("ls-files", "-z").AddDashesAndList(filenames...).
Run(ctx, &gitcmd.RunOpts{ WithDir(t.basePath).
Dir: t.basePath, WithStdout(stdOut).
Stdout: stdOut, WithStderr(stdErr).
Stderr: stdErr, Run(ctx); err != nil {
}); 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()) 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()) 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 return nil, err
@@ -154,12 +153,11 @@ func (t *TemporaryUploadRepository) RemoveFilesFromIndex(ctx context.Context, fi
} }
if err := gitcmd.NewCommand("update-index", "--remove", "-z", "--index-info"). if err := gitcmd.NewCommand("update-index", "--remove", "-z", "--index-info").
Run(ctx, &gitcmd.RunOpts{ WithDir(t.basePath).
Dir: t.basePath, WithStdout(stdOut).
Stdin: stdIn, WithStderr(stdErr).
Stdout: stdOut, WithStdin(stdIn).
Stderr: stdErr, Run(ctx); err != nil {
}); 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()) 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())
} }
return nil return nil
@@ -171,12 +169,11 @@ func (t *TemporaryUploadRepository) HashObjectAndWrite(ctx context.Context, cont
stdErr := new(bytes.Buffer) stdErr := new(bytes.Buffer)
if err := gitcmd.NewCommand("hash-object", "-w", "--stdin"). if err := gitcmd.NewCommand("hash-object", "-w", "--stdin").
Run(ctx, &gitcmd.RunOpts{ WithDir(t.basePath).
Dir: t.basePath, WithStdout(stdOut).
Stdin: content, WithStderr(stdErr).
Stdout: stdOut, WithStdin(content).
Stderr: stdErr, Run(ctx); err != nil {
}); 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()) 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()) 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())
} }
@@ -186,7 +183,8 @@ func (t *TemporaryUploadRepository) HashObjectAndWrite(ctx context.Context, cont
// AddObjectToIndex adds the provided object hash to the index with the provided mode and path // 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 { 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).RunStdString(ctx, &gitcmd.RunOpts{Dir: t.basePath}); err != nil { if _, _, err := gitcmd.NewCommand("update-index", "--add", "--replace", "--cacheinfo").
AddDynamicArguments(mode, objectHash, objectPath).WithDir(t.basePath).RunStdString(ctx); err != nil {
stderr := err.Error() stderr := err.Error()
if matched, _ := regexp.MatchString(".*Invalid path '.*", stderr); matched { if matched, _ := regexp.MatchString(".*Invalid path '.*", stderr); matched {
return ErrFilePathInvalid{ return ErrFilePathInvalid{
@@ -202,7 +200,7 @@ func (t *TemporaryUploadRepository) AddObjectToIndex(ctx context.Context, mode,
// WriteTree writes the current index as a tree to the object db and returns its hash // WriteTree writes the current index as a tree to the object db and returns its hash
func (t *TemporaryUploadRepository) WriteTree(ctx context.Context) (string, error) { func (t *TemporaryUploadRepository) WriteTree(ctx context.Context) (string, error) {
stdout, _, err := gitcmd.NewCommand("write-tree").RunStdString(ctx, &gitcmd.RunOpts{Dir: t.basePath}) stdout, _, err := gitcmd.NewCommand("write-tree").WithDir(t.basePath).RunStdString(ctx)
if err != nil { if err != nil {
log.Error("Unable to write tree in temporary repo: %s(%s): Error: %v", t.repo.FullName(), t.basePath, err) log.Error("Unable to write tree in temporary repo: %s(%s): Error: %v", t.repo.FullName(), t.basePath, err)
return "", fmt.Errorf("Unable to write-tree in temporary repo for: %s Error: %w", t.repo.FullName(), err) return "", fmt.Errorf("Unable to write-tree in temporary repo for: %s Error: %w", t.repo.FullName(), err)
@@ -220,7 +218,7 @@ func (t *TemporaryUploadRepository) GetLastCommitByRef(ctx context.Context, ref
if ref == "" { if ref == "" {
ref = "HEAD" ref = "HEAD"
} }
stdout, _, err := gitcmd.NewCommand("rev-parse").AddDynamicArguments(ref).RunStdString(ctx, &gitcmd.RunOpts{Dir: t.basePath}) stdout, _, err := gitcmd.NewCommand("rev-parse").AddDynamicArguments(ref).WithDir(t.basePath).RunStdString(ctx)
if err != nil { if err != nil {
log.Error("Unable to get last ref for %s in temporary repo: %s(%s): Error: %v", ref, t.repo.FullName(), t.basePath, err) log.Error("Unable to get last ref for %s in temporary repo: %s(%s): Error: %v", ref, t.repo.FullName(), t.basePath, err)
return "", fmt.Errorf("Unable to rev-parse %s in temporary repo for: %s Error: %w", ref, t.repo.FullName(), err) return "", fmt.Errorf("Unable to rev-parse %s in temporary repo for: %s Error: %w", ref, t.repo.FullName(), err)
@@ -338,13 +336,12 @@ func (t *TemporaryUploadRepository) CommitTree(ctx context.Context, opts *Commit
stdout := new(bytes.Buffer) stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
if err := cmdCommitTree. if err := cmdCommitTree.
Run(ctx, &gitcmd.RunOpts{ WithEnv(env).
Env: env, WithDir(t.basePath).
Dir: t.basePath, WithStdout(stdout).
Stdin: messageBytes, WithStderr(stderr).
Stdout: stdout, WithStdin(messageBytes).
Stderr: stderr, Run(ctx); err != nil {
}); err != nil {
log.Error("Unable to commit-tree in temporary repo: %s (%s) Error: %v\nStdout: %s\nStderr: %s", 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) 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", return "", fmt.Errorf("Unable to commit-tree in temporary repo: %s Error: %w\nStdout: %s\nStderr: %s",
@@ -389,12 +386,11 @@ func (t *TemporaryUploadRepository) DiffIndex(ctx context.Context) (*gitdiff.Dif
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
var diff *gitdiff.Diff var diff *gitdiff.Diff
err = gitcmd.NewCommand("diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD"). err = gitcmd.NewCommand("diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD").
Run(ctx, &gitcmd.RunOpts{ WithTimeout(30 * time.Second).
Timeout: 30 * time.Second, WithDir(t.basePath).
Dir: t.basePath, WithStdout(stdoutWriter).
Stdout: stdoutWriter, WithStderr(stderr).
Stderr: stderr, WithPipelineFunc(func(ctx context.Context, cancel context.CancelFunc) error {
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close() _ = stdoutWriter.Close()
defer cancel() defer cancel()
var diffErr error var diffErr error
@@ -405,8 +401,8 @@ func (t *TemporaryUploadRepository) DiffIndex(ctx context.Context) (*gitdiff.Dif
return fmt.Errorf("ParsePatch: %w", diffErr) return fmt.Errorf("ParsePatch: %w", diffErr)
} }
return nil return nil
}, }).
}) Run(ctx)
if err != nil && !git.IsErrCanceledOrKilled(err) { 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) 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) return nil, fmt.Errorf("unable to run diff-index pipeline in temporary repo: %w", err)

View File

@@ -153,7 +153,8 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
} }
var stdout []byte var stdout []byte
if stdout, _, err = cloneCmd.AddDynamicArguments(opts.BaseRepo.RepoPath(), repo.RepoPath()). if stdout, _, err = cloneCmd.AddDynamicArguments(opts.BaseRepo.RepoPath(), repo.RepoPath()).
RunStdBytes(ctx, &gitcmd.RunOpts{Timeout: 10 * time.Minute}); err != nil { WithTimeout(10 * time.Minute).
RunStdBytes(ctx); 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 nil, fmt.Errorf("git clone: %w", err) return nil, fmt.Errorf("git clone: %w", err)
} }

View File

@@ -235,8 +235,11 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r
return err return err
} }
if stdout, _, err := gitcmd.NewCommand("remote", "add", "origin").AddDynamicArguments(repo.RepoPath()). if stdout, _, err := gitcmd.NewCommand("remote", "add", "origin").
RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpDir, Env: env}); err != nil { AddDynamicArguments(repo.RepoPath()).
WithDir(tmpDir).
WithEnv(env).
RunStdString(ctx); err != nil {
log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err) log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err)
return fmt.Errorf("git remote add: %w", err) return fmt.Errorf("git remote add: %w", err)
} }

Some files were not shown because too many files have changed in this diff Show More