Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <psteinhardt@gitlab.com>2020-12-15 12:20:57 +0300
committerPatrick Steinhardt <psteinhardt@gitlab.com>2020-12-15 12:20:57 +0300
commitd52ea41f12a33a5213bf0067f0bc0beabb949556 (patch)
treebabbecd901b0d133af03ef632fe4afe13b0ccebf
parent1296ac533238b39a382a77ced57db6994e28d032 (diff)
parentd28dc57cdd449c5cd4c2e0d2f75c3ef2aaa2a565 (diff)
Merge branch 'pks-git-safecmd-refactorings' into 'master'
git: Improve SafeCmd interface See merge request gitlab-org/gitaly!2891
-rw-r--r--cmd/gitaly-ssh/upload_pack_test.go4
-rw-r--r--internal/git/catfile/batch.go15
-rw-r--r--internal/git/catfile/batchcheck.go15
-rw-r--r--internal/git/command.go8
-rw-r--r--internal/git/command_test.go2
-rw-r--r--internal/git/objectpool/clone.go2
-rw-r--r--internal/git/objectpool/fetch.go2
-rw-r--r--internal/git/objectpool/pool.go2
-rw-r--r--internal/git/proto.go13
-rw-r--r--internal/git/receivepack.go4
-rw-r--r--internal/git/remote.go26
-rw-r--r--internal/git/remote/remote.go8
-rw-r--r--internal/git/repository.go4
-rw-r--r--internal/git/repository_test.go2
-rw-r--r--internal/git/safecmd.go176
-rw-r--r--internal/git/safecmd_test.go195
-rw-r--r--internal/git/staticargs.go5
-rw-r--r--internal/git/uploadpack.go10
-rw-r--r--internal/git/version.go2
-rw-r--r--internal/gitaly/service/commit/languages.go6
-rw-r--r--internal/gitaly/service/conflicts/resolve_conflicts.go6
-rw-r--r--internal/gitaly/service/diff/commit.go2
-rw-r--r--internal/gitaly/service/operations/squash.go65
-rw-r--r--internal/gitaly/service/ref/refname.go11
-rw-r--r--internal/gitaly/service/ref/refname_test.go2
-rw-r--r--internal/gitaly/service/remote/find_remote_root_ref.go6
-rw-r--r--internal/gitaly/service/remote/remotes.go2
-rw-r--r--internal/gitaly/service/repository/archive.go2
-rw-r--r--internal/gitaly/service/repository/calculate_checksum.go13
-rw-r--r--internal/gitaly/service/repository/cleanup.go14
-rw-r--r--internal/gitaly/service/repository/clone_from_pool_internal.go2
-rw-r--r--internal/gitaly/service/repository/commit_graph.go6
-rw-r--r--internal/gitaly/service/repository/create.go3
-rw-r--r--internal/gitaly/service/repository/create_bundle.go7
-rw-r--r--internal/gitaly/service/repository/create_from_bundle.go8
-rw-r--r--internal/gitaly/service/repository/create_from_url.go5
-rw-r--r--internal/gitaly/service/repository/fetch.go4
-rw-r--r--internal/gitaly/service/repository/fork.go2
-rw-r--r--internal/gitaly/service/repository/fsck.go6
-rw-r--r--internal/gitaly/service/repository/midx.go32
-rw-r--r--internal/gitaly/service/repository/repack.go4
-rw-r--r--internal/gitaly/service/smarthttp/inforefs.go4
-rw-r--r--internal/gitaly/service/smarthttp/receive_pack.go4
-rw-r--r--internal/gitaly/service/smarthttp/upload_pack.go4
-rw-r--r--internal/gitaly/service/ssh/monitor_stdin_command.go6
-rw-r--r--internal/gitaly/service/ssh/receive_pack.go5
-rw-r--r--internal/gitalyssh/gitalyssh.go6
47 files changed, 403 insertions, 329 deletions
diff --git a/cmd/gitaly-ssh/upload_pack_test.go b/cmd/gitaly-ssh/upload_pack_test.go
index 618e98e33..8529ac980 100644
--- a/cmd/gitaly-ssh/upload_pack_test.go
+++ b/cmd/gitaly-ssh/upload_pack_test.go
@@ -86,13 +86,13 @@ func TestVisibilityOfHiddenRefs(t *testing.T) {
}
stdout := &bytes.Buffer{}
- cmd, err := git.SafeBareCmd(ctx, git.CmdStream{Out: stdout}, env, nil, git.SubCmd{
+ cmd, err := git.SafeBareCmd(ctx, env, nil, git.SubCmd{
Name: "ls-remote",
Args: []string{
fmt.Sprintf("%s:%s", "git@localhost", testRepoPath),
keepAroundRef,
},
- })
+ }, git.WithStdout(stdout))
require.NoError(t, err)
err = cmd.Wait()
diff --git a/internal/git/catfile/batch.go b/internal/git/catfile/batch.go
index 358ba9ade..58bf98c30 100644
--- a/internal/git/catfile/batch.go
+++ b/internal/git/catfile/batch.go
@@ -51,9 +51,18 @@ func newBatchProcess(ctx context.Context, repo repository.GitRepo) (*batchProces
ctx = correlation.ContextWithCorrelation(ctx, "")
ctx = opentracing.ContextWithSpan(ctx, nil)
- batchCmd, err := git.SafeBareCmd(ctx, git.CmdStream{In: stdinReader}, env,
- []git.Option{git.ValueFlag{Name: "--git-dir", Value: repoPath}},
- git.SubCmd{Name: "cat-file", Flags: []git.Option{git.Flag{Name: "--batch"}}})
+ batchCmd, err := git.SafeBareCmd(ctx, env,
+ []git.GlobalOption{
+ git.ValueFlag{Name: "--git-dir", Value: repoPath},
+ },
+ git.SubCmd{
+ Name: "cat-file",
+ Flags: []git.Option{
+ git.Flag{Name: "--batch"},
+ },
+ },
+ git.WithStdin(stdinReader),
+ )
if err != nil {
return nil, err
}
diff --git a/internal/git/catfile/batchcheck.go b/internal/git/catfile/batchcheck.go
index 9a283f86a..28afdd9b7 100644
--- a/internal/git/catfile/batchcheck.go
+++ b/internal/git/catfile/batchcheck.go
@@ -37,9 +37,18 @@ func newBatchCheck(ctx context.Context, repo repository.GitRepo) (*batchCheck, e
ctx = correlation.ContextWithCorrelation(ctx, "")
ctx = opentracing.ContextWithSpan(ctx, nil)
- batchCmd, err := git.SafeBareCmd(ctx, git.CmdStream{In: stdinReader}, env,
- []git.Option{git.ValueFlag{Name: "--git-dir", Value: repoPath}},
- git.SubCmd{Name: "cat-file", Flags: []git.Option{git.Flag{Name: "--batch-check"}}})
+ batchCmd, err := git.SafeBareCmd(ctx, env,
+ []git.GlobalOption{
+ git.ValueFlag{Name: "--git-dir", Value: repoPath},
+ },
+ git.SubCmd{
+ Name: "cat-file",
+ Flags: []git.Option{
+ git.Flag{Name: "--batch-check"},
+ },
+ },
+ git.WithStdin(stdinReader),
+ )
if err != nil {
return nil, err
}
diff --git a/internal/git/command.go b/internal/git/command.go
index d4c7e883a..72a7f8516 100644
--- a/internal/git/command.go
+++ b/internal/git/command.go
@@ -35,7 +35,7 @@ func (cf *CommandFactory) gitPath() string {
}
// unsafeCmdWithEnv creates a git.unsafeCmd with the given args, environment, and Repository
-func (cf *CommandFactory) unsafeCmdWithEnv(ctx context.Context, extraEnv []string, stream CmdStream, repo repository.GitRepo, args ...string) (*command.Command, error) {
+func (cf *CommandFactory) unsafeCmdWithEnv(ctx context.Context, extraEnv []string, stream cmdStream, repo repository.GitRepo, args ...string) (*command.Command, error) {
args, env, err := cf.argsAndEnv(repo, args...)
if err != nil {
return nil, err
@@ -56,7 +56,7 @@ func (cf *CommandFactory) unsafeStdinCmd(ctx context.Context, extraEnv []string,
env = append(env, extraEnv...)
- return cf.unsafeBareCmd(ctx, CmdStream{In: command.SetupStdin}, env, args...)
+ return cf.unsafeBareCmd(ctx, cmdStream{In: command.SetupStdin}, env, args...)
}
func (cf *CommandFactory) argsAndEnv(repo repository.GitRepo, args ...string) ([]string, []string, error) {
@@ -72,7 +72,7 @@ func (cf *CommandFactory) argsAndEnv(repo repository.GitRepo, args ...string) ([
}
// unsafeBareCmd creates a git.Command with the given args, stdin/stdout/stderr, and env
-func (cf *CommandFactory) unsafeBareCmd(ctx context.Context, stream CmdStream, env []string, args ...string) (*command.Command, error) {
+func (cf *CommandFactory) unsafeBareCmd(ctx context.Context, stream cmdStream, env []string, args ...string) (*command.Command, error) {
env = append(env, command.GitEnv...)
cmd, err := command.New(ctx, exec.Command(cf.gitPath(), args...), stream.In, stream.Out, stream.Err, env...)
@@ -88,7 +88,7 @@ func (cf *CommandFactory) unsafeBareCmd(ctx context.Context, stream CmdStream, e
}
// unsafeBareCmdInDir calls unsafeBareCmd in dir.
-func (cf *CommandFactory) unsafeBareCmdInDir(ctx context.Context, dir string, stream CmdStream, env []string, args ...string) (*command.Command, error) {
+func (cf *CommandFactory) unsafeBareCmdInDir(ctx context.Context, dir string, stream cmdStream, env []string, args ...string) (*command.Command, error) {
env = append(env, command.GitEnv...)
cmd1 := exec.Command(cf.gitPath(), args...)
diff --git a/internal/git/command_test.go b/internal/git/command_test.go
index 1215fbbb1..55032a8de 100644
--- a/internal/git/command_test.go
+++ b/internal/git/command_test.go
@@ -29,7 +29,7 @@ func TestGitCommandProxy(t *testing.T) {
dir, cleanup := testhelper.TempDir(t)
defer cleanup()
- cmd, err := NewCommandFactory().unsafeBareCmd(ctx, CmdStream{}, nil, "clone", "http://gitlab.com/bogus-repo", dir)
+ cmd, err := NewCommandFactory().unsafeBareCmd(ctx, cmdStream{}, nil, "clone", "http://gitlab.com/bogus-repo", dir)
require.NoError(t, err)
err = cmd.Wait()
diff --git a/internal/git/objectpool/clone.go b/internal/git/objectpool/clone.go
index af9b00688..f9330610d 100644
--- a/internal/git/objectpool/clone.go
+++ b/internal/git/objectpool/clone.go
@@ -17,7 +17,7 @@ func (o *ObjectPool) clone(ctx context.Context, repo *gitalypb.Repository) error
return err
}
- cmd, err := git.SafeCmdWithoutRepo(ctx, git.CmdStream{}, nil,
+ cmd, err := git.SafeCmdWithoutRepo(ctx, nil,
git.SubCmd{
Name: "clone",
Flags: []git.Option{
diff --git a/internal/git/objectpool/fetch.go b/internal/git/objectpool/fetch.go
index ddbfc1e23..995f124a1 100644
--- a/internal/git/objectpool/fetch.go
+++ b/internal/git/objectpool/fetch.go
@@ -186,7 +186,7 @@ func rescueDanglingObjects(ctx context.Context, repo repository.GitRepo) error {
}
func repackPool(ctx context.Context, pool repository.GitRepo) error {
- repackArgs := []git.Option{
+ repackArgs := []git.GlobalOption{
git.ValueFlag{"-c", "pack.island=" + sourceRefNamespace + "/he(a)ds"},
git.ValueFlag{"-c", "pack.island=" + sourceRefNamespace + "/t(a)gs"},
git.ValueFlag{"-c", "pack.islandCore=a"},
diff --git a/internal/git/objectpool/pool.go b/internal/git/objectpool/pool.go
index bf73f2929..df9e7c285 100644
--- a/internal/git/objectpool/pool.go
+++ b/internal/git/objectpool/pool.go
@@ -124,7 +124,7 @@ func (o *ObjectPool) Init(ctx context.Context) (err error) {
return nil
}
- cmd, err := git.SafeCmdWithoutRepo(ctx, git.CmdStream{}, nil,
+ cmd, err := git.SafeCmdWithoutRepo(ctx, nil,
git.SubCmd{
Name: "init",
Flags: []git.Option{
diff --git a/internal/git/proto.go b/internal/git/proto.go
index cb8982712..97dac733b 100644
--- a/internal/git/proto.go
+++ b/internal/git/proto.go
@@ -40,16 +40,3 @@ func ValidateRevisionAllowEmpty(revision []byte) error {
func ValidateRevision(revision []byte) error {
return validateRevision(revision, false)
}
-
-// BuildGitOptions helps to generate options to the git command.
-// If gitOpts is not empty then its values are passed as part of
-// the "-c" option of the git command, the other values are passed along with the subcommand.
-func BuildGitOptions(gitOpts []string, otherOpts ...string) []string {
- args := []string{}
-
- for _, opt := range gitOpts {
- args = append(args, "-c", opt)
- }
-
- return append(args, otherOpts...)
-}
diff --git a/internal/git/receivepack.go b/internal/git/receivepack.go
index a253469a1..97fbc98f4 100644
--- a/internal/git/receivepack.go
+++ b/internal/git/receivepack.go
@@ -2,8 +2,8 @@ package git
// ReceivePackConfig contains config options we want to enforce when
// receiving a push with git-receive-pack.
-func ReceivePackConfig() []Option {
- return []Option{
+func ReceivePackConfig() []GlobalOption {
+ return []GlobalOption{
// In case the repository belongs to an object pool, we want to prevent
// Git from including the pool's refs in the ref advertisement. We do
// this by rigging core.alternateRefsCommand to produce no output.
diff --git a/internal/git/remote.go b/internal/git/remote.go
index b6b29dcfb..4026a3a01 100644
--- a/internal/git/remote.go
+++ b/internal/git/remote.go
@@ -120,10 +120,11 @@ func (repo RepositoryRemote) Add(ctx context.Context, name, url string, opts Rem
stderr := bytes.Buffer{}
cmd, err := SafeCmd(ctx, repo.repo, nil,
- SubCmd{
- Name: "remote",
- Flags: append([]Option{SubSubCmd{Name: "add"}}, opts.buildFlags()...),
- Args: []string{name, url},
+ SubSubCmd{
+ Name: "remote",
+ Action: "add",
+ Flags: opts.buildFlags(),
+ Args: []string{name, url},
},
WithStderr(&stderr),
WithRefTxHook(ctx, helper.ProtoRepoFromRepo(repo.repo), config.Config),
@@ -158,10 +159,10 @@ func (repo RepositoryRemote) Remove(ctx context.Context, name string) error {
var stderr bytes.Buffer
cmd, err := SafeCmd(ctx, repo.repo, nil,
- SubCmd{
- Name: "remote",
- Flags: []Option{SubSubCmd{Name: "remove"}},
- Args: []string{name},
+ SubSubCmd{
+ Name: "remote",
+ Action: "remove",
+ Args: []string{name},
},
WithStderr(&stderr),
WithRefTxHook(ctx, helper.ProtoRepoFromRepo(repo.repo), config.Config),
@@ -214,10 +215,11 @@ func (repo RepositoryRemote) SetURL(ctx context.Context, name, url string, opts
var stderr bytes.Buffer
cmd, err := SafeCmd(ctx, repo.repo, nil,
- SubCmd{
- Name: "remote",
- Flags: append([]Option{SubSubCmd{Name: "set-url"}}, opts.buildFlags()...),
- Args: []string{name, url},
+ SubSubCmd{
+ Name: "remote",
+ Action: "set-url",
+ Flags: opts.buildFlags(),
+ Args: []string{name, url},
},
WithStderr(&stderr),
WithRefTxHook(ctx, helper.ProtoRepoFromRepo(repo.repo), config.Config),
diff --git a/internal/git/remote/remote.go b/internal/git/remote/remote.go
index a21bb5af7..defb0f5e4 100644
--- a/internal/git/remote/remote.go
+++ b/internal/git/remote/remote.go
@@ -12,10 +12,10 @@ import (
//Remove removes the remote from repository
func Remove(ctx context.Context, cfg config.Cfg, repo repository.GitRepo, name string) error {
- cmd, err := git.SafeCmd(ctx, repo, nil, git.SubCmd{
- Name: "remote",
- Flags: []git.Option{git.SubSubCmd{Name: "remove"}},
- Args: []string{name},
+ cmd, err := git.SafeCmd(ctx, repo, nil, git.SubSubCmd{
+ Name: "remote",
+ Action: "remove",
+ Args: []string{name},
}, git.WithRefTxHook(ctx, helper.ProtoRepoFromRepo(repo), cfg))
if err != nil {
return err
diff --git a/internal/git/repository.go b/internal/git/repository.go
index e72f2e69d..5d15c9858 100644
--- a/internal/git/repository.go
+++ b/internal/git/repository.go
@@ -56,7 +56,7 @@ type FetchOpts struct {
// Env is a list of env vars to pass to the cmd.
Env []string
// Global is a list of global flags to use with 'git' command.
- Global []Option
+ Global []GlobalOption
// Prune if set fetch removes any remote-tracking references that no longer exist on the remote.
// https://git-scm.com/docs/git-fetch#Documentation/git-fetch.txt---prune
Prune bool
@@ -117,7 +117,7 @@ func NewRepository(repo repository.GitRepo) *LocalRepository {
// command creates a Git Command with the given args and Repository, executed
// in the Repository. It validates the arguments in the command before
// executing.
-func (repo *LocalRepository) command(ctx context.Context, globals []Option, cmd SubCmd, opts ...CmdOpt) (*command.Command, error) {
+func (repo *LocalRepository) command(ctx context.Context, globals []GlobalOption, cmd SubCmd, opts ...CmdOpt) (*command.Command, error) {
return SafeCmd(ctx, repo.repo, globals, cmd, opts...)
}
diff --git a/internal/git/repository_test.go b/internal/git/repository_test.go
index 0720c0af9..d371f3dfa 100644
--- a/internal/git/repository_test.go
+++ b/internal/git/repository_test.go
@@ -571,7 +571,7 @@ func TestLocalRepository_FetchRemote(t *testing.T) {
ctx,
"source",
FetchOpts{
- Global: []Option{ValueFlag{Name: "-c", Value: "fetch.prune=true"}},
+ Global: []GlobalOption{ValueFlag{Name: "-c", Value: "fetch.prune=true"}},
}),
)
diff --git a/internal/git/safecmd.go b/internal/git/safecmd.go
index 24638d4e7..82a90658e 100644
--- a/internal/git/safecmd.go
+++ b/internal/git/safecmd.go
@@ -37,8 +37,7 @@ func incrInvalidArg(subcmdName string) {
// Cmd is an interface for safe git commands
type Cmd interface {
- ValidateArgs() ([]string, error)
- IsCmd()
+ CommandArgs() ([]string, error)
Subcommand() string
}
@@ -50,22 +49,17 @@ type SubCmd struct {
PostSepArgs []string // post separator (i.e. "--") positional args
}
-// CmdStream represents standard input/output streams for a command
-type CmdStream struct {
+// cmdStream represents standard input/output streams for a command
+type cmdStream struct {
In io.Reader // standard input
Out, Err io.Writer // standard output and error
}
-var subCmdNameRegex = regexp.MustCompile(`^[[:alnum:]]+(-[[:alnum:]]+)*$`)
-
-// IsCmd allows SubCmd to satisfy the Cmd interface
-func (sc SubCmd) IsCmd() {}
-
// Subcommand returns the subcommand name
func (sc SubCmd) Subcommand() string { return sc.Name }
-// ValidateArgs checks all arguments in the sub command and validates them
-func (sc SubCmd) ValidateArgs() ([]string, error) {
+// CommandArgs checks all arguments in the sub command and validates them
+func (sc SubCmd) CommandArgs() ([]string, error) {
var safeArgs []string
if _, ok := subcommands[sc.Name]; !ok {
@@ -73,57 +67,100 @@ func (sc SubCmd) ValidateArgs() ([]string, error) {
}
safeArgs = append(safeArgs, sc.Name)
- for _, o := range sc.Flags {
- args, err := o.ValidateArgs()
+ commandArgs, err := assembleCommandArgs(sc.Name, sc.Flags, sc.Args, sc.PostSepArgs)
+ if err != nil {
+ return nil, err
+ }
+ safeArgs = append(safeArgs, commandArgs...)
+
+ return safeArgs, nil
+}
+
+func assembleCommandArgs(command string, flags []Option, args []string, postSepArgs []string) ([]string, error) {
+ var commandArgs []string
+
+ for _, o := range flags {
+ args, err := o.OptionArgs()
if err != nil {
return nil, err
}
- safeArgs = append(safeArgs, args...)
+ commandArgs = append(commandArgs, args...)
}
- for _, a := range sc.Args {
+ for _, a := range args {
if err := validatePositionalArg(a); err != nil {
return nil, err
}
- safeArgs = append(safeArgs, a)
+ commandArgs = append(commandArgs, a)
}
- if supportsEndOfOptions(sc.Name) {
- safeArgs = append(safeArgs, "--end-of-options")
+ if supportsEndOfOptions(command) {
+ commandArgs = append(commandArgs, "--end-of-options")
}
- if len(sc.PostSepArgs) > 0 {
- safeArgs = append(safeArgs, "--")
+ if len(postSepArgs) > 0 {
+ commandArgs = append(commandArgs, "--")
}
// post separator args do not need any validation
- safeArgs = append(safeArgs, sc.PostSepArgs...)
+ commandArgs = append(commandArgs, postSepArgs...)
- return safeArgs, nil
+ return commandArgs, nil
+}
+
+// GlobalOption is an interface for all options which can be globally applied
+// to git commands. This is the command-inspecific part before the actual
+// command that's being run, e.g. the `-c` part in `git -c foo.bar=value
+// command`.
+type GlobalOption interface {
+ GlobalArgs() ([]string, error)
}
// Option is a git command line flag with validation logic
type Option interface {
- IsOption()
- ValidateArgs() ([]string, error)
+ OptionArgs() ([]string, error)
}
// SubSubCmd is a positional argument that appears in the list of options for
// a subcommand.
type SubSubCmd struct {
+ // Name is the name of the subcommand, e.g. "remote" in `git remote set-url`
Name string
+ // Action is the action of the subcommand, e.g. "set-url" in `git remote set-url`
+ Action string
+
+ // Flags are optional flags before the positional args
+ Flags []Option
+ // Args are positional arguments after all flags
+ Args []string
+ // PostSepArgs are positional args after the "--" separator
+ PostSepArgs []string
}
-// IsOption is a method present on all Flag interface implementations
-func (SubSubCmd) IsOption() {}
+func (sc SubSubCmd) Subcommand() string { return sc.Name }
+
+var actionRegex = regexp.MustCompile(`^[[:alnum:]]+[-[:alnum:]]*$`)
+
+func (sc SubSubCmd) CommandArgs() ([]string, error) {
+ var safeArgs []string
-// ValidateArgs returns an error if the command name or options are not
-// sanitary
-func (sc SubSubCmd) ValidateArgs() ([]string, error) {
- if !subCmdNameRegex.MatchString(sc.Name) {
- return nil, fmt.Errorf("invalid sub-sub command name %q: %w", sc.Name, ErrInvalidArg)
+ if _, ok := subcommands[sc.Name]; !ok {
+ return nil, fmt.Errorf("invalid sub command name %q: %w", sc.Name, ErrInvalidArg)
}
- return []string{sc.Name}, nil
+ safeArgs = append(safeArgs, sc.Name)
+
+ if !actionRegex.MatchString(sc.Action) {
+ return nil, fmt.Errorf("invalid sub command action %q: %w", sc.Action, ErrInvalidArg)
+ }
+ safeArgs = append(safeArgs, sc.Action)
+
+ commandArgs, err := assembleCommandArgs(sc.Name, sc.Flags, sc.Args, sc.PostSepArgs)
+ if err != nil {
+ return nil, err
+ }
+ safeArgs = append(safeArgs, commandArgs...)
+
+ return safeArgs, nil
}
// ConfigPair is a sub-command option for use with commands like "git config"
@@ -138,13 +175,10 @@ type ConfigPair struct {
Scope string
}
-// IsOption is a method present on all Flag interface implementations
-func (ConfigPair) IsOption() {}
-
var configKeyRegex = regexp.MustCompile(`^[[:alnum:]]+[-[:alnum:]]*\.(.+\.)*[[:alnum:]]+[-[:alnum:]]*$`)
-// ValidateArgs validates the config pair args
-func (cp ConfigPair) ValidateArgs() ([]string, error) {
+// OptionArgs validates the config pair args
+func (cp ConfigPair) OptionArgs() ([]string, error) {
if !configKeyRegex.MatchString(cp.Key) {
return nil, fmt.Errorf("config key %q failed regexp validation: %w", cp.Key, ErrInvalidArg)
}
@@ -157,11 +191,12 @@ type Flag struct {
Name string
}
-// IsOption is a method present on all Flag interface implementations
-func (Flag) IsOption() {}
+func (f Flag) GlobalArgs() ([]string, error) {
+ return f.OptionArgs()
+}
-// ValidateArgs returns an error if the flag is not sanitary
-func (f Flag) ValidateArgs() ([]string, error) {
+// OptionArgs returns an error if the flag is not sanitary
+func (f Flag) OptionArgs() ([]string, error) {
if !flagRegex.MatchString(f.Name) {
return nil, fmt.Errorf("flag %q failed regex validation: %w", f.Name, ErrInvalidArg)
}
@@ -175,11 +210,12 @@ type ValueFlag struct {
Value string
}
-// IsOption is a method present on all Flag interface implementations
-func (ValueFlag) IsOption() {}
+func (vf ValueFlag) GlobalArgs() ([]string, error) {
+ return vf.OptionArgs()
+}
-// ValidateArgs returns an error if the flag is not sanitary
-func (vf ValueFlag) ValidateArgs() ([]string, error) {
+// OptionArgs returns an error if the flag is not sanitary
+func (vf ValueFlag) OptionArgs() ([]string, error) {
if !flagRegex.MatchString(vf.Name) {
return nil, fmt.Errorf("value flag %q failed regex validation: %w", vf.Name, ErrInvalidArg)
}
@@ -201,8 +237,8 @@ func validatePositionalArg(arg string) error {
}
// ConvertGlobalOptions converts a protobuf message to command-line flags
-func ConvertGlobalOptions(options *gitalypb.GlobalOptions) []Option {
- var globals []Option
+func ConvertGlobalOptions(options *gitalypb.GlobalOptions) []GlobalOption {
+ var globals []GlobalOption
if options == nil {
return globals
@@ -217,7 +253,7 @@ func ConvertGlobalOptions(options *gitalypb.GlobalOptions) []Option {
type cmdCfg struct {
env []string
- globals []Option
+ globals []GlobalOption
stdin io.Reader
stdout io.Writer
stderr io.Writer
@@ -284,13 +320,13 @@ func handleOpts(ctx context.Context, sc Cmd, cc *cmdCfg, opts []CmdOpt) error {
// SafeCmd creates a command.Command with the given args and Repository. It
// validates the arguments in the command before executing.
-func SafeCmd(ctx context.Context, repo repository.GitRepo, globals []Option, sc Cmd, opts ...CmdOpt) (*command.Command, error) {
+func SafeCmd(ctx context.Context, repo repository.GitRepo, globals []GlobalOption, sc Cmd, opts ...CmdOpt) (*command.Command, error) {
return SafeCmdWithEnv(ctx, nil, repo, globals, sc, opts...)
}
// SafeCmdWithEnv creates a command.Command with the given args, environment, and Repository. It
// validates the arguments in the command before executing.
-func SafeCmdWithEnv(ctx context.Context, env []string, repo repository.GitRepo, globals []Option, sc Cmd, opts ...CmdOpt) (*command.Command, error) {
+func SafeCmdWithEnv(ctx context.Context, env []string, repo repository.GitRepo, globals []GlobalOption, sc Cmd, opts ...CmdOpt) (*command.Command, error) {
cc := &cmdCfg{}
if err := handleOpts(ctx, sc, cc, opts); err != nil {
@@ -302,16 +338,16 @@ func SafeCmdWithEnv(ctx context.Context, env []string, repo repository.GitRepo,
return nil, err
}
- return NewCommandFactory().unsafeCmdWithEnv(ctx, append(env, cc.env...), CmdStream{
+ return NewCommandFactory().unsafeCmdWithEnv(ctx, append(env, cc.env...), cmdStream{
In: cc.stdin,
Out: cc.stdout,
Err: cc.stderr,
}, repo, args...)
}
-// SafeBareCmd creates a git.Command with the given args, stream, and env. It
+// SafeBareCmd creates a git.Command with the given args and env. It
// validates the arguments in the command before executing.
-func SafeBareCmd(ctx context.Context, stream CmdStream, env []string, globals []Option, sc Cmd, opts ...CmdOpt) (*command.Command, error) {
+func SafeBareCmd(ctx context.Context, env []string, globals []GlobalOption, sc Cmd, opts ...CmdOpt) (*command.Command, error) {
cc := &cmdCfg{}
if err := handleOpts(ctx, sc, cc, opts); err != nil {
@@ -323,11 +359,15 @@ func SafeBareCmd(ctx context.Context, stream CmdStream, env []string, globals []
return nil, err
}
- return NewCommandFactory().unsafeBareCmd(ctx, stream, append(env, cc.env...), args...)
+ return NewCommandFactory().unsafeBareCmd(ctx, cmdStream{
+ In: cc.stdin,
+ Out: cc.stdout,
+ Err: cc.stderr,
+ }, append(env, cc.env...), args...)
}
// SafeBareCmdInDir runs SafeBareCmd in the dir.
-func SafeBareCmdInDir(ctx context.Context, dir string, stream CmdStream, env []string, globals []Option, sc Cmd, opts ...CmdOpt) (*command.Command, error) {
+func SafeBareCmdInDir(ctx context.Context, dir string, env []string, globals []GlobalOption, sc Cmd, opts ...CmdOpt) (*command.Command, error) {
if dir == "" {
return nil, errors.New("no 'dir' provided")
}
@@ -343,13 +383,17 @@ func SafeBareCmdInDir(ctx context.Context, dir string, stream CmdStream, env []s
return nil, err
}
- return NewCommandFactory().unsafeBareCmdInDir(ctx, dir, stream, append(env, cc.env...), args...)
+ return NewCommandFactory().unsafeBareCmdInDir(ctx, dir, cmdStream{
+ In: cc.stdin,
+ Out: cc.stdout,
+ Err: cc.stderr,
+ }, append(env, cc.env...), args...)
}
// SafeStdinCmd creates a git.Command with the given args and Repository that is
// suitable for Write()ing to. It validates the arguments in the command before
// executing.
-func SafeStdinCmd(ctx context.Context, repo repository.GitRepo, globals []Option, sc SubCmd, opts ...CmdOpt) (*command.Command, error) {
+func SafeStdinCmd(ctx context.Context, repo repository.GitRepo, globals []GlobalOption, sc Cmd, opts ...CmdOpt) (*command.Command, error) {
cc := &cmdCfg{}
if err := handleOpts(ctx, sc, cc, opts); err != nil {
@@ -366,7 +410,7 @@ func SafeStdinCmd(ctx context.Context, repo repository.GitRepo, globals []Option
// SafeCmdWithoutRepo works like Command but without a git repository. It
// validates the arguments in the command before executing.
-func SafeCmdWithoutRepo(ctx context.Context, stream CmdStream, globals []Option, sc SubCmd, opts ...CmdOpt) (*command.Command, error) {
+func SafeCmdWithoutRepo(ctx context.Context, globals []GlobalOption, sc Cmd, opts ...CmdOpt) (*command.Command, error) {
cc := &cmdCfg{}
if err := handleOpts(ctx, sc, cc, opts); err != nil {
@@ -378,10 +422,14 @@ func SafeCmdWithoutRepo(ctx context.Context, stream CmdStream, globals []Option,
return nil, err
}
- return NewCommandFactory().unsafeBareCmd(ctx, stream, cc.env, args...)
+ return NewCommandFactory().unsafeBareCmd(ctx, cmdStream{
+ In: cc.stdin,
+ Out: cc.stdout,
+ Err: cc.stderr,
+ }, cc.env, args...)
}
-func combineArgs(globals []Option, sc Cmd, cc *cmdCfg) (_ []string, err error) {
+func combineArgs(globals []GlobalOption, sc Cmd, cc *cmdCfg) (_ []string, err error) {
var args []string
defer func() {
@@ -390,15 +438,15 @@ func combineArgs(globals []Option, sc Cmd, cc *cmdCfg) (_ []string, err error) {
}
}()
- for _, g := range append(globals, cc.globals...) {
- gargs, err := g.ValidateArgs()
+ for _, global := range append(globals, cc.globals...) {
+ globalArgs, err := global.GlobalArgs()
if err != nil {
return nil, err
}
- args = append(args, gargs...)
+ args = append(args, globalArgs...)
}
- scArgs, err := sc.ValidateArgs()
+ scArgs, err := sc.CommandArgs()
if err != nil {
return nil, err
}
diff --git a/internal/git/safecmd_test.go b/internal/git/safecmd_test.go
index 578d19585..f66eaed44 100644
--- a/internal/git/safecmd_test.go
+++ b/internal/git/safecmd_test.go
@@ -1,4 +1,4 @@
-package git_test
+package git
import (
"bytes"
@@ -7,7 +7,6 @@ import (
"testing"
"github.com/stretchr/testify/require"
- "gitlab.com/gitlab-org/gitaly/internal/git"
"gitlab.com/gitlab-org/gitaly/internal/git/hooks"
"gitlab.com/gitlab-org/gitaly/internal/gitaly/config"
"gitlab.com/gitlab-org/gitaly/internal/helper/text"
@@ -17,106 +16,100 @@ import (
func TestFlagValidation(t *testing.T) {
for _, tt := range []struct {
- option git.Option
+ option Option
valid bool
}{
// valid Flag inputs
- {option: git.Flag{Name: "-k"}, valid: true},
- {option: git.Flag{Name: "-K"}, valid: true},
- {option: git.Flag{Name: "--asdf"}, valid: true},
- {option: git.Flag{Name: "--asdf-qwer"}, valid: true},
- {option: git.Flag{Name: "--asdf=qwerty"}, valid: true},
- {option: git.Flag{Name: "-D=A"}, valid: true},
- {option: git.Flag{Name: "-D="}, valid: true},
+ {option: Flag{Name: "-k"}, valid: true},
+ {option: Flag{Name: "-K"}, valid: true},
+ {option: Flag{Name: "--asdf"}, valid: true},
+ {option: Flag{Name: "--asdf-qwer"}, valid: true},
+ {option: Flag{Name: "--asdf=qwerty"}, valid: true},
+ {option: Flag{Name: "-D=A"}, valid: true},
+ {option: Flag{Name: "-D="}, valid: true},
// valid ValueFlag inputs
- {option: git.ValueFlag{"-k", "adsf"}, valid: true},
- {option: git.ValueFlag{"-k", "--anything"}, valid: true},
- {option: git.ValueFlag{"-k", ""}, valid: true},
-
- // valid SubSubCmd inputs
- {option: git.SubSubCmd{"meow"}, valid: true},
+ {option: ValueFlag{"-k", "adsf"}, valid: true},
+ {option: ValueFlag{"-k", "--anything"}, valid: true},
+ {option: ValueFlag{"-k", ""}, valid: true},
// valid ConfigPair inputs
- {option: git.ConfigPair{Key: "a.b.c", Value: "d"}, valid: true},
- {option: git.ConfigPair{Key: "core.sound", Value: "meow"}, valid: true},
- {option: git.ConfigPair{Key: "asdf-qwer.1234-5678", Value: ""}, valid: true},
- {option: git.ConfigPair{Key: "http.https://user@example.com/repo.git.user", Value: "kitty"}, valid: true},
+ {option: ConfigPair{Key: "a.b.c", Value: "d"}, valid: true},
+ {option: ConfigPair{Key: "core.sound", Value: "meow"}, valid: true},
+ {option: ConfigPair{Key: "asdf-qwer.1234-5678", Value: ""}, valid: true},
+ {option: ConfigPair{Key: "http.https://user@example.com/repo.git.user", Value: "kitty"}, valid: true},
// invalid Flag inputs
- {option: git.Flag{Name: "-*"}}, // invalid character
- {option: git.Flag{Name: "a"}}, // missing dash
- {option: git.Flag{Name: "[["}}, // suspicious characters
- {option: git.Flag{Name: "||"}}, // suspicious characters
- {option: git.Flag{Name: "asdf=qwerty"}}, // missing dash
+ {option: Flag{Name: "-*"}}, // invalid character
+ {option: Flag{Name: "a"}}, // missing dash
+ {option: Flag{Name: "[["}}, // suspicious characters
+ {option: Flag{Name: "||"}}, // suspicious characters
+ {option: Flag{Name: "asdf=qwerty"}}, // missing dash
// invalid ValueFlag inputs
- {option: git.ValueFlag{"k", "asdf"}}, // missing dash
-
- // invalid SubSubCmd inputs
- {option: git.SubSubCmd{"--meow"}}, // cannot start with dash
+ {option: ValueFlag{"k", "asdf"}}, // missing dash
// invalid ConfigPair inputs
- {option: git.ConfigPair{Key: "", Value: ""}}, // key cannot be empty
- {option: git.ConfigPair{Key: " ", Value: ""}}, // key cannot be whitespace
- {option: git.ConfigPair{Key: "asdf", Value: ""}}, // two components required
- {option: git.ConfigPair{Key: "asdf.", Value: ""}}, // 2nd component must be non-empty
- {option: git.ConfigPair{Key: "--asdf.asdf", Value: ""}}, // key cannot start with dash
- {option: git.ConfigPair{Key: "as[[df.asdf", Value: ""}}, // 1st component cannot contain non-alphanumeric
- {option: git.ConfigPair{Key: "asdf.as]]df", Value: ""}}, // 2nd component cannot contain non-alphanumeric
+ {option: ConfigPair{Key: "", Value: ""}}, // key cannot be empty
+ {option: ConfigPair{Key: " ", Value: ""}}, // key cannot be whitespace
+ {option: ConfigPair{Key: "asdf", Value: ""}}, // two components required
+ {option: ConfigPair{Key: "asdf.", Value: ""}}, // 2nd component must be non-empty
+ {option: ConfigPair{Key: "--asdf.asdf", Value: ""}}, // key cannot start with dash
+ {option: ConfigPair{Key: "as[[df.asdf", Value: ""}}, // 1st component cannot contain non-alphanumeric
+ {option: ConfigPair{Key: "asdf.as]]df", Value: ""}}, // 2nd component cannot contain non-alphanumeric
} {
- args, err := tt.option.ValidateArgs()
+ args, err := tt.option.OptionArgs()
if tt.valid {
require.NoError(t, err)
} else {
require.Error(t, err,
"expected error, but args %v passed validation", args)
- require.True(t, git.IsInvalidArgErr(err))
+ require.True(t, IsInvalidArgErr(err))
}
}
}
func TestSafeCmdInvalidArg(t *testing.T) {
for _, tt := range []struct {
- globals []git.Option
- subCmd git.SubCmd
+ globals []GlobalOption
+ subCmd Cmd
errMsg string
}{
{
- subCmd: git.SubCmd{Name: "--meow"},
+ subCmd: SubCmd{Name: "--meow"},
errMsg: `invalid sub command name "--meow": invalid argument`,
},
{
- subCmd: git.SubCmd{
+ subCmd: SubCmd{
Name: "update-ref",
- Flags: []git.Option{git.Flag{Name: "woof"}},
+ Flags: []Option{Flag{Name: "woof"}},
},
errMsg: `flag "woof" failed regex validation: invalid argument`,
},
{
- subCmd: git.SubCmd{
+ subCmd: SubCmd{
Name: "update-ref",
Args: []string{"--tweet"},
},
errMsg: `positional arg "--tweet" cannot start with dash '-': invalid argument`,
},
{
- subCmd: git.SubCmd{
- Name: "update-ref",
- Flags: []git.Option{git.SubSubCmd{"-invalid"}},
+ subCmd: SubSubCmd{
+ Name: "update-ref",
+ Action: "-invalid",
},
- errMsg: `invalid sub-sub command name "-invalid": invalid argument`,
+ errMsg: `invalid sub command action "-invalid": invalid argument`,
},
} {
- _, err := git.SafeCmd(
+ _, err := SafeCmd(
context.Background(),
&gitalypb.Repository{},
tt.globals,
tt.subCmd,
- git.WithRefTxHook(context.Background(), &gitalypb.Repository{}, config.Config),
+ WithRefTxHook(context.Background(), &gitalypb.Repository{}, config.Config),
)
require.EqualError(t, err, tt.errMsg)
- require.True(t, git.IsInvalidArgErr(err))
+ require.True(t, IsInvalidArgErr(err))
}
}
@@ -136,26 +129,26 @@ func TestSafeCmdValid(t *testing.T) {
for _, tt := range []struct {
desc string
- globals []git.Option
- subCmd git.SubCmd
+ globals []GlobalOption
+ subCmd Cmd
expectArgs []string
}{
{
desc: "no args",
- subCmd: git.SubCmd{Name: "update-ref"},
+ subCmd: SubCmd{Name: "update-ref"},
expectArgs: []string{"-c", hooksPath, "update-ref", endOfOptions},
},
{
desc: "single option",
- globals: []git.Option{
- git.Flag{Name: "--aaaa-bbbb"},
+ globals: []GlobalOption{
+ Flag{Name: "--aaaa-bbbb"},
},
- subCmd: git.SubCmd{Name: "update-ref"},
+ subCmd: SubCmd{Name: "update-ref"},
expectArgs: []string{"--aaaa-bbbb", "-c", hooksPath, "update-ref", endOfOptions},
},
{
desc: "empty arg and postsep args",
- subCmd: git.SubCmd{
+ subCmd: SubCmd{
Name: "update-ref",
Args: []string{""},
PostSepArgs: []string{"-woof", ""},
@@ -164,16 +157,16 @@ func TestSafeCmdValid(t *testing.T) {
},
{
desc: "full blown",
- globals: []git.Option{
- git.Flag{Name: "-a"},
- git.ValueFlag{"-b", "c"},
+ globals: []GlobalOption{
+ Flag{Name: "-a"},
+ ValueFlag{"-b", "c"},
},
- subCmd: git.SubCmd{
+ subCmd: SubCmd{
Name: "update-ref",
- Flags: []git.Option{
- git.Flag{Name: "-e"},
- git.ValueFlag{"-f", "g"},
- git.Flag{Name: "-h=i"},
+ Flags: []Option{
+ Flag{Name: "-e"},
+ ValueFlag{"-f", "g"},
+ Flag{Name: "-h=i"},
},
Args: []string{"1", "2"},
PostSepArgs: []string{"3", "4", "5"},
@@ -182,60 +175,60 @@ func TestSafeCmdValid(t *testing.T) {
},
{
desc: "output to stdout",
- subCmd: git.SubCmd{
- Name: "update-ref",
- Flags: []git.Option{
- git.SubSubCmd{"verb"},
- git.OutputToStdout,
- git.Flag{Name: "--adjective"},
+ subCmd: SubSubCmd{
+ Name: "update-ref",
+ Action: "verb",
+ Flags: []Option{
+ OutputToStdout,
+ Flag{Name: "--adjective"},
},
},
expectArgs: []string{"-c", hooksPath, "update-ref", "verb", "-", "--adjective", endOfOptions},
},
{
desc: "multiple value flags",
- globals: []git.Option{
- git.Flag{Name: "--contributing"},
- git.ValueFlag{"--author", "a-gopher"},
+ globals: []GlobalOption{
+ Flag{Name: "--contributing"},
+ ValueFlag{"--author", "a-gopher"},
},
- subCmd: git.SubCmd{
+ subCmd: SubCmd{
Name: "update-ref",
Args: []string{"mr"},
- Flags: []git.Option{
- git.Flag{Name: "--is-important"},
- git.ValueFlag{"--why", "looking-for-first-contribution"},
+ Flags: []Option{
+ Flag{Name: "--is-important"},
+ ValueFlag{"--why", "looking-for-first-contribution"},
},
},
expectArgs: []string{"--contributing", "--author", "a-gopher", "-c", hooksPath, "update-ref", "--is-important", "--why", "looking-for-first-contribution", "mr", endOfOptions},
},
} {
t.Run(tt.desc, func(t *testing.T) {
- opts := []git.CmdOpt{git.WithRefTxHook(ctx, &gitalypb.Repository{}, config.Config)}
+ opts := []CmdOpt{WithRefTxHook(ctx, &gitalypb.Repository{}, config.Config)}
- cmd, err := git.SafeCmd(ctx, testRepo, tt.globals, tt.subCmd, opts...)
+ cmd, err := SafeCmd(ctx, testRepo, tt.globals, tt.subCmd, opts...)
require.NoError(t, err)
// ignore first 3 indeterministic args (executable path and repo args)
require.Equal(t, tt.expectArgs, cmd.Args()[3:])
- cmd, err = git.SafeCmdWithEnv(ctx, nil, testRepo, tt.globals, tt.subCmd, opts...)
+ cmd, err = SafeCmdWithEnv(ctx, nil, testRepo, tt.globals, tt.subCmd, opts...)
require.NoError(t, err)
// ignore first 3 indeterministic args (executable path and repo args)
require.Equal(t, tt.expectArgs, cmd.Args()[3:])
- cmd, err = git.SafeStdinCmd(ctx, testRepo, tt.globals, tt.subCmd, opts...)
+ cmd, err = SafeStdinCmd(ctx, testRepo, tt.globals, tt.subCmd, opts...)
require.NoError(t, err)
require.Equal(t, tt.expectArgs, cmd.Args()[3:])
- cmd, err = git.SafeBareCmd(ctx, git.CmdStream{}, nil, tt.globals, tt.subCmd, opts...)
+ cmd, err = SafeBareCmd(ctx, nil, tt.globals, tt.subCmd, opts...)
require.NoError(t, err)
// ignore first indeterministic arg (executable path)
require.Equal(t, tt.expectArgs, cmd.Args()[1:])
- cmd, err = git.SafeCmdWithoutRepo(ctx, git.CmdStream{}, tt.globals, tt.subCmd, opts...)
+ cmd, err = SafeCmdWithoutRepo(ctx, tt.globals, tt.subCmd, opts...)
require.NoError(t, err)
require.Equal(t, tt.expectArgs, cmd.Args()[1:])
- cmd, err = git.SafeBareCmdInDir(ctx, testRepoPath, git.CmdStream{}, nil, tt.globals, tt.subCmd, opts...)
+ cmd, err = SafeBareCmdInDir(ctx, testRepoPath, nil, tt.globals, tt.subCmd, opts...)
require.NoError(t, err)
require.Equal(t, tt.expectArgs, cmd.Args()[1:])
})
@@ -252,18 +245,18 @@ func TestSafeCmdWithEnv(t *testing.T) {
reenableGitCmd := disableGitCmd()
defer reenableGitCmd()
- globals := []git.Option{
- git.Flag{Name: "--aaaa-bbbb"},
+ globals := []GlobalOption{
+ Flag{Name: "--aaaa-bbbb"},
}
- subCmd := git.SubCmd{Name: "update-ref"}
+ subCmd := SubCmd{Name: "update-ref"}
endOfOptions := "--end-of-options"
expectArgs := []string{"--aaaa-bbbb", "-c", "core.hooksPath=" + hooks.Path(config.Config), "update-ref", endOfOptions}
env := []string{"TEST_VAR1=1", "TEST_VAR2=2"}
- opts := []git.CmdOpt{git.WithRefTxHook(ctx, &gitalypb.Repository{}, config.Config)}
- cmd, err := git.SafeCmdWithEnv(ctx, env, testRepo, globals, subCmd, opts...)
+ opts := []CmdOpt{WithRefTxHook(ctx, &gitalypb.Repository{}, config.Config)}
+ cmd, err := SafeCmdWithEnv(ctx, env, testRepo, globals, subCmd, opts...)
require.NoError(t, err)
// ignore first 3 indeterministic args (executable path and repo args)
require.Equal(t, expectArgs, cmd.Args()[3:])
@@ -281,7 +274,7 @@ func TestSafeBareCmdInDir(t *testing.T) {
ctx, cancel := testhelper.Context()
defer cancel()
- _, err := git.SafeBareCmdInDir(ctx, "", git.CmdStream{}, nil, nil, nil)
+ _, err := SafeBareCmdInDir(ctx, "", nil, nil, nil)
require.Error(t, err)
require.Contains(t, err.Error(), "no 'dir' provided")
})
@@ -294,10 +287,10 @@ func TestSafeBareCmdInDir(t *testing.T) {
defer cancel()
var stderr bytes.Buffer
- cmd, err := git.SafeBareCmdInDir(ctx, repoPath, git.CmdStream{Err: &stderr}, nil, nil, git.SubCmd{
+ cmd, err := SafeBareCmdInDir(ctx, repoPath, nil, nil, SubCmd{
Name: "rev-parse",
Args: []string{"master"},
- })
+ }, WithStderr(&stderr))
require.NoError(t, err)
revData, err := ioutil.ReadAll(cmd)
@@ -313,10 +306,10 @@ func TestSafeBareCmdInDir(t *testing.T) {
defer cancel()
var stderr bytes.Buffer
- _, err := git.SafeBareCmdInDir(ctx, "non-existing-dir", git.CmdStream{Err: &stderr}, nil, nil, git.SubCmd{
+ _, err := SafeBareCmdInDir(ctx, "non-existing-dir", nil, nil, SubCmd{
Name: "rev-parse",
Args: []string{"master"},
- })
+ }, WithStderr(&stderr))
require.Error(t, err)
require.Contains(t, err.Error(), "no such file or directory")
})
@@ -331,12 +324,12 @@ func TestSafeCmd(t *testing.T) {
defer cancel()
var stderr bytes.Buffer
- cmd, err := git.SafeCmd(ctx, repo, nil,
- git.SubCmd{
+ cmd, err := SafeCmd(ctx, repo, nil,
+ SubCmd{
Name: "rev-parse",
Args: []string{"master"},
},
- git.WithStderr(&stderr),
+ WithStderr(&stderr),
)
require.NoError(t, err)
@@ -357,11 +350,11 @@ func TestSafeCmd(t *testing.T) {
defer cancel()
var stderr bytes.Buffer
- cmd, err := git.SafeCmd(ctx, repo, nil, git.SubCmd{
+ cmd, err := SafeCmd(ctx, repo, nil, SubCmd{
Name: "rev-parse",
Args: []string{"invalid-ref"},
},
- git.WithStderr(&stderr),
+ WithStderr(&stderr),
)
require.NoError(t, err)
diff --git a/internal/git/staticargs.go b/internal/git/staticargs.go
index 7caaf9475..c56083805 100644
--- a/internal/git/staticargs.go
+++ b/internal/git/staticargs.go
@@ -5,12 +5,9 @@ type StaticOption struct {
value string
}
-// IsOption is a method present on all Flag interface implementations
-func (sa StaticOption) IsOption() {}
-
// ValidateArgs just passes through the already trusted value. This never
// returns an error.
-func (sa StaticOption) ValidateArgs() ([]string, error) { return []string{sa.value}, nil }
+func (sa StaticOption) OptionArgs() ([]string, error) { return []string{sa.value}, nil }
var (
// OutputToStdout is used indicate the output should be sent to STDOUT
diff --git a/internal/git/uploadpack.go b/internal/git/uploadpack.go
index 7eab25d3f..c5c7d64c2 100644
--- a/internal/git/uploadpack.go
+++ b/internal/git/uploadpack.go
@@ -1,12 +1,12 @@
package git
-import "gitlab.com/gitlab-org/gitaly/internal/gitalyssh"
-
// UploadPackFilterConfig confines config options that are required to allow
// partial-clone filters.
-func UploadPackFilterConfig() []Option {
- return []Option{
+func UploadPackFilterConfig() []GlobalOption {
+ return []GlobalOption{
ValueFlag{"-c", "uploadpack.allowFilter=true"},
- ValueFlag{"-c", gitalyssh.EnvVarUploadPackAllowAnySHA1InWant},
+ // Enables the capability to request individual SHA1's from the
+ // remote repo.
+ ValueFlag{"-c", "uploadpack.allowAnySHA1InWant=true"},
}
}
diff --git a/internal/git/version.go b/internal/git/version.go
index 87cf01126..6f6f4e4f3 100644
--- a/internal/git/version.go
+++ b/internal/git/version.go
@@ -21,7 +21,7 @@ type version struct {
// Version returns the used git version.
func Version(ctx context.Context) (string, error) {
var buf bytes.Buffer
- cmd, err := NewCommandFactory().unsafeBareCmd(ctx, CmdStream{Out: &buf}, nil, "version")
+ cmd, err := NewCommandFactory().unsafeBareCmd(ctx, cmdStream{Out: &buf}, nil, "version")
if err != nil {
return "", err
}
diff --git a/internal/gitaly/service/commit/languages.go b/internal/gitaly/service/commit/languages.go
index 9344f0739..ebb94de23 100644
--- a/internal/gitaly/service/commit/languages.go
+++ b/internal/gitaly/service/commit/languages.go
@@ -117,11 +117,13 @@ func (s *server) lookupRevision(ctx context.Context, repo *gitalypb.Repository,
}
func checkRevision(ctx context.Context, repoPath string, env []string, revision string) (string, error) {
- opts := []git.Option{git.ValueFlag{"-C", repoPath}}
+ opts := []git.GlobalOption{git.ValueFlag{"-C", repoPath}}
var stdout, stderr bytes.Buffer
- revParse, err := git.SafeBareCmd(ctx, git.CmdStream{Out: &stdout, Err: &stderr}, env, opts,
+ revParse, err := git.SafeBareCmd(ctx, env, opts,
git.SubCmd{Name: "rev-parse", Args: []string{revision}},
+ git.WithStdout(&stdout),
+ git.WithStderr(&stderr),
)
if err != nil {
diff --git a/internal/gitaly/service/conflicts/resolve_conflicts.go b/internal/gitaly/service/conflicts/resolve_conflicts.go
index 390ae99db..2bf6b9f23 100644
--- a/internal/gitaly/service/conflicts/resolve_conflicts.go
+++ b/internal/gitaly/service/conflicts/resolve_conflicts.go
@@ -283,16 +283,14 @@ func (s *server) repoWithBranchCommit(ctx context.Context, srcRepo, targetRepo *
if err != nil {
return err
}
- // to enable fetching a specific SHA:
- env = append(env, gitalyssh.EnvVarUploadPackAllowAnySHA1InWant)
srcRepoPath, err := s.locator.GetRepoPath(srcRepo)
if err != nil {
return err
}
- cmd, err := git.SafeBareCmd(ctx, git.CmdStream{}, env,
- []git.Option{git.ValueFlag{"--git-dir", srcRepoPath}},
+ cmd, err := git.SafeBareCmd(ctx, env,
+ []git.GlobalOption{git.ValueFlag{"--git-dir", srcRepoPath}},
git.SubCmd{
Name: "fetch",
Flags: []git.Option{git.Flag{Name: "--no-tags"}},
diff --git a/internal/gitaly/service/diff/commit.go b/internal/gitaly/service/diff/commit.go
index 590177d89..ac137617d 100644
--- a/internal/gitaly/service/diff/commit.go
+++ b/internal/gitaly/service/diff/commit.go
@@ -206,7 +206,7 @@ func validateRequest(in requestWithLeftRightCommitIds) error {
}
func eachDiff(ctx context.Context, rpc string, repo *gitalypb.Repository, subCmd git.Cmd, limits diff.Limits, callback func(*diff.Diff) error) error {
- diffArgs := []git.Option{
+ diffArgs := []git.GlobalOption{
git.ValueFlag{"-c", "diff.noprefix=false"},
}
diff --git a/internal/gitaly/service/operations/squash.go b/internal/gitaly/service/operations/squash.go
index f621165d7..1ab1c68dc 100644
--- a/internal/gitaly/service/operations/squash.go
+++ b/internal/gitaly/service/operations/squash.go
@@ -167,13 +167,15 @@ func (s *Server) runUserSquashGo(ctx context.Context, req *gitalypb.UserSquashRe
func (s *Server) diffFiles(ctx context.Context, env []string, repoPath string, req *gitalypb.UserSquashRequest) ([]byte, error) {
var stdout, stderr bytes.Buffer
- cmd, err := git.SafeBareCmd(ctx, git.CmdStream{Out: &stdout, Err: &stderr}, env,
- []git.Option{git.ValueFlag{Name: "--git-dir", Value: repoPath}},
+ cmd, err := git.SafeBareCmd(ctx, env,
+ []git.GlobalOption{git.ValueFlag{Name: "--git-dir", Value: repoPath}},
git.SubCmd{
Name: "diff",
Flags: []git.Option{git.Flag{Name: "--name-only"}, git.Flag{Name: "--diff-filter=ar"}, git.Flag{Name: "--binary"}},
Args: []string{diffRange(req)},
},
+ git.WithStdout(&stdout),
+ git.WithStderr(&stderr),
)
if err != nil {
return nil, fmt.Errorf("create 'git diff': %w", gitError{ErrMsg: stderr.String(), Err: err})
@@ -243,12 +245,13 @@ func (s *Server) userSquashWithDiffInFiles(ctx context.Context, req *gitalypb.Us
func (s *Server) checkout(ctx context.Context, repo *gitalypb.Repository, worktreePath string, req *gitalypb.UserSquashRequest) error {
var stderr bytes.Buffer
- checkoutCmd, err := git.SafeBareCmdInDir(ctx, worktreePath, git.CmdStream{Err: &stderr}, nil, nil,
+ checkoutCmd, err := git.SafeBareCmdInDir(ctx, worktreePath, nil, nil,
git.SubCmd{
Name: "checkout",
Flags: []git.Option{git.Flag{Name: "--detach"}},
Args: []string{req.GetStartSha()},
},
+ git.WithStderr(&stderr),
git.WithRefTxHook(ctx, repo, s.cfg),
)
if err != nil {
@@ -268,10 +271,14 @@ func (s *Server) checkout(ctx context.Context, repo *gitalypb.Repository, worktr
func (s *Server) revParseGitDir(ctx context.Context, worktreePath string) (string, error) {
var stdout, stderr bytes.Buffer
- cmd, err := git.SafeBareCmdInDir(ctx, worktreePath, git.CmdStream{Out: &stdout, Err: &stderr}, nil, nil, git.SubCmd{
- Name: "rev-parse",
- Flags: []git.Option{git.Flag{Name: "--git-dir"}},
- })
+ cmd, err := git.SafeBareCmdInDir(ctx, worktreePath, nil, nil,
+ git.SubCmd{
+ Name: "rev-parse",
+ Flags: []git.Option{git.Flag{Name: "--git-dir"}},
+ },
+ git.WithStdout(&stdout),
+ git.WithStderr(&stderr),
+ )
if err != nil {
return "", fmt.Errorf("creation of 'git rev-parse --git-dir': %w", gitError{ErrMsg: stderr.String(), Err: err})
}
@@ -314,7 +321,7 @@ func (s *Server) addWorktree(ctx context.Context, repo *gitalypb.Repository, wor
}
args := []string{worktreePath}
- flags := []git.Option{git.SubSubCmd{Name: "add"}, git.Flag{Name: "--detach"}}
+ flags := []git.Option{git.Flag{Name: "--detach"}}
if committish != "" {
args = append(args, committish)
} else {
@@ -323,7 +330,12 @@ func (s *Server) addWorktree(ctx context.Context, repo *gitalypb.Repository, wor
var stderr bytes.Buffer
cmd, err := git.SafeCmd(ctx, repo, nil,
- git.SubCmd{Name: "worktree", Flags: flags, Args: args},
+ git.SubSubCmd{
+ Name: "worktree",
+ Action: "add",
+ Flags: flags,
+ Args: args,
+ },
git.WithStderr(&stderr),
git.WithRefTxHook(ctx, repo, s.cfg),
)
@@ -340,10 +352,11 @@ func (s *Server) addWorktree(ctx context.Context, repo *gitalypb.Repository, wor
func (s *Server) removeWorktree(ctx context.Context, repo *gitalypb.Repository, worktreeName string) error {
cmd, err := git.SafeCmd(ctx, repo, nil,
- git.SubCmd{
- Name: "worktree",
- Flags: []git.Option{git.SubSubCmd{Name: "remove"}, git.Flag{Name: "--force"}},
- Args: []string{worktreeName},
+ git.SubSubCmd{
+ Name: "worktree",
+ Action: "remove",
+ Flags: []git.Option{git.Flag{Name: "--force"}},
+ Args: []string{worktreeName},
},
git.WithRefTxHook(ctx, repo, s.cfg),
)
@@ -377,14 +390,18 @@ func (s *Server) applyDiff(ctx context.Context, repo *gitalypb.Repository, req *
}
var applyStderr bytes.Buffer
- cmdApply, err := git.SafeBareCmdInDir(ctx, worktreePath, git.CmdStream{In: command.SetupStdin, Err: &applyStderr}, env, nil, git.SubCmd{
- Name: "apply",
- Flags: []git.Option{
- git.Flag{Name: "--index"},
- git.Flag{Name: "--3way"},
- git.Flag{Name: "--whitespace=nowarn"},
+ cmdApply, err := git.SafeBareCmdInDir(ctx, worktreePath, env, nil,
+ git.SubCmd{
+ Name: "apply",
+ Flags: []git.Option{
+ git.Flag{Name: "--index"},
+ git.Flag{Name: "--3way"},
+ git.Flag{Name: "--whitespace=nowarn"},
+ },
},
- })
+ git.WithStdin(command.SetupStdin),
+ git.WithStderr(&applyStderr),
+ )
if err != nil {
return "", fmt.Errorf("creation of 'git apply' for range %q: %w", diffRange, gitError{ErrMsg: applyStderr.String(), Err: err})
}
@@ -409,14 +426,14 @@ func (s *Server) applyDiff(ctx context.Context, repo *gitalypb.Repository, req *
)
var commitStderr bytes.Buffer
- cmdCommit, err := git.SafeBareCmdInDir(ctx, worktreePath, git.CmdStream{Err: &commitStderr}, commitEnv, nil, git.SubCmd{
+ cmdCommit, err := git.SafeBareCmdInDir(ctx, worktreePath, commitEnv, nil, git.SubCmd{
Name: "commit",
Flags: []git.Option{
git.Flag{Name: "--no-verify"},
git.Flag{Name: "--quiet"},
git.ValueFlag{Name: "--message", Value: string(req.GetCommitMessage())},
},
- }, git.WithRefTxHook(ctx, repo, s.cfg))
+ }, git.WithStderr(&commitStderr), git.WithRefTxHook(ctx, repo, s.cfg))
if err != nil {
return "", fmt.Errorf("creation of 'git commit': %w", gitError{ErrMsg: commitStderr.String(), Err: err})
}
@@ -426,14 +443,14 @@ func (s *Server) applyDiff(ctx context.Context, repo *gitalypb.Repository, req *
}
var revParseStdout, revParseStderr bytes.Buffer
- revParseCmd, err := git.SafeBareCmdInDir(ctx, worktreePath, git.CmdStream{Out: &revParseStdout, Err: &revParseStderr}, env, nil, git.SubCmd{
+ revParseCmd, err := git.SafeBareCmdInDir(ctx, worktreePath, env, nil, git.SubCmd{
Name: "rev-parse",
Flags: []git.Option{
git.Flag{Name: "--quiet"},
git.Flag{Name: "--verify"},
},
Args: []string{"HEAD^{commit}"},
- })
+ }, git.WithStdout(&revParseStdout), git.WithStderr(&revParseStderr))
if err != nil {
return "", fmt.Errorf("creation of 'git rev-parse': %w", gitError{ErrMsg: revParseStderr.String(), Err: err})
}
diff --git a/internal/gitaly/service/ref/refname.go b/internal/gitaly/service/ref/refname.go
index fdd8a4c2c..0e8928bc2 100644
--- a/internal/gitaly/service/ref/refname.go
+++ b/internal/gitaly/service/ref/refname.go
@@ -72,9 +72,6 @@ type ForEachRefCmd struct {
PostArgFlags []git.Option
}
-// IsCmd is to satisfy the git.Cmd interface
-func (f ForEachRefCmd) IsCmd() {}
-
var (
// ErrOnlyForEachRefAllowed indicates a command other than for-each-ref is being used with ForEachRefCmd
ErrOnlyForEachRefAllowed = errors.New("only for-each-ref allowed")
@@ -83,13 +80,13 @@ var (
ErrNoPostSeparatorArgsAllowed = errors.New("post separator args not allowed")
)
-// ValidateArgs validates and returns the flags and arguments for the for-each-ref command
-func (f ForEachRefCmd) ValidateArgs() ([]string, error) {
+// CommandArgs validates and returns the flags and arguments for the for-each-ref command
+func (f ForEachRefCmd) CommandArgs() ([]string, error) {
if f.Name != "for-each-ref" {
return nil, ErrOnlyForEachRefAllowed
}
- args, err := f.SubCmd.ValidateArgs()
+ args, err := f.SubCmd.CommandArgs()
if err != nil {
return nil, err
}
@@ -97,7 +94,7 @@ func (f ForEachRefCmd) ValidateArgs() ([]string, error) {
var postArgFlags []string
for _, o := range f.PostArgFlags {
- args, err := o.ValidateArgs()
+ args, err := o.OptionArgs()
if err != nil {
return nil, err
}
diff --git a/internal/gitaly/service/ref/refname_test.go b/internal/gitaly/service/ref/refname_test.go
index 0b67a0b9b..c078e92a7 100644
--- a/internal/gitaly/service/ref/refname_test.go
+++ b/internal/gitaly/service/ref/refname_test.go
@@ -210,7 +210,7 @@ func TestFindRefCmd(t *testing.T) {
}
for _, tc := range testCases {
- args, err := tc.cmd.ValidateArgs()
+ args, err := tc.cmd.CommandArgs()
require.Equal(t, tc.expectedErr, err)
require.Equal(t, tc.expectedArgs, args)
}
diff --git a/internal/gitaly/service/remote/find_remote_root_ref.go b/internal/gitaly/service/remote/find_remote_root_ref.go
index c3ef6a978..0eaea33e3 100644
--- a/internal/gitaly/service/remote/find_remote_root_ref.go
+++ b/internal/gitaly/service/remote/find_remote_root_ref.go
@@ -16,7 +16,11 @@ const headPrefix = "HEAD branch: "
func findRemoteRootRef(ctx context.Context, repo *gitalypb.Repository, remote string) (string, error) {
cmd, err := git.SafeCmd(ctx, repo, nil,
- git.SubCmd{Name: "remote", Flags: []git.Option{git.SubSubCmd{Name: "show"}}, Args: []string{remote}},
+ git.SubSubCmd{
+ Name: "remote",
+ Action: "show",
+ Args: []string{remote},
+ },
git.WithRefTxHook(ctx, repo, config.Config),
)
if err != nil {
diff --git a/internal/gitaly/service/remote/remotes.go b/internal/gitaly/service/remote/remotes.go
index 074969d08..7afdec9d2 100644
--- a/internal/gitaly/service/remote/remotes.go
+++ b/internal/gitaly/service/remote/remotes.go
@@ -75,7 +75,7 @@ func (s *server) FindRemoteRepository(ctx context.Context, req *gitalypb.FindRem
return nil, status.Error(codes.InvalidArgument, "FindRemoteRepository: empty remote can't be checked.")
}
- cmd, err := git.SafeCmdWithoutRepo(ctx, git.CmdStream{}, nil,
+ cmd, err := git.SafeCmdWithoutRepo(ctx, nil,
git.SubCmd{
Name: "ls-remote",
Args: []string{
diff --git a/internal/gitaly/service/repository/archive.go b/internal/gitaly/service/repository/archive.go
index f046d7752..4cbc96273 100644
--- a/internal/gitaly/service/repository/archive.go
+++ b/internal/gitaly/service/repository/archive.go
@@ -200,7 +200,7 @@ func handleArchive(p archiveParams) error {
fmt.Sprintf("%s=%s", log.GitalyLogDirEnvKey, p.loggingDir),
}
- var globals []git.Option
+ var globals []git.GlobalOption
if p.in.GetIncludeLfsBlobs() {
binary := filepath.Join(p.binDir, "gitaly-lfs-smudge")
diff --git a/internal/gitaly/service/repository/calculate_checksum.go b/internal/gitaly/service/repository/calculate_checksum.go
index 1bdf570a6..2b7a26f12 100644
--- a/internal/gitaly/service/repository/calculate_checksum.go
+++ b/internal/gitaly/service/repository/calculate_checksum.go
@@ -85,9 +85,16 @@ func (s *server) isValidRepo(ctx context.Context, repo *gitalypb.Repository) boo
env := alternates.Env(repoPath, repo.GetGitObjectDirectory(), repo.GetGitAlternateObjectDirectories())
stdout := &bytes.Buffer{}
- opts := []git.Option{git.ValueFlag{"-C", repoPath}}
- cmd, err := git.SafeBareCmd(ctx, git.CmdStream{Out: stdout}, env, opts,
- git.SubCmd{Name: "rev-parse", Flags: []git.Option{git.Flag{Name: "--is-inside-git-dir"}}})
+ globals := []git.GlobalOption{git.ValueFlag{"-C", repoPath}}
+ cmd, err := git.SafeBareCmd(ctx, env, globals,
+ git.SubCmd{
+ Name: "rev-parse",
+ Flags: []git.Option{
+ git.Flag{Name: "--is-inside-git-dir"},
+ },
+ },
+ git.WithStdout(stdout),
+ )
if err != nil {
return false
}
diff --git a/internal/gitaly/service/repository/cleanup.go b/internal/gitaly/service/repository/cleanup.go
index 752b89c8e..48c774563 100644
--- a/internal/gitaly/service/repository/cleanup.go
+++ b/internal/gitaly/service/repository/cleanup.go
@@ -127,9 +127,11 @@ func (s *server) cleanStaleWorktrees(ctx context.Context, repo *gitalypb.Reposit
if info.ModTime().Before(threshold) {
cmd, err := git.SafeCmd(ctx, repo, nil,
- git.SubCmd{
- Name: "worktree",
- Flags: []git.Option{git.SubSubCmd{"remove"}, git.Flag{Name: "--force"}, git.SubSubCmd{info.Name()}},
+ git.SubSubCmd{
+ Name: "worktree",
+ Action: "remove",
+ Flags: []git.Option{git.Flag{Name: "--force"}},
+ Args: []string{info.Name()},
},
git.WithRefTxHook(ctx, repo, s.cfg),
)
@@ -148,9 +150,9 @@ func (s *server) cleanStaleWorktrees(ctx context.Context, repo *gitalypb.Reposit
func (s *server) cleanDisconnectedWorktrees(ctx context.Context, repo *gitalypb.Repository) error {
cmd, err := git.SafeCmd(ctx, repo, nil,
- git.SubCmd{
- Name: "worktree",
- Flags: []git.Option{git.SubSubCmd{"prune"}},
+ git.SubSubCmd{
+ Name: "worktree",
+ Action: "prune",
},
git.WithRefTxHook(ctx, repo, s.cfg),
)
diff --git a/internal/gitaly/service/repository/clone_from_pool_internal.go b/internal/gitaly/service/repository/clone_from_pool_internal.go
index 7034568b3..47d94be4f 100644
--- a/internal/gitaly/service/repository/clone_from_pool_internal.go
+++ b/internal/gitaly/service/repository/clone_from_pool_internal.go
@@ -125,7 +125,7 @@ func (s *server) cloneFromPool(ctx context.Context, objectPoolRepo *gitalypb.Obj
return fmt.Errorf("expected *gitlaypb.Repository but got %T", repo)
}
- cmd, err := git.SafeBareCmd(ctx, git.CmdStream{}, nil, nil,
+ cmd, err := git.SafeBareCmd(ctx, nil, nil,
git.SubCmd{
Name: "clone",
Flags: []git.Option{git.Flag{Name: "--bare"}, git.Flag{Name: "--shared"}},
diff --git a/internal/gitaly/service/repository/commit_graph.go b/internal/gitaly/service/repository/commit_graph.go
index 20459e498..21d6e326e 100644
--- a/internal/gitaly/service/repository/commit_graph.go
+++ b/internal/gitaly/service/repository/commit_graph.go
@@ -24,10 +24,10 @@ func (s *server) WriteCommitGraph(ctx context.Context, in *gitalypb.WriteCommitG
func (s *server) writeCommitGraph(ctx context.Context, in *gitalypb.WriteCommitGraphRequest) error {
cmd, err := git.SafeCmd(ctx, in.GetRepository(), nil,
- git.SubCmd{
- Name: "commit-graph",
+ git.SubSubCmd{
+ Name: "commit-graph",
+ Action: "write",
Flags: []git.Option{
- git.SubSubCmd{Name: "write"},
git.Flag{Name: "--reachable"},
},
},
diff --git a/internal/gitaly/service/repository/create.go b/internal/gitaly/service/repository/create.go
index b9fc42d3a..861d6aafe 100644
--- a/internal/gitaly/service/repository/create.go
+++ b/internal/gitaly/service/repository/create.go
@@ -21,7 +21,7 @@ func (s *server) CreateRepository(ctx context.Context, req *gitalypb.CreateRepos
}
stderr := &bytes.Buffer{}
- cmd, err := git.SafeCmdWithoutRepo(ctx, git.CmdStream{Err: stderr}, nil,
+ cmd, err := git.SafeCmdWithoutRepo(ctx, nil,
git.SubCmd{
Name: "init",
Flags: []git.Option{
@@ -30,6 +30,7 @@ func (s *server) CreateRepository(ctx context.Context, req *gitalypb.CreateRepos
},
Args: []string{diskPath},
},
+ git.WithStderr(stderr),
)
if err != nil {
return nil, helper.ErrInternalf("create git init: %w", err)
diff --git a/internal/gitaly/service/repository/create_bundle.go b/internal/gitaly/service/repository/create_bundle.go
index 4f2d631c5..b2e1962fc 100644
--- a/internal/gitaly/service/repository/create_bundle.go
+++ b/internal/gitaly/service/repository/create_bundle.go
@@ -23,9 +23,10 @@ func (s *server) CreateBundle(req *gitalypb.CreateBundleRequest, stream gitalypb
return helper.ErrInternalf("running Cleanup on repository: %w", err)
}
- cmd, err := git.SafeCmd(ctx, repo, nil, git.SubCmd{
- Name: "bundle",
- Flags: []git.Option{git.SubSubCmd{"create"}, git.OutputToStdout, git.Flag{Name: "--all"}},
+ cmd, err := git.SafeCmd(ctx, repo, nil, git.SubSubCmd{
+ Name: "bundle",
+ Action: "create",
+ Flags: []git.Option{git.OutputToStdout, git.Flag{Name: "--all"}},
})
if err != nil {
return status.Errorf(codes.Internal, "CreateBundle: cmd start failed: %v", err)
diff --git a/internal/gitaly/service/repository/create_from_bundle.go b/internal/gitaly/service/repository/create_from_bundle.go
index a92486fdc..7414b7041 100644
--- a/internal/gitaly/service/repository/create_from_bundle.go
+++ b/internal/gitaly/service/repository/create_from_bundle.go
@@ -71,7 +71,7 @@ func (s *server) CreateRepositoryFromBundle(stream gitalypb.RepositoryService_Cr
}
stderr := bytes.Buffer{}
- cmd, err := git.SafeCmdWithoutRepo(ctx, git.CmdStream{Err: &stderr}, nil,
+ cmd, err := git.SafeCmdWithoutRepo(ctx, nil,
git.SubCmd{
Name: "clone",
Flags: []git.Option{
@@ -80,6 +80,7 @@ func (s *server) CreateRepositoryFromBundle(stream gitalypb.RepositoryService_Cr
},
PostSepArgs: []string{bundlePath, repoPath},
},
+ git.WithStderr(&stderr),
git.WithRefTxHook(ctx, repo, s.cfg),
)
if err != nil {
@@ -93,13 +94,14 @@ func (s *server) CreateRepositoryFromBundle(stream gitalypb.RepositoryService_Cr
// We do a fetch to get all refs including keep-around refs
stderr.Reset()
- cmd, err = git.SafeCmdWithoutRepo(ctx, git.CmdStream{Err: &stderr},
- []git.Option{git.ValueFlag{Name: "-C", Value: repoPath}},
+ cmd, err = git.SafeCmdWithoutRepo(ctx,
+ []git.GlobalOption{git.ValueFlag{Name: "-C", Value: repoPath}},
git.SubCmd{
Name: "fetch",
Flags: []git.Option{git.Flag{Name: "--quiet"}},
Args: []string{bundlePath, "refs/*:refs/*"},
},
+ git.WithStderr(&stderr),
git.WithRefTxHook(ctx, repo, s.cfg),
)
if err != nil {
diff --git a/internal/gitaly/service/repository/create_from_url.go b/internal/gitaly/service/repository/create_from_url.go
index b430b9565..15a28a36b 100644
--- a/internal/gitaly/service/repository/create_from_url.go
+++ b/internal/gitaly/service/repository/create_from_url.go
@@ -23,7 +23,7 @@ func (s *server) cloneFromURLCommand(ctx context.Context, repo *gitalypb.Reposit
return nil, helper.ErrInternal(err)
}
- globalFlags := []git.Option{
+ globalFlags := []git.GlobalOption{
git.ValueFlag{Name: "-c", Value: "http.followRedirects=false"},
}
@@ -47,12 +47,13 @@ func (s *server) cloneFromURLCommand(ctx context.Context, repo *gitalypb.Reposit
globalFlags = append(globalFlags, git.ValueFlag{Name: "-c", Value: fmt.Sprintf("http.extraHeader=%s", authHeader)})
}
- return git.SafeBareCmd(ctx, git.CmdStream{Err: stderr}, nil, globalFlags,
+ return git.SafeBareCmd(ctx, nil, globalFlags,
git.SubCmd{
Name: "clone",
Flags: cloneFlags,
PostSepArgs: []string{u.String(), repositoryFullPath},
},
+ git.WithStderr(stderr),
git.WithRefTxHook(ctx, repo, s.cfg),
)
}
diff --git a/internal/gitaly/service/repository/fetch.go b/internal/gitaly/service/repository/fetch.go
index 9bdc059df..16198e581 100644
--- a/internal/gitaly/service/repository/fetch.go
+++ b/internal/gitaly/service/repository/fetch.go
@@ -45,8 +45,8 @@ func (s *server) FetchSourceBranch(ctx context.Context, req *gitalypb.FetchSourc
}
}
- cmd, err := git.SafeBareCmd(ctx, git.CmdStream{}, env,
- []git.Option{git.ValueFlag{"--git-dir", repoPath}},
+ cmd, err := git.SafeBareCmd(ctx, env,
+ []git.GlobalOption{git.ValueFlag{"--git-dir", repoPath}},
git.SubCmd{
Name: "fetch",
Flags: []git.Option{git.Flag{Name: "--prune"}},
diff --git a/internal/gitaly/service/repository/fork.go b/internal/gitaly/service/repository/fork.go
index 8ebd735e3..53c487ef6 100644
--- a/internal/gitaly/service/repository/fork.go
+++ b/internal/gitaly/service/repository/fork.go
@@ -53,7 +53,7 @@ func (s *server) CreateFork(ctx context.Context, req *gitalypb.CreateForkRequest
return nil, err
}
- cmd, err := git.SafeBareCmd(ctx, git.CmdStream{}, env, nil,
+ cmd, err := git.SafeBareCmd(ctx, env, nil,
git.SubCmd{
Name: "clone",
Flags: []git.Option{
diff --git a/internal/gitaly/service/repository/fsck.go b/internal/gitaly/service/repository/fsck.go
index 209e2875a..da07c0557 100644
--- a/internal/gitaly/service/repository/fsck.go
+++ b/internal/gitaly/service/repository/fsck.go
@@ -20,9 +20,11 @@ func (s *server) Fsck(ctx context.Context, req *gitalypb.FsckRequest) (*gitalypb
env := alternates.Env(repoPath, repo.GetGitObjectDirectory(), repo.GetGitAlternateObjectDirectories())
- cmd, err := git.SafeBareCmd(ctx, git.CmdStream{Out: &stdout, Err: &stderr}, env,
- []git.Option{git.ValueFlag{"--git-dir", repoPath}},
+ cmd, err := git.SafeBareCmd(ctx, env,
+ []git.GlobalOption{git.ValueFlag{"--git-dir", repoPath}},
git.SubCmd{Name: "fsck"},
+ git.WithStdout(&stdout),
+ git.WithStderr(&stderr),
)
if err != nil {
return nil, err
diff --git a/internal/gitaly/service/repository/midx.go b/internal/gitaly/service/repository/midx.go
index ec04e3dc6..c1e0e1d03 100644
--- a/internal/gitaly/service/repository/midx.go
+++ b/internal/gitaly/service/repository/midx.go
@@ -76,11 +76,9 @@ func midxSetConfig(ctx context.Context, repo repository.GitRepo) error {
func midxWrite(ctx context.Context, repo repository.GitRepo) error {
cmd, err := git.SafeCmd(ctx, repo, nil,
- git.SubCmd{
- Name: "multi-pack-index",
- Flags: []git.Option{
- git.SubSubCmd{Name: "write"},
- },
+ git.SubSubCmd{
+ Name: "multi-pack-index",
+ Action: "write",
},
)
@@ -114,11 +112,9 @@ func midxVerify(ctx context.Context, repo repository.GitRepo) error {
ctxlogger := ctxlogrus.Extract(ctx)
cmd, err := git.SafeCmd(ctx, repo, nil,
- git.SubCmd{
- Name: "multi-pack-index",
- Flags: []git.Option{
- git.SubSubCmd{Name: "verify"},
- },
+ git.SubSubCmd{
+ Name: "multi-pack-index",
+ Action: "verify",
},
)
if err != nil {
@@ -151,12 +147,10 @@ func (s *server) midxRewrite(ctx context.Context, repo repository.GitRepo) error
}
func midxExpire(ctx context.Context, repo repository.GitRepo) error {
- cmd, err := git.SafeCmd(ctx, repo, []git.Option{},
- git.SubCmd{
- Name: "multi-pack-index",
- Flags: []git.Option{
- git.SubSubCmd{Name: "expire"},
- },
+ cmd, err := git.SafeCmd(ctx, repo, nil,
+ git.SubSubCmd{
+ Name: "multi-pack-index",
+ Action: "expire",
},
)
if err != nil {
@@ -196,10 +190,10 @@ func (s *server) midxRepack(ctx context.Context, repo repository.GitRepo) error
// Bitmap index 'repack.writeBitmaps' is not yet supported.
cmd, err := git.SafeCmd(ctx, repo,
repackConfig(ctx, false),
- git.SubCmd{
- Name: "multi-pack-index",
+ git.SubSubCmd{
+ Name: "multi-pack-index",
+ Action: "repack",
Flags: []git.Option{
- git.SubSubCmd{Name: "repack"},
git.ValueFlag{Name: "--batch-size", Value: strconv.FormatInt(batchSize, 10)},
},
},
diff --git a/internal/gitaly/service/repository/repack.go b/internal/gitaly/service/repository/repack.go
index c4aa38b3a..214cf482f 100644
--- a/internal/gitaly/service/repository/repack.go
+++ b/internal/gitaly/service/repository/repack.go
@@ -70,8 +70,8 @@ func repackCommand(ctx context.Context, repo repository.GitRepo, bitmap bool, ar
return nil
}
-func repackConfig(ctx context.Context, bitmap bool) []git.Option {
- args := []git.Option{
+func repackConfig(ctx context.Context, bitmap bool) []git.GlobalOption {
+ args := []git.GlobalOption{
git.ValueFlag{"-c", "pack.island=r(e)fs/heads"},
git.ValueFlag{"-c", "pack.island=r(e)fs/tags"},
git.ValueFlag{"-c", "pack.islandCore=e"},
diff --git a/internal/gitaly/service/smarthttp/inforefs.go b/internal/gitaly/service/smarthttp/inforefs.go
index 71034daaa..65b3afbca 100644
--- a/internal/gitaly/service/smarthttp/inforefs.go
+++ b/internal/gitaly/service/smarthttp/inforefs.go
@@ -48,7 +48,7 @@ func (s *server) handleInfoRefs(ctx context.Context, service string, req *gitaly
return err
}
- var globalOpts []git.Option
+ var globalOpts []git.GlobalOption
cmdOpts := []git.CmdOpt{git.WithGitProtocol(ctx, req)}
if service == "receive-pack" {
globalOpts = append(globalOpts, git.ReceivePackConfig()...)
@@ -63,7 +63,7 @@ func (s *server) handleInfoRefs(ctx context.Context, service string, req *gitaly
globalOpts = append(globalOpts, git.ValueFlag{"-c", o})
}
- cmd, err := git.SafeBareCmd(ctx, git.CmdStream{}, nil, globalOpts, git.SubCmd{
+ cmd, err := git.SafeBareCmd(ctx, nil, globalOpts, git.SubCmd{
Name: service,
Flags: []git.Option{git.Flag{Name: "--stateless-rpc"}, git.Flag{Name: "--advertise-refs"}},
Args: []string{repoPath},
diff --git a/internal/gitaly/service/smarthttp/receive_pack.go b/internal/gitaly/service/smarthttp/receive_pack.go
index 29b11cb65..abb9f4453 100644
--- a/internal/gitaly/service/smarthttp/receive_pack.go
+++ b/internal/gitaly/service/smarthttp/receive_pack.go
@@ -48,12 +48,14 @@ func (s *server) PostReceivePack(stream gitalypb.SmartHTTPService_PostReceivePac
globalOpts = append(globalOpts, git.ValueFlag{"-c", o})
}
- cmd, err := git.SafeBareCmd(ctx, git.CmdStream{In: stdin, Out: stdout}, nil, globalOpts,
+ cmd, err := git.SafeBareCmd(ctx, nil, globalOpts,
git.SubCmd{
Name: "receive-pack",
Flags: []git.Option{git.Flag{Name: "--stateless-rpc"}},
Args: []string{repoPath},
},
+ git.WithStdin(stdin),
+ git.WithStdout(stdout),
git.WithReceivePackHooks(ctx, config.Config, req, "http"),
git.WithGitProtocol(ctx, req),
)
diff --git a/internal/gitaly/service/smarthttp/upload_pack.go b/internal/gitaly/service/smarthttp/upload_pack.go
index ceac032c0..cec97e4fe 100644
--- a/internal/gitaly/service/smarthttp/upload_pack.go
+++ b/internal/gitaly/service/smarthttp/upload_pack.go
@@ -77,11 +77,11 @@ func (s *server) PostUploadPack(stream gitalypb.SmartHTTPService_PostUploadPackS
globalOpts = append(globalOpts, git.ValueFlag{"-c", o})
}
- cmd, err := git.SafeBareCmd(ctx, git.CmdStream{In: stdin, Out: stdout}, nil, globalOpts, git.SubCmd{
+ cmd, err := git.SafeBareCmd(ctx, nil, globalOpts, git.SubCmd{
Name: "upload-pack",
Flags: []git.Option{git.Flag{Name: "--stateless-rpc"}},
Args: []string{repoPath},
- }, git.WithGitProtocol(ctx, req))
+ }, git.WithStdin(stdin), git.WithStdout(stdout), git.WithGitProtocol(ctx, req))
if err != nil {
return status.Errorf(codes.Unavailable, "PostUploadPack: cmd: %v", err)
diff --git a/internal/gitaly/service/ssh/monitor_stdin_command.go b/internal/gitaly/service/ssh/monitor_stdin_command.go
index 658602a70..aa08e250f 100644
--- a/internal/gitaly/service/ssh/monitor_stdin_command.go
+++ b/internal/gitaly/service/ssh/monitor_stdin_command.go
@@ -10,13 +10,15 @@ import (
"gitlab.com/gitlab-org/gitaly/internal/git/pktline"
)
-func monitorStdinCommand(ctx context.Context, stdin io.Reader, stdout, stderr io.Writer, env []string, globals []git.Option, sc git.SubCmd, opts ...git.CmdOpt) (*command.Command, *pktline.ReadMonitor, error) {
+func monitorStdinCommand(ctx context.Context, stdin io.Reader, stdout, stderr io.Writer, env []string, globals []git.GlobalOption, sc git.SubCmd, opts ...git.CmdOpt) (*command.Command, *pktline.ReadMonitor, error) {
stdinPipe, monitor, err := pktline.NewReadMonitor(ctx, stdin)
if err != nil {
return nil, nil, fmt.Errorf("create monitor: %v", err)
}
- cmd, err := git.SafeBareCmd(ctx, git.CmdStream{In: stdinPipe, Out: stdout, Err: stderr}, env, globals, sc, opts...)
+ cmd, err := git.SafeBareCmd(ctx, env, globals, sc, append([]git.CmdOpt{
+ git.WithStdin(stdinPipe), git.WithStdout(stdout), git.WithStderr(stderr),
+ }, opts...)...)
stdinPipe.Close() // this now belongs to cmd
if err != nil {
return nil, nil, fmt.Errorf("start cmd: %v", err)
diff --git a/internal/gitaly/service/ssh/receive_pack.go b/internal/gitaly/service/ssh/receive_pack.go
index 9e8ee81f0..cf6a5d2e4 100644
--- a/internal/gitaly/service/ssh/receive_pack.go
+++ b/internal/gitaly/service/ssh/receive_pack.go
@@ -66,11 +66,14 @@ func (s *server) sshReceivePack(stream gitalypb.SSHService_SSHReceivePackServer,
globalOpts = append(globalOpts, git.ValueFlag{"-c", o})
}
- cmd, err := git.SafeBareCmd(ctx, git.CmdStream{In: stdin, Out: stdout, Err: stderr}, nil, globalOpts,
+ cmd, err := git.SafeBareCmd(ctx, nil, globalOpts,
git.SubCmd{
Name: "receive-pack",
Args: []string{repoPath},
},
+ git.WithStdin(stdin),
+ git.WithStdout(stdout),
+ git.WithStderr(stderr),
git.WithReceivePackHooks(ctx, config.Config, req, "ssh"),
git.WithGitProtocol(ctx, req),
)
diff --git a/internal/gitalyssh/gitalyssh.go b/internal/gitalyssh/gitalyssh.go
index 253073934..4b57f6168 100644
--- a/internal/gitalyssh/gitalyssh.go
+++ b/internal/gitalyssh/gitalyssh.go
@@ -26,12 +26,6 @@ const (
GitalyInternalURL = "ssh://gitaly/internal.git"
)
-const (
- // EnvVarUploadPackAllowAnySHA1InWant enables the capability to request
- // individual SHA1's from the remote repo
- EnvVarUploadPackAllowAnySHA1InWant = "uploadpack.allowAnySHA1InWant=true"
-)
-
var (
envInjector = tracing.NewEnvInjector()
)