mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-27 00:23:41 +09:00 
			
		
		
		
	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:
		| @@ -83,17 +83,17 @@ func fixMergeBase(x *xorm.Engine) error { | ||||
|  | ||||
| 			if !pr.HasMerged { | ||||
| 				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 { | ||||
| 					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 { | ||||
| 						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 | ||||
| 					} | ||||
| 				} | ||||
| 			} 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 { | ||||
| 					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 | ||||
| @@ -103,10 +103,11 @@ func fixMergeBase(x *xorm.Engine) error { | ||||
| 					continue | ||||
| 				} | ||||
|  | ||||
| 				args := append([]string{"merge-base", "--"}, parents[1:]...) | ||||
| 				args = append(args, gitRefName) | ||||
| 				refs := append([]string{}, parents[1:]...) | ||||
| 				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 { | ||||
| 					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 | ||||
|   | ||||
| @@ -80,7 +80,7 @@ func refixMergeBase(x *xorm.Engine) error { | ||||
|  | ||||
| 			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 { | ||||
| 				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 | ||||
| @@ -91,10 +91,11 @@ func refixMergeBase(x *xorm.Engine) error { | ||||
| 			} | ||||
|  | ||||
| 			// we should recalculate | ||||
| 			args := append([]string{"merge-base", "--"}, parents[1:]...) | ||||
| 			args = append(args, gitRefName) | ||||
| 			refs := append([]string{}, parents[1:]...) | ||||
| 			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 { | ||||
| 				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 | ||||
|   | ||||
| @@ -21,7 +21,7 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) | ||||
| 		numRepos++ | ||||
| 		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) | ||||
|  | ||||
| @@ -49,7 +49,7 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) | ||||
| 		} | ||||
|  | ||||
| 		// 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 { | ||||
| 			logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err) | ||||
| 			return nil | ||||
|   | ||||
| @@ -44,17 +44,17 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro | ||||
|  | ||||
| 			if !pr.HasMerged { | ||||
| 				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 { | ||||
| 					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 { | ||||
| 						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 | ||||
| 					} | ||||
| 				} | ||||
| 			} 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 { | ||||
| 					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 | ||||
| @@ -64,10 +64,10 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro | ||||
| 					return nil | ||||
| 				} | ||||
|  | ||||
| 				args := append([]string{"merge-base", "--"}, parents[1:]...) | ||||
| 				args = append(args, pr.GetGitRefName()) | ||||
|  | ||||
| 				pr.MergeBase, _, err = git.NewCommand(ctx, args...).RunStdString(&git.RunOpts{Dir: repoPath}) | ||||
| 				refs := append([]string{}, parents[1:]...) | ||||
| 				refs = append(refs, pr.GetGitRefName()) | ||||
| 				cmd := git.NewCommand(ctx, "merge-base").AddDashesAndList(refs...) | ||||
| 				pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath}) | ||||
| 				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) | ||||
| 					return nil | ||||
|   | ||||
| @@ -24,7 +24,7 @@ import ( | ||||
|  | ||||
| var ( | ||||
| 	// globalCommandArgs global command args for external package setting | ||||
| 	globalCommandArgs []string | ||||
| 	globalCommandArgs []CmdArg | ||||
|  | ||||
| 	// defaultCommandExecutionTimeout default command execution timeout duration | ||||
| 	defaultCommandExecutionTimeout = 360 * time.Second | ||||
| @@ -43,6 +43,8 @@ type Command struct { | ||||
| 	brokenArgs       []string | ||||
| } | ||||
|  | ||||
| type CmdArg string | ||||
|  | ||||
| func (c *Command) String() string { | ||||
| 	if len(c.args) == 0 { | ||||
| 		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. | ||||
| // 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 | ||||
| 	cargs := make([]string, len(globalCommandArgs)) | ||||
| 	copy(cargs, globalCommandArgs) | ||||
| 	cargs := make([]string, 0, len(globalCommandArgs)+len(args)) | ||||
| 	for _, arg := range globalCommandArgs { | ||||
| 		cargs = append(cargs, string(arg)) | ||||
| 	} | ||||
| 	for _, arg := range args { | ||||
| 		cargs = append(cargs, string(arg)) | ||||
| 	} | ||||
| 	return &Command{ | ||||
| 		name:             GitExecutable, | ||||
| 		args:             append(cargs, args...), | ||||
| 		args:             cargs, | ||||
| 		parentContext:    ctx, | ||||
| 		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 | ||||
| // 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...) | ||||
| } | ||||
|  | ||||
| // 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. | ||||
| 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{ | ||||
| 		name:          GitExecutable, | ||||
| 		args:          args, | ||||
| 		args:          cargs, | ||||
| 		parentContext: ctx, | ||||
| 	} | ||||
| } | ||||
| @@ -93,10 +104,12 @@ func (c *Command) SetDescription(desc string) *Command { | ||||
| 	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. | ||||
| func (c *Command) AddArguments(args ...string) *Command { | ||||
| 	c.args = append(c.args, args...) | ||||
| func (c *Command) AddArguments(args ...CmdArg) *Command { | ||||
| 	for _, arg := range args { | ||||
| 		c.args = append(c.args, string(arg)) | ||||
| 	} | ||||
| 	return c | ||||
| } | ||||
|  | ||||
| @@ -115,6 +128,26 @@ func (c *Command) AddDynamicArguments(args ...string) *Command { | ||||
| 	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. | ||||
| type RunOpts struct { | ||||
| 	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 { | ||||
| 	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 | ||||
| func AllowLFSFiltersArgs() []string { | ||||
| func AllowLFSFiltersArgs() []CmdArg { | ||||
| 	// Now here we should explicitly allow lfs filters to run | ||||
| 	filteredLFSGlobalArgs := make([]string, len(globalCommandArgs)) | ||||
| 	filteredLFSGlobalArgs := make([]CmdArg, len(globalCommandArgs)) | ||||
| 	j := 0 | ||||
| 	for _, arg := range globalCommandArgs { | ||||
| 		if strings.Contains(arg, "lfs") { | ||||
| 		if strings.Contains(string(arg), "lfs") { | ||||
| 			j-- | ||||
| 		} else { | ||||
| 			filteredLFSGlobalArgs[j] = arg | ||||
|   | ||||
| @@ -92,13 +92,13 @@ func AddChanges(repoPath string, all bool, files ...string) error { | ||||
| } | ||||
|  | ||||
| // 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")...) | ||||
| 	if all { | ||||
| 		cmd.AddArguments("--all") | ||||
| 	} | ||||
| 	cmd.AddArguments("--") | ||||
| 	_, _, err := cmd.AddArguments(files...).RunStdString(&RunOpts{Dir: repoPath}) | ||||
| 	cmd.AddDashesAndList(files...) | ||||
| 	_, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath}) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| @@ -112,17 +112,17 @@ type CommitChangesOptions struct { | ||||
| // CommitChanges commits local changes with given committer, author and message. | ||||
| // If author is nil, it will be the same as committer. | ||||
| func CommitChanges(repoPath string, opts CommitChangesOptions) error { | ||||
| 	cargs := make([]string, len(globalCommandArgs)) | ||||
| 	cargs := make([]CmdArg, len(globalCommandArgs)) | ||||
| 	copy(cargs, globalCommandArgs) | ||||
| 	return CommitChangesWithArgs(repoPath, cargs, opts) | ||||
| } | ||||
|  | ||||
| // CommitChangesWithArgs commits local changes with given committer, author and message. | ||||
| // 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...) | ||||
| 	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") | ||||
|  | ||||
| @@ -130,9 +130,9 @@ func CommitChangesWithArgs(repoPath string, args []string, opts CommitChangesOpt | ||||
| 		opts.Author = opts.Committer | ||||
| 	} | ||||
| 	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}) | ||||
| 	// 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 | ||||
| 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.AddArguments(args...) | ||||
| 	if hidePRRefs { | ||||
| 		cmd.AddArguments("--exclude=" + PullPrefix + "*") | ||||
| 	} | ||||
| 	cmd.AddArguments("--all", "--count") | ||||
| 	if len(files) > 0 { | ||||
| 		cmd.AddArguments("--") | ||||
| 		cmd.AddArguments(files...) | ||||
| 		cmd.AddDashesAndList(files...) | ||||
| 	} | ||||
|  | ||||
| 	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.AddDynamicArguments(revision...) | ||||
| 	if len(relpath) > 0 { | ||||
| 		cmd.AddArguments("--") | ||||
| 		cmd.AddArguments(relpath...) | ||||
| 		cmd.AddDashesAndList(relpath...) | ||||
| 	} | ||||
|  | ||||
| 	stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath}) | ||||
| @@ -209,7 +206,7 @@ func (c *Commit) HasPreviousCommit(commitHash SHA1) (bool, error) { | ||||
| 		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 { | ||||
| 		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') | ||||
| func (c *Commit) GetBranchName() (string, error) { | ||||
| 	args := []string{ | ||||
| 		"name-rev", | ||||
| 	} | ||||
| 	cmd := NewCommand(c.repo.Ctx, "name-rev") | ||||
| 	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()) | ||||
|  | ||||
| 	data, _, err := NewCommand(c.repo.Ctx, args...).RunStdString(&RunOpts{Dir: c.repo.Path}) | ||||
| 	cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String()) | ||||
| 	data, _, err := cmd.RunStdString(&RunOpts{Dir: c.repo.Path}) | ||||
| 	if err != nil { | ||||
| 		// handle special case where git can not describe commit | ||||
| 		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 | ||||
| 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 { | ||||
| 		// handle special case where there is no tag for this commit | ||||
| 		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) | ||||
| 	args := []string{"log", "--name-status", "-c", "--pretty=format:", "--parents", "--no-renames", "-z", "-1", commitID} | ||||
|  | ||||
| 	err := NewCommand(ctx, args...).Run(&RunOpts{ | ||||
| 	err := NewCommand(ctx, "log", "--name-status", "-c", "--pretty=format:", "--parents", "--no-renames", "-z", "-1").AddDynamicArguments(commitID).Run(&RunOpts{ | ||||
| 		Dir:    repoPath, | ||||
| 		Stdout: w, | ||||
| 		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. | ||||
| 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 strings.Contains(err.Error(), "exit status 128") { | ||||
| 			return "", ErrNotExist{shortID, ""} | ||||
|   | ||||
| @@ -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. | ||||
| func GetReverseRawDiff(ctx context.Context, repoPath, commitID string, writer io.Writer) error { | ||||
| 	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{ | ||||
| 		Dir:    repoPath, | ||||
| 		Stdout: writer, | ||||
| @@ -52,39 +52,38 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	fileArgs := make([]string, 0) | ||||
| 	var files []string | ||||
| 	if len(file) > 0 { | ||||
| 		fileArgs = append(fileArgs, "--", file) | ||||
| 		files = append(files, file) | ||||
| 	} | ||||
|  | ||||
| 	var args []string | ||||
| 	cmd := NewCommand(repo.Ctx) | ||||
| 	switch diffType { | ||||
| 	case RawDiffNormal: | ||||
| 		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 { | ||||
| 			args = append([]string{"show", endCommit}, fileArgs...) | ||||
| 			cmd.AddArguments("show").AddDynamicArguments(endCommit).AddDashesAndList(files...) | ||||
| 		} else { | ||||
| 			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: | ||||
| 		if len(startCommit) != 0 { | ||||
| 			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 { | ||||
| 			args = append([]string{"format-patch", "--no-signature", "--stdout", "--root", endCommit}, fileArgs...) | ||||
| 			cmd.AddArguments("format-patch", "--no-signature", "--stdout", "--root").AddDynamicArguments(endCommit).AddDashesAndList(files...) | ||||
| 		} else { | ||||
| 			c, _ := commit.Parent(0) | ||||
| 			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: | ||||
| 		return fmt.Errorf("invalid diffType: %s", diffType) | ||||
| 	} | ||||
|  | ||||
| 	stderr := new(bytes.Buffer) | ||||
| 	cmd := NewCommand(repo.Ctx, args...) | ||||
| 	if err = cmd.Run(&RunOpts{ | ||||
| 		Dir:    repo.Path, | ||||
| 		Stdout: writer, | ||||
| @@ -287,7 +286,7 @@ func GetAffectedFiles(repo *Repository, oldCommitID, newCommitID string, env []s | ||||
| 	affectedFiles := make([]string, 0, 32) | ||||
|  | ||||
| 	// 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{ | ||||
| 			Env:    env, | ||||
| 			Dir:    repo.Path, | ||||
|   | ||||
| @@ -313,7 +313,7 @@ func CheckGitVersionAtLeast(atLeast 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) { | ||||
| 		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 | ||||
| 	} | ||||
|  | ||||
| 	_, _, err = NewCommand(DefaultContext, "config", "--global", key, value).RunStdString(nil) | ||||
| 	_, _, err = NewCommand(DefaultContext, "config", "--global").AddDynamicArguments(key, value).RunStdString(nil) | ||||
| 	if err != nil { | ||||
| 		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 { | ||||
| 	_, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil) | ||||
| 	_, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key).RunStdString(nil) | ||||
| 	if err == nil { | ||||
| 		// already exist | ||||
| 		return nil | ||||
| 	} | ||||
| 	if err.IsExitCode(1) { | ||||
| 		// 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 { | ||||
| 			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 { | ||||
| 	_, _, 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 { | ||||
| 		// already exist | ||||
| 		return nil | ||||
| 	} | ||||
| 	if err.IsExitCode(1) { | ||||
| 		// 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 { | ||||
| 			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 { | ||||
| 	_, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil) | ||||
| 	_, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key).RunStdString(nil) | ||||
| 	if err == nil { | ||||
| 		// 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 { | ||||
| 			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 | ||||
| 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}) | ||||
| } | ||||
|   | ||||
| @@ -35,30 +35,33 @@ func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, p | ||||
| 		_ = stdoutWriter.Close() | ||||
| 	} | ||||
|  | ||||
| 	args := make([]string, 0, 8+len(paths)) | ||||
| 	args = append(args, "log", "--name-status", "-c", "--format=commit%x00%H %P%x00", "--parents", "--no-renames", "-t", "-z", head, "--") | ||||
| 	cmd := NewCommand(ctx) | ||||
| 	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 treepath != "" { | ||||
| 			args = append(args, treepath) | ||||
| 			files = append(files, treepath) | ||||
| 			for _, pth := range paths { | ||||
| 				if pth != "" { | ||||
| 					args = append(args, path.Join(treepath, pth)) | ||||
| 					files = append(files, path.Join(treepath, pth)) | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			for _, pth := range paths { | ||||
| 				if pth != "" { | ||||
| 					args = append(args, pth) | ||||
| 					files = append(files, pth) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} else if treepath != "" { | ||||
| 		args = append(args, treepath) | ||||
| 		files = append(files, treepath) | ||||
| 	} | ||||
| 	cmd.AddDashesAndList(files...) | ||||
|  | ||||
| 	go func() { | ||||
| 		stderr := strings.Builder{} | ||||
| 		err := NewCommand(ctx, args...).Run(&RunOpts{ | ||||
| 		err := cmd.Run(&RunOpts{ | ||||
| 			Dir:    repository, | ||||
| 			Stdout: stdoutWriter, | ||||
| 			Stderr: &stderr, | ||||
|   | ||||
| @@ -43,7 +43,7 @@ func RevListObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync. | ||||
| 	defer revListWriter.Close() | ||||
| 	stderr := new(bytes.Buffer) | ||||
| 	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{ | ||||
| 		Dir:    tmpBasePath, | ||||
| 		Stdout: revListWriter, | ||||
|   | ||||
| @@ -14,9 +14,9 @@ import ( | ||||
| func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) { | ||||
| 	var cmd *Command | ||||
| 	if CheckGitVersionAtLeast("2.7") == nil { | ||||
| 		cmd = NewCommand(ctx, "remote", "get-url", remoteName) | ||||
| 		cmd = NewCommand(ctx, "remote", "get-url").AddDynamicArguments(remoteName) | ||||
| 	} 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}) | ||||
|   | ||||
| @@ -59,7 +59,7 @@ func (repo *Repository) parsePrettyFormatLogToList(logs []byte) ([]*Commit, erro | ||||
|  | ||||
| // IsRepoURLAccessible checks if given repository URL is accessible. | ||||
| func IsRepoURLAccessible(ctx context.Context, url string) bool { | ||||
| 	_, _, err := NewCommand(ctx, "ls-remote", "-q", "-h", url, "HEAD").RunStdString(nil) | ||||
| 	_, _, err := NewCommand(ctx, "ls-remote", "-q", "-h").AddDynamicArguments(url, "HEAD").RunStdString(nil) | ||||
| 	return err == nil | ||||
| } | ||||
|  | ||||
| @@ -112,13 +112,11 @@ type CloneRepoOptions struct { | ||||
|  | ||||
| // Clone clones original repository to target path. | ||||
| func Clone(ctx context.Context, from, to string, opts CloneRepoOptions) error { | ||||
| 	cargs := make([]string, len(globalCommandArgs)) | ||||
| 	copy(cargs, globalCommandArgs) | ||||
| 	return CloneWithArgs(ctx, from, to, cargs, opts) | ||||
| 	return CloneWithArgs(ctx, globalCommandArgs, from, to, opts) | ||||
| } | ||||
|  | ||||
| // 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) | ||||
| 	if err = os.MkdirAll(toDir, os.ModePerm); err != nil { | ||||
| 		return err | ||||
| @@ -144,15 +142,15 @@ func CloneWithArgs(ctx context.Context, from, to string, args []string, opts Clo | ||||
| 		cmd.AddArguments("--no-checkout") | ||||
| 	} | ||||
| 	if opts.Depth > 0 { | ||||
| 		cmd.AddArguments("--depth", strconv.Itoa(opts.Depth)) | ||||
| 		cmd.AddArguments("--depth").AddDynamicArguments(strconv.Itoa(opts.Depth)) | ||||
| 	} | ||||
| 	if opts.Filter != "" { | ||||
| 		cmd.AddArguments("--filter", opts.Filter) | ||||
| 		cmd.AddArguments("--filter").AddDynamicArguments(opts.Filter) | ||||
| 	} | ||||
| 	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, "@") { | ||||
| 		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 { | ||||
| 		cmd.AddArguments("--mirror") | ||||
| 	} | ||||
| 	cmd.AddArguments("--", opts.Remote) | ||||
| 	remoteBranchArgs := []string{opts.Remote} | ||||
| 	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, "@") { | ||||
| 		cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, util.SanitizeCredentialURLs(opts.Remote), opts.Force, opts.Mirror)) | ||||
| 	} else { | ||||
| @@ -276,7 +276,7 @@ type DivergeObject struct { | ||||
|  | ||||
| func checkDivergence(ctx context.Context, repoPath, baseBranch, targetBranch string) (int, error) { | ||||
| 	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}) | ||||
| 	if err != nil { | ||||
| 		return -1, err | ||||
| @@ -319,7 +319,7 @@ func (repo *Repository) CreateBundle(ctx context.Context, commit string, out io. | ||||
| 		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 { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -330,7 +330,7 @@ func (repo *Repository) CreateBundle(ctx context.Context, commit string, out io. | ||||
| 	} | ||||
|  | ||||
| 	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 { | ||||
| 		return err | ||||
| 	} | ||||
|   | ||||
| @@ -44,20 +44,15 @@ func (repo *Repository) CreateArchive(ctx context.Context, format ArchiveType, t | ||||
| 		return fmt.Errorf("unknown format: %v", format) | ||||
| 	} | ||||
|  | ||||
| 	args := []string{ | ||||
| 		"archive", | ||||
| 	} | ||||
| 	cmd := NewCommand(ctx, "archive") | ||||
| 	if usePrefix { | ||||
| 		args = append(args, "--prefix="+filepath.Base(strings.TrimSuffix(repo.Path, ".git"))+"/") | ||||
| 		cmd.AddArguments(CmdArg("--prefix=" + filepath.Base(strings.TrimSuffix(repo.Path, ".git")) + "/")) | ||||
| 	} | ||||
|  | ||||
| 	args = append(args, | ||||
| 		"--format="+format.String(), | ||||
| 		commitID, | ||||
| 	) | ||||
| 	cmd.AddArguments(CmdArg("--format=" + format.String())) | ||||
| 	cmd.AddDynamicArguments(commitID) | ||||
|  | ||||
| 	var stderr strings.Builder | ||||
| 	err := NewCommand(ctx, args...).Run(&RunOpts{ | ||||
| 	err := cmd.Run(&RunOpts{ | ||||
| 		Dir:    repo.Path, | ||||
| 		Stdout: target, | ||||
| 		Stderr: &stderr, | ||||
|   | ||||
| @@ -20,7 +20,7 @@ import ( | ||||
| type CheckAttributeOpts struct { | ||||
| 	CachedOnly    bool | ||||
| 	AllAttributes bool | ||||
| 	Attributes    []string | ||||
| 	Attributes    []CmdArg | ||||
| 	Filenames     []string | ||||
| 	IndexFile     string | ||||
| 	WorkTree      string | ||||
| @@ -44,31 +44,23 @@ func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[ | ||||
| 	stdOut := new(bytes.Buffer) | ||||
| 	stdErr := new(bytes.Buffer) | ||||
|  | ||||
| 	cmdArgs := []string{"check-attr", "-z"} | ||||
| 	cmd := NewCommand(repo.Ctx, "check-attr", "-z") | ||||
|  | ||||
| 	if opts.AllAttributes { | ||||
| 		cmdArgs = append(cmdArgs, "-a") | ||||
| 		cmd.AddArguments("-a") | ||||
| 	} else { | ||||
| 		for _, attribute := range opts.Attributes { | ||||
| 			if attribute != "" { | ||||
| 				cmdArgs = append(cmdArgs, attribute) | ||||
| 				cmd.AddArguments(attribute) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if opts.CachedOnly { | ||||
| 		cmdArgs = append(cmdArgs, "--cached") | ||||
| 		cmd.AddArguments("--cached") | ||||
| 	} | ||||
|  | ||||
| 	cmdArgs = append(cmdArgs, "--") | ||||
|  | ||||
| 	for _, arg := range opts.Filenames { | ||||
| 		if arg != "" { | ||||
| 			cmdArgs = append(cmdArgs, arg) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	cmd := NewCommand(repo.Ctx, cmdArgs...) | ||||
| 	cmd.AddDashesAndList(opts.Filenames...) | ||||
|  | ||||
| 	if err := cmd.Run(&RunOpts{ | ||||
| 		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 | ||||
| type CheckAttributeReader struct { | ||||
| 	// params | ||||
| 	Attributes []string | ||||
| 	Attributes []CmdArg | ||||
| 	Repo       *Repository | ||||
| 	IndexFile  string | ||||
| 	WorkTree   string | ||||
| @@ -122,7 +114,7 @@ type CheckAttributeReader struct { | ||||
|  | ||||
| // Init initializes the CheckAttributeReader | ||||
| 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 { | ||||
| 		cmdArgs = append(cmdArgs, "--cached") | ||||
| @@ -401,7 +393,7 @@ func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeRe | ||||
| 	} | ||||
|  | ||||
| 	checker := &CheckAttributeReader{ | ||||
| 		Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"}, | ||||
| 		Attributes: []CmdArg{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"}, | ||||
| 		Repo:       repo, | ||||
| 		IndexFile:  indexFilename, | ||||
| 		WorkTree:   worktree, | ||||
|   | ||||
| @@ -8,13 +8,16 @@ import "fmt" | ||||
|  | ||||
| // FileBlame return the Blame object of file | ||||
| 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 | ||||
| } | ||||
|  | ||||
| // LineBlame returns the latest commit at the given line | ||||
| 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 { | ||||
| 		return nil, err | ||||
| 	} | ||||
|   | ||||
| @@ -25,7 +25,7 @@ const PullRequestPrefix = "refs/for/" | ||||
|  | ||||
| // IsReferenceExist returns true if given reference exists in the repository. | ||||
| 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 | ||||
| } | ||||
|  | ||||
| @@ -66,7 +66,7 @@ func (repo *Repository) GetHEADBranch() (*Branch, error) { | ||||
|  | ||||
| // SetDefaultBranch sets default branch of repository. | ||||
| 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 | ||||
| } | ||||
|  | ||||
| @@ -141,7 +141,7 @@ func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) erro | ||||
| 		cmd.AddArguments("-d") | ||||
| 	} | ||||
|  | ||||
| 	cmd.AddArguments("--", name) | ||||
| 	cmd.AddDashesAndList(name) | ||||
| 	_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) | ||||
|  | ||||
| 	return err | ||||
| @@ -150,7 +150,7 @@ func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) erro | ||||
| // CreateBranch create a new branch | ||||
| func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error { | ||||
| 	cmd := NewCommand(repo.Ctx, "branch") | ||||
| 	cmd.AddArguments("--", branch, oldbranchOrCommit) | ||||
| 	cmd.AddDashesAndList(branch, oldbranchOrCommit) | ||||
|  | ||||
| 	_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) | ||||
|  | ||||
| @@ -163,7 +163,7 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error { | ||||
| 	if fetch { | ||||
| 		cmd.AddArguments("-f") | ||||
| 	} | ||||
| 	cmd.AddArguments(name, url) | ||||
| 	cmd.AddDynamicArguments(name, url) | ||||
|  | ||||
| 	_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) | ||||
| 	return err | ||||
| @@ -171,7 +171,7 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error { | ||||
|  | ||||
| // RemoveRemote removes a remote from repository. | ||||
| 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 | ||||
| } | ||||
|  | ||||
| @@ -182,6 +182,6 @@ func (branch *Branch) GetCommit() (*Commit, error) { | ||||
|  | ||||
| // RenameBranch rename a branch | ||||
| 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 | ||||
| } | ||||
|   | ||||
| @@ -63,7 +63,7 @@ func (repo *Repository) IsBranchExist(name string) bool { | ||||
| // GetBranchNames returns branches from the repository, skipping skip initial branches and | ||||
| // returning at most limit branches, or all branches if limit is 0. | ||||
| func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) { | ||||
| 	return callShowRef(repo.Ctx, repo.Path, BranchPrefix, []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 | ||||
| @@ -74,19 +74,19 @@ func WalkReferences(ctx context.Context, repoPath string, walkfn func(sha1, refn | ||||
| // WalkReferences walks all the references from the repository | ||||
| // refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty. | ||||
| func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) { | ||||
| 	var args []string | ||||
| 	var args []CmdArg | ||||
| 	switch refType { | ||||
| 	case ObjectTag: | ||||
| 		args = []string{TagPrefix, "--sort=-taggerdate"} | ||||
| 		args = []CmdArg{TagPrefix, "--sort=-taggerdate"} | ||||
| 	case ObjectBranch: | ||||
| 		args = []string{BranchPrefix, "--sort=-committerdate"} | ||||
| 		args = []CmdArg{BranchPrefix, "--sort=-committerdate"} | ||||
| 	} | ||||
|  | ||||
| 	return walkShowRef(repo.Ctx, repo.Path, args, skip, limit, walkfn) | ||||
| } | ||||
|  | ||||
| // callShowRef return refs, if limit = 0 it will not limit | ||||
| func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs []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 { | ||||
| 		branchName = strings.TrimPrefix(branchName, trimPrefix) | ||||
| 		branchNames = append(branchNames, branchName) | ||||
| @@ -96,7 +96,7 @@ func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs []s | ||||
| 	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() | ||||
| 	defer func() { | ||||
| 		_ = stdoutReader.Close() | ||||
| @@ -105,7 +105,7 @@ func walkShowRef(ctx context.Context, repoPath string, extraArgs []string, skip, | ||||
|  | ||||
| 	go func() { | ||||
| 		stderrBuilder := &strings.Builder{} | ||||
| 		args := []string{"for-each-ref", "--format=%(objectname) %(refname)"} | ||||
| 		args := []CmdArg{"for-each-ref", "--format=%(objectname) %(refname)"} | ||||
| 		args = append(args, extraArgs...) | ||||
| 		err := NewCommand(ctx, args...).Run(&RunOpts{ | ||||
| 			Dir:    repoPath, | ||||
|   | ||||
| @@ -61,7 +61,7 @@ func (repo *Repository) getCommitByPathWithID(id SHA1, relpath string) (*Commit, | ||||
| 		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 { | ||||
| 		return nil, runErr | ||||
| 	} | ||||
| @@ -76,7 +76,7 @@ func (repo *Repository) getCommitByPathWithID(id SHA1, relpath string) (*Commit, | ||||
|  | ||||
| // GetCommitByPath returns the last commit of relative path. | ||||
| 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 { | ||||
| 		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) { | ||||
| 	stdout, _, err := NewCommand(repo.Ctx, "log", id.String(), "--skip="+strconv.Itoa((page-1)*pageSize), | ||||
| 		"--max-count="+strconv.Itoa(pageSize), prettyLogFormat).RunStdBytes(&RunOpts{Dir: repo.Path}) | ||||
| 	stdout, _, err := NewCommand(repo.Ctx, "log"). | ||||
| 		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 { | ||||
| 		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) { | ||||
| 	// 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 | ||||
| 	args := []string{"-i"} | ||||
| 	args := []CmdArg{"-i"} | ||||
|  | ||||
| 	// add authors if present in search query | ||||
| 	if len(opts.Authors) > 0 { | ||||
| 		for _, v := range opts.Authors { | ||||
| 			args = append(args, "--author="+v) | ||||
| 			args = append(args, CmdArg("--author="+v)) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// add committers if present in search query | ||||
| 	if len(opts.Committers) > 0 { | ||||
| 		for _, v := range opts.Committers { | ||||
| 			args = append(args, "--committer="+v) | ||||
| 			args = append(args, CmdArg("--committer="+v)) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// add time constraints if present in search query | ||||
| 	if len(opts.After) > 0 { | ||||
| 		args = append(args, "--after="+opts.After) | ||||
| 		args = append(args, CmdArg("--after="+opts.After)) | ||||
| 	} | ||||
| 	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> | ||||
| @@ -136,7 +138,7 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co | ||||
| 	// note this is done only for command created above | ||||
| 	if len(opts.Keywords) > 0 { | ||||
| 		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) { | ||||
| 	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 { | ||||
| 		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 | ||||
| // You must ensure that id1 and id2 are valid commit ids. | ||||
| 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 { | ||||
| 		return false, err | ||||
| 	} | ||||
| @@ -211,12 +213,11 @@ func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) ( | ||||
| 	}() | ||||
| 	go func() { | ||||
| 		stderr := strings.Builder{} | ||||
| 		gitCmd := NewCommand(repo.Ctx, "rev-list", | ||||
| 			"--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize*page), | ||||
| 			"--skip="+strconv.Itoa(skip), | ||||
| 		) | ||||
| 		gitCmd := NewCommand(repo.Ctx, "rev-list"). | ||||
| 			AddArguments(CmdArg("--max-count=" + strconv.Itoa(setting.Git.CommitsRangeSize*page))). | ||||
| 			AddArguments(CmdArg("--skip=" + strconv.Itoa(skip))) | ||||
| 		gitCmd.AddDynamicArguments(revision) | ||||
| 		gitCmd.AddArguments("--", file) | ||||
| 		gitCmd.AddDashesAndList(file) | ||||
| 		err := gitCmd.Run(&RunOpts{ | ||||
| 			Dir:    repo.Path, | ||||
| 			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 | ||||
| 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") { | ||||
| 		// git >= 2.28 now returns an error if startCommitID and endCommitID have become unrelated. | ||||
| 		// previously it would return the results of git diff --name-only startCommitID endCommitID so let's try that... | ||||
| 		stdout, _, err = NewCommand(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 { | ||||
| 		return 0, err | ||||
| @@ -275,13 +276,13 @@ func (repo *Repository) CommitsBetween(last, before *Commit) ([]*Commit, error) | ||||
| 	var stdout []byte | ||||
| 	var err error | ||||
| 	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 { | ||||
| 		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") { | ||||
| 			// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated. | ||||
| 			// previously it would return the results of git rev-list before last so let's try that... | ||||
| 			stdout, _, err = NewCommand(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 { | ||||
| @@ -295,13 +296,22 @@ func (repo *Repository) CommitsBetweenLimit(last, before *Commit, limit, skip in | ||||
| 	var stdout []byte | ||||
| 	var err error | ||||
| 	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 { | ||||
| 		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") { | ||||
| 			// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated. | ||||
| 			// previously it would return the results of git rev-list --max-count n before last so let's try that... | ||||
| 			stdout, _, err = NewCommand(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 { | ||||
| @@ -342,9 +352,9 @@ func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) { | ||||
| func (repo *Repository) commitsBefore(id SHA1, limit int) ([]*Commit, error) { | ||||
| 	cmd := NewCommand(repo.Ctx, "log") | ||||
| 	if limit > 0 { | ||||
| 		cmd.AddArguments("-"+strconv.Itoa(limit), prettyLogFormat, id.String()) | ||||
| 		cmd.AddArguments(CmdArg("-"+strconv.Itoa(limit)), prettyLogFormat).AddDynamicArguments(id.String()) | ||||
| 	} else { | ||||
| 		cmd.AddArguments(prettyLogFormat, id.String()) | ||||
| 		cmd.AddArguments(prettyLogFormat).AddDynamicArguments(id.String()) | ||||
| 	} | ||||
|  | ||||
| 	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) { | ||||
| 	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 { | ||||
| 			return nil, err | ||||
| 		} | ||||
| @@ -393,7 +407,7 @@ func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) | ||||
| 		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 { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @@ -432,7 +446,7 @@ func (repo *Repository) GetCommitsFromIDs(commitIDs []string) []*Commit { | ||||
|  | ||||
| // IsCommitInBranch check if the commit is on the branch | ||||
| func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err error) { | ||||
| 	stdout, _, err := NewCommand(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 { | ||||
| 		return false, err | ||||
| 	} | ||||
|   | ||||
| @@ -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 strings.Contains(err.Error(), "unknown revision or path") || | ||||
| 			strings.Contains(err.Error(), "fatal: Needed a single revision") { | ||||
|   | ||||
| @@ -17,7 +17,7 @@ import ( | ||||
|  | ||||
| // ResolveReference resolves a name to a reference | ||||
| 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 strings.Contains(err.Error(), "not a valid ref") { | ||||
| 			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). | ||||
| 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 | ||||
| } | ||||
|  | ||||
| // RemoveReference removes the given reference (e.g. branch or tag). | ||||
| 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 | ||||
| } | ||||
|  | ||||
| // IsCommitExist returns true if given commit exists in current repository. | ||||
| 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 | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -40,13 +40,13 @@ func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, stri | ||||
| 	if tmpRemote != "origin" { | ||||
| 		tmpBaseName := RemotePrefix + tmpRemote + "/tmp_" + base | ||||
| 		// Fetch commit into a temporary branch in order to be able to handle commits and tags | ||||
| 		_, _, err := NewCommand(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 { | ||||
| 			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 | ||||
| } | ||||
|  | ||||
| @@ -94,7 +94,7 @@ func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string, | ||||
| 		// We have a common base - therefore we know that ... should work | ||||
| 		if !fileOnly { | ||||
| 			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 { | ||||
| 				return nil, err | ||||
| 			} | ||||
| @@ -147,7 +147,7 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis | ||||
| 		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{ | ||||
| 			Dir:    repo.Path, | ||||
| 			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... | ||||
| 			w = &lineCountWriter{} | ||||
| 			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, | ||||
| 				Stdout: w, | ||||
| 				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 | ||||
| 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") { | ||||
| 		return GetDiffShortStat(repo.Ctx, repo.Path, base, head) | ||||
| 		return GetDiffShortStat(repo.Ctx, repo.Path, CmdArgCheck(base), CmdArgCheck(head)) | ||||
| 	} | ||||
| 	return numFiles, totalAdditions, totalDeletions, err | ||||
| } | ||||
|  | ||||
| // 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: | ||||
| 	// $ git diff --shortstat 1ebb35b98889ff77299f24d82da426b434b0cca0...788b8b1440462d477f45b0088875 | ||||
| 	// we get: | ||||
| 	// " 9902 files changed, 2034198 insertions(+), 298800 deletions(-)\n" | ||||
| 	args = append([]string{ | ||||
| 	args = append([]CmdArg{ | ||||
| 		"diff", | ||||
| 		"--shortstat", | ||||
| 	}, 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 | ||||
| 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, | ||||
| 		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. | ||||
| 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, | ||||
| 		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` | ||||
| func (repo *Repository) GetPatch(base, head string, w io.Writer) error { | ||||
| 	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{ | ||||
| 			Dir:    repo.Path, | ||||
| 			Stdout: w, | ||||
| 			Stderr: stderr, | ||||
| 		}) | ||||
| 	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{ | ||||
| 				Dir:    repo.Path, | ||||
| 				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 | ||||
| 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 { | ||||
| 		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 | ||||
| func (repo *Repository) GetDiffFromMergeBase(base, head string, w io.Writer) error { | ||||
| 	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{ | ||||
| 			Dir:    repo.Path, | ||||
| 			Stdout: w, | ||||
|   | ||||
| @@ -18,7 +18,7 @@ import ( | ||||
| // ReadTreeToIndex reads a treeish to the index | ||||
| func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string) error { | ||||
| 	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 { | ||||
| 			return err | ||||
| 		} | ||||
| @@ -38,7 +38,7 @@ func (repo *Repository) readTreeToIndex(id SHA1, indexFilename ...string) error | ||||
| 	if len(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 { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -75,12 +75,7 @@ func (repo *Repository) EmptyIndex() error { | ||||
|  | ||||
| // LsFiles checks if the given filenames are in the index | ||||
| func (repo *Repository) LsFiles(filenames ...string) ([]string, error) { | ||||
| 	cmd := NewCommand(repo.Ctx, "ls-files", "-z", "--") | ||||
| 	for _, arg := range filenames { | ||||
| 		if arg != "" { | ||||
| 			cmd.AddArguments(arg) | ||||
| 		} | ||||
| 	} | ||||
| 	cmd := NewCommand(repo.Ctx, "ls-files", "-z").AddDashesAndList(filenames...) | ||||
| 	res, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path}) | ||||
| 	if err != nil { | ||||
| 		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 | ||||
| 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}) | ||||
| 	return err | ||||
| } | ||||
|   | ||||
| @@ -41,7 +41,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string) | ||||
|  | ||||
| 	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 { | ||||
| 		return nil, runErr | ||||
| 	} | ||||
| @@ -61,7 +61,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string) | ||||
| 		_ = 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 { | ||||
| 		gitCmd.AddArguments("--branches=*") | ||||
| 	} else { | ||||
|   | ||||
| @@ -25,13 +25,13 @@ func IsTagExist(ctx context.Context, repoPath, name string) bool { | ||||
|  | ||||
| // CreateTag create one tag in the repository | ||||
| 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 | ||||
| } | ||||
|  | ||||
| // CreateAnnotatedTag create one annotated tag in the repository | ||||
| 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 | ||||
| } | ||||
|  | ||||
| @@ -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) | ||||
| 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 { | ||||
| 		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} | ||||
|  | ||||
| 	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 { | ||||
| 			_ = stdoutWriter.CloseWithError(ConcatenateError(err, stderr.String())) | ||||
| 		} else { | ||||
|   | ||||
| @@ -26,7 +26,7 @@ func (repo *Repository) IsTagExist(name string) bool { | ||||
| // GetTags returns all tags of the repository. | ||||
| // returning at most limit tags, or all if limit is 0. | ||||
| 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 | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -35,10 +35,10 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt | ||||
| 		"GIT_COMMITTER_EMAIL="+committer.Email, | ||||
| 		"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 { | ||||
| 		cmd.AddArguments("-p", parent) | ||||
| 		cmd.AddArguments("-p").AddDynamicArguments(parent) | ||||
| 	} | ||||
|  | ||||
| 	messageBytes := new(bytes.Buffer) | ||||
| @@ -46,7 +46,7 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt | ||||
| 	_, _ = messageBytes.WriteString("\n") | ||||
|  | ||||
| 	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 { | ||||
|   | ||||
| @@ -21,7 +21,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) { | ||||
| // GetTree find the tree object in the repository. | ||||
| func (repo *Repository) GetTree(idStr string) (*Tree, error) { | ||||
| 	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 { | ||||
| 			return nil, err | ||||
| 		} | ||||
|   | ||||
| @@ -49,12 +49,9 @@ func (t *Tree) SubTree(rpath string) (*Tree, error) { | ||||
|  | ||||
| // LsTree checks if the given filenames are in the tree | ||||
| func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error) { | ||||
| 	cmd := NewCommand(repo.Ctx, "ls-tree", "-z", "--name-only", "--", ref) | ||||
| 	for _, arg := range filenames { | ||||
| 		if arg != "" { | ||||
| 			cmd.AddArguments(arg) | ||||
| 		} | ||||
| 	} | ||||
| 	cmd := NewCommand(repo.Ctx, "ls-tree", "-z", "--name-only"). | ||||
| 		AddDashesAndList(append([]string{ref}, filenames...)...) | ||||
|  | ||||
| 	res, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
|   | ||||
| @@ -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 strings.Contains(runErr.Error(), "fatal: Not a valid object name") || strings.Contains(runErr.Error(), "fatal: not a tree object") { | ||||
| 			return nil, ErrNotExist{ | ||||
| @@ -101,13 +101,13 @@ func (t *Tree) ListEntries() (Entries, error) { | ||||
|  | ||||
| // listEntriesRecursive returns all entries of current tree recursively including all subtrees | ||||
| // extraArgs could be "-l" to get the size, which is slower | ||||
| func (t *Tree) listEntriesRecursive(extraArgs ...string) (Entries, error) { | ||||
| func (t *Tree) listEntriesRecursive(extraArgs ...CmdArg) (Entries, error) { | ||||
| 	if t.entriesRecursiveParsed { | ||||
| 		return t.entriesRecursive, nil | ||||
| 	} | ||||
|  | ||||
| 	args := append([]string{"ls-tree", "-t", "-r"}, extraArgs...) | ||||
| 	args = append(args, t.ID.String()) | ||||
| 	args := append([]CmdArg{"ls-tree", "-t", "-r"}, extraArgs...) | ||||
| 	args = append(args, CmdArg(t.ID.String())) | ||||
| 	stdout, _, runErr := NewCommand(t.repo.Ctx, args...).RunStdBytes(&RunOpts{Dir: t.repo.Path}) | ||||
| 	if runErr != nil { | ||||
| 		return nil, runErr | ||||
|   | ||||
| @@ -37,16 +37,15 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo | ||||
| 	graphCmd.AddArguments( | ||||
| 		"-C", | ||||
| 		"-M", | ||||
| 		fmt.Sprintf("-n %d", setting.UI.GraphMaxCommitNum*page), | ||||
| 		git.CmdArg(fmt.Sprintf("-n %d", setting.UI.GraphMaxCommitNum*page)), | ||||
| 		"--date=iso", | ||||
| 		fmt.Sprintf("--pretty=format:%s", format)) | ||||
| 		git.CmdArg(fmt.Sprintf("--pretty=format:%s", format))) | ||||
|  | ||||
| 	if len(branches) > 0 { | ||||
| 		graphCmd.AddDynamicArguments(branches...) | ||||
| 	} | ||||
| 	if len(files) > 0 { | ||||
| 		graphCmd.AddArguments("--") | ||||
| 		graphCmd.AddArguments(files...) | ||||
| 		graphCmd.AddDashesAndList(files...) | ||||
| 	} | ||||
| 	graph := NewGraph() | ||||
|  | ||||
|   | ||||
| @@ -194,7 +194,7 @@ func (b *BleveIndexer) addUpdate(ctx context.Context, batchWriter git.WriteClose | ||||
| 	var err error | ||||
| 	if !update.Sized { | ||||
| 		var stdout string | ||||
| 		stdout, _, err = git.NewCommand(ctx, "cat-file", "-s", update.BlobSha).RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) | ||||
| 		stdout, _, err = git.NewCommand(ctx, "cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|   | ||||
| @@ -223,7 +223,7 @@ func (b *ElasticSearchIndexer) addUpdate(ctx context.Context, batchWriter git.Wr | ||||
| 	var err error | ||||
| 	if !update.Sized { | ||||
| 		var stdout string | ||||
| 		stdout, _, err = git.NewCommand(ctx, "cat-file", "-s", update.BlobSha).RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) | ||||
| 		stdout, _, err = git.NewCommand(ctx, "cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|   | ||||
| @@ -29,7 +29,7 @@ type repoChanges struct { | ||||
| } | ||||
|  | ||||
| func getDefaultBranchSha(ctx context.Context, repo *repo_model.Repository) (string, error) { | ||||
| 	stdout, _, err := git.NewCommand(ctx, "show-ref", "-s", git.BranchPrefix+repo.DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) | ||||
| 	stdout, _, err := git.NewCommand(ctx, "show-ref", "-s").AddDynamicArguments(git.BranchPrefix + repo.DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| @@ -92,7 +92,7 @@ func parseGitLsTreeOutput(stdout []byte) ([]fileUpdate, error) { | ||||
| // genesisChanges get changes to add repo to the indexer for the first time | ||||
| func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*repoChanges, error) { | ||||
| 	var changes repoChanges | ||||
| 	stdout, _, runErr := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l", "-r", revision).RunStdBytes(&git.RunOpts{Dir: repo.RepoPath()}) | ||||
| 	stdout, _, runErr := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l", "-r").AddDynamicArguments(revision).RunStdBytes(&git.RunOpts{Dir: repo.RepoPath()}) | ||||
| 	if runErr != nil { | ||||
| 		return nil, runErr | ||||
| 	} | ||||
| @@ -104,7 +104,7 @@ func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision s | ||||
|  | ||||
| // nonGenesisChanges get changes since the previous indexer update | ||||
| func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*repoChanges, error) { | ||||
| 	diffCmd := git.NewCommand(ctx, "diff", "--name-status", repo.CodeIndexerStatus.CommitSha, revision) | ||||
| 	diffCmd := git.NewCommand(ctx, "diff", "--name-status").AddDynamicArguments(repo.CodeIndexerStatus.CommitSha, revision) | ||||
| 	stdout, _, runErr := diffCmd.RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) | ||||
| 	if runErr != nil { | ||||
| 		// previous commit sha may have been removed by a force push, so | ||||
| @@ -169,8 +169,8 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	cmd := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l", revision, "--") | ||||
| 	cmd.AddArguments(updatedFilenames...) | ||||
| 	cmd := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l").AddDynamicArguments(revision). | ||||
| 		AddDashesAndList(updatedFilenames...) | ||||
| 	lsTreeStdout, _, err := cmd.RunStdBytes(&git.RunOpts{Dir: repo.RepoPath()}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
|   | ||||
| @@ -211,7 +211,7 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r | ||||
| 	} | ||||
|  | ||||
| 	repoPath := repo.RepoPath() | ||||
| 	if stdout, _, err := git.NewCommand(ctx, "remote", "add", "origin", repoPath). | ||||
| 	if stdout, _, err := git.NewCommand(ctx, "remote", "add", "origin").AddDynamicArguments(repoPath). | ||||
| 		SetDescription(fmt.Sprintf("generateRepoCommit (git remote add): %s to %s", templateRepoPath, tmpDir)). | ||||
| 		RunStdString(&git.RunOpts{Dir: tmpDir, Env: env}); err != nil { | ||||
| 		log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err) | ||||
|   | ||||
| @@ -228,7 +228,7 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, | ||||
| 	) | ||||
|  | ||||
| 	// Clone to temporary path and do the init commit. | ||||
| 	if stdout, _, err := git.NewCommand(ctx, "clone", repoPath, tmpDir). | ||||
| 	if stdout, _, err := git.NewCommand(ctx, "clone").AddDynamicArguments(repoPath, tmpDir). | ||||
| 		SetDescription(fmt.Sprintf("prepareRepoCommit (git clone): %s to %s", repoPath, tmpDir)). | ||||
| 		RunStdString(&git.RunOpts{Dir: "", Env: env}); err != nil { | ||||
| 		log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err) | ||||
| @@ -317,14 +317,14 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi | ||||
| 		return fmt.Errorf("git add --all: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	args := []string{ | ||||
| 		"commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), | ||||
| 	cmd := git.NewCommand(ctx, | ||||
| 		"commit", git.CmdArg(fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email)), | ||||
| 		"-m", "Initial commit", | ||||
| 	} | ||||
| 	) | ||||
|  | ||||
| 	sign, keyID, signer, _ := asymkey_service.SignInitialCommit(ctx, tmpPath, u) | ||||
| 	if sign { | ||||
| 		args = append(args, "-S"+keyID) | ||||
| 		cmd.AddArguments(git.CmdArg("-S" + keyID)) | ||||
|  | ||||
| 		if repo.GetTrustModel() == repo_model.CommitterTrustModel || repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { | ||||
| 			// need to set the committer to the KeyID owner | ||||
| @@ -332,7 +332,7 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi | ||||
| 			committerEmail = signer.Email | ||||
| 		} | ||||
| 	} else { | ||||
| 		args = append(args, "--no-gpg-sign") | ||||
| 		cmd.AddArguments("--no-gpg-sign") | ||||
| 	} | ||||
|  | ||||
| 	env = append(env, | ||||
| @@ -340,10 +340,10 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi | ||||
| 		"GIT_COMMITTER_EMAIL="+committerEmail, | ||||
| 	) | ||||
|  | ||||
| 	if stdout, _, err := git.NewCommand(ctx, args...). | ||||
| 	if stdout, _, err := cmd. | ||||
| 		SetDescription(fmt.Sprintf("initRepoCommit (git commit): %s", tmpPath)). | ||||
| 		RunStdString(&git.RunOpts{Dir: tmpPath, Env: env}); err != nil { | ||||
| 		log.Error("Failed to commit: %v: Stdout: %s\nError: %v", args, stdout, err) | ||||
| 		log.Error("Failed to commit: %v: Stdout: %s\nError: %v", cmd.String(), stdout, err) | ||||
| 		return fmt.Errorf("git commit: %v", err) | ||||
| 	} | ||||
|  | ||||
| @@ -351,7 +351,7 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi | ||||
| 		defaultBranch = setting.Repository.DefaultBranch | ||||
| 	} | ||||
|  | ||||
| 	if stdout, _, err := git.NewCommand(ctx, "push", "origin", "HEAD:"+defaultBranch). | ||||
| 	if stdout, _, err := git.NewCommand(ctx, "push", "origin").AddDynamicArguments("HEAD:" + defaultBranch). | ||||
| 		SetDescription(fmt.Sprintf("initRepoCommit (git push): %s", tmpPath)). | ||||
| 		RunStdString(&git.RunOpts{Dir: tmpPath, Env: InternalPushingEnvironment(u, repo)}); err != nil { | ||||
| 		log.Error("Failed to push back to HEAD: Stdout: %s\nError: %v", stdout, err) | ||||
|   | ||||
| @@ -104,7 +104,7 @@ func IsForcePush(ctx context.Context, opts *PushUpdateOptions) (bool, error) { | ||||
| 		return false, nil | ||||
| 	} | ||||
|  | ||||
| 	output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1", opts.OldCommitID, "^"+opts.NewCommitID). | ||||
| 	output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1").AddDynamicArguments(opts.OldCommitID, "^"+opts.NewCommitID). | ||||
| 		RunStdString(&git.RunOpts{Dir: repo_model.RepoPath(opts.RepoUserName, opts.RepoName)}) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
|   | ||||
| @@ -186,7 +186,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID, refFullN | ||||
|  | ||||
| 	// 2. Disallow force pushes to protected branches | ||||
| 	if git.EmptySHA != oldCommitID { | ||||
| 		output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunStdString(&git.RunOpts{Dir: repo.RepoPath(), Env: ctx.env}) | ||||
| 		output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+newCommitID).RunStdString(&git.RunOpts{Dir: repo.RepoPath(), Env: ctx.env}) | ||||
| 		if err != nil { | ||||
| 			log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, newCommitID, repo, err) | ||||
| 			ctx.JSON(http.StatusInternalServerError, private.Response{ | ||||
|   | ||||
| @@ -44,7 +44,7 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env [] | ||||
| 	}() | ||||
|  | ||||
| 	// This is safe as force pushes are already forbidden | ||||
| 	err = git.NewCommand(repo.Ctx, "rev-list", oldCommitID+"..."+newCommitID). | ||||
| 	err = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID). | ||||
| 		Run(&git.RunOpts{ | ||||
| 			Env:    env, | ||||
| 			Dir:    repo.Path, | ||||
| @@ -91,7 +91,7 @@ func readAndVerifyCommit(sha string, repo *git.Repository, env []string) error { | ||||
| 	}() | ||||
| 	hash := git.MustIDFromString(sha) | ||||
|  | ||||
| 	return git.NewCommand(repo.Ctx, "cat-file", "commit", sha). | ||||
| 	return git.NewCommand(repo.Ctx, "cat-file", "commit").AddDynamicArguments(sha). | ||||
| 		Run(&git.RunOpts{ | ||||
| 			Env:    env, | ||||
| 			Dir:    repo.Path, | ||||
|   | ||||
| @@ -216,7 +216,7 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m | ||||
|  | ||||
| 		filename2attribute2info, err := ctx.Repo.GitRepo.CheckAttribute(git.CheckAttributeOpts{ | ||||
| 			CachedOnly: true, | ||||
| 			Attributes: []string{"linguist-language", "gitlab-language"}, | ||||
| 			Attributes: []git.CmdArg{"linguist-language", "gitlab-language"}, | ||||
| 			Filenames:  []string{ctx.Repo.TreePath}, | ||||
| 			IndexFile:  indexFilename, | ||||
| 			WorkTree:   worktree, | ||||
|   | ||||
| @@ -560,7 +560,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo { | ||||
| func PrepareCompareDiff( | ||||
| 	ctx *context.Context, | ||||
| 	ci *CompareInfo, | ||||
| 	whitespaceBehavior string, | ||||
| 	whitespaceBehavior git.CmdArg, | ||||
| ) bool { | ||||
| 	var ( | ||||
| 		repo  = ctx.Repo.Repository | ||||
|   | ||||
| @@ -398,7 +398,7 @@ func (h *serviceHandler) sendFile(contentType, file string) { | ||||
| var safeGitProtocolHeader = regexp.MustCompile(`^[0-9a-zA-Z]+=[0-9a-zA-Z]+(:[0-9a-zA-Z]+=[0-9a-zA-Z]+)*$`) | ||||
|  | ||||
| func getGitConfig(ctx gocontext.Context, option, dir string) string { | ||||
| 	out, _, err := git.NewCommand(ctx, "config", option).RunStdString(&git.RunOpts{Dir: dir}) | ||||
| 	out, _, err := git.NewCommand(ctx, "config").AddDynamicArguments(option).RunStdString(&git.RunOpts{Dir: dir}) | ||||
| 	if err != nil { | ||||
| 		log.Error("%v - %s", err, out) | ||||
| 	} | ||||
| @@ -471,7 +471,7 @@ func serviceRPC(ctx gocontext.Context, h serviceHandler, service string) { | ||||
| 	} | ||||
|  | ||||
| 	var stderr bytes.Buffer | ||||
| 	cmd := git.NewCommand(h.r.Context(), service, "--stateless-rpc", h.dir) | ||||
| 	cmd := git.NewCommand(h.r.Context(), git.CmdArgCheck(service), "--stateless-rpc").AddDynamicArguments(h.dir) | ||||
| 	cmd.SetDescription(fmt.Sprintf("%s %s %s [repo_path: %s]", git.GitExecutable, service, "--stateless-rpc", h.dir)) | ||||
| 	if err := cmd.Run(&git.RunOpts{ | ||||
| 		Dir:               h.dir, | ||||
| @@ -543,7 +543,7 @@ func GetInfoRefs(ctx *context.Context) { | ||||
| 		} | ||||
| 		h.environ = append(os.Environ(), h.environ...) | ||||
|  | ||||
| 		refs, _, err := git.NewCommand(ctx, service, "--stateless-rpc", "--advertise-refs", ".").RunStdBytes(&git.RunOpts{Env: h.environ, Dir: h.dir}) | ||||
| 		refs, _, err := git.NewCommand(ctx, git.CmdArgCheck(service), "--stateless-rpc", "--advertise-refs", ".").RunStdBytes(&git.RunOpts{Env: h.environ, Dir: h.dir}) | ||||
| 		if err != nil { | ||||
| 			log.Error(fmt.Sprintf("%v - %s", err, string(refs))) | ||||
| 		} | ||||
|   | ||||
| @@ -147,7 +147,7 @@ func LFSLocks(ctx *context.Context) { | ||||
| 	} | ||||
|  | ||||
| 	name2attribute2info, err := gitRepo.CheckAttribute(git.CheckAttributeOpts{ | ||||
| 		Attributes: []string{"lockable"}, | ||||
| 		Attributes: []git.CmdArg{"lockable"}, | ||||
| 		Filenames:  filenames, | ||||
| 		CachedOnly: true, | ||||
| 	}) | ||||
|   | ||||
| @@ -359,7 +359,7 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue) | ||||
| 		} | ||||
| 		if commitSHA != "" { | ||||
| 			// Get immediate parent of the first commit in the patch, grab history back | ||||
| 			parentCommit, _, err = git.NewCommand(ctx, "rev-list", "-1", "--skip=1", commitSHA).RunStdString(&git.RunOpts{Dir: ctx.Repo.GitRepo.Path}) | ||||
| 			parentCommit, _, err = git.NewCommand(ctx, "rev-list", "-1", "--skip=1").AddDynamicArguments(commitSHA).RunStdString(&git.RunOpts{Dir: ctx.Repo.GitRepo.Path}) | ||||
| 			if err == nil { | ||||
| 				parentCommit = strings.TrimSpace(parentCommit) | ||||
| 			} | ||||
|   | ||||
| @@ -551,7 +551,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st | ||||
|  | ||||
| 				filename2attribute2info, err := ctx.Repo.GitRepo.CheckAttribute(git.CheckAttributeOpts{ | ||||
| 					CachedOnly: true, | ||||
| 					Attributes: []string{"linguist-language", "gitlab-language"}, | ||||
| 					Attributes: []git.CmdArg{"linguist-language", "gitlab-language"}, | ||||
| 					Filenames:  []string{ctx.Repo.TreePath}, | ||||
| 					IndexFile:  indexFilename, | ||||
| 					WorkTree:   worktree, | ||||
|   | ||||
| @@ -179,7 +179,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. | ||||
| 		} | ||||
|  | ||||
| 		if !forcePush { | ||||
| 			output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1", oldCommitID, "^"+opts.NewCommitIDs[i]).RunStdString(&git.RunOpts{Dir: repo.RepoPath(), Env: os.Environ()}) | ||||
| 			output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+opts.NewCommitIDs[i]).RunStdString(&git.RunOpts{Dir: repo.RepoPath(), Env: os.Environ()}) | ||||
| 			if err != nil { | ||||
| 				return nil, fmt.Errorf("Fail to detect force push: %v", err) | ||||
| 			} else if len(output) > 0 { | ||||
|   | ||||
| @@ -12,6 +12,7 @@ import ( | ||||
| 	git_model "code.gitea.io/gitea/models/git" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/models/webhook" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/services/auth" | ||||
| 	"code.gitea.io/gitea/services/migrations" | ||||
| @@ -58,7 +59,12 @@ func registerRepoHealthCheck() { | ||||
| 		Args:    []string{}, | ||||
| 	}, func(ctx context.Context, _ *user_model.User, config Config) error { | ||||
| 		rhcConfig := config.(*RepoHealthCheckConfig) | ||||
| 		return repo_service.GitFsck(ctx, rhcConfig.Timeout, rhcConfig.Args) | ||||
| 		// the git args are set by config, they can be safe to be trusted | ||||
| 		args := make([]git.CmdArg, 0, len(rhcConfig.Args)) | ||||
| 		for _, arg := range rhcConfig.Args { | ||||
| 			args = append(args, git.CmdArg(arg)) | ||||
| 		} | ||||
| 		return repo_service.GitFsck(ctx, rhcConfig.Timeout, args) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -13,6 +13,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/models/db" | ||||
| 	"code.gitea.io/gitea/models/system" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/updatechecker" | ||||
| 	repo_service "code.gitea.io/gitea/services/repository" | ||||
| @@ -60,7 +61,12 @@ func registerGarbageCollectRepositories() { | ||||
| 		Args:    setting.Git.GCArgs, | ||||
| 	}, func(ctx context.Context, _ *user_model.User, config Config) error { | ||||
| 		rhcConfig := config.(*RepoHealthCheckConfig) | ||||
| 		return repo_service.GitGcRepos(ctx, rhcConfig.Timeout, rhcConfig.Args...) | ||||
| 		// the git args are set by config, they can be safe to be trusted | ||||
| 		args := make([]git.CmdArg, 0, len(rhcConfig.Args)) | ||||
| 		for _, arg := range rhcConfig.Args { | ||||
| 			args = append(args, git.CmdArg(arg)) | ||||
| 		} | ||||
| 		return repo_service.GitGcRepos(ctx, rhcConfig.Timeout, args...) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1056,7 +1056,7 @@ type DiffOptions struct { | ||||
| 	MaxLines           int | ||||
| 	MaxLineCharacters  int | ||||
| 	MaxFiles           int | ||||
| 	WhitespaceBehavior string | ||||
| 	WhitespaceBehavior git.CmdArg | ||||
| 	DirectComparison   bool | ||||
| } | ||||
|  | ||||
| @@ -1082,7 +1082,7 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff | ||||
| 		argsLength += len(files) + 1 | ||||
| 	} | ||||
|  | ||||
| 	diffArgs := make([]string, 0, argsLength) | ||||
| 	diffArgs := make([]git.CmdArg, 0, argsLength) | ||||
| 	if (len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == git.EmptySHA) && commit.ParentCount() == 0 { | ||||
| 		diffArgs = append(diffArgs, "diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M") | ||||
| 		if len(opts.WhitespaceBehavior) != 0 { | ||||
| @@ -1090,7 +1090,7 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff | ||||
| 		} | ||||
| 		// append empty tree ref | ||||
| 		diffArgs = append(diffArgs, "4b825dc642cb6eb9a060e54bf8d69288fbee4904") | ||||
| 		diffArgs = append(diffArgs, opts.AfterCommitID) | ||||
| 		diffArgs = append(diffArgs, git.CmdArgCheck(opts.AfterCommitID)) | ||||
| 	} else { | ||||
| 		actualBeforeCommitID := opts.BeforeCommitID | ||||
| 		if len(actualBeforeCommitID) == 0 { | ||||
| @@ -1101,8 +1101,8 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff | ||||
| 		if len(opts.WhitespaceBehavior) != 0 { | ||||
| 			diffArgs = append(diffArgs, opts.WhitespaceBehavior) | ||||
| 		} | ||||
| 		diffArgs = append(diffArgs, actualBeforeCommitID) | ||||
| 		diffArgs = append(diffArgs, opts.AfterCommitID) | ||||
| 		diffArgs = append(diffArgs, git.CmdArgCheck(actualBeforeCommitID)) | ||||
| 		diffArgs = append(diffArgs, git.CmdArgCheck(opts.AfterCommitID)) | ||||
| 		opts.BeforeCommitID = actualBeforeCommitID | ||||
| 	} | ||||
|  | ||||
| @@ -1111,13 +1111,15 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff | ||||
| 	// the skipping for us | ||||
| 	parsePatchSkipToFile := opts.SkipTo | ||||
| 	if opts.SkipTo != "" && git.CheckGitVersionAtLeast("2.31") == nil { | ||||
| 		diffArgs = append(diffArgs, "--skip-to="+opts.SkipTo) | ||||
| 		diffArgs = append(diffArgs, git.CmdArg("--skip-to="+opts.SkipTo)) | ||||
| 		parsePatchSkipToFile = "" | ||||
| 	} | ||||
|  | ||||
| 	if len(files) > 0 { | ||||
| 		diffArgs = append(diffArgs, "--") | ||||
| 		diffArgs = append(diffArgs, files...) | ||||
| 		for _, file := range files { | ||||
| 			diffArgs = append(diffArgs, git.CmdArg(file)) // it's safe to cast it to CmdArg because there is a "--" before | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	reader, writer := io.Pipe() | ||||
| @@ -1126,7 +1128,7 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff | ||||
| 		_ = writer.Close() | ||||
| 	}() | ||||
|  | ||||
| 	go func(ctx context.Context, diffArgs []string, repoPath string, writer *io.PipeWriter) { | ||||
| 	go func(ctx context.Context, diffArgs []git.CmdArg, repoPath string, writer *io.PipeWriter) { | ||||
| 		cmd := git.NewCommand(ctx, diffArgs...) | ||||
| 		cmd.SetDescription(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath)) | ||||
| 		if err := cmd.Run(&git.RunOpts{ | ||||
| @@ -1199,15 +1201,15 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff | ||||
| 		separator = ".." | ||||
| 	} | ||||
|  | ||||
| 	shortstatArgs := []string{opts.BeforeCommitID + separator + opts.AfterCommitID} | ||||
| 	shortstatArgs := []git.CmdArg{git.CmdArgCheck(opts.BeforeCommitID + separator + opts.AfterCommitID)} | ||||
| 	if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == git.EmptySHA { | ||||
| 		shortstatArgs = []string{git.EmptyTreeSHA, opts.AfterCommitID} | ||||
| 		shortstatArgs = []git.CmdArg{git.EmptyTreeSHA, git.CmdArgCheck(opts.AfterCommitID)} | ||||
| 	} | ||||
| 	diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, shortstatArgs...) | ||||
| 	if err != nil && strings.Contains(err.Error(), "no merge base") { | ||||
| 		// git >= 2.28 now returns an error if base and head have become unrelated. | ||||
| 		// previously it would return the results of git diff --shortstat base head so let's try that... | ||||
| 		shortstatArgs = []string{opts.BeforeCommitID, opts.AfterCommitID} | ||||
| 		shortstatArgs = []git.CmdArg{git.CmdArgCheck(opts.BeforeCommitID), git.CmdArgCheck(opts.AfterCommitID)} | ||||
| 		diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, shortstatArgs...) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| @@ -1322,7 +1324,7 @@ func CommentMustAsDiff(c *issues_model.Comment) *Diff { | ||||
| } | ||||
|  | ||||
| // GetWhitespaceFlag returns git diff flag for treating whitespaces | ||||
| func GetWhitespaceFlag(whitespaceBehavior string) string { | ||||
| func GetWhitespaceFlag(whitespaceBehavior string) git.CmdArg { | ||||
| 	whitespaceFlags := map[string]string{ | ||||
| 		"ignore-all":    "-w", | ||||
| 		"ignore-change": "-b", | ||||
| @@ -1331,7 +1333,7 @@ func GetWhitespaceFlag(whitespaceBehavior string) string { | ||||
| 	} | ||||
|  | ||||
| 	if flag, ok := whitespaceFlags[whitespaceBehavior]; ok { | ||||
| 		return flag | ||||
| 		return git.CmdArg(flag) | ||||
| 	} | ||||
| 	log.Warn("unknown whitespace behavior: %q, default to 'show-all'", whitespaceBehavior) | ||||
| 	return "" | ||||
|   | ||||
| @@ -627,7 +627,7 @@ func TestGetDiffRangeWithWhitespaceBehavior(t *testing.T) { | ||||
| 		return | ||||
| 	} | ||||
| 	defer gitRepo.Close() | ||||
| 	for _, behavior := range []string{"-w", "--ignore-space-at-eol", "-b", ""} { | ||||
| 	for _, behavior := range []git.CmdArg{"-w", "--ignore-space-at-eol", "-b", ""} { | ||||
| 		diffs, err := GetDiff(gitRepo, | ||||
| 			&DiffOptions{ | ||||
| 				AfterCommitID:      "bd7063cc7c04689c4d082183d32a604ed27a24f9", | ||||
|   | ||||
| @@ -491,7 +491,7 @@ func (g *RepositoryDumper) handlePullRequest(pr *base.PullRequest) error { | ||||
| 	if pr.Head.CloneURL == "" || pr.Head.Ref == "" { | ||||
| 		// Set head information if pr.Head.SHA is available | ||||
| 		if pr.Head.SHA != "" { | ||||
| 			_, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref", pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.gitPath()}) | ||||
| 			_, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.gitPath()}) | ||||
| 			if err != nil { | ||||
| 				log.Error("PR #%d in %s/%s unable to update-ref for pr HEAD: %v", pr.Number, g.repoOwner, g.repoName, err) | ||||
| 			} | ||||
| @@ -521,7 +521,7 @@ func (g *RepositoryDumper) handlePullRequest(pr *base.PullRequest) error { | ||||
| 	if !ok { | ||||
| 		// Set head information if pr.Head.SHA is available | ||||
| 		if pr.Head.SHA != "" { | ||||
| 			_, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref", pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.gitPath()}) | ||||
| 			_, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.gitPath()}) | ||||
| 			if err != nil { | ||||
| 				log.Error("PR #%d in %s/%s unable to update-ref for pr HEAD: %v", pr.Number, g.repoOwner, g.repoName, err) | ||||
| 			} | ||||
| @@ -556,7 +556,7 @@ func (g *RepositoryDumper) handlePullRequest(pr *base.PullRequest) error { | ||||
| 			fetchArg = git.BranchPrefix + fetchArg | ||||
| 		} | ||||
|  | ||||
| 		_, _, err = git.NewCommand(g.ctx, "fetch", "--no-tags", "--", remote, fetchArg).RunStdString(&git.RunOpts{Dir: g.gitPath()}) | ||||
| 		_, _, err = git.NewCommand(g.ctx, "fetch", "--no-tags").AddDashesAndList(remote, fetchArg).RunStdString(&git.RunOpts{Dir: g.gitPath()}) | ||||
| 		if err != nil { | ||||
| 			log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err) | ||||
| 			// We need to continue here so that the Head.Ref is reset and we attempt to set the gitref for the PR | ||||
| @@ -580,7 +580,7 @@ func (g *RepositoryDumper) handlePullRequest(pr *base.PullRequest) error { | ||||
| 		pr.Head.SHA = headSha | ||||
| 	} | ||||
| 	if pr.Head.SHA != "" { | ||||
| 		_, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref", pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.gitPath()}) | ||||
| 		_, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.gitPath()}) | ||||
| 		if err != nil { | ||||
| 			log.Error("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v", pr.Head.SHA, pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err) | ||||
| 		} | ||||
|   | ||||
| @@ -626,7 +626,7 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head | ||||
| 				fetchArg = git.BranchPrefix + fetchArg | ||||
| 			} | ||||
|  | ||||
| 			_, _, err = git.NewCommand(g.ctx, "fetch", "--no-tags", "--", remote, fetchArg).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()}) | ||||
| 			_, _, err = git.NewCommand(g.ctx, "fetch", "--no-tags").AddDashesAndList(remote, fetchArg).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()}) | ||||
| 			if err != nil { | ||||
| 				log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err) | ||||
| 				return head, nil | ||||
| @@ -645,7 +645,7 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head | ||||
| 			pr.Head.SHA = headSha | ||||
| 		} | ||||
|  | ||||
| 		_, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref", pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()}) | ||||
| 		_, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()}) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| @@ -662,13 +662,13 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head | ||||
| 		// The SHA is empty | ||||
| 		log.Warn("Empty reference, no pull head for PR #%d in %s/%s", pr.Number, g.repoOwner, g.repoName) | ||||
| 	} else { | ||||
| 		_, _, err = git.NewCommand(g.ctx, "rev-list", "--quiet", "-1", pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()}) | ||||
| 		_, _, err = git.NewCommand(g.ctx, "rev-list", "--quiet", "-1").AddDynamicArguments(pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()}) | ||||
| 		if err != nil { | ||||
| 			// Git update-ref remove bad references with a relative path | ||||
| 			log.Warn("Deprecated local head %s for PR #%d in %s/%s, removing  %s", pr.Head.SHA, pr.Number, g.repoOwner, g.repoName, pr.GetGitRefName()) | ||||
| 		} else { | ||||
| 			// set head information | ||||
| 			_, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref", pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()}) | ||||
| 			_, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()}) | ||||
| 			if err != nil { | ||||
| 				log.Error("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v", pr.Head.SHA, pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err) | ||||
| 			} | ||||
|   | ||||
| @@ -234,7 +234,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { | ||||
| 	fromRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | ||||
| 	baseRef := "master" | ||||
| 	assert.NoError(t, git.InitRepository(git.DefaultContext, fromRepo.RepoPath(), false)) | ||||
| 	err := git.NewCommand(git.DefaultContext, "symbolic-ref", "HEAD", git.BranchPrefix+baseRef).Run(&git.RunOpts{Dir: fromRepo.RepoPath()}) | ||||
| 	err := git.NewCommand(git.DefaultContext, "symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseRef).Run(&git.RunOpts{Dir: fromRepo.RepoPath()}) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte(fmt.Sprintf("# Testing Repository\n\nOriginally created in: %s", fromRepo.RepoPath())), 0o644)) | ||||
| 	assert.NoError(t, git.AddChanges(fromRepo.RepoPath(), true)) | ||||
| @@ -258,7 +258,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { | ||||
| 	// fromRepo branch1 | ||||
| 	// | ||||
| 	headRef := "branch1" | ||||
| 	_, _, err = git.NewCommand(git.DefaultContext, "checkout", "-b", headRef).RunStdString(&git.RunOpts{Dir: fromRepo.RepoPath()}) | ||||
| 	_, _, err = git.NewCommand(git.DefaultContext, "checkout", "-b").AddDynamicArguments(headRef).RunStdString(&git.RunOpts{Dir: fromRepo.RepoPath()}) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte("SOMETHING"), 0o644)) | ||||
| 	assert.NoError(t, git.AddChanges(fromRepo.RepoPath(), true)) | ||||
| @@ -279,10 +279,10 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { | ||||
| 	// | ||||
| 	forkHeadRef := "branch2" | ||||
| 	forkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 8}) | ||||
| 	assert.NoError(t, git.CloneWithArgs(git.DefaultContext, fromRepo.RepoPath(), forkRepo.RepoPath(), []string{}, git.CloneRepoOptions{ | ||||
| 	assert.NoError(t, git.CloneWithArgs(git.DefaultContext, nil, fromRepo.RepoPath(), forkRepo.RepoPath(), git.CloneRepoOptions{ | ||||
| 		Branch: headRef, | ||||
| 	})) | ||||
| 	_, _, err = git.NewCommand(git.DefaultContext, "checkout", "-b", forkHeadRef).RunStdString(&git.RunOpts{Dir: forkRepo.RepoPath()}) | ||||
| 	_, _, err = git.NewCommand(git.DefaultContext, "checkout", "-b").AddDynamicArguments(forkHeadRef).RunStdString(&git.RunOpts{Dir: forkRepo.RepoPath()}) | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.NoError(t, os.WriteFile(filepath.Join(forkRepo.RepoPath(), "README.md"), []byte(fmt.Sprintf("# branch2 %s", forkRepo.RepoPath())), 0o644)) | ||||
| 	assert.NoError(t, git.AddChanges(forkRepo.RepoPath(), true)) | ||||
|   | ||||
| @@ -33,12 +33,12 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error | ||||
| 	remoteName := m.GetRemoteName() | ||||
| 	repoPath := m.GetRepository().RepoPath() | ||||
| 	// Remove old remote | ||||
| 	_, _, err := git.NewCommand(ctx, "remote", "rm", remoteName).RunStdString(&git.RunOpts{Dir: repoPath}) | ||||
| 	_, _, err := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath}) | ||||
| 	if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	cmd := git.NewCommand(ctx, "remote", "add", remoteName, "--mirror=fetch", addr) | ||||
| 	cmd := git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(addr) | ||||
| 	if strings.Contains(addr, "://") && strings.Contains(addr, "@") { | ||||
| 		cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.SanitizeCredentialURLs(addr), repoPath)) | ||||
| 	} else { | ||||
| @@ -53,12 +53,12 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error | ||||
| 		wikiPath := m.Repo.WikiPath() | ||||
| 		wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr) | ||||
| 		// Remove old remote of wiki | ||||
| 		_, _, err = git.NewCommand(ctx, "remote", "rm", remoteName).RunStdString(&git.RunOpts{Dir: wikiPath}) | ||||
| 		_, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: wikiPath}) | ||||
| 		if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		cmd = git.NewCommand(ctx, "remote", "add", remoteName, "--mirror=fetch", wikiRemotePath) | ||||
| 		cmd = git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(wikiRemotePath) | ||||
| 		if strings.Contains(wikiRemotePath, "://") && strings.Contains(wikiRemotePath, "@") { | ||||
| 			cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.SanitizeCredentialURLs(wikiRemotePath), wikiPath)) | ||||
| 		} else { | ||||
| @@ -169,7 +169,7 @@ func pruneBrokenReferences(ctx context.Context, | ||||
|  | ||||
| 	stderrBuilder.Reset() | ||||
| 	stdoutBuilder.Reset() | ||||
| 	pruneErr := git.NewCommand(ctx, "remote", "prune", m.GetRemoteName()). | ||||
| 	pruneErr := git.NewCommand(ctx, "remote", "prune").AddDynamicArguments(m.GetRemoteName()). | ||||
| 		SetDescription(fmt.Sprintf("Mirror.runSync %ssPrune references: %s ", wiki, m.Repo.FullName())). | ||||
| 		Run(&git.RunOpts{ | ||||
| 			Timeout: timeout, | ||||
| @@ -204,11 +204,11 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo | ||||
|  | ||||
| 	log.Trace("SyncMirrors [repo: %-v]: running git remote update...", m.Repo) | ||||
|  | ||||
| 	gitArgs := []string{"remote", "update"} | ||||
| 	gitArgs := []git.CmdArg{"remote", "update"} | ||||
| 	if m.EnablePrune { | ||||
| 		gitArgs = append(gitArgs, "--prune") | ||||
| 	} | ||||
| 	gitArgs = append(gitArgs, m.GetRemoteName()) | ||||
| 	gitArgs = append(gitArgs, git.CmdArgCheck(m.GetRemoteName())) | ||||
|  | ||||
| 	remoteURL, remoteErr := git.GetRemoteURL(ctx, repoPath, m.GetRemoteName()) | ||||
| 	if remoteErr != nil { | ||||
| @@ -309,7 +309,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo | ||||
| 		log.Trace("SyncMirrors [repo: %-v Wiki]: running git remote update...", m.Repo) | ||||
| 		stderrBuilder.Reset() | ||||
| 		stdoutBuilder.Reset() | ||||
| 		if err := git.NewCommand(ctx, "remote", "update", "--prune", m.GetRemoteName()). | ||||
| 		if err := git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). | ||||
| 			SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())). | ||||
| 			Run(&git.RunOpts{ | ||||
| 				Timeout: timeout, | ||||
| @@ -336,7 +336,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo | ||||
| 					stderrBuilder.Reset() | ||||
| 					stdoutBuilder.Reset() | ||||
|  | ||||
| 					if err = git.NewCommand(ctx, "remote", "update", "--prune", m.GetRemoteName()). | ||||
| 					if err = git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). | ||||
| 						SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())). | ||||
| 						Run(&git.RunOpts{ | ||||
| 							Timeout: timeout, | ||||
|   | ||||
| @@ -29,7 +29,7 @@ var stripExitStatus = regexp.MustCompile(`exit status \d+ - `) | ||||
| // AddPushMirrorRemote registers the push mirror remote. | ||||
| func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error { | ||||
| 	addRemoteAndConfig := func(addr, path string) error { | ||||
| 		cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push", m.RemoteName, addr) | ||||
| 		cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr) | ||||
| 		if strings.Contains(addr, "://") && strings.Contains(addr, "@") { | ||||
| 			cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, util.SanitizeCredentialURLs(addr), path)) | ||||
| 		} else { | ||||
| @@ -38,10 +38,10 @@ func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str | ||||
| 		if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: path}); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if _, _, err := git.NewCommand(ctx, "config", "--add", "remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunStdString(&git.RunOpts{Dir: path}); err != nil { | ||||
| 		if _, _, err := git.NewCommand(ctx, "config", "--add", git.CmdArg("remote."+m.RemoteName+".push"), "+refs/heads/*:refs/heads/*").RunStdString(&git.RunOpts{Dir: path}); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if _, _, err := git.NewCommand(ctx, "config", "--add", "remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunStdString(&git.RunOpts{Dir: path}); err != nil { | ||||
| 		if _, _, err := git.NewCommand(ctx, "config", "--add", git.CmdArg("remote."+m.RemoteName+".push"), "+refs/tags/*:refs/tags/*").RunStdString(&git.RunOpts{Dir: path}); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return nil | ||||
| @@ -65,7 +65,7 @@ func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str | ||||
|  | ||||
| // RemovePushMirrorRemote removes the push mirror remote. | ||||
| func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error { | ||||
| 	cmd := git.NewCommand(ctx, "remote", "rm", m.RemoteName) | ||||
| 	cmd := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(m.RemoteName) | ||||
| 	_ = m.GetRepository() | ||||
|  | ||||
| 	if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: m.Repo.RepoPath()}); err != nil { | ||||
|   | ||||
| @@ -185,7 +185,7 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com | ||||
| 	headFile := pr.GetGitRefName() | ||||
|  | ||||
| 	// Check if a pull request is merged into BaseBranch | ||||
| 	_, _, err = git.NewCommand(ctx, "merge-base", "--is-ancestor", headFile, pr.BaseBranch). | ||||
| 	_, _, err = git.NewCommand(ctx, "merge-base", "--is-ancestor").AddDynamicArguments(headFile, pr.BaseBranch). | ||||
| 		RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath(), Env: []string{"GIT_INDEX_FILE=" + indexTmpPath, "GIT_DIR=" + pr.BaseRepo.RepoPath()}}) | ||||
| 	if err != nil { | ||||
| 		// Errors are signaled by a non-zero status that is not 1 | ||||
| @@ -206,7 +206,7 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com | ||||
| 	cmd := commitID[:40] + ".." + pr.BaseBranch | ||||
|  | ||||
| 	// Get the commit from BaseBranch where the pull request got merged | ||||
| 	mergeCommit, _, err := git.NewCommand(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse", cmd). | ||||
| 	mergeCommit, _, err := git.NewCommand(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse").AddDynamicArguments(cmd). | ||||
| 		RunStdString(&git.RunOpts{Dir: "", Env: []string{"GIT_INDEX_FILE=" + indexTmpPath, "GIT_DIR=" + pr.BaseRepo.RepoPath()}}) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("git rev-list --ancestry-path --merges --reverse: %v", err) | ||||
|   | ||||
| @@ -244,7 +244,7 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode | ||||
| 	stagingBranch := "staging" | ||||
|  | ||||
| 	if expectedHeadCommitID != "" { | ||||
| 		trackingCommitID, _, err := git.NewCommand(ctx, "show-ref", "--hash", git.BranchPrefix+trackingBranch).RunStdString(&git.RunOpts{Dir: tmpBasePath}) | ||||
| 		trackingCommitID, _, err := git.NewCommand(ctx, "show-ref", "--hash").AddDynamicArguments(git.BranchPrefix + trackingBranch).RunStdString(&git.RunOpts{Dir: tmpBasePath}) | ||||
| 		if err != nil { | ||||
| 			log.Error("show-ref[%s] --hash refs/heads/trackingn: %v", tmpBasePath, git.BranchPrefix+trackingBranch, err) | ||||
| 			return "", fmt.Errorf("getDiffTree: %v", err) | ||||
| @@ -360,15 +360,15 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode | ||||
| 	committer := sig | ||||
|  | ||||
| 	// Determine if we should sign | ||||
| 	var signArg string | ||||
| 	var signArg git.CmdArg | ||||
| 	sign, keyID, signer, _ := asymkey_service.SignMerge(ctx, pr, doer, tmpBasePath, "HEAD", trackingBranch) | ||||
| 	if sign { | ||||
| 		signArg = "-S" + keyID | ||||
| 		signArg = git.CmdArg("-S" + keyID) | ||||
| 		if pr.BaseRepo.GetTrustModel() == repo_model.CommitterTrustModel || pr.BaseRepo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { | ||||
| 			committer = signer | ||||
| 		} | ||||
| 	} else { | ||||
| 		signArg = "--no-gpg-sign" | ||||
| 		signArg = git.CmdArg("--no-gpg-sign") | ||||
| 	} | ||||
|  | ||||
| 	commitTimeStr := time.Now().Format(time.RFC3339) | ||||
| @@ -386,7 +386,7 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode | ||||
| 	// Merge commits. | ||||
| 	switch mergeStyle { | ||||
| 	case repo_model.MergeStyleMerge: | ||||
| 		cmd := git.NewCommand(ctx, "merge", "--no-ff", "--no-commit", trackingBranch) | ||||
| 		cmd := git.NewCommand(ctx, "merge", "--no-ff", "--no-commit").AddDynamicArguments(trackingBranch) | ||||
| 		if err := runMergeCommand(pr, mergeStyle, cmd, tmpBasePath); err != nil { | ||||
| 			log.Error("Unable to merge tracking into base: %v", err) | ||||
| 			return "", err | ||||
| @@ -402,7 +402,7 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode | ||||
| 		fallthrough | ||||
| 	case repo_model.MergeStyleRebaseMerge: | ||||
| 		// Checkout head branch | ||||
| 		if err := git.NewCommand(ctx, "checkout", "-b", stagingBranch, trackingBranch). | ||||
| 		if err := git.NewCommand(ctx, "checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch). | ||||
| 			Run(&git.RunOpts{ | ||||
| 				Dir:    tmpBasePath, | ||||
| 				Stdout: &outbuf, | ||||
| @@ -415,7 +415,7 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode | ||||
| 		errbuf.Reset() | ||||
|  | ||||
| 		// Rebase before merging | ||||
| 		if err := git.NewCommand(ctx, "rebase", baseBranch). | ||||
| 		if err := git.NewCommand(ctx, "rebase").AddDynamicArguments(baseBranch). | ||||
| 			Run(&git.RunOpts{ | ||||
| 				Dir:    tmpBasePath, | ||||
| 				Stdout: &outbuf, | ||||
| @@ -468,7 +468,7 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode | ||||
| 		} | ||||
|  | ||||
| 		// Checkout base branch again | ||||
| 		if err := git.NewCommand(ctx, "checkout", baseBranch). | ||||
| 		if err := git.NewCommand(ctx, "checkout").AddDynamicArguments(baseBranch). | ||||
| 			Run(&git.RunOpts{ | ||||
| 				Dir:    tmpBasePath, | ||||
| 				Stdout: &outbuf, | ||||
| @@ -486,7 +486,7 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode | ||||
| 		} else { | ||||
| 			cmd.AddArguments("--no-ff", "--no-commit") | ||||
| 		} | ||||
| 		cmd.AddArguments(stagingBranch) | ||||
| 		cmd.AddDynamicArguments(stagingBranch) | ||||
|  | ||||
| 		// Prepare merge with commit | ||||
| 		if err := runMergeCommand(pr, mergeStyle, cmd, tmpBasePath); err != nil { | ||||
| @@ -501,7 +501,7 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode | ||||
| 		} | ||||
| 	case repo_model.MergeStyleSquash: | ||||
| 		// Merge with squash | ||||
| 		cmd := git.NewCommand(ctx, "merge", "--squash", trackingBranch) | ||||
| 		cmd := git.NewCommand(ctx, "merge", "--squash").AddDynamicArguments(trackingBranch) | ||||
| 		if err := runMergeCommand(pr, mergeStyle, cmd, tmpBasePath); err != nil { | ||||
| 			log.Error("Unable to merge --squash tracking into base: %v", err) | ||||
| 			return "", err | ||||
| @@ -513,7 +513,7 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode | ||||
| 		} | ||||
| 		sig := pr.Issue.Poster.NewGitSig() | ||||
| 		if signArg == "" { | ||||
| 			if err := git.NewCommand(ctx, "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message). | ||||
| 			if err := git.NewCommand(ctx, "commit", git.CmdArg(fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email)), "-m").AddDynamicArguments(message). | ||||
| 				Run(&git.RunOpts{ | ||||
| 					Env:    env, | ||||
| 					Dir:    tmpBasePath, | ||||
| @@ -528,7 +528,10 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode | ||||
| 				// add trailer | ||||
| 				message += fmt.Sprintf("\nCo-authored-by: %s\nCo-committed-by: %s\n", sig.String(), sig.String()) | ||||
| 			} | ||||
| 			if err := git.NewCommand(ctx, "commit", signArg, fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message). | ||||
| 			if err := git.NewCommand(ctx, "commit"). | ||||
| 				AddArguments(signArg). | ||||
| 				AddArguments(git.CmdArg(fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email))). | ||||
| 				AddArguments("-m").AddDynamicArguments(message). | ||||
| 				Run(&git.RunOpts{ | ||||
| 					Env:    env, | ||||
| 					Dir:    tmpBasePath, | ||||
| @@ -592,9 +595,9 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode | ||||
| 	var pushCmd *git.Command | ||||
| 	if mergeStyle == repo_model.MergeStyleRebaseUpdate { | ||||
| 		// force push the rebase result to head branch | ||||
| 		pushCmd = git.NewCommand(ctx, "push", "-f", "head_repo", stagingBranch+":"+git.BranchPrefix+pr.HeadBranch) | ||||
| 		pushCmd = git.NewCommand(ctx, "push", "-f", "head_repo").AddDynamicArguments(stagingBranch + ":" + git.BranchPrefix + pr.HeadBranch) | ||||
| 	} else { | ||||
| 		pushCmd = git.NewCommand(ctx, "push", "origin", baseBranch+":"+git.BranchPrefix+pr.BaseBranch) | ||||
| 		pushCmd = git.NewCommand(ctx, "push", "origin").AddDynamicArguments(baseBranch + ":" + git.BranchPrefix + pr.BaseBranch) | ||||
| 	} | ||||
|  | ||||
| 	// Push back to upstream. | ||||
| @@ -629,10 +632,10 @@ func rawMerge(ctx context.Context, pr *issues_model.PullRequest, doer *user_mode | ||||
| 	return mergeCommitID, nil | ||||
| } | ||||
|  | ||||
| func commitAndSignNoAuthor(ctx context.Context, pr *issues_model.PullRequest, message, signArg, tmpBasePath string, env []string) error { | ||||
| func commitAndSignNoAuthor(ctx context.Context, pr *issues_model.PullRequest, message string, signArg git.CmdArg, tmpBasePath string, env []string) error { | ||||
| 	var outbuf, errbuf strings.Builder | ||||
| 	if signArg == "" { | ||||
| 		if err := git.NewCommand(ctx, "commit", "-m", message). | ||||
| 		if err := git.NewCommand(ctx, "commit", "-m").AddDynamicArguments(message). | ||||
| 			Run(&git.RunOpts{ | ||||
| 				Env:    env, | ||||
| 				Dir:    tmpBasePath, | ||||
| @@ -643,7 +646,7 @@ func commitAndSignNoAuthor(ctx context.Context, pr *issues_model.PullRequest, me | ||||
| 			return fmt.Errorf("git commit [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String()) | ||||
| 		} | ||||
| 	} else { | ||||
| 		if err := git.NewCommand(ctx, "commit", signArg, "-m", message). | ||||
| 		if err := git.NewCommand(ctx, "commit").AddArguments(signArg).AddArguments("-m").AddDynamicArguments(message). | ||||
| 			Run(&git.RunOpts{ | ||||
| 				Env:    env, | ||||
| 				Dir:    tmpBasePath, | ||||
| @@ -696,7 +699,7 @@ func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string) ( | ||||
| 	getDiffTreeFromBranch := func(repoPath, baseBranch, headBranch string) (string, error) { | ||||
| 		var outbuf, errbuf strings.Builder | ||||
| 		// Compute the diff-tree for sparse-checkout | ||||
| 		if err := git.NewCommand(ctx, "diff-tree", "--no-commit-id", "--name-only", "-r", "-z", "--root", baseBranch, headBranch, "--"). | ||||
| 		if err := git.NewCommand(ctx, "diff-tree", "--no-commit-id", "--name-only", "-r", "-z", "--root").AddDynamicArguments(baseBranch, headBranch). | ||||
| 			Run(&git.RunOpts{ | ||||
| 				Dir:    repoPath, | ||||
| 				Stdout: &outbuf, | ||||
|   | ||||
| @@ -178,7 +178,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g | ||||
| 		} | ||||
|  | ||||
| 		// Need to get the objects from the object db to attempt to merge | ||||
| 		root, _, err := git.NewCommand(ctx, "unpack-file", file.stage1.sha).RunStdString(&git.RunOpts{Dir: tmpBasePath}) | ||||
| 		root, _, err := git.NewCommand(ctx, "unpack-file").AddDynamicArguments(file.stage1.sha).RunStdString(&git.RunOpts{Dir: tmpBasePath}) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("unable to get root object: %s at path: %s for merging. Error: %w", file.stage1.sha, file.stage1.path, err) | ||||
| 		} | ||||
| @@ -187,7 +187,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g | ||||
| 			_ = util.Remove(filepath.Join(tmpBasePath, root)) | ||||
| 		}() | ||||
|  | ||||
| 		base, _, err := git.NewCommand(ctx, "unpack-file", file.stage2.sha).RunStdString(&git.RunOpts{Dir: tmpBasePath}) | ||||
| 		base, _, err := git.NewCommand(ctx, "unpack-file").AddDynamicArguments(file.stage2.sha).RunStdString(&git.RunOpts{Dir: tmpBasePath}) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("unable to get base object: %s at path: %s for merging. Error: %w", file.stage2.sha, file.stage2.path, err) | ||||
| 		} | ||||
| @@ -195,7 +195,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g | ||||
| 		defer func() { | ||||
| 			_ = util.Remove(base) | ||||
| 		}() | ||||
| 		head, _, err := git.NewCommand(ctx, "unpack-file", file.stage3.sha).RunStdString(&git.RunOpts{Dir: tmpBasePath}) | ||||
| 		head, _, err := git.NewCommand(ctx, "unpack-file").AddDynamicArguments(file.stage3.sha).RunStdString(&git.RunOpts{Dir: tmpBasePath}) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("unable to get head object:%s at path: %s for merging. Error: %w", file.stage3.sha, file.stage3.path, err) | ||||
| 		} | ||||
| @@ -205,13 +205,13 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, g | ||||
| 		}() | ||||
|  | ||||
| 		// now git merge-file annoyingly takes a different order to the merge-tree ... | ||||
| 		_, _, conflictErr := git.NewCommand(ctx, "merge-file", base, root, head).RunStdString(&git.RunOpts{Dir: tmpBasePath}) | ||||
| 		_, _, conflictErr := git.NewCommand(ctx, "merge-file").AddDynamicArguments(base, root, head).RunStdString(&git.RunOpts{Dir: tmpBasePath}) | ||||
| 		if conflictErr != nil { | ||||
| 			return &errMergeConflict{file.stage2.path} | ||||
| 		} | ||||
|  | ||||
| 		// base now contains the merged data | ||||
| 		hash, _, err := git.NewCommand(ctx, "hash-object", "-w", "--path", file.stage2.path, base).RunStdString(&git.RunOpts{Dir: tmpBasePath}) | ||||
| 		hash, _, err := git.NewCommand(ctx, "hash-object", "-w", "--path").AddDynamicArguments(file.stage2.path, base).RunStdString(&git.RunOpts{Dir: tmpBasePath}) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| @@ -235,7 +235,7 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo | ||||
| 	defer cancel() | ||||
|  | ||||
| 	// First we use read-tree to do a simple three-way merge | ||||
| 	if _, _, err := git.NewCommand(ctx, "read-tree", "-m", base, ours, theirs).RunStdString(&git.RunOpts{Dir: gitPath}); err != nil { | ||||
| 	if _, _, err := git.NewCommand(ctx, "read-tree", "-m").AddDynamicArguments(base, ours, theirs).RunStdString(&git.RunOpts{Dir: gitPath}); err != nil { | ||||
| 		log.Error("Unable to run read-tree -m! Error: %v", err) | ||||
| 		return false, nil, fmt.Errorf("unable to run read-tree -m! Error: %v", err) | ||||
| 	} | ||||
| @@ -361,7 +361,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo * | ||||
| 	prConfig := prUnit.PullRequestsConfig() | ||||
|  | ||||
| 	// 6. Prepare the arguments to apply the patch against the index | ||||
| 	args := []string{"apply", "--check", "--cached"} | ||||
| 	args := []git.CmdArg{"apply", "--check", "--cached"} | ||||
| 	if prConfig.IgnoreWhitespaceConflicts { | ||||
| 		args = append(args, "--ignore-whitespace") | ||||
| 	} | ||||
| @@ -370,7 +370,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo * | ||||
| 		args = append(args, "--3way") | ||||
| 		is3way = true | ||||
| 	} | ||||
| 	args = append(args, patchPath) | ||||
| 	args = append(args, git.CmdArgCheck(patchPath)) | ||||
|  | ||||
| 	// 7. Prep the pipe: | ||||
| 	//   - Here we could do the equivalent of: | ||||
|   | ||||
| @@ -490,7 +490,7 @@ func UpdateRef(ctx context.Context, pr *issues_model.PullRequest) (err error) { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	_, _, err = git.NewCommand(ctx, "update-ref", pr.GetGitRefName(), pr.HeadCommitID).RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath()}) | ||||
| 	_, _, err = git.NewCommand(ctx, "update-ref").AddDynamicArguments(pr.GetGitRefName(), pr.HeadCommitID).RunStdString(&git.RunOpts{Dir: pr.BaseRepo.RepoPath()}) | ||||
| 	if err != nil { | ||||
| 		log.Error("Unable to update ref in base repository for PR[%d] Error: %v", pr.ID, err) | ||||
| 	} | ||||
|   | ||||
| @@ -94,7 +94,7 @@ func createTemporaryRepo(ctx context.Context, pr *issues_model.PullRequest) (str | ||||
| 	} | ||||
|  | ||||
| 	var outbuf, errbuf strings.Builder | ||||
| 	if err := git.NewCommand(ctx, "remote", "add", "-t", pr.BaseBranch, "-m", pr.BaseBranch, "origin", baseRepoPath). | ||||
| 	if err := git.NewCommand(ctx, "remote", "add", "-t").AddDynamicArguments(pr.BaseBranch).AddArguments("-m").AddDynamicArguments(pr.BaseBranch).AddDynamicArguments("origin", baseRepoPath). | ||||
| 		Run(&git.RunOpts{ | ||||
| 			Dir:    tmpBasePath, | ||||
| 			Stdout: &outbuf, | ||||
| @@ -109,7 +109,7 @@ func createTemporaryRepo(ctx context.Context, pr *issues_model.PullRequest) (str | ||||
| 	outbuf.Reset() | ||||
| 	errbuf.Reset() | ||||
|  | ||||
| 	if err := git.NewCommand(ctx, "fetch", "origin", "--no-tags", "--", pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch). | ||||
| 	if err := git.NewCommand(ctx, "fetch", "origin", "--no-tags").AddDashesAndList(pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch). | ||||
| 		Run(&git.RunOpts{ | ||||
| 			Dir:    tmpBasePath, | ||||
| 			Stdout: &outbuf, | ||||
| @@ -124,7 +124,7 @@ func createTemporaryRepo(ctx context.Context, pr *issues_model.PullRequest) (str | ||||
| 	outbuf.Reset() | ||||
| 	errbuf.Reset() | ||||
|  | ||||
| 	if err := git.NewCommand(ctx, "symbolic-ref", "HEAD", git.BranchPrefix+baseBranch). | ||||
| 	if err := git.NewCommand(ctx, "symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseBranch). | ||||
| 		Run(&git.RunOpts{ | ||||
| 			Dir:    tmpBasePath, | ||||
| 			Stdout: &outbuf, | ||||
| @@ -147,7 +147,7 @@ func createTemporaryRepo(ctx context.Context, pr *issues_model.PullRequest) (str | ||||
| 		return "", fmt.Errorf("Unable to head base repository to temporary repo [%s -> tmpBasePath]: %v", pr.HeadRepo.FullName(), err) | ||||
| 	} | ||||
|  | ||||
| 	if err := git.NewCommand(ctx, "remote", "add", remoteRepoName, headRepoPath). | ||||
| 	if err := git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteRepoName, headRepoPath). | ||||
| 		Run(&git.RunOpts{ | ||||
| 			Dir:    tmpBasePath, | ||||
| 			Stdout: &outbuf, | ||||
| @@ -172,7 +172,7 @@ func createTemporaryRepo(ctx context.Context, pr *issues_model.PullRequest) (str | ||||
| 	} else { | ||||
| 		headBranch = pr.GetGitRefName() | ||||
| 	} | ||||
| 	if err := git.NewCommand(ctx, "fetch", "--no-tags", remoteRepoName, headBranch+":"+trackingBranch). | ||||
| 	if err := git.NewCommand(ctx, "fetch", "--no-tags").AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch). | ||||
| 		Run(&git.RunOpts{ | ||||
| 			Dir:    tmpBasePath, | ||||
| 			Stdout: &outbuf, | ||||
|   | ||||
| @@ -309,7 +309,7 @@ func DeleteReleaseByID(ctx context.Context, id int64, doer *user_model.User, del | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if stdout, _, err := git.NewCommand(ctx, "tag", "-d", "--", rel.TagName). | ||||
| 		if stdout, _, err := git.NewCommand(ctx, "tag", "-d").AddDashesAndList(rel.TagName). | ||||
| 			SetDescription(fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID)). | ||||
| 			RunStdString(&git.RunOpts{Dir: repo.RepoPath()}); err != nil && !strings.Contains(err.Error(), "not found") { | ||||
| 			log.Error("DeleteReleaseByID (git tag -d): %d in %v Failed:\nStdout: %s\nError: %v", rel.ID, repo, stdout, err) | ||||
|   | ||||
| @@ -24,7 +24,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| // GitFsck calls 'git fsck' to check repository health. | ||||
| func GitFsck(ctx context.Context, timeout time.Duration, args []string) error { | ||||
| func GitFsck(ctx context.Context, timeout time.Duration, args []git.CmdArg) error { | ||||
| 	log.Trace("Doing: GitFsck") | ||||
|  | ||||
| 	if err := db.Iterate( | ||||
| @@ -58,9 +58,9 @@ func GitFsck(ctx context.Context, timeout time.Duration, args []string) error { | ||||
| } | ||||
|  | ||||
| // GitGcRepos calls 'git gc' to remove unnecessary files and optimize the local repository | ||||
| func GitGcRepos(ctx context.Context, timeout time.Duration, args ...string) error { | ||||
| func GitGcRepos(ctx context.Context, timeout time.Duration, args ...git.CmdArg) error { | ||||
| 	log.Trace("Doing: GitGcRepos") | ||||
| 	args = append([]string{"gc"}, args...) | ||||
| 	args = append([]git.CmdArg{"gc"}, args...) | ||||
|  | ||||
| 	if err := db.Iterate( | ||||
| 		ctx, | ||||
|   | ||||
| @@ -139,7 +139,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user | ||||
| 	stdout := &strings.Builder{} | ||||
| 	stderr := &strings.Builder{} | ||||
|  | ||||
| 	args := []string{"apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary"} | ||||
| 	args := []git.CmdArg{"apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary"} | ||||
|  | ||||
| 	if git.CheckGitVersionAtLeast("2.32") == nil { | ||||
| 		args = append(args, "-3") | ||||
|   | ||||
| @@ -53,7 +53,7 @@ func (t *TemporaryUploadRepository) Close() { | ||||
|  | ||||
| // Clone the base repository to our path and set branch as the HEAD | ||||
| func (t *TemporaryUploadRepository) Clone(branch string) error { | ||||
| 	if _, _, err := git.NewCommand(t.ctx, "clone", "-s", "--bare", "-b", branch, t.repo.RepoPath(), t.basePath).RunStdString(nil); err != nil { | ||||
| 	if _, _, err := git.NewCommand(t.ctx, "clone", "-s", "--bare", "-b").AddDynamicArguments(branch, t.repo.RepoPath(), t.basePath).RunStdString(nil); err != nil { | ||||
| 		stderr := err.Error() | ||||
| 		if matched, _ := regexp.MatchString(".*Remote branch .* not found in upstream origin.*", stderr); matched { | ||||
| 			return git.ErrBranchNotExist{ | ||||
| @@ -104,14 +104,7 @@ func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, erro | ||||
| 	stdOut := new(bytes.Buffer) | ||||
| 	stdErr := new(bytes.Buffer) | ||||
|  | ||||
| 	cmdArgs := []string{"ls-files", "-z", "--"} | ||||
| 	for _, arg := range filenames { | ||||
| 		if arg != "" { | ||||
| 			cmdArgs = append(cmdArgs, arg) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if err := git.NewCommand(t.ctx, cmdArgs...). | ||||
| 	if err := git.NewCommand(t.ctx, "ls-files", "-z").AddDashesAndList(filenames...). | ||||
| 		Run(&git.RunOpts{ | ||||
| 			Dir:    t.basePath, | ||||
| 			Stdout: stdOut, | ||||
| @@ -177,7 +170,7 @@ func (t *TemporaryUploadRepository) HashObject(content io.Reader) (string, error | ||||
|  | ||||
| // AddObjectToIndex adds the provided object hash to the index with the provided mode and path | ||||
| func (t *TemporaryUploadRepository) AddObjectToIndex(mode, objectHash, objectPath string) error { | ||||
| 	if _, _, err := git.NewCommand(t.ctx, "update-index", "--add", "--replace", "--cacheinfo", mode, objectHash, objectPath).RunStdString(&git.RunOpts{Dir: t.basePath}); err != nil { | ||||
| 	if _, _, err := git.NewCommand(t.ctx, "update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments(mode, objectHash, objectPath).RunStdString(&git.RunOpts{Dir: t.basePath}); err != nil { | ||||
| 		stderr := err.Error() | ||||
| 		if matched, _ := regexp.MatchString(".*Invalid path '.*", stderr); matched { | ||||
| 			return models.ErrFilePathInvalid{ | ||||
| @@ -211,7 +204,7 @@ func (t *TemporaryUploadRepository) GetLastCommitByRef(ref string) (string, erro | ||||
| 	if ref == "" { | ||||
| 		ref = "HEAD" | ||||
| 	} | ||||
| 	stdout, _, err := git.NewCommand(t.ctx, "rev-parse", ref).RunStdString(&git.RunOpts{Dir: t.basePath}) | ||||
| 	stdout, _, err := git.NewCommand(t.ctx, "rev-parse").AddDynamicArguments(ref).RunStdString(&git.RunOpts{Dir: t.basePath}) | ||||
| 	if err != nil { | ||||
| 		log.Error("Unable to get last ref for %s in temporary repo: %s(%s): Error: %v", ref, t.repo.FullName(), t.basePath, err) | ||||
| 		return "", fmt.Errorf("Unable to rev-parse %s in temporary repo for: %s Error: %v", ref, t.repo.FullName(), err) | ||||
| @@ -241,11 +234,11 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, co | ||||
| 	_, _ = messageBytes.WriteString(message) | ||||
| 	_, _ = messageBytes.WriteString("\n") | ||||
|  | ||||
| 	var args []string | ||||
| 	var args []git.CmdArg | ||||
| 	if parent != "" { | ||||
| 		args = []string{"commit-tree", treeHash, "-p", parent} | ||||
| 		args = []git.CmdArg{"commit-tree", git.CmdArgCheck(treeHash), "-p", git.CmdArgCheck(parent)} | ||||
| 	} else { | ||||
| 		args = []string{"commit-tree", treeHash} | ||||
| 		args = []git.CmdArg{"commit-tree", git.CmdArgCheck(treeHash)} | ||||
| 	} | ||||
|  | ||||
| 	var sign bool | ||||
| @@ -257,7 +250,7 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, co | ||||
| 		sign, keyID, signer, _ = asymkey_service.SignInitialCommit(t.ctx, t.repo.RepoPath(), author) | ||||
| 	} | ||||
| 	if sign { | ||||
| 		args = append(args, "-S"+keyID) | ||||
| 		args = append(args, git.CmdArg("-S"+keyID)) | ||||
| 		if t.repo.GetTrustModel() == repo_model.CommitterTrustModel || t.repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel { | ||||
| 			if committerSig.Name != authorSig.Name || committerSig.Email != authorSig.Email { | ||||
| 				// Add trailers | ||||
|   | ||||
| @@ -370,7 +370,7 @@ func CreateOrUpdateRepoFile(ctx context.Context, repo *repo_model.Repository, do | ||||
| 	if setting.LFS.StartServer && hasOldBranch { | ||||
| 		// Check there is no way this can return multiple infos | ||||
| 		filename2attribute2info, err := t.gitRepo.CheckAttribute(git.CheckAttributeOpts{ | ||||
| 			Attributes: []string{"filter"}, | ||||
| 			Attributes: []git.CmdArg{"filter"}, | ||||
| 			Filenames:  []string{treePath}, | ||||
| 			CachedOnly: true, | ||||
| 		}) | ||||
|   | ||||
| @@ -96,7 +96,7 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use | ||||
| 	var filename2attribute2info map[string]map[string]string | ||||
| 	if setting.LFS.StartServer { | ||||
| 		filename2attribute2info, err = t.gitRepo.CheckAttribute(git.CheckAttributeOpts{ | ||||
| 			Attributes: []string{"filter"}, | ||||
| 			Attributes: []git.CmdArg{"filter"}, | ||||
| 			Filenames:  names, | ||||
| 			CachedOnly: true, | ||||
| 		}) | ||||
|   | ||||
| @@ -130,7 +130,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork | ||||
|  | ||||
| 		repoPath := repo_model.RepoPath(owner.Name, repo.Name) | ||||
| 		if stdout, _, err := git.NewCommand(txCtx, | ||||
| 			"clone", "--bare", oldRepoPath, repoPath). | ||||
| 			"clone", "--bare").AddDynamicArguments(oldRepoPath, repoPath). | ||||
| 			SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", opts.BaseRepo.FullName(), repo.FullName())). | ||||
| 			RunStdBytes(&git.RunOpts{Timeout: 10 * time.Minute}); err != nil { | ||||
| 			log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, opts.BaseRepo, stdout, err) | ||||
|   | ||||
| @@ -29,8 +29,8 @@ func TestAPIGitTags(t *testing.T) { | ||||
| 	token := getTokenForLoggedInUser(t, session) | ||||
|  | ||||
| 	// Set up git config for the tagger | ||||
| 	_ = git.NewCommand(git.DefaultContext, "config", "user.name", user.Name).Run(&git.RunOpts{Dir: repo.RepoPath()}) | ||||
| 	_ = git.NewCommand(git.DefaultContext, "config", "user.email", user.Email).Run(&git.RunOpts{Dir: repo.RepoPath()}) | ||||
| 	_ = git.NewCommand(git.DefaultContext, "config", "user.name").AddDynamicArguments(user.Name).Run(&git.RunOpts{Dir: repo.RepoPath()}) | ||||
| 	_ = git.NewCommand(git.DefaultContext, "config", "user.email").AddDynamicArguments(user.Email).Run(&git.RunOpts{Dir: repo.RepoPath()}) | ||||
|  | ||||
| 	gitRepo, _ := git.OpenRepository(git.DefaultContext, repo.RepoPath()) | ||||
| 	defer gitRepo.Close() | ||||
|   | ||||
| @@ -41,7 +41,7 @@ func TestRepoCloneWiki(t *testing.T) { | ||||
| 		u, _ = url.Parse(r) | ||||
| 		u.User = url.UserPassword("user2", userPassword) | ||||
| 		t.Run("Clone", func(t *testing.T) { | ||||
| 			assert.NoError(t, git.CloneWithArgs(context.Background(), u.String(), dstPath, git.AllowLFSFiltersArgs(), git.CloneRepoOptions{})) | ||||
| 			assert.NoError(t, git.CloneWithArgs(context.Background(), git.AllowLFSFiltersArgs(), u.String(), dstPath, git.CloneRepoOptions{})) | ||||
| 			assertFileEqual(t, filepath.Join(dstPath, "Home.md"), []byte("# Home page\n\nThis is the home page!\n")) | ||||
| 			assertFileExist(t, filepath.Join(dstPath, "Page-With-Image.md")) | ||||
| 			assertFileExist(t, filepath.Join(dstPath, "Page-With-Spaced-Name.md")) | ||||
|   | ||||
| @@ -98,7 +98,7 @@ func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL), prepare ...bo | ||||
|  | ||||
| func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) { | ||||
| 	return func(t *testing.T) { | ||||
| 		assert.NoError(t, git.CloneWithArgs(context.Background(), u.String(), dstLocalPath, git.AllowLFSFiltersArgs(), git.CloneRepoOptions{})) | ||||
| 		assert.NoError(t, git.CloneWithArgs(context.Background(), git.AllowLFSFiltersArgs(), u.String(), dstLocalPath, git.CloneRepoOptions{})) | ||||
| 		exist, err := util.IsExist(filepath.Join(dstLocalPath, "README.md")) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.True(t, exist) | ||||
| @@ -107,7 +107,7 @@ func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) { | ||||
|  | ||||
| func doPartialGitClone(dstLocalPath string, u *url.URL) func(*testing.T) { | ||||
| 	return func(t *testing.T) { | ||||
| 		assert.NoError(t, git.CloneWithArgs(context.Background(), u.String(), dstLocalPath, git.AllowLFSFiltersArgs(), git.CloneRepoOptions{ | ||||
| 		assert.NoError(t, git.CloneWithArgs(context.Background(), git.AllowLFSFiltersArgs(), u.String(), dstLocalPath, git.CloneRepoOptions{ | ||||
| 			Filter: "blob:none", | ||||
| 		})) | ||||
| 		exist, err := util.IsExist(filepath.Join(dstLocalPath, "README.md")) | ||||
| @@ -150,47 +150,47 @@ func doGitInitTestRepository(dstPath string) func(*testing.T) { | ||||
|  | ||||
| func doGitAddRemote(dstPath, remoteName string, u *url.URL) func(*testing.T) { | ||||
| 	return func(t *testing.T) { | ||||
| 		_, _, err := git.NewCommand(git.DefaultContext, "remote", "add", remoteName, u.String()).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 		_, _, err := git.NewCommand(git.DefaultContext, "remote", "add").AddDynamicArguments(remoteName, u.String()).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 		assert.NoError(t, err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func doGitPushTestRepository(dstPath string, args ...string) func(*testing.T) { | ||||
| func doGitPushTestRepository(dstPath string, args ...git.CmdArg) func(*testing.T) { | ||||
| 	return func(t *testing.T) { | ||||
| 		_, _, err := git.NewCommand(git.DefaultContext, append([]string{"push", "-u"}, args...)...).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 		_, _, err := git.NewCommand(git.DefaultContext, append([]git.CmdArg{"push", "-u"}, args...)...).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 		assert.NoError(t, err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func doGitPushTestRepositoryFail(dstPath string, args ...string) func(*testing.T) { | ||||
| func doGitPushTestRepositoryFail(dstPath string, args ...git.CmdArg) func(*testing.T) { | ||||
| 	return func(t *testing.T) { | ||||
| 		_, _, err := git.NewCommand(git.DefaultContext, append([]string{"push"}, args...)...).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 		_, _, err := git.NewCommand(git.DefaultContext, append([]git.CmdArg{"push"}, args...)...).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 		assert.Error(t, err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func doGitCreateBranch(dstPath, branch string) func(*testing.T) { | ||||
| 	return func(t *testing.T) { | ||||
| 		_, _, err := git.NewCommand(git.DefaultContext, "checkout", "-b", branch).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 		_, _, err := git.NewCommand(git.DefaultContext, "checkout", "-b").AddDynamicArguments(branch).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 		assert.NoError(t, err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func doGitCheckoutBranch(dstPath string, args ...string) func(*testing.T) { | ||||
| func doGitCheckoutBranch(dstPath string, args ...git.CmdArg) func(*testing.T) { | ||||
| 	return func(t *testing.T) { | ||||
| 		_, _, err := git.NewCommandNoGlobals(append(append(git.AllowLFSFiltersArgs(), "checkout"), args...)...).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 		assert.NoError(t, err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func doGitMerge(dstPath string, args ...string) func(*testing.T) { | ||||
| func doGitMerge(dstPath string, args ...git.CmdArg) func(*testing.T) { | ||||
| 	return func(t *testing.T) { | ||||
| 		_, _, err := git.NewCommand(git.DefaultContext, append([]string{"merge"}, args...)...).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 		_, _, err := git.NewCommand(git.DefaultContext, append([]git.CmdArg{"merge"}, args...)...).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 		assert.NoError(t, err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func doGitPull(dstPath string, args ...string) func(*testing.T) { | ||||
| func doGitPull(dstPath string, args ...git.CmdArg) func(*testing.T) { | ||||
| 	return func(t *testing.T) { | ||||
| 		_, _, err := git.NewCommandNoGlobals(append(append(git.AllowLFSFiltersArgs(), "pull"), args...)...).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 		assert.NoError(t, err) | ||||
|   | ||||
| @@ -151,7 +151,7 @@ func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS strin | ||||
| 		prefix := "lfs-data-file-" | ||||
| 		err := git.NewCommand(git.DefaultContext, "lfs").AddArguments("install").Run(&git.RunOpts{Dir: dstPath}) | ||||
| 		assert.NoError(t, err) | ||||
| 		_, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("track", prefix+"*").RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 		_, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("track").AddDynamicArguments(prefix + "*").RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 		assert.NoError(t, err) | ||||
| 		err = git.AddChanges(dstPath, false, ".gitattributes") | ||||
| 		assert.NoError(t, err) | ||||
| @@ -279,11 +279,11 @@ func lockTest(t *testing.T, repoPath string) { | ||||
| func lockFileTest(t *testing.T, filename, repoPath string) { | ||||
| 	_, _, err := git.NewCommand(git.DefaultContext, "lfs").AddArguments("locks").RunStdString(&git.RunOpts{Dir: repoPath}) | ||||
| 	assert.NoError(t, err) | ||||
| 	_, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("lock", filename).RunStdString(&git.RunOpts{Dir: repoPath}) | ||||
| 	_, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("lock").AddDynamicArguments(filename).RunStdString(&git.RunOpts{Dir: repoPath}) | ||||
| 	assert.NoError(t, err) | ||||
| 	_, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("locks").RunStdString(&git.RunOpts{Dir: repoPath}) | ||||
| 	assert.NoError(t, err) | ||||
| 	_, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("unlock", filename).RunStdString(&git.RunOpts{Dir: repoPath}) | ||||
| 	_, _, err = git.NewCommand(git.DefaultContext, "lfs").AddArguments("unlock").AddDynamicArguments(filename).RunStdString(&git.RunOpts{Dir: repoPath}) | ||||
| 	assert.NoError(t, err) | ||||
| } | ||||
|  | ||||
| @@ -509,7 +509,7 @@ func doCreatePRAndSetManuallyMerged(ctx, baseCtx APITestContext, dstPath, baseBr | ||||
| 		})) | ||||
|  | ||||
| 		t.Run("CreateHeadBranch", doGitCreateBranch(dstPath, headBranch)) | ||||
| 		t.Run("PushToHeadBranch", doGitPushTestRepository(dstPath, "origin", headBranch)) | ||||
| 		t.Run("PushToHeadBranch", doGitPushTestRepository(dstPath, "origin", git.CmdArgCheck(headBranch))) | ||||
| 		t.Run("CreateEmptyPullRequest", func(t *testing.T) { | ||||
| 			pr, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, baseBranch, headBranch)(t) | ||||
| 			assert.NoError(t, err) | ||||
| @@ -736,7 +736,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB | ||||
| 		}) | ||||
|  | ||||
| 		t.Run("Push", func(t *testing.T) { | ||||
| 			err := git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master", "-o", "topic="+headBranch).Run(&git.RunOpts{Dir: dstPath}) | ||||
| 			err := git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master", "-o").AddDynamicArguments("topic=" + headBranch).Run(&git.RunOpts{Dir: dstPath}) | ||||
| 			if !assert.NoError(t, err) { | ||||
| 				return | ||||
| 			} | ||||
| @@ -757,7 +757,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB | ||||
| 			assert.Contains(t, "Testing commit 1", prMsg.Body) | ||||
| 			assert.Equal(t, commit, prMsg.Head.Sha) | ||||
|  | ||||
| 			_, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master/test/"+headBranch).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 			_, _, err = git.NewCommand(git.DefaultContext, "push", "origin").AddDynamicArguments("HEAD:refs/for/master/test/" + headBranch).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 			if !assert.NoError(t, err) { | ||||
| 				return | ||||
| 			} | ||||
| @@ -810,7 +810,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB | ||||
| 		}) | ||||
|  | ||||
| 		t.Run("Push2", func(t *testing.T) { | ||||
| 			err := git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master", "-o", "topic="+headBranch).Run(&git.RunOpts{Dir: dstPath}) | ||||
| 			err := git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master", "-o").AddDynamicArguments("topic=" + headBranch).Run(&git.RunOpts{Dir: dstPath}) | ||||
| 			if !assert.NoError(t, err) { | ||||
| 				return | ||||
| 			} | ||||
| @@ -822,7 +822,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB | ||||
| 			assert.Equal(t, false, prMsg.HasMerged) | ||||
| 			assert.Equal(t, commit, prMsg.Head.Sha) | ||||
|  | ||||
| 			_, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master/test/"+headBranch).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 			_, _, err = git.NewCommand(git.DefaultContext, "push", "origin").AddDynamicArguments("HEAD:refs/for/master/test/" + headBranch).RunStdString(&git.RunOpts{Dir: dstPath}) | ||||
| 			if !assert.NoError(t, err) { | ||||
| 				return | ||||
| 			} | ||||
|   | ||||
| @@ -287,7 +287,7 @@ func TestCantMergeUnrelated(t *testing.T) { | ||||
| 		assert.NoError(t, err) | ||||
| 		sha := strings.TrimSpace(stdout.String()) | ||||
|  | ||||
| 		_, _, err = git.NewCommand(git.DefaultContext, "update-index", "--add", "--replace", "--cacheinfo", "100644", sha, "somewher-over-the-rainbow").RunStdString(&git.RunOpts{Dir: path}) | ||||
| 		_, _, err = git.NewCommand(git.DefaultContext, "update-index", "--add", "--replace", "--cacheinfo", "100644", git.CmdArgCheck(sha), "somewher-over-the-rainbow").RunStdString(&git.RunOpts{Dir: path}) | ||||
| 		assert.NoError(t, err) | ||||
|  | ||||
| 		treeSha, _, err := git.NewCommand(git.DefaultContext, "write-tree").RunStdString(&git.RunOpts{Dir: path}) | ||||
| @@ -310,7 +310,7 @@ func TestCantMergeUnrelated(t *testing.T) { | ||||
| 		_, _ = messageBytes.WriteString("\n") | ||||
|  | ||||
| 		stdout.Reset() | ||||
| 		err = git.NewCommand(git.DefaultContext, "commit-tree", treeSha). | ||||
| 		err = git.NewCommand(git.DefaultContext, "commit-tree").AddDynamicArguments(treeSha). | ||||
| 			Run(&git.RunOpts{ | ||||
| 				Env:    env, | ||||
| 				Dir:    path, | ||||
| @@ -320,7 +320,7 @@ func TestCantMergeUnrelated(t *testing.T) { | ||||
| 		assert.NoError(t, err) | ||||
| 		commitSha := strings.TrimSpace(stdout.String()) | ||||
|  | ||||
| 		_, _, err = git.NewCommand(git.DefaultContext, "branch", "unrelated", commitSha).RunStdString(&git.RunOpts{Dir: path}) | ||||
| 		_, _, err = git.NewCommand(git.DefaultContext, "branch", "unrelated").AddDynamicArguments(commitSha).RunStdString(&git.RunOpts{Dir: path}) | ||||
| 		assert.NoError(t, err) | ||||
|  | ||||
| 		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user