Move git command to git/gitcmd (#35483)

The name cmd is already used in many places and may cause conflicts, so
I chose `gitcmd` instead to minimize potential naming conflicts.
This commit is contained in:
Lunny Xiao
2025-09-15 23:33:12 -07:00
committed by GitHub
parent fe5afcb022
commit 9332ff291b
107 changed files with 690 additions and 558 deletions

View File

@@ -12,6 +12,7 @@ import (
"time"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/log"
)
@@ -23,7 +24,7 @@ type BatchChecker struct {
stdOut *nulSeparatedAttributeWriter
ctx context.Context
cancel context.CancelFunc
cmd *git.Command
cmd *gitcmd.Command
}
// NewBatchChecker creates a check attribute reader for the current repository and provided commit ID
@@ -76,7 +77,7 @@ func NewBatchChecker(repo *git.Repository, treeish string, attributes []string)
_ = lw.Close()
}()
stdErr := new(bytes.Buffer)
err := cmd.Run(ctx, &git.RunOpts{
err := cmd.Run(ctx, &gitcmd.RunOpts{
Env: envs,
Dir: repo.Path,
Stdin: stdinReader,

View File

@@ -11,12 +11,13 @@ import (
"os"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd"
)
func checkAttrCommand(gitRepo *git.Repository, treeish string, filenames, attributes []string) (*git.Command, []string, func(), error) {
func checkAttrCommand(gitRepo *git.Repository, treeish string, filenames, attributes []string) (*gitcmd.Command, []string, func(), error) {
cancel := func() {}
envs := []string{"GIT_FLUSH=1"}
cmd := git.NewCommand("check-attr", "-z")
cmd := gitcmd.NewCommand("check-attr", "-z")
if len(attributes) == 0 {
cmd.AddArguments("--all")
}
@@ -70,7 +71,7 @@ func CheckAttributes(ctx context.Context, gitRepo *git.Repository, treeish strin
stdOut := new(bytes.Buffer)
stdErr := new(bytes.Buffer)
if err := cmd.Run(ctx, &git.RunOpts{
if err := cmd.Run(ctx, &gitcmd.RunOpts{
Env: append(os.Environ(), envs...),
Dir: gitRepo.Path,
Stdout: stdOut,

View File

@@ -12,6 +12,7 @@ import (
"strconv"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/log"
"github.com/djherbis/buffer"
@@ -29,13 +30,13 @@ type WriteCloserError interface {
// This is needed otherwise the git cat-file will hang for invalid repositories.
func ensureValidGitRepository(ctx context.Context, repoPath string) error {
stderr := strings.Builder{}
err := NewCommand("rev-parse").
Run(ctx, &RunOpts{
err := gitcmd.NewCommand("rev-parse").
Run(ctx, &gitcmd.RunOpts{
Dir: repoPath,
Stderr: &stderr,
})
if err != nil {
return ConcatenateError(err, (&stderr).String())
return gitcmd.ConcatenateError(err, (&stderr).String())
}
return nil
}
@@ -61,8 +62,8 @@ func catFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError,
go func() {
stderr := strings.Builder{}
err := NewCommand("cat-file", "--batch-check").
Run(ctx, &RunOpts{
err := gitcmd.NewCommand("cat-file", "--batch-check").
Run(ctx, &gitcmd.RunOpts{
Dir: repoPath,
Stdin: batchStdinReader,
Stdout: batchStdoutWriter,
@@ -71,8 +72,8 @@ func catFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError,
UseContextTimeout: true,
})
if err != nil {
_ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
_ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String()))
_ = batchStdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
_ = batchStdinReader.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
} else {
_ = batchStdoutWriter.Close()
_ = batchStdinReader.Close()
@@ -109,8 +110,8 @@ func catFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
go func() {
stderr := strings.Builder{}
err := NewCommand("cat-file", "--batch").
Run(ctx, &RunOpts{
err := gitcmd.NewCommand("cat-file", "--batch").
Run(ctx, &gitcmd.RunOpts{
Dir: repoPath,
Stdin: batchStdinReader,
Stdout: batchStdoutWriter,
@@ -119,8 +120,8 @@ func catFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
UseContextTimeout: true,
})
if err != nil {
_ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
_ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String()))
_ = batchStdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
_ = batchStdinReader.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
} else {
_ = batchStdoutWriter.Close()
_ = batchStdinReader.Close()

View File

@@ -10,6 +10,7 @@ import (
"io"
"os"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
@@ -141,7 +142,7 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath
}
}()
cmd := NewCommand("blame", "--porcelain")
cmd := gitcmd.NewCommand("blame", "--porcelain")
if DefaultFeatures().CheckVersionAtLeast("2.23") && !bypassBlameIgnore {
ignoreRevsFileName, ignoreRevsFileCleanup, err = tryCreateBlameIgnoreRevsFile(commit)
@@ -165,7 +166,7 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath
go func() {
stderr := bytes.Buffer{}
// TODO: it doesn't work for directories (the directories shouldn't be "blamed"), and the "err" should be returned by "Read" but not by "Close"
err := cmd.Run(ctx, &RunOpts{
err := cmd.Run(ctx, &gitcmd.RunOpts{
UseContextTimeout: true,
Dir: repoPath,
Stdout: stdout,

View File

@@ -14,6 +14,7 @@ import (
"strconv"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
)
@@ -87,12 +88,12 @@ func (c *Commit) GetCommitByPath(relpath string) (*Commit, error) {
// AddChanges marks local changes to be ready for commit.
func AddChanges(ctx context.Context, repoPath string, all bool, files ...string) error {
cmd := NewCommand().AddArguments("add")
cmd := gitcmd.NewCommand().AddArguments("add")
if all {
cmd.AddArguments("--all")
}
cmd.AddDashesAndList(files...)
_, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath})
_, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
return err
}
@@ -106,7 +107,7 @@ type CommitChangesOptions struct {
// CommitChanges commits local changes with given committer, author and message.
// If author is nil, it will be the same as committer.
func CommitChanges(ctx context.Context, repoPath string, opts CommitChangesOptions) error {
cmd := NewCommand()
cmd := gitcmd.NewCommand()
if opts.Committer != nil {
cmd.AddOptionValues("-c", "user.name="+opts.Committer.Name)
cmd.AddOptionValues("-c", "user.email="+opts.Committer.Email)
@@ -121,7 +122,7 @@ func CommitChanges(ctx context.Context, repoPath string, opts CommitChangesOptio
}
cmd.AddOptionFormat("--message=%s", opts.Message)
_, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath})
_, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
// No stderr but exit status 1 means nothing to commit.
if err != nil && err.Error() == "exit status 1" {
return nil
@@ -131,7 +132,7 @@ func CommitChanges(ctx context.Context, repoPath string, opts CommitChangesOptio
// AllCommitsCount returns count of all commits in repository
func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, files ...string) (int64, error) {
cmd := NewCommand("rev-list")
cmd := gitcmd.NewCommand("rev-list")
if hidePRRefs {
cmd.AddArguments("--exclude=" + PullPrefix + "*")
}
@@ -140,7 +141,7 @@ func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, file
cmd.AddDashesAndList(files...)
}
stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath})
stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
if err != nil {
return 0, err
}
@@ -160,7 +161,7 @@ type CommitsCountOptions struct {
// CommitsCount returns number of total commits of until given revision.
func CommitsCount(ctx context.Context, opts CommitsCountOptions) (int64, error) {
cmd := NewCommand("rev-list", "--count")
cmd := gitcmd.NewCommand("rev-list", "--count")
cmd.AddDynamicArguments(opts.Revision...)
@@ -172,7 +173,7 @@ func CommitsCount(ctx context.Context, opts CommitsCountOptions) (int64, error)
cmd.AddDashesAndList(opts.RelPath...)
}
stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: opts.RepoPath})
stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: opts.RepoPath})
if err != nil {
return 0, err
}
@@ -207,7 +208,7 @@ func (c *Commit) HasPreviousCommit(objectID ObjectID) (bool, error) {
return false, nil
}
_, _, err := NewCommand("merge-base", "--is-ancestor").AddDynamicArguments(that, this).RunStdString(c.repo.Ctx, &RunOpts{Dir: c.repo.Path})
_, _, err := gitcmd.NewCommand("merge-base", "--is-ancestor").AddDynamicArguments(that, this).RunStdString(c.repo.Ctx, &gitcmd.RunOpts{Dir: c.repo.Path})
if err == nil {
return true, nil
}
@@ -348,12 +349,12 @@ func (c *Commit) GetFileContent(filename string, limit int) (string, error) {
// GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only')
func (c *Commit) GetBranchName() (string, error) {
cmd := NewCommand("name-rev")
cmd := gitcmd.NewCommand("name-rev")
if DefaultFeatures().CheckVersionAtLeast("2.13.0") {
cmd.AddArguments("--exclude", "refs/tags/*")
}
cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String())
data, _, err := cmd.RunStdString(c.repo.Ctx, &RunOpts{Dir: c.repo.Path})
data, _, err := cmd.RunStdString(c.repo.Ctx, &gitcmd.RunOpts{Dir: c.repo.Path})
if err != nil {
// handle special case where git can not describe commit
if strings.Contains(err.Error(), "cannot describe") {
@@ -431,14 +432,14 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi
}()
stderr := new(bytes.Buffer)
err := NewCommand("log", "--name-status", "-m", "--pretty=format:", "--first-parent", "--no-renames", "-z", "-1").AddDynamicArguments(commitID).Run(ctx, &RunOpts{
err := gitcmd.NewCommand("log", "--name-status", "-m", "--pretty=format:", "--first-parent", "--no-renames", "-z", "-1").AddDynamicArguments(commitID).Run(ctx, &gitcmd.RunOpts{
Dir: repoPath,
Stdout: w,
Stderr: stderr,
})
w.Close() // Close writer to exit parsing goroutine
if err != nil {
return nil, ConcatenateError(err, stderr.String())
return nil, gitcmd.ConcatenateError(err, stderr.String())
}
<-done
@@ -447,7 +448,7 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi
// 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) {
commitID, _, err := NewCommand("rev-parse").AddDynamicArguments(shortID).RunStdString(ctx, &RunOpts{Dir: repoPath})
commitID, _, err := gitcmd.NewCommand("rev-parse").AddDynamicArguments(shortID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
if err != nil {
if strings.Contains(err.Error(), "exit status 128") {
return "", ErrNotExist{shortID, ""}

View File

@@ -11,13 +11,14 @@ import (
"runtime"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/setting"
)
// syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem)
func syncGitConfig(ctx context.Context) (err error) {
if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil {
return fmt.Errorf("unable to prepare git home directory %s, err: %w", HomeDir(), err)
if err = os.MkdirAll(gitcmd.HomeDir(), os.ModePerm); err != nil {
return fmt.Errorf("unable to prepare git home directory %s, err: %w", gitcmd.HomeDir(), err)
}
// first, write user's git config options to git config file
@@ -117,8 +118,8 @@ func syncGitConfig(ctx context.Context) (err error) {
}
func configSet(ctx context.Context, key, value string) error {
stdout, _, err := NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil)
if err != nil && !IsErrorExitCode(err, 1) {
stdout, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil)
if err != nil && !gitcmd.IsErrorExitCode(err, 1) {
return fmt.Errorf("failed to get git config %s, err: %w", key, err)
}
@@ -127,7 +128,7 @@ func configSet(ctx context.Context, key, value string) error {
return nil
}
_, _, err = NewCommand("config", "--global").AddDynamicArguments(key, value).RunStdString(ctx, nil)
_, _, err = gitcmd.NewCommand("config", "--global").AddDynamicArguments(key, value).RunStdString(ctx, nil)
if err != nil {
return fmt.Errorf("failed to set git global config %s, err: %w", key, err)
}
@@ -136,14 +137,14 @@ func configSet(ctx context.Context, key, value string) error {
}
func configSetNonExist(ctx context.Context, key, value string) error {
_, _, err := NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil)
_, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil)
if err == nil {
// already exist
return nil
}
if IsErrorExitCode(err, 1) {
if gitcmd.IsErrorExitCode(err, 1) {
// not exist, set new config
_, _, err = NewCommand("config", "--global").AddDynamicArguments(key, value).RunStdString(ctx, nil)
_, _, err = gitcmd.NewCommand("config", "--global").AddDynamicArguments(key, value).RunStdString(ctx, nil)
if err != nil {
return fmt.Errorf("failed to set git global config %s, err: %w", key, err)
}
@@ -154,14 +155,14 @@ func configSetNonExist(ctx context.Context, key, value string) error {
}
func configAddNonExist(ctx context.Context, key, value string) error {
_, _, err := 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, nil)
if err == nil {
// already exist
return nil
}
if IsErrorExitCode(err, 1) {
if gitcmd.IsErrorExitCode(err, 1) {
// not exist, add new config
_, _, err = NewCommand("config", "--global", "--add").AddDynamicArguments(key, value).RunStdString(ctx, nil)
_, _, err = gitcmd.NewCommand("config", "--global", "--add").AddDynamicArguments(key, value).RunStdString(ctx, nil)
if err != nil {
return fmt.Errorf("failed to add git global config %s, err: %w", key, err)
}
@@ -171,16 +172,16 @@ func configAddNonExist(ctx context.Context, key, value string) error {
}
func configUnsetAll(ctx context.Context, key, value string) error {
_, _, err := NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil)
_, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil)
if err == nil {
// exist, need to remove
_, _, err = 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, nil)
if err != nil {
return fmt.Errorf("failed to unset git global config %s, err: %w", key, err)
}
return nil
}
if IsErrorExitCode(err, 1) {
if gitcmd.IsErrorExitCode(err, 1) {
// not exist
return nil
}

View File

@@ -8,13 +8,14 @@ import (
"strings"
"testing"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
)
func gitConfigContains(sub string) bool {
if b, err := os.ReadFile(HomeDir() + "/.gitconfig"); err == nil {
if b, err := os.ReadFile(gitcmd.HomeDir() + "/.gitconfig"); err == nil {
return strings.Contains(string(b), sub)
}
return false

View File

@@ -14,6 +14,7 @@ import (
"strconv"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/log"
)
@@ -34,8 +35,8 @@ func GetRawDiff(repo *Repository, commitID string, diffType RawDiffType, 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 {
stderr := new(bytes.Buffer)
cmd := NewCommand("show", "--pretty=format:revert %H%n", "-R").AddDynamicArguments(commitID)
if err := cmd.Run(ctx, &RunOpts{
cmd := gitcmd.NewCommand("show", "--pretty=format:revert %H%n", "-R").AddDynamicArguments(commitID)
if err := cmd.Run(ctx, &gitcmd.RunOpts{
Dir: repoPath,
Stdout: writer,
Stderr: stderr,
@@ -56,7 +57,7 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
files = append(files, file)
}
cmd := NewCommand()
cmd := gitcmd.NewCommand()
switch diffType {
case RawDiffNormal:
if len(startCommit) != 0 {
@@ -89,7 +90,7 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
}
stderr := new(bytes.Buffer)
if err = cmd.Run(repo.Ctx, &RunOpts{
if err = cmd.Run(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdout: writer,
Stderr: stderr,
@@ -312,8 +313,8 @@ func GetAffectedFiles(repo *Repository, branchName, oldCommitID, newCommitID str
affectedFiles := make([]string, 0, 32)
// Run `git diff --name-only` to get the names of the changed files
err = NewCommand("diff", "--name-only").AddDynamicArguments(oldCommitID, newCommitID).
Run(repo.Ctx, &RunOpts{
err = gitcmd.NewCommand("diff", "--name-only").AddDynamicArguments(oldCommitID, newCommitID).
Run(repo.Ctx, &gitcmd.RunOpts{
Env: env,
Dir: repo.Path,
Stdout: stdoutWriter,

View File

@@ -9,12 +9,12 @@ import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -33,10 +33,7 @@ type Features struct {
SupportCheckAttrOnBare bool // >= 2.40
}
var (
GitExecutable = "git" // the command name of git, will be updated to an absolute path during initialization
defaultFeatures *Features
)
var defaultFeatures *Features
func (f *Features) CheckVersionAtLeast(atLeast string) bool {
return f.gitVersion.Compare(version.Must(version.NewVersion(atLeast))) >= 0
@@ -60,7 +57,7 @@ func DefaultFeatures() *Features {
}
func loadGitVersionFeatures() (*Features, error) {
stdout, _, runErr := NewCommand("version").RunStdString(context.Background(), nil)
stdout, _, runErr := gitcmd.NewCommand("version").RunStdString(context.Background(), nil)
if runErr != nil {
return nil, runErr
}
@@ -129,32 +126,6 @@ func ensureGitVersion() error {
return nil
}
// SetExecutablePath changes the path of git executable and checks the file permission and version.
func SetExecutablePath(path string) error {
// If path is empty, we use the default value of GitExecutable "git" to search for the location of git.
if path != "" {
GitExecutable = path
}
absPath, err := exec.LookPath(GitExecutable)
if err != nil {
return fmt.Errorf("git not found: %w", err)
}
GitExecutable = absPath
return nil
}
// HomeDir is the home dir for git to store the global config file used by Gitea internally
func HomeDir() string {
if setting.Git.HomePath == "" {
// strict check, make sure the git module is initialized correctly.
// attention: when the git module is called in gitea sub-command (serv/hook), the log module might not obviously show messages to users/developers.
// for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons.
log.Fatal("Unable to init Git's HomeDir, incorrect initialization of the setting and git modules")
return ""
}
return setting.Git.HomePath
}
// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
// This method doesn't change anything to filesystem. At the moment, it is only used by some Gitea sub-commands.
func InitSimple() error {
@@ -167,10 +138,10 @@ func InitSimple() error {
}
if setting.Git.Timeout.Default > 0 {
defaultCommandExecutionTimeout = time.Duration(setting.Git.Timeout.Default) * time.Second
gitcmd.SetDefaultCommandExecutionTimeout(time.Duration(setting.Git.Timeout.Default) * time.Second)
}
if err := SetExecutablePath(setting.Git.Path); err != nil {
if err := gitcmd.SetExecutablePath(setting.Git.Path); err != nil {
return err
}
@@ -185,7 +156,7 @@ func InitSimple() error {
// when git works with gnupg (commit signing), there should be a stable home for gnupg commands
if _, ok := os.LookupEnv("GNUPGHOME"); !ok {
_ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg"))
_ = os.Setenv("GNUPGHOME", filepath.Join(gitcmd.HomeDir(), ".gnupg"))
}
return nil
}

View File

@@ -2,7 +2,7 @@
// Copyright 2016 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
package gitcmd
import (
"bytes"
@@ -32,6 +32,10 @@ type TrustedCmdArgs []internal.CmdArg
// defaultCommandExecutionTimeout default command execution timeout duration
var defaultCommandExecutionTimeout = 360 * time.Second
func SetDefaultCommandExecutionTimeout(timeout time.Duration) {
defaultCommandExecutionTimeout = timeout
}
// DefaultLocale is the default LC_ALL to run git commands in.
const DefaultLocale = "C"

View File

@@ -3,7 +3,7 @@
//go:build race
package git
package gitcmd
import (
"context"

View File

@@ -1,14 +1,30 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
package gitcmd
import (
"fmt"
"os"
"testing"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/tempdir"
"github.com/stretchr/testify/assert"
)
func TestMain(m *testing.M) {
gitHomePath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("git-home")
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "unable to create temp dir: %v", err)
os.Exit(1)
}
defer cleanup()
setting.Git.HomePath = gitHomePath
}
func TestRunWithContextStd(t *testing.T) {
cmd := NewCommand("--version")
stdout, stderr, err := cmd.RunStdString(t.Context(), &RunOpts{})

40
modules/git/gitcmd/env.go Normal file
View File

@@ -0,0 +1,40 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package gitcmd
import (
"fmt"
"os/exec"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
var GitExecutable = "git" // the command name of git, will be updated to an absolute path during initialization
// SetExecutablePath changes the path of git executable and checks the file permission and version.
func SetExecutablePath(path string) error {
// If path is empty, we use the default value of GitExecutable "git" to search for the location of git.
if path != "" {
GitExecutable = path
}
absPath, err := exec.LookPath(GitExecutable)
if err != nil {
return fmt.Errorf("git not found: %w", err)
}
GitExecutable = absPath
return nil
}
// HomeDir is the home dir for git to store the global config file used by Gitea internally
func HomeDir() string {
if setting.Git.HomePath == "" {
// strict check, make sure the git module is initialized correctly.
// attention: when the git module is called in gitea sub-command (serv/hook), the log module might not obviously show messages to users/developers.
// for example: if there is gitea git hook code calling NewCommand before git.InitXxx, the integration test won't show the real failure reasons.
log.Fatal("Unable to init Git's HomeDir, incorrect initialization of the setting and git modules")
return ""
}
return setting.Git.HomePath
}

View File

@@ -0,0 +1,14 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package gitcmd
import "fmt"
// ConcatenateError concatenats an error with stderr string
func ConcatenateError(err error, stderr string) error {
if len(stderr) == 0 {
return err
}
return fmt.Errorf("%w - %s", err, stderr)
}

View File

@@ -14,6 +14,7 @@ import (
"strconv"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/util"
)
@@ -60,7 +61,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
2^@repo: go-gitea/gitea
*/
var results []*GrepResult
cmd := NewCommand("grep", "--null", "--break", "--heading", "--line-number", "--full-name")
cmd := gitcmd.NewCommand("grep", "--null", "--break", "--heading", "--line-number", "--full-name")
cmd.AddOptionValues("--context", strconv.Itoa(opts.ContextLineNumber))
switch opts.GrepMode {
case GrepModeExact:
@@ -83,7 +84,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
cmd.AddDashesAndList(opts.PathspecList...)
opts.MaxResultLimit = util.IfZero(opts.MaxResultLimit, 50)
stderr := bytes.Buffer{}
err = cmd.Run(ctx, &RunOpts{
err = cmd.Run(ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdout: stdoutWriter,
Stderr: &stderr,
@@ -135,11 +136,11 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
},
})
// git grep exits by cancel (killed), usually it is caused by the limit of results
if IsErrorExitCode(err, -1) && stderr.Len() == 0 {
if gitcmd.IsErrorExitCode(err, -1) && stderr.Len() == 0 {
return results, nil
}
// git grep exits with 1 if no results are found
if IsErrorExitCode(err, 1) && stderr.Len() == 0 {
if gitcmd.IsErrorExitCode(err, 1) && stderr.Len() == 0 {
return nil, nil
}
if err != nil && !errors.Is(err, context.Canceled) {

View File

@@ -14,6 +14,7 @@ import (
"strings"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git/gitcmd"
"github.com/djherbis/buffer"
"github.com/djherbis/nio/v3"
@@ -34,7 +35,7 @@ func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, p
_ = stdoutWriter.Close()
}
cmd := NewCommand()
cmd := gitcmd.NewCommand()
cmd.AddArguments("log", "--name-status", "-c", "--format=commit%x00%H %P%x00", "--parents", "--no-renames", "-t", "-z").AddDynamicArguments(head)
var files []string
@@ -64,13 +65,13 @@ func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, p
go func() {
stderr := strings.Builder{}
err := cmd.Run(ctx, &RunOpts{
err := cmd.Run(ctx, &gitcmd.RunOpts{
Dir: repository,
Stdout: stdoutWriter,
Stderr: &stderr,
})
if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
_ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
return
}

View File

@@ -13,7 +13,7 @@ import (
"strings"
"sync"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/log"
)
@@ -25,8 +25,8 @@ func CatFileBatchCheck(ctx context.Context, shasToCheckReader *io.PipeReader, ca
stderr := new(bytes.Buffer)
var errbuf strings.Builder
cmd := git.NewCommand("cat-file", "--batch-check")
if err := cmd.Run(ctx, &git.RunOpts{
cmd := gitcmd.NewCommand("cat-file", "--batch-check")
if err := cmd.Run(ctx, &gitcmd.RunOpts{
Dir: tmpBasePath,
Stdin: shasToCheckReader,
Stdout: catFileCheckWriter,
@@ -43,8 +43,8 @@ func CatFileBatchCheckAllObjects(ctx context.Context, catFileCheckWriter *io.Pip
stderr := new(bytes.Buffer)
var errbuf strings.Builder
cmd := git.NewCommand("cat-file", "--batch-check", "--batch-all-objects")
if err := cmd.Run(ctx, &git.RunOpts{
cmd := gitcmd.NewCommand("cat-file", "--batch-check", "--batch-all-objects")
if err := cmd.Run(ctx, &gitcmd.RunOpts{
Dir: tmpBasePath,
Stdout: catFileCheckWriter,
Stderr: stderr,
@@ -64,7 +64,7 @@ func CatFileBatch(ctx context.Context, shasToBatchReader *io.PipeReader, catFile
stderr := new(bytes.Buffer)
var errbuf strings.Builder
if err := git.NewCommand("cat-file", "--batch").Run(ctx, &git.RunOpts{
if err := gitcmd.NewCommand("cat-file", "--batch").Run(ctx, &gitcmd.RunOpts{
Dir: tmpBasePath,
Stdout: catFileBatchWriter,
Stdin: shasToBatchReader,

View File

@@ -14,6 +14,7 @@ import (
"sync"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd"
)
// FindLFSFile finds commits that contain a provided pointer file hash
@@ -32,13 +33,13 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
go func() {
stderr := strings.Builder{}
err := git.NewCommand("rev-list", "--all").Run(repo.Ctx, &git.RunOpts{
err := gitcmd.NewCommand("rev-list", "--all").Run(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdout: revListWriter,
Stderr: &stderr,
})
if err != nil {
_ = revListWriter.CloseWithError(git.ConcatenateError(err, (&stderr).String()))
_ = revListWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
} else {
_ = revListWriter.Close()
}

View File

@@ -11,7 +11,7 @@ import (
"strings"
"sync"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd"
)
// NameRevStdin runs name-rev --stdin
@@ -22,7 +22,7 @@ func NameRevStdin(ctx context.Context, shasToNameReader *io.PipeReader, nameRevS
stderr := new(bytes.Buffer)
var errbuf strings.Builder
if err := git.NewCommand("name-rev", "--stdin", "--name-only", "--always").Run(ctx, &git.RunOpts{
if err := gitcmd.NewCommand("name-rev", "--stdin", "--name-only", "--always").Run(ctx, &gitcmd.RunOpts{
Dir: tmpBasePath,
Stdout: nameRevStdinWriter,
Stdin: shasToNameReader,

View File

@@ -12,7 +12,7 @@ import (
"strings"
"sync"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/log"
)
@@ -23,8 +23,8 @@ func RevListAllObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sy
stderr := new(bytes.Buffer)
var errbuf strings.Builder
cmd := git.NewCommand("rev-list", "--objects", "--all")
if err := cmd.Run(ctx, &git.RunOpts{
cmd := gitcmd.NewCommand("rev-list", "--objects", "--all")
if err := cmd.Run(ctx, &gitcmd.RunOpts{
Dir: basePath,
Stdout: revListWriter,
Stderr: stderr,
@@ -42,11 +42,11 @@ func RevListObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.
defer revListWriter.Close()
stderr := new(bytes.Buffer)
var errbuf strings.Builder
cmd := git.NewCommand("rev-list", "--objects").AddDynamicArguments(headSHA)
cmd := gitcmd.NewCommand("rev-list", "--objects").AddDynamicArguments(headSHA)
if baseSHA != "" {
cmd = cmd.AddArguments("--not").AddDynamicArguments(baseSHA)
}
if err := cmd.Run(ctx, &git.RunOpts{
if err := cmd.Run(ctx, &gitcmd.RunOpts{
Dir: tmpBasePath,
Stdout: revListWriter,
Stderr: stderr,

View File

@@ -9,19 +9,20 @@ import (
"net/url"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/util"
)
// GetRemoteAddress returns remote url of git repository in the repoPath with special remote name
func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) {
var cmd *Command
var cmd *gitcmd.Command
if DefaultFeatures().CheckVersionAtLeast("2.7") {
cmd = NewCommand("remote", "get-url").AddDynamicArguments(remoteName)
cmd = gitcmd.NewCommand("remote", "get-url").AddDynamicArguments(remoteName)
} else {
cmd = NewCommand("config", "--get").AddDynamicArguments("remote." + remoteName + ".url")
cmd = gitcmd.NewCommand("config", "--get").AddDynamicArguments("remote." + remoteName + ".url")
}
result, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath})
result, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
if err != nil {
return "", err
}

View File

@@ -17,6 +17,7 @@ import (
"strings"
"time"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/setting"
)
@@ -40,9 +41,9 @@ func (repo *Repository) GetAllCommitsCount() (int64, 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>...]'
logs, _, err := NewCommand("log").AddArguments(prettyLogFormat).
logs, _, err := gitcmd.NewCommand("log").AddArguments(prettyLogFormat).
AddDynamicArguments(revisionRange).AddArguments("--").
RunStdBytes(ctx, &RunOpts{Dir: repo.Path})
RunStdBytes(ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
@@ -70,7 +71,7 @@ func (repo *Repository) parsePrettyFormatLogToList(logs []byte) ([]*Commit, erro
// IsRepoURLAccessible checks if given repository URL is accessible.
func IsRepoURLAccessible(ctx context.Context, url string) bool {
_, _, err := NewCommand("ls-remote", "-q", "-h").AddDynamicArguments(url, "HEAD").RunStdString(ctx, nil)
_, _, err := gitcmd.NewCommand("ls-remote", "-q", "-h").AddDynamicArguments(url, "HEAD").RunStdString(ctx, nil)
return err == nil
}
@@ -81,7 +82,7 @@ func InitRepository(ctx context.Context, repoPath string, bare bool, objectForma
return err
}
cmd := NewCommand("init")
cmd := gitcmd.NewCommand("init")
if !IsValidObjectFormat(objectFormatName) {
return fmt.Errorf("invalid object format: %s", objectFormatName)
@@ -93,15 +94,15 @@ func InitRepository(ctx context.Context, repoPath string, bare bool, objectForma
if bare {
cmd.AddArguments("--bare")
}
_, _, err = cmd.RunStdString(ctx, &RunOpts{Dir: repoPath})
_, _, err = cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
return err
}
// IsEmpty Check if repository is empty.
func (repo *Repository) IsEmpty() (bool, error) {
var errbuf, output strings.Builder
if err := NewCommand().AddOptionFormat("--git-dir=%s", repo.Path).AddArguments("rev-list", "-n", "1", "--all").
Run(repo.Ctx, &RunOpts{
if err := gitcmd.NewCommand().AddOptionFormat("--git-dir=%s", repo.Path).AddArguments("rev-list", "-n", "1", "--all").
Run(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdout: &output,
Stderr: &errbuf,
@@ -137,7 +138,7 @@ func Clone(ctx context.Context, from, to string, opts CloneRepoOptions) error {
return err
}
cmd := NewCommand().AddArguments("clone")
cmd := gitcmd.NewCommand().AddArguments("clone")
if opts.SkipTLSVerify {
cmd.AddArguments("-c", "http.sslVerify=false")
}
@@ -178,13 +179,13 @@ func Clone(ctx context.Context, from, to string, opts CloneRepoOptions) error {
}
stderr := new(bytes.Buffer)
if err = cmd.Run(ctx, &RunOpts{
if err = cmd.Run(ctx, &gitcmd.RunOpts{
Timeout: opts.Timeout,
Env: envs,
Stdout: io.Discard,
Stderr: stderr,
}); err != nil {
return ConcatenateError(err, stderr.String())
return gitcmd.ConcatenateError(err, stderr.String())
}
return nil
}
@@ -201,7 +202,7 @@ type PushOptions struct {
// Push pushs local commits to given remote branch.
func Push(ctx context.Context, repoPath string, opts PushOptions) error {
cmd := NewCommand("push")
cmd := gitcmd.NewCommand("push")
if opts.Force {
cmd.AddArguments("-f")
}
@@ -214,7 +215,7 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
}
cmd.AddDashesAndList(remoteBranchArgs...)
stdout, stderr, err := cmd.RunStdString(ctx, &RunOpts{Env: opts.Env, Timeout: opts.Timeout, Dir: repoPath})
stdout, stderr, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Env: opts.Env, Timeout: opts.Timeout, Dir: repoPath})
if err != nil {
if strings.Contains(stderr, "non-fast-forward") {
return &ErrPushOutOfDate{StdOut: stdout, StdErr: stderr, Err: err}
@@ -233,8 +234,8 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
// GetLatestCommitTime returns time for latest commit in repository (across all branches)
func GetLatestCommitTime(ctx context.Context, repoPath string) (time.Time, error) {
cmd := NewCommand("for-each-ref", "--sort=-committerdate", BranchPrefix, "--count", "1", "--format=%(committerdate)")
stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath})
cmd := gitcmd.NewCommand("for-each-ref", "--sort=-committerdate", BranchPrefix, "--count", "1", "--format=%(committerdate)")
stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
if err != nil {
return time.Time{}, err
}
@@ -250,9 +251,9 @@ type DivergeObject struct {
// GetDivergingCommits returns the number of commits a targetBranch is ahead or behind a baseBranch
func GetDivergingCommits(ctx context.Context, repoPath, baseBranch, targetBranch string) (do DivergeObject, err error) {
cmd := NewCommand("rev-list", "--count", "--left-right").
cmd := gitcmd.NewCommand("rev-list", "--count", "--left-right").
AddDynamicArguments(baseBranch + "..." + targetBranch).AddArguments("--")
stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath})
stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
if err != nil {
return do, err
}
@@ -281,23 +282,23 @@ func (repo *Repository) CreateBundle(ctx context.Context, commit string, out io.
defer cleanup()
env := append(os.Environ(), "GIT_OBJECT_DIRECTORY="+filepath.Join(repo.Path, "objects"))
_, _, err = NewCommand("init", "--bare").RunStdString(ctx, &RunOpts{Dir: tmp, Env: env})
_, _, err = gitcmd.NewCommand("init", "--bare").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env})
if err != nil {
return err
}
_, _, err = NewCommand("reset", "--soft").AddDynamicArguments(commit).RunStdString(ctx, &RunOpts{Dir: tmp, Env: env})
_, _, err = gitcmd.NewCommand("reset", "--soft").AddDynamicArguments(commit).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env})
if err != nil {
return err
}
_, _, err = NewCommand("branch", "-m", "bundle").RunStdString(ctx, &RunOpts{Dir: tmp, Env: env})
_, _, err = gitcmd.NewCommand("branch", "-m", "bundle").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env})
if err != nil {
return err
}
tmpFile := filepath.Join(tmp, "bundle")
_, _, err = NewCommand("bundle", "create").AddDynamicArguments(tmpFile, "bundle", "HEAD").RunStdString(ctx, &RunOpts{Dir: tmp, Env: env})
_, _, err = gitcmd.NewCommand("bundle", "create").AddDynamicArguments(tmpFile, "bundle", "HEAD").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env})
if err != nil {
return err
}

View File

@@ -10,6 +10,8 @@ import (
"io"
"path/filepath"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
)
// ArchiveType archive types
@@ -53,7 +55,7 @@ func (repo *Repository) CreateArchive(ctx context.Context, format ArchiveType, t
return fmt.Errorf("unknown format: %v", format)
}
cmd := NewCommand("archive")
cmd := gitcmd.NewCommand("archive")
if usePrefix {
cmd.AddOptionFormat("--prefix=%s", filepath.Base(strings.TrimSuffix(repo.Path, ".git"))+"/")
}
@@ -61,13 +63,13 @@ func (repo *Repository) CreateArchive(ctx context.Context, format ArchiveType, t
cmd.AddDynamicArguments(commitID)
var stderr strings.Builder
err := cmd.Run(ctx, &RunOpts{
err := cmd.Run(ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdout: target,
Stderr: &stderr,
})
if err != nil {
return ConcatenateError(err, stderr.String())
return gitcmd.ConcatenateError(err, stderr.String())
}
return nil
}

View File

@@ -5,14 +5,16 @@ package git
import (
"fmt"
"code.gitea.io/gitea/modules/git/gitcmd"
)
// LineBlame returns the latest commit at the given line
func (repo *Repository) LineBlame(revision, path, file string, line uint) (*Commit, error) {
res, _, err := NewCommand("blame").
res, _, err := gitcmd.NewCommand("blame").
AddOptionFormat("-L %d,%d", line, line).
AddOptionValues("-p", revision).
AddDashesAndList(file).RunStdString(repo.Ctx, &RunOpts{Dir: path})
AddDashesAndList(file).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: path})
if err != nil {
return nil, err
}

View File

@@ -8,6 +8,8 @@ import (
"context"
"errors"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
)
// BranchPrefix base dir of the branch information file store on git
@@ -15,7 +17,7 @@ const BranchPrefix = "refs/heads/"
// IsReferenceExist returns true if given reference exists in the repository.
func IsReferenceExist(ctx context.Context, repoPath, name string) bool {
_, _, err := NewCommand("show-ref", "--verify").AddDashesAndList(name).RunStdString(ctx, &RunOpts{Dir: repoPath})
_, _, err := gitcmd.NewCommand("show-ref", "--verify").AddDashesAndList(name).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
return err == nil
}
@@ -25,7 +27,7 @@ func IsBranchExist(ctx context.Context, repoPath, name string) bool {
}
func GetDefaultBranch(ctx context.Context, repoPath string) (string, error) {
stdout, _, err := NewCommand("symbolic-ref", "HEAD").RunStdString(ctx, &RunOpts{Dir: repoPath})
stdout, _, err := gitcmd.NewCommand("symbolic-ref", "HEAD").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
if err != nil {
return "", err
}
@@ -43,7 +45,7 @@ type DeleteBranchOptions struct {
// DeleteBranch delete a branch by name on repository.
func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) error {
cmd := NewCommand("branch")
cmd := gitcmd.NewCommand("branch")
if opts.Force {
cmd.AddArguments("-D")
@@ -52,35 +54,35 @@ func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) erro
}
cmd.AddDashesAndList(name)
_, _, err := cmd.RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
_, _, err := cmd.RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
return err
}
// CreateBranch create a new branch
func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error {
cmd := NewCommand("branch")
cmd := gitcmd.NewCommand("branch")
cmd.AddDashesAndList(branch, oldbranchOrCommit)
_, _, err := cmd.RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
_, _, err := cmd.RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
return err
}
// AddRemote adds a new remote to repository.
func (repo *Repository) AddRemote(name, url string, fetch bool) error {
cmd := NewCommand("remote", "add")
cmd := gitcmd.NewCommand("remote", "add")
if fetch {
cmd.AddArguments("-f")
}
cmd.AddDynamicArguments(name, url)
_, _, err := cmd.RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
_, _, err := cmd.RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
return err
}
// RenameBranch rename a branch
func (repo *Repository) RenameBranch(from, to string) error {
_, _, err := NewCommand("branch", "-m").AddDynamicArguments(from, to).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
_, _, err := gitcmd.NewCommand("branch", "-m").AddDynamicArguments(from, to).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
return err
}

View File

@@ -13,6 +13,7 @@ import (
"io"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/log"
)
@@ -70,25 +71,25 @@ func (repo *Repository) IsBranchExist(name string) bool {
// GetBranchNames returns branches from the repository, skipping "skip" initial branches and
// returning at most "limit" branches, or all branches if "limit" is 0.
func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
return callShowRef(repo.Ctx, repo.Path, BranchPrefix, TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}, skip, limit)
return callShowRef(repo.Ctx, repo.Path, BranchPrefix, gitcmd.TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}, skip, limit)
}
// WalkReferences walks all the references from the repository
// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
var args TrustedCmdArgs
var args gitcmd.TrustedCmdArgs
switch refType {
case ObjectTag:
args = TrustedCmdArgs{TagPrefix, "--sort=-taggerdate"}
args = gitcmd.TrustedCmdArgs{TagPrefix, "--sort=-taggerdate"}
case ObjectBranch:
args = TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}
args = gitcmd.TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}
}
return WalkShowRef(repo.Ctx, repo.Path, args, skip, limit, walkfn)
}
// callShowRef return refs, if limit = 0 it will not limit
func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs TrustedCmdArgs, skip, limit int) (branchNames []string, countAll int, err error) {
func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs gitcmd.TrustedCmdArgs, skip, limit int) (branchNames []string, countAll int, err error) {
countAll, err = WalkShowRef(ctx, repoPath, extraArgs, skip, limit, func(_, branchName string) error {
branchName = strings.TrimPrefix(branchName, trimPrefix)
branchNames = append(branchNames, branchName)
@@ -98,7 +99,7 @@ func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs Tru
return branchNames, countAll, err
}
func WalkShowRef(ctx context.Context, repoPath string, extraArgs TrustedCmdArgs, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
func WalkShowRef(ctx context.Context, repoPath string, extraArgs gitcmd.TrustedCmdArgs, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
stdoutReader, stdoutWriter := io.Pipe()
defer func() {
_ = stdoutReader.Close()
@@ -107,9 +108,9 @@ func WalkShowRef(ctx context.Context, repoPath string, extraArgs TrustedCmdArgs,
go func() {
stderrBuilder := &strings.Builder{}
args := TrustedCmdArgs{"for-each-ref", "--format=%(objectname) %(refname)"}
args := gitcmd.TrustedCmdArgs{"for-each-ref", "--format=%(objectname) %(refname)"}
args = append(args, extraArgs...)
err := NewCommand(args...).Run(ctx, &RunOpts{
err := gitcmd.NewCommand(args...).Run(ctx, &gitcmd.RunOpts{
Dir: repoPath,
Stdout: stdoutWriter,
Stderr: stderrBuilder,
@@ -119,7 +120,7 @@ func WalkShowRef(ctx context.Context, repoPath string, extraArgs TrustedCmdArgs,
_ = stdoutWriter.Close()
return
}
_ = stdoutWriter.CloseWithError(ConcatenateError(err, stderrBuilder.String()))
_ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, stderrBuilder.String()))
} else {
_ = stdoutWriter.Close()
}

View File

@@ -12,6 +12,7 @@ import (
"strings"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/setting"
)
@@ -59,7 +60,7 @@ func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Com
relpath = `\` + relpath
}
stdout, _, runErr := NewCommand("log", "-1", prettyLogFormat).AddDynamicArguments(id.String()).AddDashesAndList(relpath).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, runErr := gitcmd.NewCommand("log", "-1", prettyLogFormat).AddDynamicArguments(id.String()).AddDashesAndList(relpath).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if runErr != nil {
return nil, runErr
}
@@ -74,7 +75,7 @@ func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Com
// GetCommitByPath returns the last commit of relative path.
func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
stdout, _, runErr := NewCommand("log", "-1", prettyLogFormat).AddDashesAndList(relpath).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, runErr := gitcmd.NewCommand("log", "-1", prettyLogFormat).AddDashesAndList(relpath).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if runErr != nil {
return nil, runErr
}
@@ -91,7 +92,7 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
// commitsByRangeWithTime returns the specific page commits before current revision, with not, since, until support
func (repo *Repository) commitsByRangeWithTime(id ObjectID, page, pageSize int, not, since, until string) ([]*Commit, error) {
cmd := NewCommand("log").
cmd := gitcmd.NewCommand("log").
AddOptionFormat("--skip=%d", (page-1)*pageSize).
AddOptionFormat("--max-count=%d", pageSize).
AddArguments(prettyLogFormat).
@@ -107,7 +108,7 @@ func (repo *Repository) commitsByRangeWithTime(id ObjectID, page, pageSize int,
cmd.AddOptionFormat("--until=%s", until)
}
stdout, _, err := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
@@ -117,7 +118,7 @@ func (repo *Repository) commitsByRangeWithTime(id ObjectID, page, pageSize int,
func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([]*Commit, error) {
// add common arguments to git command
addCommonSearchArgs := func(c *Command) {
addCommonSearchArgs := func(c *gitcmd.Command) {
// ignore case
c.AddArguments("-i")
@@ -141,7 +142,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([
}
// create new git log command with limit of 100 commits
cmd := NewCommand("log", "-100", prettyLogFormat).AddDynamicArguments(id.String())
cmd := gitcmd.NewCommand("log", "-100", prettyLogFormat).AddDynamicArguments(id.String())
// pretend that all refs along with HEAD were listed on command line as <commis>
// https://git-scm.com/docs/git-log#Documentation/git-log.txt---all
@@ -161,7 +162,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([
// search for commits matching given constraints and keywords in commit msg
addCommonSearchArgs(cmd)
stdout, _, err := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
@@ -175,14 +176,14 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([
// ignore anything not matching a valid sha pattern
if id.Type().IsValid(v) {
// create new git log command with 1 commit limit
hashCmd := NewCommand("log", "-1", prettyLogFormat)
hashCmd := gitcmd.NewCommand("log", "-1", prettyLogFormat)
// add previous arguments except for --grep and --all
addCommonSearchArgs(hashCmd)
// add keyword as <commit>
hashCmd.AddDynamicArguments(v)
// search with given constraints for commit matching sha hash of v
hashMatching, _, err := hashCmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
hashMatching, _, err := hashCmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil || bytes.Contains(stdout, hashMatching) {
continue
}
@@ -197,7 +198,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([
// FileChangedBetweenCommits Returns true if the file changed between commit IDs id1 and id2
// You must ensure that id1 and id2 are valid commit ids.
func (repo *Repository) FileChangedBetweenCommits(filename, id1, id2 string) (bool, error) {
stdout, _, err := NewCommand("diff", "--name-only", "-z").AddDynamicArguments(id1, id2).AddDashesAndList(filename).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err := gitcmd.NewCommand("diff", "--name-only", "-z").AddDynamicArguments(id1, id2).AddDashesAndList(filename).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
return false, err
}
@@ -232,7 +233,7 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
}()
go func() {
stderr := strings.Builder{}
gitCmd := NewCommand("rev-list").
gitCmd := gitcmd.NewCommand("rev-list").
AddOptionFormat("--max-count=%d", setting.Git.CommitsRangeSize).
AddOptionFormat("--skip=%d", (opts.Page-1)*setting.Git.CommitsRangeSize)
gitCmd.AddDynamicArguments(opts.Revision)
@@ -248,13 +249,13 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
}
gitCmd.AddDashesAndList(opts.File)
err := gitCmd.Run(repo.Ctx, &RunOpts{
err := gitCmd.Run(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdout: stdoutWriter,
Stderr: &stderr,
})
if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
_ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String()))
} else {
_ = stdoutWriter.Close()
}
@@ -290,11 +291,11 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
// FilesCountBetween return the number of files changed between two commits
func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) {
stdout, _, err := NewCommand("diff", "--name-only").AddDynamicArguments(startCommitID+"..."+endCommitID).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err := gitcmd.NewCommand("diff", "--name-only").AddDynamicArguments(startCommitID+"..."+endCommitID).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil && strings.Contains(err.Error(), "no merge base") {
// 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...
stdout, _, err = NewCommand("diff", "--name-only").AddDynamicArguments(startCommitID, endCommitID).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err = gitcmd.NewCommand("diff", "--name-only").AddDynamicArguments(startCommitID, endCommitID).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
}
if err != nil {
return 0, err
@@ -308,13 +309,13 @@ func (repo *Repository) CommitsBetween(last, before *Commit) ([]*Commit, error)
var stdout []byte
var err error
if before == nil {
stdout, _, err = NewCommand("rev-list").AddDynamicArguments(last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
} else {
stdout, _, err = NewCommand("rev-list").AddDynamicArguments(before.ID.String()+".."+last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(before.ID.String()+".."+last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
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.
// previously it would return the results of git rev-list before last so let's try that...
stdout, _, err = NewCommand("rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
}
}
if err != nil {
@@ -328,22 +329,22 @@ func (repo *Repository) CommitsBetweenLimit(last, before *Commit, limit, skip in
var stdout []byte
var err error
if before == nil {
stdout, _, err = NewCommand("rev-list").
stdout, _, err = gitcmd.NewCommand("rev-list").
AddOptionValues("--max-count", strconv.Itoa(limit)).
AddOptionValues("--skip", strconv.Itoa(skip)).
AddDynamicArguments(last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
AddDynamicArguments(last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
} else {
stdout, _, err = NewCommand("rev-list").
stdout, _, err = gitcmd.NewCommand("rev-list").
AddOptionValues("--max-count", strconv.Itoa(limit)).
AddOptionValues("--skip", strconv.Itoa(skip)).
AddDynamicArguments(before.ID.String()+".."+last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
AddDynamicArguments(before.ID.String()+".."+last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
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.
// previously it would return the results of git rev-list --max-count n before last so let's try that...
stdout, _, err = NewCommand("rev-list").
stdout, _, err = gitcmd.NewCommand("rev-list").
AddOptionValues("--max-count", strconv.Itoa(limit)).
AddOptionValues("--skip", strconv.Itoa(skip)).
AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
}
}
if err != nil {
@@ -358,13 +359,13 @@ func (repo *Repository) CommitsBetweenNotBase(last, before *Commit, baseBranch s
var stdout []byte
var err error
if before == nil {
stdout, _, err = NewCommand("rev-list").AddDynamicArguments(last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
} else {
stdout, _, err = NewCommand("rev-list").AddDynamicArguments(before.ID.String()+".."+last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(before.ID.String()+".."+last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
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.
// previously it would return the results of git rev-list before last so let's try that...
stdout, _, err = NewCommand("rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
}
}
if err != nil {
@@ -410,13 +411,13 @@ func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
// commitsBefore the limit is depth, not total number of returned commits.
func (repo *Repository) commitsBefore(id ObjectID, limit int) ([]*Commit, error) {
cmd := NewCommand("log", prettyLogFormat)
cmd := gitcmd.NewCommand("log", prettyLogFormat)
if limit > 0 {
cmd.AddOptionFormat("-%d", limit)
}
cmd.AddDynamicArguments(id.String())
stdout, _, runErr := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, runErr := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if runErr != nil {
return nil, runErr
}
@@ -453,10 +454,10 @@ func (repo *Repository) getCommitsBeforeLimit(id ObjectID, num int) ([]*Commit,
func (repo *Repository) getBranches(env []string, commitID string, limit int) ([]string, error) {
if DefaultFeatures().CheckVersionAtLeast("2.7.0") {
stdout, _, err := NewCommand("for-each-ref", "--format=%(refname:strip=2)").
stdout, _, err := gitcmd.NewCommand("for-each-ref", "--format=%(refname:strip=2)").
AddOptionFormat("--count=%d", limit).
AddOptionValues("--contains", commitID, BranchPrefix).
RunStdString(repo.Ctx, &RunOpts{
RunStdString(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Env: env,
})
@@ -468,7 +469,7 @@ func (repo *Repository) getBranches(env []string, commitID string, limit int) ([
return branches, nil
}
stdout, _, err := NewCommand("branch").AddOptionValues("--contains", commitID).RunStdString(repo.Ctx, &RunOpts{
stdout, _, err := gitcmd.NewCommand("branch").AddOptionValues("--contains", commitID).RunStdString(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Env: env,
})
@@ -510,7 +511,7 @@ func (repo *Repository) GetCommitsFromIDs(commitIDs []string) []*Commit {
// IsCommitInBranch check if the commit is on the branch
func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err error) {
stdout, _, err := NewCommand("branch", "--contains").AddDynamicArguments(commitID, branch).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err := gitcmd.NewCommand("branch", "--contains").AddDynamicArguments(commitID, branch).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
return false, err
}
@@ -536,10 +537,10 @@ func (repo *Repository) AddLastCommitCache(cacheKey, fullName, sha string) error
// GetCommitBranchStart returns the commit where the branch diverged
func (repo *Repository) GetCommitBranchStart(env []string, branch, endCommitID string) (string, error) {
cmd := NewCommand("log", prettyLogFormat)
cmd := gitcmd.NewCommand("log", prettyLogFormat)
cmd.AddDynamicArguments(endCommitID)
stdout, _, runErr := cmd.RunStdBytes(repo.Ctx, &RunOpts{
stdout, _, runErr := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Env: env,
})

View File

@@ -9,6 +9,8 @@ package git
import (
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/hash"
"github.com/go-git/go-git/v5/plumbing/object"
@@ -59,7 +61,7 @@ func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) {
}
}
actualCommitID, _, err := NewCommand("rev-parse", "--verify").AddDynamicArguments(commitID).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
actualCommitID, _, err := gitcmd.NewCommand("rev-parse", "--verify").AddDynamicArguments(commitID).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
actualCommitID = strings.TrimSpace(actualCommitID)
if err != nil {
if strings.Contains(err.Error(), "unknown revision or path") ||

View File

@@ -11,12 +11,13 @@ import (
"io"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/log"
)
// ResolveReference resolves a name to a reference
func (repo *Repository) ResolveReference(name string) (string, error) {
stdout, _, err := NewCommand("show-ref", "--hash").AddDynamicArguments(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err := gitcmd.NewCommand("show-ref", "--hash").AddDynamicArguments(name).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
if strings.Contains(err.Error(), "not a valid ref") {
return "", ErrNotExist{name, ""}
@@ -52,13 +53,13 @@ func (repo *Repository) GetRefCommitID(name string) (string, error) {
// SetReference sets the commit ID string of given reference (e.g. branch or tag).
func (repo *Repository) SetReference(name, commitID string) error {
_, _, err := NewCommand("update-ref").AddDynamicArguments(name, commitID).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
_, _, err := gitcmd.NewCommand("update-ref").AddDynamicArguments(name, commitID).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
return err
}
// RemoveReference removes the given reference (e.g. branch or tag).
func (repo *Repository) RemoveReference(name string) error {
_, _, err := NewCommand("update-ref", "--no-deref", "-d").AddDynamicArguments(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
_, _, err := gitcmd.NewCommand("update-ref", "--no-deref", "-d").AddDynamicArguments(name).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
return err
}
@@ -68,7 +69,7 @@ func (repo *Repository) IsCommitExist(name string) bool {
log.Error("IsCommitExist: %v", err)
return false
}
_, _, err := NewCommand("cat-file", "-e").AddDynamicArguments(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
_, _, err := gitcmd.NewCommand("cat-file", "-e").AddDynamicArguments(name).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
return err == nil
}

View File

@@ -6,13 +6,15 @@ package git
import (
"context"
"fmt"
"code.gitea.io/gitea/modules/git/gitcmd"
)
// WriteCommitGraph write commit graph to speed up repo access
// this requires git v2.18 to be installed
func WriteCommitGraph(ctx context.Context, repoPath string) error {
if DefaultFeatures().CheckVersionAtLeast("2.18") {
if _, _, err := NewCommand("commit-graph", "write").RunStdString(ctx, &RunOpts{Dir: repoPath}); err != nil {
if _, _, err := gitcmd.NewCommand("commit-graph", "write").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}); err != nil {
return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err)
}
}

View File

@@ -16,6 +16,8 @@ import (
"regexp"
"strconv"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
)
// GetMergeBase checks and returns merge base of two branches and the reference used as base.
@@ -27,13 +29,13 @@ func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, stri
if tmpRemote != "origin" {
tmpBaseName := RemotePrefix + tmpRemote + "/tmp_" + base
// Fetch commit into a temporary branch in order to be able to handle commits and tags
_, _, err := NewCommand("fetch", "--no-tags").AddDynamicArguments(tmpRemote).AddDashesAndList(base+":"+tmpBaseName).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
_, _, err := gitcmd.NewCommand("fetch", "--no-tags").AddDynamicArguments(tmpRemote).AddDashesAndList(base+":"+tmpBaseName).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err == nil {
base = tmpBaseName
}
}
stdout, _, err := NewCommand("merge-base").AddDashesAndList(base, head).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err := gitcmd.NewCommand("merge-base").AddDashesAndList(base, head).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
return strings.TrimSpace(stdout), base, err
}
@@ -61,8 +63,8 @@ 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>...]'
if err := NewCommand("diff", "-z", "--name-only").AddDynamicArguments(base+separator+head).AddArguments("--").
Run(repo.Ctx, &RunOpts{
if err := gitcmd.NewCommand("diff", "-z", "--name-only").AddDynamicArguments(base+separator+head).AddArguments("--").
Run(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdout: w,
Stderr: stderr,
@@ -72,7 +74,7 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
// previously it would return the results of git diff -z --name-only base head so let's try that...
w = &lineCountWriter{}
stderr.Reset()
if err = NewCommand("diff", "-z", "--name-only").AddDynamicArguments(base, head).AddArguments("--").Run(repo.Ctx, &RunOpts{
if err = gitcmd.NewCommand("diff", "-z", "--name-only").AddDynamicArguments(base, head).AddArguments("--").Run(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdout: w,
Stderr: stderr,
@@ -87,13 +89,13 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
// GetDiffShortStatByCmdArgs counts number of changed files, number of additions and deletions
// TODO: it can be merged with another "GetDiffShortStat" in the future
func GetDiffShortStatByCmdArgs(ctx context.Context, repoPath string, trustedArgs TrustedCmdArgs, dynamicArgs ...string) (numFiles, totalAdditions, totalDeletions int, err error) {
func GetDiffShortStatByCmdArgs(ctx context.Context, repoPath string, trustedArgs gitcmd.TrustedCmdArgs, dynamicArgs ...string) (numFiles, totalAdditions, totalDeletions int, err error) {
// Now if we call:
// $ git diff --shortstat 1ebb35b98889ff77299f24d82da426b434b0cca0...788b8b1440462d477f45b0088875
// we get:
// " 9902 files changed, 2034198 insertions(+), 298800 deletions(-)\n"
cmd := NewCommand("diff", "--shortstat").AddArguments(trustedArgs...).AddDynamicArguments(dynamicArgs...)
stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath})
cmd := gitcmd.NewCommand("diff", "--shortstat").AddArguments(trustedArgs...).AddDynamicArguments(dynamicArgs...)
stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
if err != nil {
return 0, 0, 0, err
}
@@ -139,8 +141,8 @@ func parseDiffStat(stdout string) (numFiles, totalAdditions, totalDeletions int,
// GetDiff generates and returns patch data between given revisions, optimized for human readability
func (repo *Repository) GetDiff(compareArg string, w io.Writer) error {
stderr := new(bytes.Buffer)
return NewCommand("diff", "-p").AddDynamicArguments(compareArg).
Run(repo.Ctx, &RunOpts{
return gitcmd.NewCommand("diff", "-p").AddDynamicArguments(compareArg).
Run(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdout: w,
Stderr: stderr,
@@ -149,7 +151,7 @@ func (repo *Repository) GetDiff(compareArg string, w io.Writer) error {
// GetDiffBinary generates and returns patch data between given revisions, including binary diffs.
func (repo *Repository) GetDiffBinary(compareArg string, w io.Writer) error {
return NewCommand("diff", "-p", "--binary", "--histogram").AddDynamicArguments(compareArg).Run(repo.Ctx, &RunOpts{
return gitcmd.NewCommand("diff", "-p", "--binary", "--histogram").AddDynamicArguments(compareArg).Run(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdout: w,
})
@@ -158,8 +160,8 @@ func (repo *Repository) GetDiffBinary(compareArg string, w io.Writer) error {
// GetPatch generates and returns format-patch data between given revisions, able to be used with `git apply`
func (repo *Repository) GetPatch(compareArg string, w io.Writer) error {
stderr := new(bytes.Buffer)
return NewCommand("format-patch", "--binary", "--stdout").AddDynamicArguments(compareArg).
Run(repo.Ctx, &RunOpts{
return gitcmd.NewCommand("format-patch", "--binary", "--stdout").AddDynamicArguments(compareArg).
Run(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdout: w,
Stderr: stderr,
@@ -174,13 +176,13 @@ func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, err
if err != nil {
return nil, err
}
cmd := NewCommand("diff-tree", "--name-only", "--root", "--no-commit-id", "-r", "-z")
cmd := gitcmd.NewCommand("diff-tree", "--name-only", "--root", "--no-commit-id", "-r", "-z")
if base == objectFormat.EmptyObjectID().String() {
cmd.AddDynamicArguments(head)
} else {
cmd.AddDynamicArguments(base, head)
}
stdout, _, err := cmd.RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err := cmd.RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}

View File

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

View File

@@ -10,6 +10,7 @@ import (
"path/filepath"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/setting"
)
@@ -21,7 +22,7 @@ func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string)
}
if len(treeish) != objectFormat.FullLength() {
res, _, err := NewCommand("rev-parse", "--verify").AddDynamicArguments(treeish).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
res, _, err := gitcmd.NewCommand("rev-parse", "--verify").AddDynamicArguments(treeish).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
return err
}
@@ -41,7 +42,7 @@ func (repo *Repository) readTreeToIndex(id ObjectID, indexFilename ...string) er
if len(indexFilename) > 0 {
env = append(os.Environ(), "GIT_INDEX_FILE="+indexFilename[0])
}
_, _, err := NewCommand("read-tree").AddDynamicArguments(id.String()).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path, Env: env})
_, _, err := gitcmd.NewCommand("read-tree").AddDynamicArguments(id.String()).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path, Env: env})
if err != nil {
return err
}
@@ -74,14 +75,14 @@ func (repo *Repository) ReadTreeToTemporaryIndex(treeish string) (tmpIndexFilena
// EmptyIndex empties the index
func (repo *Repository) EmptyIndex() error {
_, _, err := NewCommand("read-tree", "--empty").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
_, _, err := gitcmd.NewCommand("read-tree", "--empty").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
return err
}
// LsFiles checks if the given filenames are in the index
func (repo *Repository) LsFiles(filenames ...string) ([]string, error) {
cmd := NewCommand("ls-files", "-z").AddDashesAndList(filenames...)
res, _, err := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
cmd := gitcmd.NewCommand("ls-files", "-z").AddDashesAndList(filenames...)
res, _, err := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
@@ -99,7 +100,7 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
if err != nil {
return err
}
cmd := NewCommand("update-index", "--remove", "-z", "--index-info")
cmd := gitcmd.NewCommand("update-index", "--remove", "-z", "--index-info")
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
buffer := new(bytes.Buffer)
@@ -109,7 +110,7 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
buffer.WriteString("0 blob " + objectFormat.EmptyObjectID().String() + "\t" + file + "\000")
}
}
return cmd.Run(repo.Ctx, &RunOpts{
return cmd.Run(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdin: bytes.NewReader(buffer.Bytes()),
Stdout: stdout,
@@ -125,7 +126,7 @@ type IndexObjectInfo struct {
// AddObjectsToIndex adds the provided object hashes to the index at the provided filenames
func (repo *Repository) AddObjectsToIndex(objects ...IndexObjectInfo) error {
cmd := NewCommand("update-index", "--add", "--replace", "-z", "--index-info")
cmd := gitcmd.NewCommand("update-index", "--add", "--replace", "-z", "--index-info")
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
buffer := new(bytes.Buffer)
@@ -133,7 +134,7 @@ func (repo *Repository) AddObjectsToIndex(objects ...IndexObjectInfo) error {
// using format: mode SP type SP sha1 TAB path
buffer.WriteString(object.Mode + " blob " + object.Object.String() + "\t" + object.Filename + "\000")
}
return cmd.Run(repo.Ctx, &RunOpts{
return cmd.Run(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdin: bytes.NewReader(buffer.Bytes()),
Stdout: stdout,
@@ -148,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
func (repo *Repository) WriteTree() (*Tree, error) {
stdout, _, runErr := NewCommand("write-tree").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, runErr := gitcmd.NewCommand("write-tree").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if runErr != nil {
return nil, runErr
}

View File

@@ -8,6 +8,8 @@ import (
"bytes"
"io"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
)
// ObjectType git object type
@@ -66,15 +68,15 @@ func (repo *Repository) HashObject(reader io.Reader) (ObjectID, error) {
}
func (repo *Repository) hashObject(reader io.Reader, save bool) (string, error) {
var cmd *Command
var cmd *gitcmd.Command
if save {
cmd = NewCommand("hash-object", "-w", "--stdin")
cmd = gitcmd.NewCommand("hash-object", "-w", "--stdin")
} else {
cmd = NewCommand("hash-object", "--stdin")
cmd = gitcmd.NewCommand("hash-object", "--stdin")
}
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
err := cmd.Run(repo.Ctx, &RunOpts{
err := cmd.Run(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdin: reader,
Stdout: stdout,

View File

@@ -7,6 +7,7 @@ import (
"context"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/util"
)
@@ -18,7 +19,7 @@ func (repo *Repository) GetRefs() ([]*Reference, error) {
// ListOccurrences lists all refs of the given refType the given commit appears in sorted by creation date DESC
// refType should only be a literal "branch" or "tag" and nothing else
func (repo *Repository) ListOccurrences(ctx context.Context, refType, commitSHA string) ([]string, error) {
cmd := NewCommand()
cmd := gitcmd.NewCommand()
switch refType {
case "branch":
cmd.AddArguments("branch")
@@ -27,7 +28,7 @@ func (repo *Repository) ListOccurrences(ctx context.Context, refType, commitSHA
default:
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, &RunOpts{Dir: repo.Path})
stdout, _, err := cmd.AddArguments("--no-color", "--sort=-creatordate", "--contains").AddDynamicArguments(commitSHA).RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}

View File

@@ -9,6 +9,8 @@ import (
"bufio"
"io"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
)
// GetRefsFiltered returns all references of the repository that matches patterm exactly or starting with.
@@ -21,13 +23,13 @@ func (repo *Repository) GetRefsFiltered(pattern string) ([]*Reference, error) {
go func() {
stderrBuilder := &strings.Builder{}
err := NewCommand("for-each-ref").Run(repo.Ctx, &RunOpts{
err := gitcmd.NewCommand("for-each-ref").Run(repo.Ctx, &gitcmd.RunOpts{
Dir: repo.Path,
Stdout: stdoutWriter,
Stderr: stderrBuilder,
})
if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, stderrBuilder.String()))
_ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, stderrBuilder.String()))
} else {
_ = stdoutWriter.Close()
}

View File

@@ -14,6 +14,7 @@ import (
"time"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git/gitcmd"
)
// CodeActivityStats represents git statistics data
@@ -40,9 +41,9 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
since := fromTime.Format(time.RFC3339)
stdout, _, runErr := 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).
RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if runErr != nil {
return nil, runErr
}
@@ -62,7 +63,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
_ = stdoutWriter.Close()
}()
gitCmd := NewCommand("log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso").
gitCmd := gitcmd.NewCommand("log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso").
AddOptionFormat("--since=%s", since)
if len(branch) == 0 {
gitCmd.AddArguments("--branches=*")
@@ -71,7 +72,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
}
stderr := new(strings.Builder)
err = gitCmd.Run(repo.Ctx, &RunOpts{
err = gitCmd.Run(repo.Ctx, &gitcmd.RunOpts{
Env: []string{},
Dir: repo.Path,
Stdout: stdoutWriter,

View File

@@ -10,6 +10,7 @@ import (
"strings"
"code.gitea.io/gitea/modules/git/foreachref"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/util"
)
@@ -18,13 +19,13 @@ const TagPrefix = "refs/tags/"
// CreateTag create one tag in the repository
func (repo *Repository) CreateTag(name, revision string) error {
_, _, err := NewCommand("tag").AddDashesAndList(name, revision).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
_, _, err := gitcmd.NewCommand("tag").AddDashesAndList(name, revision).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
return err
}
// CreateAnnotatedTag create one annotated tag in the repository
func (repo *Repository) CreateAnnotatedTag(name, message, revision string) error {
_, _, err := NewCommand("tag", "-a", "-m").AddDynamicArguments(message).AddDashesAndList(name, revision).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
_, _, err := gitcmd.NewCommand("tag", "-a", "-m").AddDynamicArguments(message).AddDashesAndList(name, revision).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
return err
}
@@ -34,7 +35,7 @@ func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
return "", fmt.Errorf("SHA is too short: %s", sha)
}
stdout, _, err := NewCommand("show-ref", "--tags", "-d").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err := gitcmd.NewCommand("show-ref", "--tags", "-d").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
return "", err
}
@@ -57,7 +58,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)
func (repo *Repository) GetTagID(name string) (string, error) {
stdout, _, err := NewCommand("show-ref", "--tags").AddDashesAndList(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
stdout, _, err := gitcmd.NewCommand("show-ref", "--tags").AddDashesAndList(name).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
return "", err
}
@@ -114,14 +115,14 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
defer stdoutReader.Close()
defer stdoutWriter.Close()
stderr := strings.Builder{}
rc := &RunOpts{Dir: repo.Path, Stdout: stdoutWriter, Stderr: &stderr}
rc := &gitcmd.RunOpts{Dir: repo.Path, Stdout: stdoutWriter, Stderr: &stderr}
go func() {
err := NewCommand("for-each-ref").
err := gitcmd.NewCommand("for-each-ref").
AddOptionFormat("--format=%s", forEachRefFmt.Flag()).
AddArguments("--sort", "-*creatordate", "refs/tags").Run(repo.Ctx, rc)
if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, stderr.String()))
_ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, stderr.String()))
} else {
_ = stdoutWriter.Close()
}

View File

@@ -9,6 +9,8 @@ import (
"os"
"strings"
"time"
"code.gitea.io/gitea/modules/git/gitcmd"
)
// CommitTreeOpts represents the possible options to CommitTree
@@ -33,7 +35,7 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
"GIT_COMMITTER_EMAIL="+committer.Email,
"GIT_COMMITTER_DATE="+commitTimeStr,
)
cmd := NewCommand("commit-tree").AddDynamicArguments(tree.ID.String())
cmd := gitcmd.NewCommand("commit-tree").AddDynamicArguments(tree.ID.String())
for _, parent := range opts.Parents {
cmd.AddArguments("-p").AddDynamicArguments(parent)
@@ -58,7 +60,7 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
err := cmd.Run(repo.Ctx, &RunOpts{
err := cmd.Run(repo.Ctx, &gitcmd.RunOpts{
Env: env,
Dir: repo.Path,
Stdin: messageBytes,
@@ -66,7 +68,7 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
Stderr: stderr,
})
if err != nil {
return nil, ConcatenateError(err, stderr.String())
return nil, gitcmd.ConcatenateError(err, stderr.String())
}
return NewIDFromString(strings.TrimSpace(stdout.String()))
}

View File

@@ -9,6 +9,8 @@ package git
import (
"errors"
"code.gitea.io/gitea/modules/git/gitcmd"
"github.com/go-git/go-git/v5/plumbing"
)
@@ -36,7 +38,7 @@ func (repo *Repository) GetTree(idStr string) (*Tree, error) {
}
if len(idStr) != objectFormat.FullLength() {
res, _, err := NewCommand("rev-parse", "--verify").AddDynamicArguments(idStr).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
res, _, err := gitcmd.NewCommand("rev-parse", "--verify").AddDynamicArguments(idStr).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}

View File

@@ -9,6 +9,7 @@ import (
"fmt"
"os"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/log"
)
@@ -24,7 +25,7 @@ func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submodul
if err != nil {
return nil, err
}
opts := &RunOpts{
opts := &gitcmd.RunOpts{
Dir: repoPath,
Stdout: stdoutWriter,
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
@@ -45,7 +46,7 @@ func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submodul
return scanner.Err()
},
}
err = NewCommand("ls-tree", "-r", "--", "HEAD").Run(ctx, opts)
err = gitcmd.NewCommand("ls-tree", "-r", "--", "HEAD").Run(ctx, opts)
if err != nil {
return nil, fmt.Errorf("GetTemplateSubmoduleCommits: error running git ls-tree: %v", err)
}
@@ -56,8 +57,8 @@ func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submodul
// It is only for generating new repos based on existing template, requires the .gitmodules file to be already present in the work dir.
func AddTemplateSubmoduleIndexes(ctx context.Context, repoPath string, submodules []TemplateSubmoduleCommit) error {
for _, submodule := range submodules {
cmd := NewCommand("update-index", "--add", "--cacheinfo", "160000").AddDynamicArguments(submodule.Commit, submodule.Path)
if stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath}); err != nil {
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 {
log.Error("Unable to add %s as submodule to repo %s: stdout %s\nError: %v", submodule.Path, repoPath, stdout, err)
return err
}

View File

@@ -8,6 +8,8 @@ import (
"path/filepath"
"testing"
"code.gitea.io/gitea/modules/git/gitcmd"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -30,14 +32,14 @@ func TestAddTemplateSubmoduleIndexes(t *testing.T) {
ctx := t.Context()
tmpDir := t.TempDir()
var err error
_, _, err = NewCommand("init").RunStdString(ctx, &RunOpts{Dir: tmpDir})
_, _, err = gitcmd.NewCommand("init").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpDir})
require.NoError(t, err)
_ = os.Mkdir(filepath.Join(tmpDir, "new-dir"), 0o755)
err = AddTemplateSubmoduleIndexes(ctx, tmpDir, []TemplateSubmoduleCommit{{Path: "new-dir", Commit: "1234567890123456789012345678901234567890"}})
require.NoError(t, err)
_, _, err = NewCommand("add", "--all").RunStdString(ctx, &RunOpts{Dir: tmpDir})
_, _, err = gitcmd.NewCommand("add", "--all").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpDir})
require.NoError(t, err)
_, _, err = NewCommand("-c", "user.name=a", "-c", "user.email=b", "commit", "-m=test").RunStdString(ctx, &RunOpts{Dir: tmpDir})
_, _, err = gitcmd.NewCommand("-c", "user.name=a", "-c", "user.email=b", "commit", "-m=test").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpDir})
require.NoError(t, err)
submodules, err := GetTemplateSubmoduleCommits(t.Context(), tmpDir)
require.NoError(t, err)

View File

@@ -7,6 +7,8 @@ package git
import (
"bytes"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
)
// NewTree create a new tree according the repository and tree id
@@ -48,10 +50,10 @@ func (t *Tree) SubTree(rpath string) (*Tree, error) {
// LsTree checks if the given filenames are in the tree
func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error) {
cmd := NewCommand("ls-tree", "-z", "--name-only").
cmd := gitcmd.NewCommand("ls-tree", "-z", "--name-only").
AddDashesAndList(append([]string{ref}, filenames...)...)
res, _, err := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path})
res, _, err := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
@@ -65,9 +67,9 @@ func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error
// GetTreePathLatestCommit returns the latest commit of a tree path
func (repo *Repository) GetTreePathLatestCommit(refName, treePath string) (*Commit, error) {
stdout, _, err := NewCommand("rev-list", "-1").
stdout, _, err := gitcmd.NewCommand("rev-list", "-1").
AddDynamicArguments(refName).AddDashesAndList(treePath).
RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path})
RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}

View File

@@ -8,6 +8,8 @@ package git
import (
"io"
"strings"
"code.gitea.io/gitea/modules/git/gitcmd"
)
// Tree represents a flat directory listing.
@@ -70,7 +72,7 @@ func (t *Tree) ListEntries() (Entries, error) {
}
}
stdout, _, runErr := NewCommand("ls-tree", "-l").AddDynamicArguments(t.ID.String()).RunStdBytes(t.repo.Ctx, &RunOpts{Dir: t.repo.Path})
stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-l").AddDynamicArguments(t.ID.String()).RunStdBytes(t.repo.Ctx, &gitcmd.RunOpts{Dir: t.repo.Path})
if runErr != nil {
if strings.Contains(runErr.Error(), "fatal: Not a valid object name") || strings.Contains(runErr.Error(), "fatal: not a tree object") {
return nil, ErrNotExist{
@@ -91,15 +93,15 @@ func (t *Tree) ListEntries() (Entries, error) {
// listEntriesRecursive returns all entries of current tree recursively including all subtrees
// extraArgs could be "-l" to get the size, which is slower
func (t *Tree) listEntriesRecursive(extraArgs TrustedCmdArgs) (Entries, error) {
func (t *Tree) listEntriesRecursive(extraArgs gitcmd.TrustedCmdArgs) (Entries, error) {
if t.entriesRecursiveParsed {
return t.entriesRecursive, nil
}
stdout, _, runErr := NewCommand("ls-tree", "-t", "-r").
stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-t", "-r").
AddArguments(extraArgs...).
AddDynamicArguments(t.ID.String()).
RunStdBytes(t.repo.Ctx, &RunOpts{Dir: t.repo.Path})
RunStdBytes(t.repo.Ctx, &gitcmd.RunOpts{Dir: t.repo.Path})
if runErr != nil {
return nil, runErr
}
@@ -120,5 +122,5 @@ func (t *Tree) ListEntriesRecursiveFast() (Entries, error) {
// ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees, with size
func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) {
return t.listEntriesRecursive(TrustedCmdArgs{"--long"})
return t.listEntriesRecursive(gitcmd.TrustedCmdArgs{"--long"})
}

View File

@@ -6,7 +6,6 @@ package git
import (
"crypto/sha1"
"encoding/hex"
"fmt"
"io"
"strconv"
"strings"
@@ -42,14 +41,6 @@ func (oc *ObjectCache[T]) Get(id string) (T, bool) {
return obj, has
}
// ConcatenateError concatenats an error with stderr string
func ConcatenateError(err error, stderr string) error {
if len(stderr) == 0 {
return err
}
return fmt.Errorf("%w - %s", err, stderr)
}
// ParseBool returns the boolean value represented by the string as per git's git_config_bool
// true will be returned for the result if the string is empty, but valid will be false.
// "true", "yes", "on" are all true, true