Last active
August 16, 2024 08:22
-
-
Save mizchi/aaf5c8f306e7d1817133c85a154858af to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
USAGE: deno run -A git-aware-cp.ts [srcs...] dest | |
EXAMPLE: deno run -A git-aware-cp.ts *.ts lib.ts examples /tmp/out | |
backupper (main)🔥 🦕 | |
$ tree ./examples | |
./examples | |
├── ignored-dir | |
│ ├── a | |
│ ├── examples | |
│ │ ├── newdir | |
│ │ │ └── xxx | |
│ │ └── y | |
│ ├── find-git.ts | |
│ ├── git-copy.ts | |
│ ├── lib.ts | |
│ ├── main.ts | |
│ └── main_test.ts | |
├── nested | |
├── newdir | |
│ └── xxx | |
├── other-git | |
│ ├── hello | |
│ └── more-git | |
│ └── stuff | |
├── out.log | |
├── x | |
└── y | |
8 directories, 14 files | |
backupper (main)🔥 🦕 | |
$ git ls-files examples | |
examples/.gitignore | |
examples/other-git | |
examples/y | |
backupper (main)🔥 🦕 | |
$ deno run -A lib.ts *.ts lib.ts examples /tmp/out | |
[COPY] find-git.ts /tmp/out/find-git.ts | |
[COPY] git-copy.ts /tmp/out/git-copy.ts | |
[COPY] lib.ts /tmp/out/lib.ts | |
[COPY] main.ts /tmp/out/main.ts | |
[COPY] main_test.ts /tmp/out/main_test.ts | |
[COPY] examples/newdir/xxx /tmp/out/examples/newdir/xxx | |
[COPY] examples/.gitignore /tmp/out/examples/.gitignore | |
[COPY] examples/y /tmp/out/examples/y | |
[WARING] ignored by git roots [ "examples/other-git" ] | |
*/ | |
import * as path from "jsr:@std/path@1.0.2"; | |
import { $ } from "jsr:@david/dax@0.40.0"; | |
const DEBUG = Deno.env.get("DEBUG") === "true"; | |
$.setPrintCommand(DEBUG); | |
const debug = DEBUG ? console.log : () => { }; | |
// list files in git | |
export async function listGitLsFiles(baseDir: string, src: string) { | |
const files = await $`git ls-files ${src}`.lines(); | |
return files.map((f: string) => path.join(baseDir, f)); | |
} | |
// check target is includable in git | |
export async function isPathGitIncluded(relpath: string) { | |
const r = await $`git check-ignore ${relpath}`.noThrow().stdout("piped"); | |
return r.code === 0; | |
} | |
export async function isGitRoot(dir: string): Promise<boolean> { | |
try { | |
const _stat = await Deno.stat(dir); // throw if not exists | |
const gitDir = await Deno.stat(path.join(dir, ".git")); | |
return gitDir.isDirectory; | |
} catch { | |
return false; | |
} | |
} | |
export async function listCopyFiles(root: string, fpath: string, files: Set<string>, gitRoots: Set<string>): Promise<void> { | |
// if path is ignored by git, skip | |
if (await isPathGitIncluded(fpath)) { | |
debug(`[SKIP] ${fpath} is ignored by git`); | |
return; | |
} | |
const stat = await Deno.stat(fpath); | |
if (stat.isFile) { | |
files.add(fpath); | |
return; | |
} | |
// recursively list files | |
if (stat.isDirectory) { | |
for await (const entry of Deno.readDir(fpath)) { | |
const childPath = path.join(fpath, entry.name); | |
if (await isGitRoot(childPath)) { | |
gitRoots.add(childPath); | |
continue; | |
} | |
await listCopyFiles(root, childPath, files, gitRoots); | |
} | |
} | |
} | |
// console.log(`[INFO] Copying files to ${dest}`); | |
async function normalizePath(root: string, fpath: string) { | |
if (path.isAbsolute(fpath)) { | |
return fpath; | |
} | |
return path.join(root, fpath); | |
} | |
if (import.meta.main) { | |
const root = path.resolve(Deno.cwd()); | |
const srcs = Deno.args.slice(0, -1); | |
const dest = Deno.args.at(-1)!; | |
const files = new Set<string>(); | |
const gitRoots = new Set<string>(); | |
debug(`[INFO] Listing files in ${srcs}`, root); | |
for (const src of srcs) { | |
await listCopyFiles(root, src, files, gitRoots); | |
} | |
// ensure dest is directory | |
try { | |
await Deno.mkdir(dest, { recursive: true }); | |
} catch (err) { | |
console.error(`[ERROR] Failed to create directory ${dest}`); | |
console.error(err); | |
Deno.exit(1); | |
} | |
for (const src of files) { | |
const srcFullPath = await normalizePath(root, src); | |
const outFullPath = await normalizePath(root, path.join(dest, src)); | |
const outDir = path.dirname(outFullPath); | |
try { | |
await Deno.mkdir(outDir, { recursive: true }); | |
} catch (err) { | |
console.error(`[ERROR] Failed to create directory ${outDir}`); | |
console.error(err); | |
Deno.exit(1); | |
} | |
await Deno.copyFile(srcFullPath, outFullPath); | |
console.log(`[COPY] ${src} ${outFullPath}`); | |
} | |
// report ignored git roots | |
if (gitRoots.size > 0) { | |
console.log("[WARING] ignored by git roots", [...gitRoots]); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment