This commit is contained in:
@@ -195,7 +195,20 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({
|
||||
const root = directory()
|
||||
const prefix = root.endsWith("/") ? root : root + "/"
|
||||
|
||||
let path = stripQueryAndHash(stripFileProtocol(input))
|
||||
let path = input
|
||||
|
||||
// Only strip protocol and decode if it's a file URI
|
||||
if (path.startsWith("file://")) {
|
||||
const raw = stripQueryAndHash(stripFileProtocol(path))
|
||||
try {
|
||||
// Attempt to treat as a standard URI
|
||||
path = decodeURIComponent(raw)
|
||||
} catch {
|
||||
// Fallback for legacy paths that might contain invalid URI sequences (e.g. "100%")
|
||||
// In this case, we treat the path as raw, but still strip the protocol
|
||||
path = raw
|
||||
}
|
||||
}
|
||||
|
||||
if (path.startsWith(prefix)) {
|
||||
path = path.slice(prefix.length)
|
||||
@@ -218,7 +231,8 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({
|
||||
|
||||
function tab(input: string) {
|
||||
const path = normalize(input)
|
||||
return `file://${path}`
|
||||
const encoded = path.split("/").map(encodeURIComponent).join("/")
|
||||
return `file://${encoded}`
|
||||
}
|
||||
|
||||
function pathFromTab(tabValue: string) {
|
||||
|
||||
@@ -104,6 +104,7 @@ export namespace Snapshot {
|
||||
.split("\n")
|
||||
.map((x) => x.trim())
|
||||
.filter(Boolean)
|
||||
.map((x) => unquote(x))
|
||||
.map((x) => path.join(Instance.worktree, x)),
|
||||
}
|
||||
}
|
||||
@@ -151,7 +152,7 @@ export namespace Snapshot {
|
||||
})
|
||||
} else {
|
||||
log.info("file did not exist in snapshot, deleting", { file })
|
||||
await fs.unlink(file).catch(() => {})
|
||||
await fs.unlink(file).catch(() => { })
|
||||
}
|
||||
}
|
||||
files.add(file)
|
||||
@@ -202,20 +203,22 @@ export namespace Snapshot {
|
||||
.nothrow()
|
||||
.lines()) {
|
||||
if (!line) continue
|
||||
const [additions, deletions, file] = line.split("\t")
|
||||
const [additions, deletions, rawFile] = line.split("\t")
|
||||
const file = unquote(rawFile)
|
||||
const isBinaryFile = additions === "-" && deletions === "-"
|
||||
const before = isBinaryFile
|
||||
? ""
|
||||
const beforeResult = isBinaryFile
|
||||
? { exitCode: 0, text: () => "", stderr: Buffer.from("") }
|
||||
: await $`git -c core.autocrlf=false --git-dir ${git} --work-tree ${Instance.worktree} show ${from}:${file}`
|
||||
.quiet()
|
||||
.nothrow()
|
||||
.text()
|
||||
const after = isBinaryFile
|
||||
? ""
|
||||
.quiet()
|
||||
.nothrow()
|
||||
const before = beforeResult.exitCode === 0 ? beforeResult.text() : `[DEBUG ERROR] git show ${from}:${file} failed: ${beforeResult.stderr.toString()}`
|
||||
|
||||
const afterResult = isBinaryFile
|
||||
? { exitCode: 0, text: () => "", stderr: Buffer.from("") }
|
||||
: await $`git -c core.autocrlf=false --git-dir ${git} --work-tree ${Instance.worktree} show ${to}:${file}`
|
||||
.quiet()
|
||||
.nothrow()
|
||||
.text()
|
||||
.quiet()
|
||||
.nothrow()
|
||||
const after = afterResult.exitCode === 0 ? afterResult.text() : `[DEBUG ERROR] git show ${to}:${file} failed: ${afterResult.stderr.toString()}`
|
||||
const added = isBinaryFile ? 0 : parseInt(additions)
|
||||
const deleted = isBinaryFile ? 0 : parseInt(deletions)
|
||||
result.push({
|
||||
@@ -229,6 +232,69 @@ export namespace Snapshot {
|
||||
return result
|
||||
}
|
||||
|
||||
export function unquote(path: string): string {
|
||||
// If the path is wrapped in quotes, it might contain octal escapes
|
||||
if (path.startsWith('"') && path.endsWith('"')) {
|
||||
const quoted = path.slice(1, -1)
|
||||
// Decode escaped characters
|
||||
const buffer: number[] = []
|
||||
for (let i = 0; i < quoted.length; i++) {
|
||||
if (quoted[i] === "\\") {
|
||||
i++
|
||||
// Check for octal escape (e.g. \344)
|
||||
if (i + 2 < quoted.length && /^[0-7]{3}$/.test(quoted.slice(i, i + 3))) {
|
||||
const octal = quoted.slice(i, i + 3)
|
||||
buffer.push(parseInt(octal, 8))
|
||||
i += 2
|
||||
} else {
|
||||
// Handle standard escapes
|
||||
switch (quoted[i]) {
|
||||
case "b":
|
||||
buffer.push(8)
|
||||
break
|
||||
case "t":
|
||||
buffer.push(9)
|
||||
break
|
||||
case "n":
|
||||
buffer.push(10)
|
||||
break
|
||||
case "v":
|
||||
buffer.push(11)
|
||||
break
|
||||
case "f":
|
||||
buffer.push(12)
|
||||
break
|
||||
case "r":
|
||||
buffer.push(13)
|
||||
break
|
||||
case '"':
|
||||
buffer.push(34)
|
||||
break
|
||||
case "\\":
|
||||
buffer.push(92)
|
||||
break
|
||||
default:
|
||||
// If unknown escape, keep original (or char code of escaped char)
|
||||
buffer.push(quoted.charCodeAt(i))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const charCode = quoted.charCodeAt(i)
|
||||
if (charCode < 128) {
|
||||
buffer.push(charCode)
|
||||
} else {
|
||||
const charBuffer = Buffer.from(quoted[i])
|
||||
for (const byte of charBuffer) {
|
||||
buffer.push(byte)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Buffer.from(buffer).toString("utf8")
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
function gitdir() {
|
||||
const project = Instance.project
|
||||
return path.join(Global.Path.data, "snapshot", project.id)
|
||||
|
||||
Reference in New Issue
Block a user