]> source.dussan.org Git - gitea.git/commitdiff
Improve grep search (#30843)
authorwxiaoguang <wxiaoguang@gmail.com>
Fri, 3 May 2024 09:13:48 +0000 (17:13 +0800)
committerGitHub <noreply@github.com>
Fri, 3 May 2024 09:13:48 +0000 (09:13 +0000)
Reduce the context line number to 1, make "git grep" search respect the
include/exclude patter, and fix #30785

modules/git/grep.go
modules/git/grep_test.go
modules/setting/glob.go [new file with mode: 0644]
modules/setting/indexer.go
routers/web/repo/search.go
routers/web/repo/search_test.go [new file with mode: 0644]

index e7d238e586aa44185093b2fff8fb51c5499af047..bf6b41a88670aa36593c15dcf21120b62ffa6609 100644 (file)
@@ -29,6 +29,7 @@ type GrepOptions struct {
        ContextLineNumber int
        IsFuzzy           bool
        MaxLineLength     int // the maximum length of a line to parse, exceeding chars will be truncated
+       PathspecList      []string
 }
 
 func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepOptions) ([]*GrepResult, error) {
@@ -62,6 +63,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO
                cmd.AddOptionValues("-e", strings.TrimLeft(search, "-"))
        }
        cmd.AddDynamicArguments(util.IfZero(opts.RefName, "HEAD"))
+       cmd.AddDashesAndList(opts.PathspecList...)
        opts.MaxResultLimit = util.IfZero(opts.MaxResultLimit, 50)
        stderr := bytes.Buffer{}
        err = cmd.Run(&RunOpts{
index 7f4ded478f57188d1fb9e29e071efde248183048..6a99f80407071a2796ffad1cce55fe5ee1a58950 100644 (file)
@@ -31,6 +31,26 @@ func TestGrepSearch(t *testing.T) {
                },
        }, res)
 
