Diff
1diff --git a/pkg/ssh/cmd/tree.go b/pkg/ssh/cmd/tree.go
2index 6399c3e3f8d021a81de54c734690b6282361053b..aec94e9165d6375058fa800a9474c6576579baf2 100644
3--- a/pkg/ssh/cmd/tree.go
4+++ b/pkg/ssh/cmd/tree.go
5@@ -6,6 +6,7 @@ import (
6 "github.com/charmbracelet/soft-serve/git"
7 "github.com/charmbracelet/soft-serve/pkg/backend"
8 "github.com/charmbracelet/soft-serve/pkg/proto"
9+ "github.com/charmbracelet/soft-serve/pkg/ui/common"
10 "github.com/dustin/go-humanize"
11 "github.com/spf13/cobra"
12 )
13@@ -93,7 +94,7 @@ func treeCommand() *cobra.Command {
14 } else {
15 ssize = humanize.Bytes(uint64(size))
16 }
17- cmd.Printf("%s\t%s\t %s\n", ent.Mode(), ssize, ent.Name())
18+ cmd.Printf("%s\t%s\t %s\n", ent.Mode(), ssize, common.UnquoteFilename(ent.Name()))
19 }
20 return nil
21 },
22diff --git a/pkg/ui/common/format.go b/pkg/ui/common/format.go
23index 8402737969c26740131a7c26f9fd762be4df76d9..4344e057d8cfba2478b977a34f01a1d28c1cc6d2 100644
24--- a/pkg/ui/common/format.go
25+++ b/pkg/ui/common/format.go
26@@ -2,6 +2,7 @@ package common
27
28 import (
29 "fmt"
30+ "strconv"
31 "strings"
32
33 "github.com/alecthomas/chroma/v2/lexers"
34@@ -54,3 +55,17 @@ func FormatHighlight(p, c string) (string, error) {
35 }
36 return r.String(), nil
37 }
38+
39+// UnquoteFilename unquotes a filename.
40+// When Git is with "core.quotePath" set to "true" (default), it will quote
41+// the filename with double quotes if it contains control characters or unicode.
42+// this function will unquote the filename.
43+func UnquoteFilename(s string) string {
44+ name := s
45+ if n, err := strconv.Unquote(`"` + s + `"`); err == nil {
46+ name = n
47+ }
48+
49+ name = strconv.Quote(name)
50+ return strings.Trim(name, `"`)
51+}
52diff --git a/pkg/ui/pages/repo/filesitem.go b/pkg/ui/pages/repo/filesitem.go
53index fde57de8c380a22932cf3992d6cc3b2cfd1f0e11..eb59313dba0b3356fe345dfa33b474a4a673474a 100644
54--- a/pkg/ui/pages/repo/filesitem.go
55+++ b/pkg/ui/pages/repo/filesitem.go
56@@ -4,7 +4,6 @@ import (
57 "fmt"
58 "io"
59 "io/fs"
60- "strconv"
61 "strings"
62
63 "github.com/charmbracelet/bubbles/key"
64@@ -23,16 +22,12 @@ type FileItem struct {
65
66 // ID returns the ID of the file item.
67 func (i FileItem) ID() string {
68- name := i.entry.Name()
69- if n, err := strconv.Unquote(name); err == nil {
70- name = n
71- }
72- return name
73+ return i.entry.Name()
74 }
75
76 // Title returns the title of the file item.
77 func (i FileItem) Title() string {
78- return i.entry.Name()
79+ return common.UnquoteFilename(i.entry.Name())
80 }
81
82 // Description returns the description of the file item.
83diff --git a/testscript/testdata/repo-tree.txtar b/testscript/testdata/repo-tree.txtar
84index bc00ef03162484262b4f65707c95d4a7cfa8d94d..b3e6320de5f84429277f4f41829db9ee4544253f 100644
85--- a/testscript/testdata/repo-tree.txtar
86+++ b/testscript/testdata/repo-tree.txtar
87@@ -1,7 +1,7 @@
88 # vi: set ft=conf
89
90 # convert crlf to lf on windows
91-[windows] dos2unix tree1.txt tree2.txt tree3.txt
92+[windows] dos2unix tree1.txt tree2.txt tree3.txt tree4.txt
93
94 # start soft serve
95 exec soft serve &
96@@ -47,6 +47,18 @@ stderr 'file not found'
97 ! stdout .
98 stderr 'revision does not exist'
99
100+# test unicode file name issue #457
101+soft repo create repo4
102+git clone ssh://localhost:$SSH_PORT/repo4 repo4
103+mkfile ./repo4/๐.md '๐'
104+git -C repo4 add -A
105+git -C repo4 commit -m 'unicode'
106+git -C repo4 push origin HEAD
107+
108+# print root tree
109+soft repo tree repo4
110+cmp stdout tree4.txt
111+
112 # stop the server
113 [windows] stopserver
114
115@@ -59,3 +71,5 @@ drwxrwxrwx - folder
116 -rw-r--r-- 5 B aa.md
117 -- tree3.txt --
118 -rw-r--r-- 2 B b.md
119+-- tree4.txt --
120+-rw-r--r-- 4 B ๐.md