diff --git a/modules/git/gitcmd/command.go b/modules/git/gitcmd/command.go index d40f3f15029..81e375cabf9 100644 --- a/modules/git/gitcmd/command.go +++ b/modules/git/gitcmd/command.go @@ -172,6 +172,16 @@ func (c *Command) AddOptionFormat(opt string, args ...any) *Command { return c } +func (c *Command) AddOptionGrepExpr(s string) *Command { + if len(c.args) == 0 || c.args[0] != "grep" { + c.handlePreErrorBrokenCommand("(not grep command)") + return c + } + // man git-grep: -e: This option has to be used for patterns starting with "-" + c.args = append(c.args, "-e", s) + return c +} + // AddDynamicArguments adds new dynamic argument values to the command. // The arguments may come from user input and can not be trusted, so no leading '-' is allowed to avoid passing options. // TODO: in the future, this function can be renamed to AddArgumentValues diff --git a/modules/git/gitcmd/command_test.go b/modules/git/gitcmd/command_test.go index 8425f6f837a..7b768274182 100644 --- a/modules/git/gitcmd/command_test.go +++ b/modules/git/gitcmd/command_test.go @@ -77,10 +77,27 @@ func TestRunWithContextStd(t *testing.T) { cmd := NewCommand() cmd.AddDynamicArguments("-test") assert.ErrorIs(t, cmd.Run(t.Context()), ErrBrokenCommand) + assert.Empty(t, cmd.args) cmd = NewCommand() cmd.AddDynamicArguments("--test") assert.ErrorIs(t, cmd.Run(t.Context()), ErrBrokenCommand) + assert.Empty(t, cmd.args) + + cmd = NewCommand() + cmd.AddOptionGrepExpr("-x") + assert.ErrorIs(t, cmd.Run(t.Context()), ErrBrokenCommand) + assert.Empty(t, cmd.args) + + cmd = NewCommand("any") + cmd.AddOptionGrepExpr("-x") + assert.ErrorIs(t, cmd.Run(t.Context()), ErrBrokenCommand) + assert.Equal(t, []string{"any"}, cmd.args) + + cmd = NewCommand("grep") + cmd.AddOptionGrepExpr("-x") + assert.NoError(t, cmd.Run(t.Context())) + assert.Equal(t, []string{"grep", "-e", "-x"}, cmd.args) } { diff --git a/modules/git/grep.go b/modules/git/grep.go index 73313c683e7..b8aeddb1ce4 100644 --- a/modules/git/grep.go +++ b/modules/git/grep.go @@ -59,16 +59,14 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO cmd.AddOptionValues("--context", strconv.Itoa(opts.ContextLineNumber)) switch opts.GrepMode { case GrepModeExact: - cmd.AddArguments("--fixed-strings") - cmd.AddOptionValues("-e", strings.TrimLeft(search, "-")) + cmd.AddArguments("--fixed-strings").AddOptionGrepExpr(search) case GrepModeRegexp: - cmd.AddArguments("--perl-regexp") - cmd.AddOptionValues("-e", strings.TrimLeft(search, "-")) + cmd.AddArguments("--perl-regexp").AddOptionGrepExpr(search) default: /* words */ words := strings.Fields(search) cmd.AddArguments("--fixed-strings", "--ignore-case") for i, word := range words { - cmd.AddOptionValues("-e", strings.TrimLeft(word, "-")) + cmd.AddOptionGrepExpr(word) if i < len(words)-1 { cmd.AddOptionValues("--and") } diff --git a/modules/git/grep_test.go b/modules/git/grep_test.go index cfde0b0f943..3b6e64038b2 100644 --- a/modules/git/grep_test.go +++ b/modules/git/grep_test.go @@ -79,6 +79,11 @@ func TestGrepSearch(t *testing.T) { assert.NoError(t, err) assert.Empty(t, res) + // search a string starting with a dash + res, err = GrepSearch(t.Context(), repo, "-v", GrepOptions{}) + assert.NoError(t, err) + assert.Equal(t, []*GrepResult{{Filename: ".gitattributes", LineNumbers: []int{1}, LineCodes: []string{"*.vendor.java linguist-vendored"}}}, res) + nonExistingRepo := &Repository{RepositoryBase: RepositoryBase{repoFacade: gitrepo.RepositoryUnmanaged("no-such-git-repo")}} res, err = GrepSearch(t.Context(), nonExistingRepo, "no-such-content", GrepOptions{}) assert.Error(t, err)