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:
authorQuang-Minh Nguyen <qmnguyen@gitlab.com>2022-08-18 08:23:30 +0300
committerQuang-Minh Nguyen <qmnguyen@gitlab.com>2022-08-18 09:48:44 +0300
commit8945253a69819de43e1160e4ea9c3ea9be10f873 (patch)
tree587718a47a63bba15277b31f0f404aaa11530de0
parentbb4376f98acdf012abd74ebb3e9b77b93aaf3b57 (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.go7
-rw-r--r--internal/gitaly/service/repository/search_files_test.go110
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)