Refactor git command arguments and make all arguments to be safe to be used (#21535)

Follow #21464

Make all git command arguments strictly safe. Most changes are one-to-one replacing, keep all existing logic.
This commit is contained in:
2022-10-23 22:44:45 +08:00
committed by GitHub
parent 4eeea7b30e
commit dcd9fc7ee8
71 changed files with 425 additions and 391 deletions

View File

@ -83,17 +83,17 @@ func fixMergeBase(x *xorm.Engine) error {
if !pr.HasMerged { if !pr.HasMerged {
var err error var err error
pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, "merge-base", "--", pr.BaseBranch, gitRefName).RunStdString(&git.RunOpts{Dir: repoPath}) pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).RunStdString(&git.RunOpts{Dir: repoPath})
if err != nil { if err != nil {
var err2 error var err2 error
pr.MergeBase, _, err2 = git.NewCommand(git.DefaultContext, "rev-parse", git.BranchPrefix+pr.BaseBranch).RunStdString(&git.RunOpts{Dir: repoPath}) pr.MergeBase, _, err2 = git.NewCommand(git.DefaultContext, "rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).RunStdString(&git.RunOpts{Dir: repoPath})
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 := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1", pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath}) parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
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
@ -103,10 +103,11 @@ func fixMergeBase(x *xorm.Engine) error {
continue continue
} }
args := append([]string{"merge-base", "--"}, parents[1:]...) refs := append([]string{}, parents[1:]...)
args = append(args, gitRefName) refs = append(refs, gitRefName)
cmd := git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(refs...)
pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, args...).RunStdString(&git.RunOpts{Dir: repoPath}) pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
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(x *xorm.Engine) error {
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index) gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1", pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath}) parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
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
@ -91,10 +91,11 @@ func refixMergeBase(x *xorm.Engine) error {
} }
// we should recalculate // we should recalculate
args := append([]string{"merge-base", "--"}, parents[1:]...) refs := append([]string{}, parents[1:]...)
args = append(args, gitRefName) refs = append(refs, gitRefName)
cmd := git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(refs...)
pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, args...).RunStdString(&git.RunOpts{Dir: repoPath}) pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
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

@ -21,7 +21,7 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool)
numRepos++ numRepos++
runOpts := &git.RunOpts{Dir: repo.RepoPath()} runOpts := &git.RunOpts{Dir: repo.RepoPath()}
_, _, defaultBranchErr := git.NewCommand(ctx, "rev-parse", "--", repo.DefaultBranch).RunStdString(runOpts) _, _, defaultBranchErr := git.NewCommand(ctx, "rev-parse").AddDashesAndList(repo.DefaultBranch).RunStdString(runOpts)
head, _, headErr := git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD").RunStdString(runOpts) head, _, headErr := git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD").RunStdString(runOpts)
@ -49,7 +49,7 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool)
} }
// otherwise, let's try fixing HEAD // otherwise, let's try fixing HEAD
err := git.NewCommand(ctx, "symbolic-ref", "--", "HEAD", repo.DefaultBranch).Run(runOpts) err := git.NewCommand(ctx, "symbolic-ref").AddDashesAndList("HEAD", repo.DefaultBranch).Run(runOpts)
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

@ -44,17 +44,17 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro
if !pr.HasMerged { if !pr.HasMerged {
var err error var err error
pr.MergeBase, _, err = git.NewCommand(ctx, "merge-base", "--", pr.BaseBranch, pr.GetGitRefName()).RunStdString(&git.RunOpts{Dir: repoPath}) pr.MergeBase, _, err = git.NewCommand(ctx, "merge-base").AddDashesAndList(pr.BaseBranch, pr.GetGitRefName()).RunStdString(&git.RunOpts{Dir: repoPath})
if err != nil { if err != nil {
var err2 error var err2 error
pr.MergeBase, _, err2 = git.NewCommand(ctx, "rev-parse", git.BranchPrefix+pr.BaseBranch).RunStdString(&git.RunOpts{Dir: repoPath}) pr.MergeBase, _, err2 = git.NewCommand(ctx, "rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).RunStdString(&git.RunOpts{Dir: repoPath})
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 := git.NewCommand(ctx, "rev-list", "--parents", "-n", "1", pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath}) parentsString, _, err := git.NewCommand(ctx, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
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
@ -64,10 +64,10 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro
return nil return nil
} }
args := append([]string{"merge-base", "--"}, parents[1:]...) refs := append([]string{}, parents[1:]...)
args = append(args, pr.GetGitRefName()) refs = append(refs, pr.GetGitRefName())
cmd := git.NewCommand(ctx, "merge-base").AddDashesAndList(refs...)
pr.MergeBase, _, err = git.NewCommand(ctx, args...).RunStdString(&git.RunOpts{Dir: repoPath}) pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
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

@ -24,7 +24,7 @@ import (
var ( var (
// globalCommandArgs global command args for external package setting // globalCommandArgs global command args for external package setting
globalCommandArgs []string globalCommandArgs []CmdArg
// defaultCommandExecutionTimeout default command execution timeout duration // defaultCommandExecutionTimeout default command execution timeout duration
defaultCommandExecutionTimeout = 360 * time.Second defaultCommandExecutionTimeout = 360 * time.Second
@ -43,6 +43,8 @@ type Command struct {
brokenArgs []string brokenArgs []string
} }
type CmdArg string
func (c *Command) String() string { func (c *Command) String() string {
if len(c.args) == 0 { if len(c.args) == 0 {
return c.name return c.name
@ -52,13 +54,18 @@ func (c *Command) String() string {
// NewCommand creates and returns a new Git Command based on given command and arguments. // NewCommand creates and returns a new Git Command based on given command and arguments.
// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead. // Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
func NewCommand(ctx context.Context, args ...string) *Command { func NewCommand(ctx context.Context, args ...CmdArg) *Command {
// Make an explicit copy of globalCommandArgs, otherwise append might overwrite it // Make an explicit copy of globalCommandArgs, otherwise append might overwrite it
cargs := make([]string, len(globalCommandArgs)) cargs := make([]string, 0, len(globalCommandArgs)+len(args))
copy(cargs, globalCommandArgs) for _, arg := range globalCommandArgs {
cargs = append(cargs, string(arg))
}
for _, arg := range args {
cargs = append(cargs, string(arg))
}
return &Command{ return &Command{
name: GitExecutable, name: GitExecutable,
args: append(cargs, args...), args: cargs,
parentContext: ctx, parentContext: ctx,
globalArgsLength: len(globalCommandArgs), globalArgsLength: len(globalCommandArgs),
} }
@ -66,16 +73,20 @@ func NewCommand(ctx context.Context, args ...string) *Command {
// NewCommandNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args // NewCommandNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args
// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead. // Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
func NewCommandNoGlobals(args ...string) *Command { func NewCommandNoGlobals(args ...CmdArg) *Command {
return NewCommandContextNoGlobals(DefaultContext, args...) return NewCommandContextNoGlobals(DefaultContext, args...)
} }
// NewCommandContextNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args // NewCommandContextNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args
// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead. // Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
func NewCommandContextNoGlobals(ctx context.Context, args ...string) *Command { func NewCommandContextNoGlobals(ctx context.Context, args ...CmdArg) *Command {
cargs := make([]string, 0, len(args))
for _, arg := range args {
cargs = append(cargs, string(arg))
}
return &Command{ return &Command{
name: GitExecutable, name: GitExecutable,
args: args, args: cargs,
parentContext: ctx, parentContext: ctx,
} }
} }
@ -93,10 +104,12 @@ func (c *Command) SetDescription(desc string) *Command {
return c return c
} }
// AddArguments adds new argument(s) to the command. Each argument must be safe to be trusted. // AddArguments adds new git argument(s) to the command. Each argument must be safe to be trusted.
// User-provided arguments should be passed to AddDynamicArguments instead. // User-provided arguments should be passed to AddDynamicArguments instead.
func (c *Command) AddArguments(args ...string) *Command { func (c *Command) AddArguments(args ...CmdArg) *Command {
c.args = append(c.args, args...) for _, arg := range args {
c.args = append(c.args, string(arg))
}
return c return c
} }
@ -115,6 +128,26 @@ func (c *Command) AddDynamicArguments(args ...string) *Command {
return c return c
} }
// AddDashesAndList adds the "--" and then add the list as arguments, it's usually for adding file list
// At the moment, this function can be only called once, maybe in future it can be refactored to support multiple calls (if necessary)
func (c *Command) AddDashesAndList(list ...string) *Command {
c.args = append(c.args, "--")
// Some old code also checks `arg != ""`, IMO it's not necessary.
// If the check is needed, the list should be prepared before the call to this function
c.args = append(c.args, list...)
return c
}
// CmdArgCheck checks whether the string is safe to be used as a dynamic argument.
// It panics if the check fails. Usually it should not be used, it's just for refactoring purpose
// deprecated
func CmdArgCheck(s string) CmdArg {
if s != "" && s[0] == '-' {
panic("invalid git cmd argument: " + s)
}
return CmdArg(s)
}
// 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
@ -153,7 +186,7 @@ func CommonGitCmdEnvs() []string {
}...) }...)
} }
// CommonCmdServEnvs is like CommonGitCmdEnvs but it only returns minimal required environment variables for the "gitea serv" command // CommonCmdServEnvs is like CommonGitCmdEnvs, but it only returns minimal required environment variables for the "gitea serv" command
func CommonCmdServEnvs() []string { func CommonCmdServEnvs() []string {
return commonBaseEnvs() return commonBaseEnvs()
} }
@ -318,12 +351,12 @@ func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunS
} }
// AllowLFSFiltersArgs return globalCommandArgs with lfs filter, it should only be used for tests // AllowLFSFiltersArgs return globalCommandArgs with lfs filter, it should only be used for tests
func AllowLFSFiltersArgs() []string { func AllowLFSFiltersArgs() []CmdArg {
// Now here we should explicitly allow lfs filters to run // Now here we should explicitly allow lfs filters to run
filteredLFSGlobalArgs := make([]string, len(globalCommandArgs)) filteredLFSGlobalArgs := make([]CmdArg, len(globalCommandArgs))
j := 0 j := 0
for _, arg := range globalCommandArgs { for _, arg := range globalCommandArgs {
if strings.Contains(arg, "lfs") { if strings.Contains(string(arg), "lfs") {
j-- j--
} else { } else {
filteredLFSGlobalArgs[j] = arg filteredLFSGlobalArgs[j] = arg

View File

@ -92,13 +92,13 @@ func AddChanges(repoPath string, all bool, files ...string) error {
} }
// AddChangesWithArgs marks local changes to be ready for commit. // AddChangesWithArgs marks local changes to be ready for commit.
func AddChangesWithArgs(repoPath string, globalArgs []string, all bool, files ...string) error { func AddChangesWithArgs(repoPath string, globalArgs []CmdArg, all bool, files ...string) error {
cmd := NewCommandNoGlobals(append(globalArgs, "add")...) cmd := NewCommandNoGlobals(append(globalArgs, "add")...)
if all { if all {
cmd.AddArguments("--all") cmd.AddArguments("--all")
} }
cmd.AddArguments("--") cmd.AddDashesAndList(files...)
_, _, err := cmd.AddArguments(files...).RunStdString(&RunOpts{Dir: repoPath}) _, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
return err return err
} }
@ -112,17 +112,17 @@ type CommitChangesOptions struct {
// CommitChanges commits local changes with given committer, author and message. // CommitChanges commits local changes with given committer, author and message.
// If author is nil, it will be the same as committer. // If author is nil, it will be the same as committer.
func CommitChanges(repoPath string, opts CommitChangesOptions) error { func CommitChanges(repoPath string, opts CommitChangesOptions) error {
cargs := make([]string, len(globalCommandArgs)) cargs := make([]CmdArg, len(globalCommandArgs))
copy(cargs, globalCommandArgs) copy(cargs, globalCommandArgs)
return CommitChangesWithArgs(repoPath, cargs, opts) return CommitChangesWithArgs(repoPath, cargs, opts)
} }
// CommitChangesWithArgs commits local changes with given committer, author and message. // CommitChangesWithArgs commits local changes with given committer, author and message.
// If author is nil, it will be the same as committer. // If author is nil, it will be the same as committer.
func CommitChangesWithArgs(repoPath string, args []string, opts CommitChangesOptions) error { func CommitChangesWithArgs(repoPath string, args []CmdArg, opts CommitChangesOptions) error {
cmd := NewCommandNoGlobals(args...) cmd := NewCommandNoGlobals(args...)
if opts.Committer != nil { if opts.Committer != nil {
cmd.AddArguments("-c", "user.name="+opts.Committer.Name, "-c", "user.email="+opts.Committer.Email) cmd.AddArguments("-c", CmdArg("user.name="+opts.Committer.Name), "-c", CmdArg("user.email="+opts.Committer.Email))
} }
cmd.AddArguments("commit") cmd.AddArguments("commit")
@ -130,9 +130,9 @@ func CommitChangesWithArgs(repoPath string, args []string, opts CommitChangesOpt
opts.Author = opts.Committer opts.Author = opts.Committer
} }
if opts.Author != nil { if opts.Author != nil {
cmd.AddArguments(fmt.Sprintf("--author='%s <%s>'", opts.Author.Name, opts.Author.Email)) cmd.AddArguments(CmdArg(fmt.Sprintf("--author='%s <%s>'", opts.Author.Name, opts.Author.Email)))
} }
cmd.AddArguments("-m", opts.Message) cmd.AddArguments("-m").AddDynamicArguments(opts.Message)
_, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath}) _, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
// No stderr but exit status 1 means nothing to commit. // No stderr but exit status 1 means nothing to commit.
@ -144,15 +144,13 @@ func CommitChangesWithArgs(repoPath string, args []string, opts CommitChangesOpt
// AllCommitsCount returns count of all commits in repository // AllCommitsCount returns count of all commits in repository
func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, files ...string) (int64, error) { func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, files ...string) (int64, error) {
args := []string{"--all", "--count"}
if hidePRRefs {
args = append([]string{"--exclude=" + PullPrefix + "*"}, args...)
}
cmd := NewCommand(ctx, "rev-list") cmd := NewCommand(ctx, "rev-list")
cmd.AddArguments(args...) if hidePRRefs {
cmd.AddArguments("--exclude=" + PullPrefix + "*")
}
cmd.AddArguments("--all", "--count")
if len(files) > 0 { if len(files) > 0 {
cmd.AddArguments("--") cmd.AddDashesAndList(files...)
cmd.AddArguments(files...)
} }
stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath}) stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
@ -168,8 +166,7 @@ func CommitsCountFiles(ctx context.Context, repoPath string, revision, relpath [
cmd := NewCommand(ctx, "rev-list", "--count") cmd := NewCommand(ctx, "rev-list", "--count")
cmd.AddDynamicArguments(revision...) cmd.AddDynamicArguments(revision...)
if len(relpath) > 0 { if len(relpath) > 0 {
cmd.AddArguments("--") cmd.AddDashesAndList(relpath...)
cmd.AddArguments(relpath...)
} }
stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath}) stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
@ -209,7 +206,7 @@ func (c *Commit) HasPreviousCommit(commitHash SHA1) (bool, error) {
return false, nil return false, nil
} }
_, _, err := NewCommand(c.repo.Ctx, "merge-base", "--is-ancestor", that, this).RunStdString(&RunOpts{Dir: c.repo.Path}) _, _, err := NewCommand(c.repo.Ctx, "merge-base", "--is-ancestor").AddDynamicArguments(that, this).RunStdString(&RunOpts{Dir: c.repo.Path})
if err == nil { if err == nil {
return true, nil return true, nil
} }
@ -392,15 +389,12 @@ func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
// GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only') // GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only')
func (c *Commit) GetBranchName() (string, error) { func (c *Commit) GetBranchName() (string, error) {
args := []string{ cmd := NewCommand(c.repo.Ctx, "name-rev")
"name-rev",
}
if CheckGitVersionAtLeast("2.13.0") == nil { if CheckGitVersionAtLeast("2.13.0") == nil {
args = append(args, "--exclude", "refs/tags/*") cmd.AddArguments("--exclude", "refs/tags/*")
} }
args = append(args, "--name-only", "--no-undefined", c.ID.String()) cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String())
data, _, err := cmd.RunStdString(&RunOpts{Dir: c.repo.Path})
data, _, err := NewCommand(c.repo.Ctx, args...).RunStdString(&RunOpts{Dir: c.repo.Path})
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") {
@ -426,7 +420,7 @@ func (c *Commit) LoadBranchName() (err error) {
// GetTagName gets the current tag name for given commit // GetTagName gets the current tag name for given commit
func (c *Commit) GetTagName() (string, error) { func (c *Commit) GetTagName() (string, error) {
data, _, err := NewCommand(c.repo.Ctx, "describe", "--exact-match", "--tags", "--always", c.ID.String()).RunStdString(&RunOpts{Dir: c.repo.Path}) data, _, err := NewCommand(c.repo.Ctx, "describe", "--exact-match", "--tags", "--always").AddDynamicArguments(c.ID.String()).RunStdString(&RunOpts{Dir: c.repo.Path})
if err != nil { if err != nil {
// handle special case where there is no tag for this commit // handle special case where there is no tag for this commit
if strings.Contains(err.Error(), "no tag exactly matches") { if strings.Contains(err.Error(), "no tag exactly matches") {
@ -503,9 +497,7 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi
}() }()
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
args := []string{"log", "--name-status", "-c", "--pretty=format:", "--parents", "--no-renames", "-z", "-1", commitID} err := NewCommand(ctx, "log", "--name-status", "-c", "--pretty=format:", "--parents", "--no-renames", "-z", "-1").AddDynamicArguments(commitID).Run(&RunOpts{
err := NewCommand(ctx, args...).Run(&RunOpts{
Dir: repoPath, Dir: repoPath,
Stdout: w, Stdout: w,
Stderr: stderr, Stderr: stderr,
@ -521,7 +513,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. // 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 := NewCommand(ctx, "rev-parse", shortID).RunStdString(&RunOpts{Dir: repoPath}) commitID, _, err := NewCommand(ctx, "rev-parse").AddDynamicArguments(shortID).RunStdString(&RunOpts{Dir: repoPath})
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

@ -35,7 +35,7 @@ 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 := NewCommand(ctx, "show", "--pretty=format:revert %H%n", "-R", commitID) cmd := NewCommand(ctx, "show", "--pretty=format:revert %H%n", "-R").AddDynamicArguments(commitID)
if err := cmd.Run(&RunOpts{ if err := cmd.Run(&RunOpts{
Dir: repoPath, Dir: repoPath,
Stdout: writer, Stdout: writer,
@ -52,39 +52,38 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
if err != nil { if err != nil {
return err return err
} }
fileArgs := make([]string, 0) var files []string
if len(file) > 0 { if len(file) > 0 {
fileArgs = append(fileArgs, "--", file) files = append(files, file)
} }
var args []string cmd := NewCommand(repo.Ctx)
switch diffType { switch diffType {
case RawDiffNormal: case RawDiffNormal:
if len(startCommit) != 0 { if len(startCommit) != 0 {
args = append([]string{"diff", "-M", startCommit, endCommit}, fileArgs...) cmd.AddArguments("diff", "-M").AddDynamicArguments(startCommit, endCommit).AddDashesAndList(files...)
} else if commit.ParentCount() == 0 { } else if commit.ParentCount() == 0 {
args = append([]string{"show", endCommit}, fileArgs...) cmd.AddArguments("show").AddDynamicArguments(endCommit).AddDashesAndList(files...)
} else { } else {
c, _ := commit.Parent(0) c, _ := commit.Parent(0)
args = append([]string{"diff", "-M", c.ID.String(), endCommit}, fileArgs...) cmd.AddArguments("diff", "-M").AddDynamicArguments(c.ID.String(), endCommit).AddDashesAndList(files...)
} }
case RawDiffPatch: case RawDiffPatch:
if len(startCommit) != 0 { if len(startCommit) != 0 {
query := fmt.Sprintf("%s...%s", endCommit, startCommit) query := fmt.Sprintf("%s...%s", endCommit, startCommit)
args = append([]string{"format-patch", "--no-signature", "--stdout", "--root", query}, fileArgs...) cmd.AddArguments("format-patch", "--no-signature", "--stdout", "--root").AddDynamicArguments(query).AddDashesAndList(files...)
} else if commit.ParentCount() == 0 { } else if commit.ParentCount() == 0 {
args = append([]string{"format-patch", "--no-signature", "--stdout", "--root", endCommit}, fileArgs...) cmd.AddArguments("format-patch", "--no-signature", "--stdout", "--root").AddDynamicArguments(endCommit).AddDashesAndList(files...)
} else { } else {
c, _ := commit.Parent(0) c, _ := commit.Parent(0)
query := fmt.Sprintf("%s...%s", endCommit, c.ID.String()) query := fmt.Sprintf("%s...%s", endCommit, c.ID.String())
args = append([]string{"format-patch", "--no-signature", "--stdout", query}, fileArgs...) cmd.AddArguments("format-patch", "--no-signature", "--stdout").AddDynamicArguments(query).AddDashesAndList(files...)
} }
default: default:
return fmt.Errorf("invalid diffType: %s", diffType) return fmt.Errorf("invalid diffType: %s", diffType)
} }
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
cmd := NewCommand(repo.Ctx, args...)
if err = cmd.Run(&RunOpts{ if err = cmd.Run(&RunOpts{
Dir: repo.Path, Dir: repo.Path,
Stdout: writer, Stdout: writer,
@ -287,7 +286,7 @@ func GetAffectedFiles(repo *Repository, oldCommitID, newCommitID string, env []s
affectedFiles := make([]string, 0, 32) affectedFiles := make([]string, 0, 32)
// 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 = NewCommand(repo.Ctx, "diff", "--name-only", oldCommitID, newCommitID). err = NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(oldCommitID, newCommitID).
Run(&RunOpts{ Run(&RunOpts{
Env: env, Env: env,
Dir: repo.Path, Dir: repo.Path,

View File

@ -313,7 +313,7 @@ func CheckGitVersionAtLeast(atLeast string) error {
} }
func configSet(key, value string) error { func configSet(key, value string) error {
stdout, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil) stdout, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key).RunStdString(nil)
if err != nil && !err.IsExitCode(1) { if err != nil && !err.IsExitCode(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)
} }
@ -323,7 +323,7 @@ func configSet(key, value string) error {
return nil return nil
} }
_, _, err = NewCommand(DefaultContext, "config", "--global", key, value).RunStdString(nil) _, _, err = NewCommand(DefaultContext, "config", "--global").AddDynamicArguments(key, value).RunStdString(nil)
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)
} }
@ -332,14 +332,14 @@ func configSet(key, value string) error {
} }
func configSetNonExist(key, value string) error { func configSetNonExist(key, value string) error {
_, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil) _, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key).RunStdString(nil)
if err == nil { if err == nil {
// already exist // already exist
return nil return nil
} }
if err.IsExitCode(1) { if err.IsExitCode(1) {
// not exist, set new config // not exist, set new config
_, _, err = NewCommand(DefaultContext, "config", "--global", key, value).RunStdString(nil) _, _, err = NewCommand(DefaultContext, "config", "--global").AddDynamicArguments(key, value).RunStdString(nil)
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)
} }
@ -350,14 +350,14 @@ func configSetNonExist(key, value string) error {
} }
func configAddNonExist(key, value string) error { func configAddNonExist(key, value string) error {
_, _, err := NewCommand(DefaultContext, "config", "--get", key, regexp.QuoteMeta(value)).RunStdString(nil) _, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(nil)
if err == nil { if err == nil {
// already exist // already exist
return nil return nil
} }
if err.IsExitCode(1) { if err.IsExitCode(1) {
// not exist, add new config // not exist, add new config
_, _, err = NewCommand(DefaultContext, "config", "--global", "--add", key, value).RunStdString(nil) _, _, err = NewCommand(DefaultContext, "config", "--global", "--add").AddDynamicArguments(key, value).RunStdString(nil)
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)
} }
@ -367,10 +367,10 @@ func configAddNonExist(key, value string) error {
} }
func configUnsetAll(key, value string) error { func configUnsetAll(key, value string) error {
_, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil) _, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key).RunStdString(nil)
if err == nil { if err == nil {
// exist, need to remove // exist, need to remove
_, _, err = NewCommand(DefaultContext, "config", "--global", "--unset-all", key, regexp.QuoteMeta(value)).RunStdString(nil) _, _, err = NewCommand(DefaultContext, "config", "--global", "--unset-all").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(nil)
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)
} }
@ -384,6 +384,6 @@ func configUnsetAll(key, value string) error {
} }
// Fsck verifies the connectivity and validity of the objects in the database // Fsck verifies the connectivity and validity of the objects in the database
func Fsck(ctx context.Context, repoPath string, timeout time.Duration, args ...string) error { func Fsck(ctx context.Context, repoPath string, timeout time.Duration, args ...CmdArg) error {
return NewCommand(ctx, "fsck").AddArguments(args...).Run(&RunOpts{Timeout: timeout, Dir: repoPath}) return NewCommand(ctx, "fsck").AddArguments(args...).Run(&RunOpts{Timeout: timeout, Dir: repoPath})
} }

View File

@ -35,30 +35,33 @@ func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, p
_ = stdoutWriter.Close() _ = stdoutWriter.Close()
} }
args := make([]string, 0, 8+len(paths)) cmd := NewCommand(ctx)
args = append(args, "log", "--name-status", "-c", "--format=commit%x00%H %P%x00", "--parents", "--no-renames", "-t", "-z", head, "--") cmd.AddArguments("log", "--name-status", "-c", "--format=commit%x00%H %P%x00", "--parents", "--no-renames", "-t", "-z").AddDynamicArguments(head)
var files []string
if len(paths) < 70 { if len(paths) < 70 {
if treepath != "" { if treepath != "" {
args = append(args, treepath) files = append(files, treepath)
for _, pth := range paths { for _, pth := range paths {
if pth != "" { if pth != "" {
args = append(args, path.Join(treepath, pth)) files = append(files, path.Join(treepath, pth))
} }
} }
} else { } else {
for _, pth := range paths { for _, pth := range paths {
if pth != "" { if pth != "" {
args = append(args, pth) files = append(files, pth)
} }
} }
} }
} else if treepath != "" { } else if treepath != "" {
args = append(args, treepath) files = append(files, treepath)
} }
cmd.AddDashesAndList(files...)
go func() { go func() {
stderr := strings.Builder{} stderr := strings.Builder{}
err := NewCommand(ctx, args...).Run(&RunOpts{ err := cmd.Run(&RunOpts{
Dir: repository, Dir: repository,
Stdout: stdoutWriter, Stdout: stdoutWriter,
Stderr: &stderr, Stderr: &stderr,

View File

@ -43,7 +43,7 @@ func RevListObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.
defer revListWriter.Close() defer revListWriter.Close()
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
var errbuf strings.Builder var errbuf strings.Builder
cmd := git.NewCommand(ctx, "rev-list", "--objects", headSHA, "--not", baseSHA) cmd := git.NewCommand(ctx, "rev-list", "--objects").AddDynamicArguments(headSHA).AddArguments("--not").AddDynamicArguments(baseSHA)
if err := cmd.Run(&git.RunOpts{ if err := cmd.Run(&git.RunOpts{
Dir: tmpBasePath, Dir: tmpBasePath,
Stdout: revListWriter, Stdout: revListWriter,

View File

@ -14,9 +14,9 @@ import (
func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) { func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) {
var cmd *Command var cmd *Command
if CheckGitVersionAtLeast("2.7") == nil { if CheckGitVersionAtLeast("2.7") == nil {
cmd = NewCommand(ctx, "remote", "get-url", remoteName) cmd = NewCommand(ctx, "remote", "get-url").AddDynamicArguments(remoteName)
} else { } else {
cmd = NewCommand(ctx, "config", "--get", "remote."+remoteName+".url") cmd = NewCommand(ctx, "config", "--get").AddDynamicArguments("remote." + remoteName + ".url")
} }
result, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath}) result, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})

View File

@ -59,7 +59,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 := NewCommand(ctx, "ls-remote", "-q", "-h", url, "HEAD").RunStdString(nil) _, _, err := NewCommand(ctx, "ls-remote", "-q", "-h").AddDynamicArguments(url, "HEAD").RunStdString(nil)
return err == nil return err == nil
} }
@ -112,13 +112,11 @@ type CloneRepoOptions struct {
// Clone clones original repository to target path. // Clone clones original repository to target path.
func Clone(ctx context.Context, from, to string, opts CloneRepoOptions) error { func Clone(ctx context.Context, from, to string, opts CloneRepoOptions) error {
cargs := make([]string, len(globalCommandArgs)) return CloneWithArgs(ctx, globalCommandArgs, from, to, opts)
copy(cargs, globalCommandArgs)
return CloneWithArgs(ctx, from, to, cargs, opts)
} }
// CloneWithArgs original repository to target path. // CloneWithArgs original repository to target path.
func CloneWithArgs(ctx context.Context, from, to string, args []string, opts CloneRepoOptions) (err error) { func CloneWithArgs(ctx context.Context, args []CmdArg, from, to string, opts CloneRepoOptions) (err error) {
toDir := path.Dir(to) toDir := path.Dir(to)
if err = os.MkdirAll(toDir, os.ModePerm); err != nil { if err = os.MkdirAll(toDir, os.ModePerm); err != nil {
return err return err
@ -144,15 +142,15 @@ func CloneWithArgs(ctx context.Context, from, to string, args []string, opts Clo
cmd.AddArguments("--no-checkout") cmd.AddArguments("--no-checkout")
} }
if opts.Depth > 0 { if opts.Depth > 0 {
cmd.AddArguments("--depth", strconv.Itoa(opts.Depth)) cmd.AddArguments("--depth").AddDynamicArguments(strconv.Itoa(opts.Depth))
} }
if opts.Filter != "" { if opts.Filter != "" {
cmd.AddArguments("--filter", opts.Filter) cmd.AddArguments("--filter").AddDynamicArguments(opts.Filter)
} }
if len(opts.Branch) > 0 { if len(opts.Branch) > 0 {
cmd.AddArguments("-b", opts.Branch) cmd.AddArguments("-b").AddDynamicArguments(opts.Branch)
} }
cmd.AddArguments("--", from, to) cmd.AddDashesAndList(from, to)
if strings.Contains(from, "://") && strings.Contains(from, "@") { if strings.Contains(from, "://") && strings.Contains(from, "@") {
cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, util.SanitizeCredentialURLs(from), to, opts.Shared, opts.Mirror, opts.Depth)) cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, util.SanitizeCredentialURLs(from), to, opts.Shared, opts.Mirror, opts.Depth))
@ -203,10 +201,12 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
if opts.Mirror { if opts.Mirror {
cmd.AddArguments("--mirror") cmd.AddArguments("--mirror")
} }
cmd.AddArguments("--", opts.Remote) remoteBranchArgs := []string{opts.Remote}
if len(opts.Branch) > 0 { if len(opts.Branch) > 0 {
cmd.AddArguments(opts.Branch) remoteBranchArgs = append(remoteBranchArgs, opts.Branch)
} }
cmd.AddDashesAndList(remoteBranchArgs...)
if strings.Contains(opts.Remote, "://") && strings.Contains(opts.Remote, "@") { if strings.Contains(opts.Remote, "://") && strings.Contains(opts.Remote, "@") {
cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, util.SanitizeCredentialURLs(opts.Remote), opts.Force, opts.Mirror)) cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, util.SanitizeCredentialURLs(opts.Remote), opts.Force, opts.Mirror))
} else { } else {
@ -276,7 +276,7 @@ type DivergeObject struct {
func checkDivergence(ctx context.Context, repoPath, baseBranch, targetBranch string) (int, error) { func checkDivergence(ctx context.Context, repoPath, baseBranch, targetBranch string) (int, error) {
branches := fmt.Sprintf("%s..%s", baseBranch, targetBranch) branches := fmt.Sprintf("%s..%s", baseBranch, targetBranch)
cmd := NewCommand(ctx, "rev-list", "--count", branches) cmd := NewCommand(ctx, "rev-list", "--count").AddDynamicArguments(branches)
stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath}) stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
if err != nil { if err != nil {
return -1, err return -1, err
@ -319,7 +319,7 @@ func (repo *Repository) CreateBundle(ctx context.Context, commit string, out io.
return err return err
} }
_, _, err = NewCommand(ctx, "reset", "--soft", commit).RunStdString(&RunOpts{Dir: tmp, Env: env}) _, _, err = NewCommand(ctx, "reset", "--soft").AddDynamicArguments(commit).RunStdString(&RunOpts{Dir: tmp, Env: env})
if err != nil { if err != nil {
return err return err
} }
@ -330,7 +330,7 @@ func (repo *Repository) CreateBundle(ctx context.Context, commit string, out io.
} }
tmpFile := filepath.Join(tmp, "bundle") tmpFile := filepath.Join(tmp, "bundle")
_, _, err = NewCommand(ctx, "bundle", "create", tmpFile, "bundle", "HEAD").RunStdString(&RunOpts{Dir: tmp, Env: env}) _, _, err = NewCommand(ctx, "bundle", "create").AddDynamicArguments(tmpFile, "bundle", "HEAD").RunStdString(&RunOpts{Dir: tmp, Env: env})
if err != nil { if err != nil {
return err return err
} }

View File

@ -44,20 +44,15 @@ func (repo *Repository) CreateArchive(ctx context.Context, format ArchiveType, t
return fmt.Errorf("unknown format: %v", format) return fmt.Errorf("unknown format: %v", format)
} }
args := []string{ cmd := NewCommand(ctx, "archive")
"archive",
}
if usePrefix { if usePrefix {
args = append(args, "--prefix="+filepath.Base(strings.TrimSuffix(repo.Path, ".git"))+"/") cmd.AddArguments(CmdArg("--prefix=" + filepath.Base(strings.TrimSuffix(repo.Path, ".git")) + "/"))
} }
cmd.AddArguments(CmdArg("--format=" + format.String()))
args = append(args, cmd.AddDynamicArguments(commitID)
"--format="+format.String(),
commitID,
)
var stderr strings.Builder var stderr strings.Builder
err := NewCommand(ctx, args...).Run(&RunOpts{ err := cmd.Run(&RunOpts{
Dir: repo.Path, Dir: repo.Path,
Stdout: target, Stdout: target,
Stderr: &stderr, Stderr: &stderr,

View File

@ -20,7 +20,7 @@ import (
type CheckAttributeOpts struct { type CheckAttributeOpts struct {
CachedOnly bool CachedOnly bool
AllAttributes bool AllAttributes bool
Attributes []string Attributes []CmdArg
Filenames []string Filenames []string
IndexFile string IndexFile string
WorkTree string WorkTree string
@ -44,31 +44,23 @@ func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[
stdOut := new(bytes.Buffer) stdOut := new(bytes.Buffer)
stdErr := new(bytes.Buffer) stdErr := new(bytes.Buffer)
cmdArgs := []string{"check-attr", "-z"} cmd := NewCommand(repo.Ctx, "check-attr", "-z")
if opts.AllAttributes { if opts.AllAttributes {
cmdArgs = append(cmdArgs, "-a") cmd.AddArguments("-a")
} else { } else {
for _, attribute := range opts.Attributes { for _, attribute := range opts.Attributes {
if attribute != "" { if attribute != "" {
cmdArgs = append(cmdArgs, attribute) cmd.AddArguments(attribute)
} }
} }
} }
if opts.CachedOnly { if opts.CachedOnly {
cmdArgs = append(cmdArgs, "--cached") cmd.AddArguments("--cached")
} }
cmdArgs = append(cmdArgs, "--") cmd.AddDashesAndList(opts.Filenames...)
for _, arg := range opts.Filenames {
if arg != "" {
cmdArgs = append(cmdArgs, arg)
}
}
cmd := NewCommand(repo.Ctx, cmdArgs...)
if err := cmd.Run(&RunOpts{ if err := cmd.Run(&RunOpts{
Env: env, Env: env,
@ -106,7 +98,7 @@ func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[
// CheckAttributeReader provides a reader for check-attribute content that can be long running // CheckAttributeReader provides a reader for check-attribute content that can be long running
type CheckAttributeReader struct { type CheckAttributeReader struct {
// params // params
Attributes []string Attributes []CmdArg
Repo *Repository Repo *Repository
IndexFile string IndexFile string
WorkTree string WorkTree string
@ -122,7 +114,7 @@ type CheckAttributeReader struct {
// Init initializes the CheckAttributeReader // Init initializes the CheckAttributeReader
func (c *CheckAttributeReader) Init(ctx context.Context) error { func (c *CheckAttributeReader) Init(ctx context.Context) error {
cmdArgs := []string{"check-attr", "--stdin", "-z"} cmdArgs := []CmdArg{"check-attr", "--stdin", "-z"}
if len(c.IndexFile) > 0 { if len(c.IndexFile) > 0 {
cmdArgs = append(cmdArgs, "--cached") cmdArgs = append(cmdArgs, "--cached")
@ -401,7 +393,7 @@ func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeRe
} }
checker := &CheckAttributeReader{ checker := &CheckAttributeReader{
Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"}, Attributes: []CmdArg{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"},
Repo: repo, Repo: repo,
IndexFile: indexFilename, IndexFile: indexFilename,
WorkTree: worktree, WorkTree: worktree,

View File

@ -8,13 +8,16 @@ import "fmt"
// FileBlame return the Blame object of file // FileBlame return the Blame object of file
func (repo *Repository) FileBlame(revision, path, file string) ([]byte, error) { func (repo *Repository) FileBlame(revision, path, file string) ([]byte, error) {
stdout, _, err := NewCommand(repo.Ctx, "blame", "--root", "--", file).RunStdBytes(&RunOpts{Dir: path}) stdout, _, err := NewCommand(repo.Ctx, "blame", "--root").AddDashesAndList(file).RunStdBytes(&RunOpts{Dir: path})
return stdout, err return stdout, err
} }
// LineBlame returns the latest commit at the given line // LineBlame returns the latest commit at the given line
func (repo *Repository) LineBlame(revision, path, file string, line uint) (*Commit, error) { func (repo *Repository) LineBlame(revision, path, file string, line uint) (*Commit, error) {
res, _, err := NewCommand(repo.Ctx, "blame", fmt.Sprintf("-L %d,%d", line, line), "-p", revision, "--", file).RunStdString(&RunOpts{Dir: path}) res, _, err := NewCommand(repo.Ctx, "blame").
AddArguments(CmdArg(fmt.Sprintf("-L %d,%d", line, line))).
AddArguments("-p").AddDynamicArguments(revision).
AddDashesAndList(file).RunStdString(&RunOpts{Dir: path})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -25,7 +25,7 @@ const PullRequestPrefix = "refs/for/"
// 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, repoPath, name string) bool { func IsReferenceExist(ctx context.Context, repoPath, name string) bool {
_, _, err := NewCommand(ctx, "show-ref", "--verify", "--", name).RunStdString(&RunOpts{Dir: repoPath}) _, _, err := NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name).RunStdString(&RunOpts{Dir: repoPath})
return err == nil return err == nil
} }
@ -66,7 +66,7 @@ func (repo *Repository) GetHEADBranch() (*Branch, error) {
// SetDefaultBranch sets default branch of repository. // SetDefaultBranch sets default branch of repository.
func (repo *Repository) SetDefaultBranch(name string) error { func (repo *Repository) SetDefaultBranch(name string) error {
_, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD", BranchPrefix+name).RunStdString(&RunOpts{Dir: repo.Path}) _, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD").AddDynamicArguments(BranchPrefix + name).RunStdString(&RunOpts{Dir: repo.Path})
return err return err
} }
@ -141,7 +141,7 @@ func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) erro
cmd.AddArguments("-d") cmd.AddArguments("-d")
} }
cmd.AddArguments("--", name) cmd.AddDashesAndList(name)
_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) _, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
return err return err
@ -150,7 +150,7 @@ func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) erro
// CreateBranch create a new branch // CreateBranch create a new branch
func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error { func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error {
cmd := NewCommand(repo.Ctx, "branch") cmd := NewCommand(repo.Ctx, "branch")
cmd.AddArguments("--", branch, oldbranchOrCommit) cmd.AddDashesAndList(branch, oldbranchOrCommit)
_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) _, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
@ -163,7 +163,7 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
if fetch { if fetch {
cmd.AddArguments("-f") cmd.AddArguments("-f")
} }
cmd.AddArguments(name, url) cmd.AddDynamicArguments(name, url)
_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) _, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
return err return err
@ -171,7 +171,7 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
// RemoveRemote removes a remote from repository. // RemoveRemote removes a remote from repository.
func (repo *Repository) RemoveRemote(name string) error { func (repo *Repository) RemoveRemote(name string) error {
_, _, err := NewCommand(repo.Ctx, "remote", "rm", name).RunStdString(&RunOpts{Dir: repo.Path}) _, _, err := NewCommand(repo.Ctx, "remote", "rm").AddDynamicArguments(name).RunStdString(&RunOpts{Dir: repo.Path})
return err return err
} }
@ -182,6 +182,6 @@ func (branch *Branch) GetCommit() (*Commit, error) {
// RenameBranch rename a branch // RenameBranch rename a branch
func (repo *Repository) RenameBranch(from, to string) error { func (repo *Repository) RenameBranch(from, to string) error {
_, _, err := NewCommand(repo.Ctx, "branch", "-m", from, to).RunStdString(&RunOpts{Dir: repo.Path}) _, _, err := NewCommand(repo.Ctx, "branch", "-m").AddDynamicArguments(from, to).RunStdString(&RunOpts{Dir: repo.Path})
return err return err
} }

View File

@ -63,7 +63,7 @@ func (repo *Repository) IsBranchExist(name string) bool {
// GetBranchNames returns branches from the repository, skipping skip initial branches and // GetBranchNames returns branches from the repository, skipping skip initial branches and
// returning at most limit branches, or all branches if limit is 0. // returning at most limit branches, or all branches if limit is 0.
func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) { func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
return callShowRef(repo.Ctx, repo.Path, BranchPrefix, []string{BranchPrefix, "--sort=-committerdate"}, skip, limit) return callShowRef(repo.Ctx, repo.Path, BranchPrefix, []CmdArg{BranchPrefix, "--sort=-committerdate"}, skip, limit)
} }
// WalkReferences walks all the references from the repository // WalkReferences walks all the references from the repository
@ -74,19 +74,19 @@ func WalkReferences(ctx context.Context, repoPath string, walkfn func(sha1, refn
// WalkReferences walks all the references from the repository // WalkReferences walks all the references from the repository
// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty. // 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) { func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
var args []string var args []CmdArg
switch refType { switch refType {
case ObjectTag: case ObjectTag:
args = []string{TagPrefix, "--sort=-taggerdate"} args = []CmdArg{TagPrefix, "--sort=-taggerdate"}
case ObjectBranch: case ObjectBranch:
args = []string{BranchPrefix, "--sort=-committerdate"} args = []CmdArg{BranchPrefix, "--sort=-committerdate"}
} }
return walkShowRef(repo.Ctx, repo.Path, args, skip, limit, walkfn) return walkShowRef(repo.Ctx, repo.Path, args, skip, limit, walkfn)
} }
// callShowRef return refs, if limit = 0 it will not limit // callShowRef return refs, if limit = 0 it will not limit
func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs []string, skip, limit int) (branchNames []string, countAll int, err error) { func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs []CmdArg, skip, limit int) (branchNames []string, countAll int, err error) {
countAll, err = walkShowRef(ctx, repoPath, extraArgs, skip, limit, func(_, branchName string) error { countAll, err = walkShowRef(ctx, repoPath, extraArgs, skip, limit, func(_, branchName string) error {
branchName = strings.TrimPrefix(branchName, trimPrefix) branchName = strings.TrimPrefix(branchName, trimPrefix)
branchNames = append(branchNames, branchName) branchNames = append(branchNames, branchName)
@ -96,7 +96,7 @@ func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs []s
return branchNames, countAll, err return branchNames, countAll, err
} }
func walkShowRef(ctx context.Context, repoPath string, extraArgs []string, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) { func walkShowRef(ctx context.Context, repoPath string, extraArgs []CmdArg, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
stdoutReader, stdoutWriter := io.Pipe() stdoutReader, stdoutWriter := io.Pipe()
defer func() { defer func() {
_ = stdoutReader.Close() _ = stdoutReader.Close()
@ -105,7 +105,7 @@ func walkShowRef(ctx context.Context, repoPath string, extraArgs []string, skip,
go func() { go func() {
stderrBuilder := &strings.Builder{} stderrBuilder := &strings.Builder{}
args := []string{"for-each-ref", "--format=%(objectname) %(refname)"} args := []CmdArg{"for-each-ref", "--format=%(objectname) %(refname)"}
args = append(args, extraArgs...) args = append(args, extraArgs...)
err := NewCommand(ctx, args...).Run(&RunOpts{ err := NewCommand(ctx, args...).Run(&RunOpts{
Dir: repoPath, Dir: repoPath,

View File

@ -61,7 +61,7 @@ func (repo *Repository) getCommitByPathWithID(id SHA1, relpath string) (*Commit,
relpath = `\` + relpath relpath = `\` + relpath
} }
stdout, _, runErr := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat, id.String(), "--", relpath).RunStdString(&RunOpts{Dir: repo.Path}) stdout, _, runErr := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat).AddDynamicArguments(id.String()).AddDashesAndList(relpath).RunStdString(&RunOpts{Dir: repo.Path})
if runErr != nil { if runErr != nil {
return nil, runErr return nil, runErr
} }
@ -76,7 +76,7 @@ func (repo *Repository) getCommitByPathWithID(id SHA1, relpath string) (*Commit,
// 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 := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat, "--", relpath).RunStdBytes(&RunOpts{Dir: repo.Path}) stdout, _, runErr := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat).AddDashesAndList(relpath).RunStdBytes(&RunOpts{Dir: repo.Path})
if runErr != nil { if runErr != nil {
return nil, runErr return nil, runErr
} }
@ -89,8 +89,10 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
} }
func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) ([]*Commit, error) { func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) ([]*Commit, error) {
stdout, _, err := NewCommand(repo.Ctx, "log", id.String(), "--skip="+strconv.Itoa((page-1)*pageSize), stdout, _, err := NewCommand(repo.Ctx, "log").
"--max-count="+strconv.Itoa(pageSize), prettyLogFormat).RunStdBytes(&RunOpts{Dir: repo.Path}) AddArguments(CmdArg("--skip="+strconv.Itoa((page-1)*pageSize)), CmdArg("--max-count="+strconv.Itoa(pageSize)), prettyLogFormat).
AddDynamicArguments(id.String()).
RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -99,30 +101,30 @@ func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) ([]*Commit,
func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Commit, error) { func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Commit, error) {
// create new git log command with limit of 100 commis // create new git log command with limit of 100 commis
cmd := NewCommand(repo.Ctx, "log", id.String(), "-100", prettyLogFormat) cmd := NewCommand(repo.Ctx, "log", "-100", prettyLogFormat).AddDynamicArguments(id.String())
// ignore case // ignore case
args := []string{"-i"} args := []CmdArg{"-i"}
// add authors if present in search query // add authors if present in search query
if len(opts.Authors) > 0 { if len(opts.Authors) > 0 {
for _, v := range opts.Authors { for _, v := range opts.Authors {
args = append(args, "--author="+v) args = append(args, CmdArg("--author="+v))
} }
} }
// add committers if present in search query // add committers if present in search query
if len(opts.Committers) > 0 { if len(opts.Committers) > 0 {
for _, v := range opts.Committers { for _, v := range opts.Committers {
args = append(args, "--committer="+v) args = append(args, CmdArg("--committer="+v))
} }
} }
// add time constraints if present in search query // add time constraints if present in search query
if len(opts.After) > 0 { if len(opts.After) > 0 {
args = append(args, "--after="+opts.After) args = append(args, CmdArg("--after="+opts.After))
} }
if len(opts.Before) > 0 { if len(opts.Before) > 0 {
args = append(args, "--before="+opts.Before) args = append(args, CmdArg("--before="+opts.Before))
} }
// pretend that all refs along with HEAD were listed on command line as <commis> // pretend that all refs along with HEAD were listed on command line as <commis>
@ -136,7 +138,7 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
// note this is done only for command created above // note this is done only for command created above
if len(opts.Keywords) > 0 { if len(opts.Keywords) > 0 {
for _, v := range opts.Keywords { for _, v := range opts.Keywords {
cmd.AddArguments("--grep=" + v) cmd.AddArguments(CmdArg("--grep=" + v))
} }
} }
@ -178,7 +180,7 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
} }
func (repo *Repository) getFilesChanged(id1, id2 string) ([]string, error) { func (repo *Repository) getFilesChanged(id1, id2 string) ([]string, error) {
stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only", id1, id2).RunStdBytes(&RunOpts{Dir: repo.Path}) stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(id1, id2).RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -188,7 +190,7 @@ func (repo *Repository) getFilesChanged(id1, id2 string) ([]string, error) {
// 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 := NewCommand(repo.Ctx, "diff", "--name-only", "-z", id1, id2, "--", filename).RunStdBytes(&RunOpts{Dir: repo.Path}) stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only", "-z").AddDynamicArguments(id1, id2).AddDashesAndList(filename).RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return false, err return false, err
} }
@ -211,12 +213,11 @@ func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (
}() }()
go func() { go func() {
stderr := strings.Builder{} stderr := strings.Builder{}
gitCmd := NewCommand(repo.Ctx, "rev-list", gitCmd := NewCommand(repo.Ctx, "rev-list").
"--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize*page), AddArguments(CmdArg("--max-count=" + strconv.Itoa(setting.Git.CommitsRangeSize*page))).
"--skip="+strconv.Itoa(skip), AddArguments(CmdArg("--skip=" + strconv.Itoa(skip)))
)
gitCmd.AddDynamicArguments(revision) gitCmd.AddDynamicArguments(revision)
gitCmd.AddArguments("--", file) gitCmd.AddDashesAndList(file)
err := gitCmd.Run(&RunOpts{ err := gitCmd.Run(&RunOpts{
Dir: repo.Path, Dir: repo.Path,
Stdout: stdoutWriter, Stdout: stdoutWriter,
@ -257,11 +258,11 @@ func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (
// 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 := NewCommand(repo.Ctx, "diff", "--name-only", startCommitID+"..."+endCommitID).RunStdString(&RunOpts{Dir: repo.Path}) stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(startCommitID + "..." + endCommitID).RunStdString(&RunOpts{Dir: repo.Path})
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 = NewCommand(repo.Ctx, "diff", "--name-only", startCommitID, endCommitID).RunStdString(&RunOpts{Dir: repo.Path}) stdout, _, err = NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(startCommitID, endCommitID).RunStdString(&RunOpts{Dir: repo.Path})
} }
if err != nil { if err != nil {
return 0, err return 0, err
@ -275,13 +276,13 @@ 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 = NewCommand(repo.Ctx, "rev-list", last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path}) stdout, _, err = NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
} else { } else {
stdout, _, err = NewCommand(repo.Ctx, "rev-list", before.ID.String()+".."+last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path}) stdout, _, err = NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(before.ID.String() + ".." + last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
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 = NewCommand(repo.Ctx, "rev-list", before.ID.String(), last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path}) stdout, _, err = NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
} }
} }
if err != nil { if err != nil {
@ -295,13 +296,22 @@ func (repo *Repository) CommitsBetweenLimit(last, before *Commit, limit, skip in
var stdout []byte var stdout []byte
var err error var err error
if before == nil { if before == nil {
stdout, _, err = NewCommand(repo.Ctx, "rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path}) stdout, _, err = NewCommand(repo.Ctx, "rev-list",
"--max-count", CmdArg(strconv.Itoa(limit)),
"--skip", CmdArg(strconv.Itoa(skip))).
AddDynamicArguments(last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
} else { } else {
stdout, _, err = NewCommand(repo.Ctx, "rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String()+".."+last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path}) stdout, _, err = NewCommand(repo.Ctx, "rev-list",
"--max-count", CmdArg(strconv.Itoa(limit)),
"--skip", CmdArg(strconv.Itoa(skip))).
AddDynamicArguments(before.ID.String() + ".." + last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
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 = NewCommand(repo.Ctx, "rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String(), last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path}) stdout, _, err = NewCommand(repo.Ctx, "rev-list",
"--max-count", CmdArg(strconv.Itoa(limit)),
"--skip", CmdArg(strconv.Itoa(skip))).
AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
} }
} }
if err != nil { if err != nil {
@ -342,9 +352,9 @@ func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
func (repo *Repository) commitsBefore(id SHA1, limit int) ([]*Commit, error) { func (repo *Repository) commitsBefore(id SHA1, limit int) ([]*Commit, error) {
cmd := NewCommand(repo.Ctx, "log") cmd := NewCommand(repo.Ctx, "log")
if limit > 0 { if limit > 0 {
cmd.AddArguments("-"+strconv.Itoa(limit), prettyLogFormat, id.String()) cmd.AddArguments(CmdArg("-"+strconv.Itoa(limit)), prettyLogFormat).AddDynamicArguments(id.String())
} else { } else {
cmd.AddArguments(prettyLogFormat, id.String()) cmd.AddArguments(prettyLogFormat).AddDynamicArguments(id.String())
} }
stdout, _, runErr := cmd.RunStdBytes(&RunOpts{Dir: repo.Path}) stdout, _, runErr := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
@ -384,7 +394,11 @@ func (repo *Repository) getCommitsBeforeLimit(id SHA1, num int) ([]*Commit, erro
func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) { func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) {
if CheckGitVersionAtLeast("2.7.0") == nil { if CheckGitVersionAtLeast("2.7.0") == nil {
stdout, _, err := NewCommand(repo.Ctx, "for-each-ref", "--count="+strconv.Itoa(limit), "--format=%(refname:strip=2)", "--contains", commit.ID.String(), BranchPrefix).RunStdString(&RunOpts{Dir: repo.Path}) stdout, _, err := NewCommand(repo.Ctx, "for-each-ref",
CmdArg("--count="+strconv.Itoa(limit)),
"--format=%(refname:strip=2)", "--contains").
AddDynamicArguments(commit.ID.String(), BranchPrefix).
RunStdString(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -393,7 +407,7 @@ func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error)
return branches, nil return branches, nil
} }
stdout, _, err := NewCommand(repo.Ctx, "branch", "--contains", commit.ID.String()).RunStdString(&RunOpts{Dir: repo.Path}) stdout, _, err := NewCommand(repo.Ctx, "branch", "--contains").AddDynamicArguments(commit.ID.String()).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -432,7 +446,7 @@ 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 := NewCommand(repo.Ctx, "branch", "--contains", commitID, branch).RunStdString(&RunOpts{Dir: repo.Path}) stdout, _, err := NewCommand(repo.Ctx, "branch", "--contains").AddDynamicArguments(commitID, branch).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@ -49,7 +49,7 @@ func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
} }
} }
actualCommitID, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify", commitID).RunStdString(&RunOpts{Dir: repo.Path}) actualCommitID, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(commitID).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
if strings.Contains(err.Error(), "unknown revision or path") || if strings.Contains(err.Error(), "unknown revision or path") ||
strings.Contains(err.Error(), "fatal: Needed a single revision") { strings.Contains(err.Error(), "fatal: Needed a single revision") {

View File

@ -17,7 +17,7 @@ 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 := NewCommand(repo.Ctx, "show-ref", "--hash", name).RunStdString(&RunOpts{Dir: repo.Path}) stdout, _, err := NewCommand(repo.Ctx, "show-ref", "--hash").AddDynamicArguments(name).RunStdString(&RunOpts{Dir: repo.Path})
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, ""}
@ -50,19 +50,19 @@ func (repo *Repository) GetRefCommitID(name string) (string, error) {
// SetReference sets the commit ID string of given reference (e.g. branch or tag). // SetReference sets the commit ID string of given reference (e.g. branch or tag).
func (repo *Repository) SetReference(name, commitID string) error { func (repo *Repository) SetReference(name, commitID string) error {
_, _, err := NewCommand(repo.Ctx, "update-ref", name, commitID).RunStdString(&RunOpts{Dir: repo.Path}) _, _, err := NewCommand(repo.Ctx, "update-ref").AddDynamicArguments(name, commitID).RunStdString(&RunOpts{Dir: repo.Path})
return err return err
} }
// RemoveReference removes the given reference (e.g. branch or tag). // RemoveReference removes the given reference (e.g. branch or tag).
func (repo *Repository) RemoveReference(name string) error { func (repo *Repository) RemoveReference(name string) error {
_, _, err := NewCommand(repo.Ctx, "update-ref", "--no-deref", "-d", name).RunStdString(&RunOpts{Dir: repo.Path}) _, _, err := NewCommand(repo.Ctx, "update-ref", "--no-deref", "-d").AddDynamicArguments(name).RunStdString(&RunOpts{Dir: repo.Path})
return err return err
} }
// IsCommitExist returns true if given commit exists in current repository. // IsCommitExist returns true if given commit exists in current repository.
func (repo *Repository) IsCommitExist(name string) bool { func (repo *Repository) IsCommitExist(name string) bool {
_, _, err := NewCommand(repo.Ctx, "cat-file", "-e", name).RunStdString(&RunOpts{Dir: repo.Path}) _, _, err := NewCommand(repo.Ctx, "cat-file", "-e").AddDynamicArguments(name).RunStdString(&RunOpts{Dir: repo.Path})
return err == nil return err == nil
} }

View File

@ -40,13 +40,13 @@ 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 := NewCommand(repo.Ctx, "fetch", "--no-tags", tmpRemote, "--", base+":"+tmpBaseName).RunStdString(&RunOpts{Dir: repo.Path}) _, _, err := NewCommand(repo.Ctx, "fetch", "--no-tags").AddDynamicArguments(tmpRemote).AddDashesAndList(base + ":" + tmpBaseName).RunStdString(&RunOpts{Dir: repo.Path})
if err == nil { if err == nil {
base = tmpBaseName base = tmpBaseName
} }
} }
stdout, _, err := NewCommand(repo.Ctx, "merge-base", "--", base, head).RunStdString(&RunOpts{Dir: repo.Path}) stdout, _, err := NewCommand(repo.Ctx, "merge-base").AddDashesAndList(base, head).RunStdString(&RunOpts{Dir: repo.Path})
return strings.TrimSpace(stdout), base, err return strings.TrimSpace(stdout), base, err
} }
@ -94,7 +94,7 @@ func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string,
// We have a common base - therefore we know that ... should work // We have a common base - therefore we know that ... should work
if !fileOnly { if !fileOnly {
var logs []byte var logs []byte
logs, _, err = NewCommand(repo.Ctx, "log", baseCommitID+separator+headBranch, prettyLogFormat).RunStdBytes(&RunOpts{Dir: repo.Path}) logs, _, err = NewCommand(repo.Ctx, "log").AddDynamicArguments(baseCommitID + separator + headBranch).AddArguments(prettyLogFormat).RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -147,7 +147,7 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
separator = ".." separator = ".."
} }
if err := NewCommand(repo.Ctx, "diff", "-z", "--name-only", base+separator+head). if err := NewCommand(repo.Ctx, "diff", "-z", "--name-only").AddDynamicArguments(base + separator + head).
Run(&RunOpts{ Run(&RunOpts{
Dir: repo.Path, Dir: repo.Path,
Stdout: w, Stdout: w,
@ -158,7 +158,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... // 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 = NewCommand(repo.Ctx, "diff", "-z", "--name-only", base, head).Run(&RunOpts{ if err = NewCommand(repo.Ctx, "diff", "-z", "--name-only").AddDynamicArguments(base, head).Run(&RunOpts{
Dir: repo.Path, Dir: repo.Path,
Stdout: w, Stdout: w,
Stderr: stderr, Stderr: stderr,
@ -173,20 +173,20 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
// GetDiffShortStat counts number of changed files, number of additions and deletions // GetDiffShortStat counts number of changed files, number of additions and deletions
func (repo *Repository) GetDiffShortStat(base, head string) (numFiles, totalAdditions, totalDeletions int, err error) { func (repo *Repository) GetDiffShortStat(base, head string) (numFiles, totalAdditions, totalDeletions int, err error) {
numFiles, totalAdditions, totalDeletions, err = GetDiffShortStat(repo.Ctx, repo.Path, base+"..."+head) numFiles, totalAdditions, totalDeletions, err = GetDiffShortStat(repo.Ctx, repo.Path, CmdArgCheck(base+"..."+head))
if err != nil && strings.Contains(err.Error(), "no merge base") { if err != nil && strings.Contains(err.Error(), "no merge base") {
return GetDiffShortStat(repo.Ctx, repo.Path, base, head) return GetDiffShortStat(repo.Ctx, repo.Path, CmdArgCheck(base), CmdArgCheck(head))
} }
return numFiles, totalAdditions, totalDeletions, err return numFiles, totalAdditions, totalDeletions, err
} }
// GetDiffShortStat counts number of changed files, number of additions and deletions // GetDiffShortStat counts number of changed files, number of additions and deletions
func GetDiffShortStat(ctx context.Context, repoPath string, args ...string) (numFiles, totalAdditions, totalDeletions int, err error) { func GetDiffShortStat(ctx context.Context, repoPath string, args ...CmdArg) (numFiles, totalAdditions, totalDeletions int, err error) {
// Now if we call: // Now if we call:
// $ git diff --shortstat 1ebb35b98889ff77299f24d82da426b434b0cca0...788b8b1440462d477f45b0088875 // $ git diff --shortstat 1ebb35b98889ff77299f24d82da426b434b0cca0...788b8b1440462d477f45b0088875
// we get: // we get:
// " 9902 files changed, 2034198 insertions(+), 298800 deletions(-)\n" // " 9902 files changed, 2034198 insertions(+), 298800 deletions(-)\n"
args = append([]string{ args = append([]CmdArg{
"diff", "diff",
"--shortstat", "--shortstat",
}, args...) }, args...)
@ -247,7 +247,7 @@ func (repo *Repository) GetDiffOrPatch(base, head string, w io.Writer, patch, bi
// GetDiff generates and returns patch data between given revisions, optimized for human readability // GetDiff generates and returns patch data between given revisions, optimized for human readability
func (repo *Repository) GetDiff(base, head string, w io.Writer) error { func (repo *Repository) GetDiff(base, head string, w io.Writer) error {
return NewCommand(repo.Ctx, "diff", "-p", base, head).Run(&RunOpts{ return NewCommand(repo.Ctx, "diff", "-p").AddDynamicArguments(base, head).Run(&RunOpts{
Dir: repo.Path, Dir: repo.Path,
Stdout: w, Stdout: w,
}) })
@ -255,7 +255,7 @@ func (repo *Repository) GetDiff(base, head string, w io.Writer) error {
// 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(base, head string, w io.Writer) error { func (repo *Repository) GetDiffBinary(base, head string, w io.Writer) error {
return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--histogram", base, head).Run(&RunOpts{ return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--histogram").AddDynamicArguments(base, head).Run(&RunOpts{
Dir: repo.Path, Dir: repo.Path,
Stdout: w, Stdout: w,
}) })
@ -264,14 +264,14 @@ func (repo *Repository) GetDiffBinary(base, head string, w io.Writer) error {
// 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(base, head string, w io.Writer) error { func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
err := NewCommand(repo.Ctx, "format-patch", "--binary", "--stdout", base+"..."+head). err := NewCommand(repo.Ctx, "format-patch", "--binary", "--stdout").AddDynamicArguments(base + "..." + head).
Run(&RunOpts{ Run(&RunOpts{
Dir: repo.Path, Dir: repo.Path,
Stdout: w, Stdout: w,
Stderr: stderr, Stderr: stderr,
}) })
if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) { if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) {
return NewCommand(repo.Ctx, "format-patch", "--binary", "--stdout", base, head). return NewCommand(repo.Ctx, "format-patch", "--binary", "--stdout").AddDynamicArguments(base, head).
Run(&RunOpts{ Run(&RunOpts{
Dir: repo.Path, Dir: repo.Path,
Stdout: w, Stdout: w,
@ -282,7 +282,7 @@ func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
// 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
func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, error) { func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, error) {
stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only", base+".."+head).RunStdString(&RunOpts{Dir: repo.Path}) stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(base + ".." + head).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -292,7 +292,7 @@ func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, err
// GetDiffFromMergeBase generates and return patch data from merge base to head // GetDiffFromMergeBase generates and return patch data from merge base to head
func (repo *Repository) GetDiffFromMergeBase(base, head string, w io.Writer) error { func (repo *Repository) GetDiffFromMergeBase(base, head string, w io.Writer) error {
stderr := new(bytes.Buffer) stderr := new(bytes.Buffer)
err := NewCommand(repo.Ctx, "diff", "-p", "--binary", base+"..."+head). err := NewCommand(repo.Ctx, "diff", "-p", "--binary").AddDynamicArguments(base + "..." + head).
Run(&RunOpts{ Run(&RunOpts{
Dir: repo.Path, Dir: repo.Path,
Stdout: w, Stdout: w,

View File

@ -18,7 +18,7 @@ import (
// ReadTreeToIndex reads a treeish to the index // ReadTreeToIndex reads a treeish to the index
func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string) error { func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string) error {
if len(treeish) != 40 { if len(treeish) != 40 {
res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify", treeish).RunStdString(&RunOpts{Dir: repo.Path}) res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(treeish).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return err return err
} }
@ -38,7 +38,7 @@ func (repo *Repository) readTreeToIndex(id SHA1, indexFilename ...string) error
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 := NewCommand(repo.Ctx, "read-tree", id.String()).RunStdString(&RunOpts{Dir: repo.Path, Env: env}) _, _, err := NewCommand(repo.Ctx, "read-tree").AddDynamicArguments(id.String()).RunStdString(&RunOpts{Dir: repo.Path, Env: env})
if err != nil { if err != nil {
return err return err
} }
@ -75,12 +75,7 @@ func (repo *Repository) EmptyIndex() error {
// 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 := NewCommand(repo.Ctx, "ls-files", "-z", "--") cmd := NewCommand(repo.Ctx, "ls-files", "-z").AddDashesAndList(filenames...)
for _, arg := range filenames {
if arg != "" {
cmd.AddArguments(arg)
}
}
res, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path}) res, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return nil, err return nil, err
@ -116,7 +111,7 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
// 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
func (repo *Repository) AddObjectToIndex(mode string, object SHA1, filename string) error { func (repo *Repository) AddObjectToIndex(mode string, object SHA1, filename string) error {
cmd := NewCommand(repo.Ctx, "update-index", "--add", "--replace", "--cacheinfo", mode, object.String(), filename) cmd := NewCommand(repo.Ctx, "update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments(mode, object.String(), filename)
_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) _, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
return err return err
} }

View File

@ -41,7 +41,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
since := fromTime.Format(time.RFC3339) since := fromTime.Format(time.RFC3339)
stdout, _, runErr := NewCommand(repo.Ctx, "rev-list", "--count", "--no-merges", "--branches=*", "--date=iso", fmt.Sprintf("--since='%s'", since)).RunStdString(&RunOpts{Dir: repo.Path}) stdout, _, runErr := NewCommand(repo.Ctx, "rev-list", "--count", "--no-merges", "--branches=*", "--date=iso", CmdArg(fmt.Sprintf("--since='%s'", since))).RunStdString(&RunOpts{Dir: repo.Path})
if runErr != nil { if runErr != nil {
return nil, runErr return nil, runErr
} }
@ -61,7 +61,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
_ = stdoutWriter.Close() _ = stdoutWriter.Close()
}() }()
gitCmd := NewCommand(repo.Ctx, "log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso", fmt.Sprintf("--since='%s'", since)) gitCmd := NewCommand(repo.Ctx, "log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso", CmdArg(fmt.Sprintf("--since='%s'", since)))
if len(branch) == 0 { if len(branch) == 0 {
gitCmd.AddArguments("--branches=*") gitCmd.AddArguments("--branches=*")
} else { } else {

View File

@ -25,13 +25,13 @@ func IsTagExist(ctx context.Context, repoPath, name string) bool {
// 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 := NewCommand(repo.Ctx, "tag", "--", name, revision).RunStdString(&RunOpts{Dir: repo.Path}) _, _, err := NewCommand(repo.Ctx, "tag").AddDashesAndList(name, revision).RunStdString(&RunOpts{Dir: repo.Path})
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 := NewCommand(repo.Ctx, "tag", "-a", "-m", message, "--", name, revision).RunStdString(&RunOpts{Dir: repo.Path}) _, _, err := NewCommand(repo.Ctx, "tag", "-a", "-m").AddDynamicArguments(message).AddDashesAndList(name, revision).RunStdString(&RunOpts{Dir: repo.Path})
return err return err
} }
@ -64,7 +64,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 := NewCommand(repo.Ctx, "show-ref", "--tags", "--", name).RunStdString(&RunOpts{Dir: repo.Path}) stdout, _, err := NewCommand(repo.Ctx, "show-ref", "--tags").AddDashesAndList(name).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return "", err return "", err
} }
@ -122,7 +122,7 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
rc := &RunOpts{Dir: repo.Path, Stdout: stdoutWriter, Stderr: &stderr} rc := &RunOpts{Dir: repo.Path, Stdout: stdoutWriter, Stderr: &stderr}
go func() { go func() {
err := NewCommand(repo.Ctx, "for-each-ref", "--format", forEachRefFmt.Flag(), "--sort", "-*creatordate", "refs/tags").Run(rc) err := NewCommand(repo.Ctx, "for-each-ref", CmdArg("--format="+forEachRefFmt.Flag()), "--sort", "-*creatordate", "refs/tags").Run(rc)
if err != nil { if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, stderr.String())) _ = stdoutWriter.CloseWithError(ConcatenateError(err, stderr.String()))
} else { } else {

View File

@ -26,7 +26,7 @@ func (repo *Repository) IsTagExist(name string) bool {
// GetTags returns all tags of the repository. // GetTags returns all tags of the repository.
// returning at most limit tags, or all if limit is 0. // returning at most limit tags, or all if limit is 0.
func (repo *Repository) GetTags(skip, limit int) (tags []string, err error) { func (repo *Repository) GetTags(skip, limit int) (tags []string, err error) {
tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, []string{TagPrefix, "--sort=-taggerdate"}, skip, limit) tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, []CmdArg{TagPrefix, "--sort=-taggerdate"}, skip, limit)
return tags, err return tags, err
} }

View File

@ -35,10 +35,10 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
"GIT_COMMITTER_EMAIL="+committer.Email, "GIT_COMMITTER_EMAIL="+committer.Email,
"GIT_COMMITTER_DATE="+commitTimeStr, "GIT_COMMITTER_DATE="+commitTimeStr,
) )
cmd := NewCommand(repo.Ctx, "commit-tree", tree.ID.String()) cmd := NewCommand(repo.Ctx, "commit-tree").AddDynamicArguments(tree.ID.String())
for _, parent := range opts.Parents { for _, parent := range opts.Parents {
cmd.AddArguments("-p", parent) cmd.AddArguments("-p").AddDynamicArguments(parent)
} }
messageBytes := new(bytes.Buffer) messageBytes := new(bytes.Buffer)
@ -46,7 +46,7 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
_, _ = messageBytes.WriteString("\n") _, _ = messageBytes.WriteString("\n")
if opts.KeyID != "" || opts.AlwaysSign { if opts.KeyID != "" || opts.AlwaysSign {
cmd.AddArguments(fmt.Sprintf("-S%s", opts.KeyID)) cmd.AddArguments(CmdArg(fmt.Sprintf("-S%s", opts.KeyID)))
} }
if opts.NoGPGSign { if opts.NoGPGSign {

View File

@ -21,7 +21,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) {
// GetTree find the tree object in the repository. // GetTree find the tree object in the repository.
func (repo *Repository) GetTree(idStr string) (*Tree, error) { func (repo *Repository) GetTree(idStr string) (*Tree, error) {
if len(idStr) != 40 { if len(idStr) != 40 {
res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify", idStr).RunStdString(&RunOpts{Dir: repo.Path}) res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(idStr).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -49,12 +49,9 @@ func (t *Tree) SubTree(rpath string) (*Tree, error) {
// LsTree checks if the given filenames are in the tree // LsTree checks if the given filenames are in the tree
func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error) { func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error) {
cmd := NewCommand(repo.Ctx, "ls-tree", "-z", "--name-only", "--", ref) cmd := NewCommand(repo.Ctx, "ls-tree", "-z", "--name-only").
for _, arg := range filenames { AddDashesAndList(append([]string{ref}, filenames...)...)
if arg != "" {
cmd.AddArguments(arg)
}
}
res, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path}) res, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -80,7 +80,7 @@ func (t *Tree) ListEntries() (Entries, error) {
} }
} }
stdout, _, runErr := NewCommand(t.repo.Ctx, "ls-tree", "-l", t.ID.String()).RunStdBytes(&RunOpts{Dir: t.repo.Path}) stdout, _, runErr := NewCommand(t.repo.Ctx, "ls-tree", "-l").AddDynamicArguments(t.ID.String()).RunStdBytes(&RunOpts{Dir: t.repo.Path})
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,13 +101,13 @@ func (t *Tree) ListEntries() (Entries, error) {
// listEntriesRecursive returns all entries of current tree recursively including all subtrees // listEntriesRecursive returns all entries of current tree recursively including all subtrees
// extraArgs could be "-l" to get the size, which is slower // extraArgs could be "-l" to get the size, which is slower
func (t *Tree) listEntriesRecursive(extraArgs ...string) (Entries, error) { func (t *Tree) listEntriesRecursive(extraArgs ...CmdArg) (Entries, error) {
if t.entriesRecursiveParsed { if t.entriesRecursiveParsed {
return t.entriesRecursive, nil return t.entriesRecursive, nil
} }
args := append([]string{"ls-tree", "-t", "-r"}, extraArgs...) args := append([]CmdArg{"ls-tree", "-t", "-r"}, extraArgs...)
args = append(args, t.ID.String()) args = append(args, CmdArg(t.ID.String()))
stdout, _, runErr := NewCommand(t.repo.Ctx, args...).RunStdBytes(&RunOpts{Dir: t.repo.Path}) stdout, _, runErr := NewCommand(t.repo.Ctx, args...).RunStdBytes(&RunOpts{Dir: t.repo.Path})
if runErr != nil { if runErr != nil {
return nil, runErr return nil, runErr

View File

@ -37,16 +37,15 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
graphCmd.AddArguments( graphCmd.AddArguments(
"-C", "-C",
"-M", "-M",
fmt.Sprintf("-n %d", setting.UI.GraphMaxCommitNum*page), git.CmdArg(fmt.Sprintf("-n %d", setting.UI.GraphMaxCommitNum*page)),
"--date=iso", "--date=iso",
fmt.Sprintf("--pretty=format:%s", format)) git.CmdArg(fmt.Sprintf("--pretty=format:%s", format)))
if len(branches) > 0 { if len(branches) > 0 {
graphCmd.AddDynamicArguments(branches...) graphCmd.AddDynamicArguments(branches...)
} }
if len(files) > 0 { if len(files) > 0 {
graphCmd.AddArguments("--") graphCmd.AddDashesAndList(files...)
graphCmd.AddArguments(files...)
} }
graph := NewGraph() graph := NewGraph()

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