diff options
author | Quang-Minh Nguyen <qmnguyen@gitlab.com> | 2022-08-18 08:23:30 +0300 |
---|---|---|
committer | Quang-Minh Nguyen <qmnguyen@gitlab.com> | 2022-08-18 09:48:44 +0300 |
commit | 8945253a69819de43e1160e4ea9c3ea9be10f873 (patch) | |
tree | 587718a47a63bba15277b31f0f404aaa11530de0 | |
parent | bb4376f98acdf012abd74ebb3e9b77b93aaf3b57 (diff) |
Fix Repository SearchFilesByName not handle non-ASCII file names wellqmnguyen0711/4440-repository-searchfilesbyname-doesn-t-work-well-with-non-ascii-file-names
Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/4440
Changelog: fixed
-rw-r--r-- | internal/gitaly/service/repository/search_files.go | 7 | ||||
-rw-r--r-- | internal/gitaly/service/repository/search_files_test.go | 110 |
2 files changed, 116 insertions, 1 deletions
diff --git a/internal/gitaly/service/repository/search_files.go b/internal/gitaly/service/repository/search_files.go index 4c6a006bb..ba7598888 100644 --- a/internal/gitaly/service/repository/search_files.go +++ b/internal/gitaly/service/repository/search_files.go @@ -131,6 +131,11 @@ func (s *server) SearchFilesByName(req *gitalypb.SearchFilesByNameRequest, strea git.Flag{Name: "--full-tree"}, git.Flag{Name: "--name-status"}, git.Flag{Name: "-r"}, + // We use -z to force NULL byte termination here to prevent git from + // quoting and escaping unusual file names. Lstree parser would be a + // more ideal solution. Unfortunately, it supports parsing full + // output while we are interested in the filenames only. + git.Flag{Name: "-z"}, }, Args: []string{string(req.GetRef()), req.GetQuery()}}) if err != nil { return helper.ErrInternalf("SearchFilesByName: cmd start failed: %v", err) @@ -140,7 +145,7 @@ func (s *server) SearchFilesByName(req *gitalypb.SearchFilesByNameRequest, strea return stream.Send(&gitalypb.SearchFilesByNameResponse{Files: objs}) } - return lines.Send(cmd, lr, lines.SenderOpts{Delimiter: '\n', Limit: math.MaxInt32, Filter: filter}) + return lines.Send(cmd, lr, lines.SenderOpts{Delimiter: 0x00, Limit: math.MaxInt32, Filter: filter}) } type searchFilesRequest interface { diff --git a/internal/gitaly/service/repository/search_files_test.go b/internal/gitaly/service/repository/search_files_test.go index ff4f20f13..9aeab951a 100644 --- a/internal/gitaly/service/repository/search_files_test.go +++ b/internal/gitaly/service/repository/search_files_test.go @@ -337,6 +337,116 @@ func TestSearchFilesByNameSuccessful(t *testing.T) { } } +func TestSearchFilesByNameUnusualFileNamesSuccessful(t *testing.T) { + t.Parallel() + ctx := testhelper.Context(t) + + cfg, repo, repoPath, client := setupRepositoryService(ctx, t) + + gittest.WriteCommit(t, cfg, repoPath, + gittest.WithBranch("unusual_file_names"), + gittest.WithMessage("commit message"), + gittest.WithTreeEntries( + gittest.TreeEntry{Path: "\"file with quote.txt", Mode: "100644", Content: "something"}, + gittest.TreeEntry{Path: ".vimrc", Mode: "100644", Content: "something"}, + gittest.TreeEntry{Path: "cuộc đời là những chuyến đi.md", Mode: "100644", Content: "something"}, + gittest.TreeEntry{Path: "编码 'foo'.md", Mode: "100644", Content: "something"}, + ), + ) + + testCases := []struct { + desc string + ref []byte + query string + filter string + testFiles [][]byte + }{ + { + desc: "file with quote", + ref: []byte("unusual_file_names"), + query: "\"file with quote.txt", + testFiles: [][]byte{[]byte("\"file with quote.txt")}, + }, + { + desc: "dotfiles", + ref: []byte("unusual_file_names"), + query: ".vimrc", + testFiles: [][]byte{[]byte(".vimrc")}, + }, + { + desc: "latin-base language", + ref: []byte("unusual_file_names"), + query: "cuộc đời là những chuyến đi.md", + testFiles: [][]byte{[]byte("cuộc đời là những chuyến đi.md")}, + }, + { + desc: "non-latin language", + ref: []byte("unusual_file_names"), + query: "编码 'foo'.md", + testFiles: [][]byte{[]byte("编码 'foo'.md")}, + }, + { + desc: "filter file with quote", + ref: []byte("unusual_file_names"), + query: ".", + filter: "^\"file.*", + testFiles: [][]byte{[]byte("\"file with quote.txt")}, + }, + { + desc: "filter dotfiles", + ref: []byte("unusual_file_names"), + query: ".", + filter: "^\\..*", + testFiles: [][]byte{[]byte(".vimrc")}, + }, + { + desc: "filter latin-base language", + ref: []byte("unusual_file_names"), + query: ".", + filter: "cuộc đời .*\\.md", + testFiles: [][]byte{[]byte("cuộc đời là những chuyến đi.md")}, + }, + { + desc: "filter non-latin language", + ref: []byte("unusual_file_names"), + query: ".", + filter: "编码 'foo'\\.(md|txt|rdoc)", + testFiles: [][]byte{[]byte("编码 'foo'.md")}, + }, + { + desc: "wildcard filter", + ref: []byte("unusual_file_names"), + query: ".", + filter: ".*", + testFiles: [][]byte{ + []byte("\"file with quote.txt"), + []byte(".vimrc"), + []byte("cuộc đời là những chuyến đi.md"), + []byte("编码 'foo'.md"), + }, + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + stream, err := client.SearchFilesByName(ctx, &gitalypb.SearchFilesByNameRequest{ + Repository: repo, + Ref: tc.ref, + Query: tc.query, + Filter: tc.filter, + }) + require.NoError(t, err) + + var files [][]byte + files, err = consumeFilenameByName(stream) + require.NoError(t, err) + require.Equal(t, len(tc.testFiles), len(files)) + for i, f := range tc.testFiles { + require.Equal(t, f, files[i]) + } + }) + } +} + func TestSearchFilesByNameFailure(t *testing.T) { t.Parallel() cfg := testcfg.Build(t) |