summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2026-01-24 13:07:01 -0500
committerDax Raad <[email protected]>2026-01-24 13:07:07 -0500
commit32e6bcae3ba50d0c453c0de7d2e103830dd69a3f (patch)
tree95663cf5ece347b83babdaca4a0e78f1bb167d78
parent087d7da14debf3ae3701d7dd787c34cd2d0b57c8 (diff)
downloadopencode-32e6bcae3ba50d0c453c0de7d2e103830dd69a3f.tar.gz
opencode-32e6bcae3ba50d0c453c0de7d2e103830dd69a3f.zip
core: fix unicode filename handling in snapshot diff by disabling quote escaping
This ensures unicode and special characters in filenames are displayed correctly when generating diff patches, allowing proper file detection and revert operations
-rw-r--r--packages/opencode/src/snapshot/index.ts2
-rw-r--r--packages/opencode/test/snapshot/snapshot.test.ts73
2 files changed, 65 insertions, 10 deletions
diff --git a/packages/opencode/src/snapshot/index.ts b/packages/opencode/src/snapshot/index.ts
index 46c97cf8d..135bd0944 100644
--- a/packages/opencode/src/snapshot/index.ts
+++ b/packages/opencode/src/snapshot/index.ts
@@ -85,7 +85,7 @@ export namespace Snapshot {
const git = gitdir()
await $`git --git-dir ${git} --work-tree ${Instance.worktree} add .`.quiet().cwd(Instance.directory).nothrow()
const result =
- await $`git -c core.autocrlf=false --git-dir ${git} --work-tree ${Instance.worktree} diff --no-ext-diff --name-only ${hash} -- .`
+ await $`git -c core.autocrlf=false -c core.quotepath=false --git-dir ${git} --work-tree ${Instance.worktree} diff --no-ext-diff --name-only ${hash} -- .`
.quiet()
.cwd(Instance.directory)
.nothrow()
diff --git a/packages/opencode/test/snapshot/snapshot.test.ts b/packages/opencode/test/snapshot/snapshot.test.ts
index cf933f812..de58f4f85 100644
--- a/packages/opencode/test/snapshot/snapshot.test.ts
+++ b/packages/opencode/test/snapshot/snapshot.test.ts
@@ -266,23 +266,78 @@ test("unicode filenames", async () => {
expect(before).toBeTruthy()
const unicodeFiles = [
- `${tmp.path}/文件.txt`,
- `${tmp.path}/🚀rocket.txt`,
- `${tmp.path}/café.txt`,
- `${tmp.path}/файл.txt`,
+ { path: `${tmp.path}/文件.txt`, content: "chinese content" },
+ { path: `${tmp.path}/🚀rocket.txt`, content: "emoji content" },
+ { path: `${tmp.path}/café.txt`, content: "accented content" },
+ { path: `${tmp.path}/файл.txt`, content: "cyrillic content" },
]
for (const file of unicodeFiles) {
- await Bun.write(file, "unicode content")
+ await Bun.write(file.path, file.content)
}
const patch = await Snapshot.patch(before!)
- // Note: git escapes unicode characters by default, so we just check that files are detected
- // The actual filenames will be escaped like "caf\303\251.txt" but functionality works
expect(patch.files.length).toBe(4)
- // Skip revert test due to git filename escaping issues
- // The functionality works but git uses escaped filenames internally
+ for (const file of unicodeFiles) {
+ expect(patch.files).toContain(file.path)
+ }
+
+ await Snapshot.revert([patch])
+
+ for (const file of unicodeFiles) {
+ expect(await Bun.file(file.path).exists()).toBe(false)
+ }
+ },
+ })
+})
+
+test("unicode filenames modification and restore", async () => {
+ await using tmp = await bootstrap()
+ await Instance.provide({
+ directory: tmp.path,
+ fn: async () => {
+ const chineseFile = `${tmp.path}/文件.txt`
+ const cyrillicFile = `${tmp.path}/файл.txt`
+
+ await Bun.write(chineseFile, "original chinese")
+ await Bun.write(cyrillicFile, "original cyrillic")
+
+ const before = await Snapshot.track()
+ expect(before).toBeTruthy()
+
+ await Bun.write(chineseFile, "modified chinese")
+ await Bun.write(cyrillicFile, "modified cyrillic")
+
+ const patch = await Snapshot.patch(before!)
+ expect(patch.files).toContain(chineseFile)
+ expect(patch.files).toContain(cyrillicFile)
+
+ await Snapshot.revert([patch])
+
+ expect(await Bun.file(chineseFile).text()).toBe("original chinese")
+ expect(await Bun.file(cyrillicFile).text()).toBe("original cyrillic")
+ },
+ })
+})
+
+test("unicode filenames in subdirectories", async () => {
+ await using tmp = await bootstrap()
+ await Instance.provide({
+ directory: tmp.path,
+ fn: async () => {
+ const before = await Snapshot.track()
+ expect(before).toBeTruthy()
+
+ await $`mkdir -p "${tmp.path}/目录/подкаталог"`.quiet()
+ const deepFile = `${tmp.path}/目录/подкаталог/文件.txt`
+ await Bun.write(deepFile, "deep unicode content")
+
+ const patch = await Snapshot.patch(before!)
+ expect(patch.files).toContain(deepFile)
+
+ await Snapshot.revert([patch])
+ expect(await Bun.file(deepFile).exists()).toBe(false)
},
})
})