+       res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{PathspecList: []string{":(glob)java-hello/*"}})
+       assert.NoError(t, err)
+       assert.Equal(t, []*GrepResult{
+               {
+                       Filename:    "java-hello/main.java",
+                       LineNumbers: []int{3},
+                       LineCodes:   []string{" public static void main(String[] args)"},
+               },
+       }, res)
+
+       res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{PathspecList: []string{":(glob,exclude)java-hello/*"}})
+       assert.NoError(t, err)
+       assert.Equal(t, []*GrepResult{
+               {
+                       Filename:    "main.vendor.java",
+                       LineNumbers: []int{3},
+                       LineCodes:   []string{" public static void main(String[] args)"},
+               },
+       }, res)
+
        res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{MaxResultLimit: 1})
        assert.NoError(t, err)
        assert.Equal(t, []*GrepResult{
diff --git a/modules/setting/glob.go b/modules/setting/glob.go
new file mode 100644 (file)
index 0000000..8f1d24d
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package setting
+
+import "github.com/gobwas/glob"
+
+type GlobMatcher struct {
+       compiledGlob  glob.Glob
+       patternString string
+}
+
+var _ glob.Glob = (*GlobMatcher)(nil)
+
+func (g *GlobMatcher) Match(s string) bool {
+       return g.compiledGlob.Match(s)
+}
+
+func (g *GlobMatcher) PatternString() string {
+       return g.patternString
+}
+
+func GlobMatcherCompile(pattern string, separators ...rune) (*GlobMatcher, error) {
+       g, err := glob.Compile(pattern, separators...)
+       if err != nil {
+               return nil, err
+       }
+       return &GlobMatcher{
+               compiledGlob:  g,
+               patternString: pattern,
+       }, nil
+}
index 6877d70e3cfcd6c366bc54fe720394ed7d4fafdd..18585602c3dd2faf8fcd25771fcd0a297d46d456 100644 (file)
@@ -10,8 +10,6 @@ import (
        "time"
 
        "code.gitea.io/gitea/modules/log"
-
-       "github.com/gobwas/glob"
 )
 
 // Indexer settings
@@ -30,8 +28,8 @@ var Indexer = struct {
        RepoConnStr          string
        RepoIndexerName      string
        MaxIndexerFileSize   int64
-       IncludePatterns      []glob.Glob
-       ExcludePatterns      []glob.Glob
+       IncludePatterns      []*GlobMatcher
+       ExcludePatterns      []*GlobMatcher
        ExcludeVendored      bool
 }{
        IssueType:        "bleve",
@@ -93,12 +91,12 @@ func loadIndexerFrom(rootCfg ConfigProvider) {
 }
 
 // IndexerGlobFromString parses a comma separated list of patterns and returns a glob.Glob slice suited for repo indexing
-func IndexerGlobFromString(globstr string) []glob.Glob {
-       extarr := make([]glob.Glob, 0, 10)
+func IndexerGlobFromString(globstr string) []*GlobMatcher {
+       extarr := make([]*GlobMatcher, 0, 10)
        for _, expr := range strings.Split(strings.ToLower(globstr), ",") {
                expr = strings.TrimSpace(expr)
                if expr != "" {
-                       if g, err := glob.Compile(expr, '.', '/'); err != nil {
+                       if g, err := GlobMatcherCompile(expr, '.', '/'); err != nil {
                                log.Info("Invalid glob expression '%s' (skipped): %v", expr, err)
                        } else {
                                extarr = append(extarr, g)
index d7854b2499b4b55385a100a2be5216cf38236974..920a865555becca6b291a106cae86f08730f87aa 100644 (file)
@@ -17,6 +17,16 @@ import (
 
 const tplSearch base.TplName = "repo/search"
 
+func indexSettingToGitGrepPathspecList() (list []string) {
+       for _, expr := range setting.Indexer.IncludePatterns {
+               list = append(list, ":(glob)"+expr.PatternString())
+       }
+       for _, expr := range setting.Indexer.ExcludePatterns {
+               list = append(list, ":(glob,exclude)"+expr.PatternString())
+       }
+       return list
+}
+
 // Search render repository search page
 func Search(ctx *context.Context) {
        language := ctx.FormTrim("l")
@@ -65,8 +75,14 @@ func Search(ctx *context.Context) {
                        ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx)
                }
        } else {
-               res, err := git.GrepSearch(ctx, ctx.Repo.GitRepo, keyword, git.GrepOptions{ContextLineNumber: 3, IsFuzzy: isFuzzy})
+               res, err := git.GrepSearch(ctx, ctx.Repo.GitRepo, keyword, git.GrepOptions{
+                       ContextLineNumber: 1,
+                       IsFuzzy:           isFuzzy,
+                       RefName:           git.RefNameFromBranch(ctx.Repo.BranchName).String(), // BranchName should be default branch or the first existing branch
+                       PathspecList:      indexSettingToGitGrepPathspecList(),
+               })
                if err != nil {
+                       // TODO: if no branch exists, it reports: exit status 128, fatal: this operation must be run in a work tree.
                        ctx.ServerError("GrepSearch", err)
                        return
                }
diff --git a/routers/web/repo/search_test.go b/routers/web/repo/search_test.go
new file mode 100644 (file)
index 0000000..33a1610
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+       "testing"
+
+       "code.gitea.io/gitea/modules/setting"
+       "code.gitea.io/gitea/modules/test"
+
+       "github.com/stretchr/testify/assert"
+)
+
+func TestIndexSettingToGitGrepPathspecList(t *testing.T) {
+       defer test.MockVariableValue(&setting.Indexer.IncludePatterns, setting.IndexerGlobFromString("a"))()
+       defer test.MockVariableValue(&setting.Indexer.ExcludePatterns, setting.IndexerGlobFromString("b"))()
+       assert.Equal(t, []string{":(glob)a", ":(glob,exclude)b"}, indexSettingToGitGrepPathspecList())
+}