mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Improve grep search (#30843)
Reduce the context line number to 1, make "git grep" search respect the include/exclude patter, and fix #30785
This commit is contained in:
		| @@ -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{ | ||||
|   | ||||
| @@ -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{ | ||||
|   | ||||
							
								
								
									
										32
									
								
								modules/setting/glob.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								modules/setting/glob.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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 | ||||
| 		} | ||||
|   | ||||
							
								
								
									
										19
									
								
								routers/web/repo/search_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								routers/web/repo/search_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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()) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user