|
@@ -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()
|
|
|
|
|
- .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}`
|
|
: 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 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)
|