This commit is contained in:
@@ -195,7 +195,20 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({
|
|||||||
const root = directory()
|
const root = directory()
|
||||||
const prefix = root.endsWith("/") ? root : root + "/"
|
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)) {
|
if (path.startsWith(prefix)) {
|
||||||
path = path.slice(prefix.length)
|
path = path.slice(prefix.length)
|
||||||
@@ -218,7 +231,8 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({
|
|||||||
|
|
||||||
function tab(input: string) {
|
function tab(input: string) {
|
||||||
const path = normalize(input)
|
const path = normalize(input)
|
||||||
return `file://${path}`
|
const encoded = path.split("/").map(encodeURIComponent).join("/")
|
||||||
|
return `file://${encoded}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function pathFromTab(tabValue: string) {
|
function pathFromTab(tabValue: string) {
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ export namespace Snapshot {
|
|||||||
.split("\n")
|
.split("\n")
|
||||||
.map((x) => x.trim())
|
.map((x) => x.trim())
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
.map((x) => unquote(x))
|
||||||
.map((x) => path.join(Instance.worktree, x)),
|
.map((x) => path.join(Instance.worktree, x)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,7 +152,7 @@ export namespace Snapshot {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
log.info("file did not exist in snapshot, deleting", { file })
|
log.info("file did not exist in snapshot, deleting", { file })
|
||||||
await fs.unlink(file).catch(() => {})
|
await fs.unlink(file).catch(() => { })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
files.add(file)
|
files.add(file)
|
||||||
@@ -202,20 +203,22 @@ export namespace Snapshot {
|
|||||||
.nothrow()
|
.nothrow()
|
||||||
.lines()) {
|
.lines()) {
|
||||||
if (!line) continue
|
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 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}`
|
: await $`git -c core.autocrlf=false --git-dir ${git} --work-tree ${Instance.worktree} show ${from}:${file}`
|
||||||
.quiet()
|
.quiet()
|
||||||
.nothrow()
|
.nothrow()
|
||||||
.text()
|
const before = beforeResult.exitCode === 0 ? beforeResult.text() : `[DEBUG ERROR] git show ${from}:${file} failed: ${beforeResult.stderr.toString()}`
|
||||||
const after = isBinaryFile
|
|
||||||
? ""
|
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}`
|
: await $`git -c core.autocrlf=false --git-dir ${git} --work-tree ${Instance.worktree} show ${to}:${file}`
|
||||||
.quiet()
|
.quiet()
|
||||||
.nothrow()
|
.nothrow()
|
||||||
.text()
|
const after = afterResult.exitCode === 0 ? afterResult.text() : `[DEBUG ERROR] git show ${to}:${file} failed: ${afterResult.stderr.toString()}`
|
||||||
const added = isBinaryFile ? 0 : parseInt(additions)
|
const added = isBinaryFile ? 0 : parseInt(additions)
|
||||||
const deleted = isBinaryFile ? 0 : parseInt(deletions)
|
const deleted = isBinaryFile ? 0 : parseInt(deletions)
|
||||||
result.push({
|
result.push({
|
||||||
@@ -229,6 +232,69 @@ export namespace Snapshot {
|
|||||||
return result
|
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() {
|
function gitdir() {
|
||||||
const project = Instance.project
|
const project = Instance.project
|
||||||
return path.join(Global.Path.data, "snapshot", project.id)
|
return path.join(Global.Path.data, "snapshot", project.id)
|
||||||
|
|||||||
Reference in New Issue
Block a user