Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions internal/tool/file_find.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ func (p *FileFindProvider) listGitFiles(parentCtx context.Context) ([]string, er

var args []string
if ref := p.FileReader.Ref; ref != "" {
args = []string{"ls-tree", "-r", "--name-only", "--end-of-options", ref}
args = []string{"ls-tree", "-r", "--name-only", "-z", "--end-of-options", ref}
} else {
args = []string{"ls-files", "--cached", "--others", "--exclude-standard"}
args = []string{"ls-files", "-z", "--cached", "--others", "--exclude-standard"}
}

if p.FileReader.Runner != nil {
Expand All @@ -107,10 +107,10 @@ func (p *FileFindProvider) listGitFiles(parentCtx context.Context) ([]string, er
}

var files []string
lines := bytes.Split(bytes.TrimRight(output, "\n"), []byte{'\n'})
for _, line := range lines {
if len(line) > 0 {
s := string(line)
paths := bytes.Split(output, []byte{0})
for _, path := range paths {
if len(path) > 0 {
s := string(path)
// Skip binary-like files that lack meaningful extensions patterns
// and filter out paths in common generated/artifact directories.
if shouldSkipFile(s) {
Expand Down
83 changes: 83 additions & 0 deletions internal/tool/file_find_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"

Expand Down Expand Up @@ -159,6 +160,88 @@ func TestFileFind_GitRepo_CommitMode(t *testing.T) {
}
}

func TestFileFind_GitRepo_PreservesSpecialPaths(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("test paths contain characters unsupported by Windows")
}

dir := setupFileFindRepo(t)
specialPaths := []string{
"unicode-测试.go",
"tab\tquote\"file.go",
"line\nbreak.go",
}
for _, path := range specialPaths {
if err := os.WriteFile(filepath.Join(dir, path), []byte("package special\n"), 0o644); err != nil {
t.Fatal(err)
}
}

git := func(args ...string) string {
t.Helper()
cmd := exec.Command("git", args...)
cmd.Dir = dir
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("git %v: %v\n%s", args, err, out)
}
return strings.TrimSpace(string(out))
}
git("add", ".")
git("commit", "-m", "add special paths")
commit := git("rev-parse", "HEAD")

tests := []struct {
name string
mode ReviewMode
ref string
}{
{name: "workspace", mode: ModeWorkspace},
{name: "commit", mode: ModeCommit, ref: commit},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := NewFileFind(&FileReader{RepoDir: dir, Mode: tt.mode, Ref: tt.ref})

files, err := p.listGitFiles(context.Background())
if err != nil {
t.Fatal(err)
}
// Newline paths are asserted only at the enumeration layer because
// Execute intentionally joins multiple results with newlines.
for _, want := range append([]string{"main.go"}, specialPaths...) {
found := false
for _, got := range files {
if got == want {
found = true
break
}
}
if !found {
t.Errorf("listGitFiles() missing original path %q; got %q", want, files)
}
}

for _, query := range []struct {
name string
want string
}{
{name: "main.go", want: "main.go"},
{name: "unicode-", want: "unicode-测试.go"},
{name: "quote", want: "tab\tquote\"file.go"},
} {
got, err := p.Execute(context.Background(), map[string]any{"query_name": query.name})
if err != nil {
t.Fatal(err)
}
if got != query.want {
t.Errorf("Execute(query_name=%q) = %q, want %q", query.name, got, query.want)
}
}
})
}
}

func TestFileFind_GitRepo_WithRunner(t *testing.T) {
dir := setupFileFindRepo(t)
runner := gitcmd.New(4)
Expand Down
Loading