|
// Find and recover the first/last entry from VSCode edit history |
|
|
|
import fs from "fs-extra"; |
|
import os from "os"; |
|
import path from "path"; |
|
|
|
const recoverDir = "/path/to/recover" |
|
|
|
// Path to search for history files |
|
const prevRoot = "file://" + recoverDir; |
|
// Path to save recovered files |
|
const nextRoot = "./recovered"; |
|
|
|
// VSCode history directory |
|
// TODO: make this cross-platform (currently only works on MacOS) |
|
const historyDir = os.homedir() + "/Library/Application Support/Code/User/History"; |
|
|
|
// List of files found with prevRoot |
|
const historyEntries = {}; |
|
|
|
// Read all edit history metadata files (entries.json) |
|
await Promise.all( |
|
fs.readdirSync(historyDir).map(async (d) => { |
|
const file = `${historyDir}/${d}/entries.json`; |
|
if (!fs.existsSync(file)) return; |
|
const metadata = await fs.readJSON(file); |
|
if (metadata && metadata.resource.startsWith(prevRoot)) { |
|
historyEntries[d] = { |
|
...metadata, |
|
entries: metadata.entries.sort((a, b) => a.timestamp - b.timestamp), |
|
}; |
|
} |
|
}) |
|
); |
|
|
|
console.log(`Found ${Object.keys(historyEntries).length} files with edit history:`); |
|
console.log( |
|
Object.values(historyEntries) |
|
.map((e) => String(e.resource).substring(prevRoot.length)) |
|
.sort() |
|
.join("\n") |
|
); |
|
|
|
// Copy last/first entries to nextRoot |
|
await Promise.all( |
|
Object.entries(historyEntries).map(async ([fileId, { resource, entries }]) => { |
|
const getPathMap = (entry) => [ |
|
path.join(historyDir, fileId, entry.id), |
|
path.join(nextRoot, resource.substring(prevRoot.length)), |
|
]; |
|
|
|
// find the last entry |
|
const [prevPath, nextPath] = getPathMap(entries.at(-1)); |
|
|
|
// copy the last entry to current dir |
|
fs.copy(prevPath, nextPath, (err) => { |
|
if (err) console.error(`Error copying ${prevPath} -> ${nextPath}:`, err); |
|
}); |
|
|
|
// check whether there is more then 1 entry |
|
if (entries.length <= 1) return; |
|
|
|
// find the first entry (intial state) |
|
let [prevPathInit, nextPathInit] = getPathMap(entries.at(0)); |
|
|
|
// check whether the file has any content |
|
const stats = fs.statSync(prevPathInit); |
|
if (stats.size === 0) return; |
|
|
|
const parsed = path.parse(nextPathInit); |
|
nextPathInit = path.join(parsed.dir, parsed.name + "_init" + parsed.ext); |
|
|
|
fs.copy(prevPathInit, nextPathInit, (err) => { |
|
if (err) console.error(`Error copying ${prevPathInit} -> ${nextPathInit}:`, err); |
|
}); |
|
}) |
|
); |