"use strict"; import { parallel } from "async"; import { EventEmitter } from "events"; import { mkdir, writeFile } from "fs"; import { Commit, Tree, TreeEntry } from "nodegit"; import { join } from "path"; import { createCopier } from "recursive-tree-copy"; interface ISource { gitTree: Tree; name: string; } interface ISimpleCallback { (err?: any): void; } interface INewTreeCallback { (err: any, newTree?: string): void; } const safeGetEntries = (tree: ISource, callback: (err: any, entries?: TreeEntry[]) => void) => { try { return callback(null, tree.gitTree.entries()); } catch (err) { return callback(err); } }; const gitToFsCopier = createCopier({ concurrency: 4, copyLeaf: (entry: TreeEntry, targetDir: string, callback: ISimpleCallback) => { const targetPath = join(targetDir, entry.name()); entry.getBlob((err, blob) => { if (err || !blob) { return callback(err); } return writeFile(targetPath, blob.content(), callback); }); }, createTargetTree: (tree: ISource, targetDir: string, callback: INewTreeCallback) => { const targetSubdir = join(targetDir, tree.name); mkdir(targetSubdir, (err) => { // Workaround for broken trees if (err && err.code !== "EEXIST") { return callback(err); } return callback(null, targetSubdir); }); }, finalizeTargetTree: (_targetSubdir: string, callback: ISimpleCallback) => callback(), walkSourceTree: (tree: ISource) => { const emitter = new EventEmitter(); process.nextTick(() => safeGetEntries(tree, (getEntriesErr, entries) => { if (getEntriesErr || !entries) { return emitter.emit("error", getEntriesErr); } return parallel(entries.map((entry) => (callback: ISimpleCallback) => { if (entry.isTree()) { return entry.getTree((getTreeErr, subTree) => { if (getTreeErr) { return callback(getTreeErr); } emitter.emit("tree", { gitTree: subTree, name: entry.name(), }); return callback(); }); } if (entry.isFile()) { emitter.emit("leaf", entry); return callback(); } return callback(); }), (parallelErr) => { if (parallelErr) { return emitter.emit("error", parallelErr); } return emitter.emit("done"); }); })); return emitter; }, }); export const gitToFs = (commit: Commit, exportDir: string, callback: ISimpleCallback) => commit.getTree().then( (tree) => gitToFsCopier.copy({ gitTree: tree, name: ".", }, exportDir, callback), (err) => callback(err), );