shell-dsl 0.0.33 → 0.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +23 -2
  2. package/dist/cjs/package.json +1 -1
  3. package/dist/cjs/src/index.cjs.map +1 -1
  4. package/dist/cjs/src/vcs/diff.cjs +46 -41
  5. package/dist/cjs/src/vcs/diff.cjs.map +3 -3
  6. package/dist/cjs/src/vcs/index.cjs.map +1 -1
  7. package/dist/cjs/src/vcs/rules.cjs +180 -0
  8. package/dist/cjs/src/vcs/rules.cjs.map +10 -0
  9. package/dist/cjs/src/vcs/snapshot.cjs +137 -21
  10. package/dist/cjs/src/vcs/snapshot.cjs.map +3 -3
  11. package/dist/cjs/src/vcs/vcs.cjs +48 -31
  12. package/dist/cjs/src/vcs/vcs.cjs.map +3 -3
  13. package/dist/cjs/src/vcs/walk.cjs +24 -18
  14. package/dist/cjs/src/vcs/walk.cjs.map +3 -3
  15. package/dist/mjs/package.json +1 -1
  16. package/dist/mjs/src/index.mjs.map +1 -1
  17. package/dist/mjs/src/vcs/diff.mjs +46 -41
  18. package/dist/mjs/src/vcs/diff.mjs.map +3 -3
  19. package/dist/mjs/src/vcs/index.mjs.map +1 -1
  20. package/dist/mjs/src/vcs/rules.mjs +140 -0
  21. package/dist/mjs/src/vcs/rules.mjs.map +10 -0
  22. package/dist/mjs/src/vcs/snapshot.mjs +138 -22
  23. package/dist/mjs/src/vcs/snapshot.mjs.map +3 -3
  24. package/dist/mjs/src/vcs/vcs.mjs +48 -31
  25. package/dist/mjs/src/vcs/vcs.mjs.map +3 -3
  26. package/dist/mjs/src/vcs/walk.mjs +24 -18
  27. package/dist/mjs/src/vcs/walk.mjs.map +3 -3
  28. package/dist/types/src/index.d.ts +1 -1
  29. package/dist/types/src/vcs/diff.d.ts +3 -2
  30. package/dist/types/src/vcs/index.d.ts +1 -1
  31. package/dist/types/src/vcs/rules.d.ts +23 -0
  32. package/dist/types/src/vcs/snapshot.d.ts +7 -1
  33. package/dist/types/src/vcs/types.d.ts +26 -1
  34. package/dist/types/src/vcs/vcs.d.ts +2 -2
  35. package/dist/types/src/vcs/walk.d.ts +15 -3
  36. package/package.json +1 -1
package/README.md CHANGED
@@ -750,6 +750,8 @@ const fs = new FileSystem("/", {}, createWebUnderlyingFS(root));
750
750
 
751
751
  `VersionControlSystem` adds git-like version control to any `VirtualFS`. It tracks changes as diffs, supports branching, and stores metadata in a `.vcs` directory.
752
752
 
753
+ Ignore and attribute rules are configured directly on the constructor:
754
+
753
755
  ```ts
754
756
  import { VersionControlSystem, createVirtualFS } from "shell-dsl";
755
757
  import { createFsFromVolume, Volume } from "memfs";
@@ -764,9 +766,25 @@ const fs = createVirtualFS(createFsFromVolume(vol));
764
766
  const vcs = new VersionControlSystem({
765
767
  fs,
766
768
  path: "/project",
769
+ ignore: ["dist", "*.log"],
770
+ attributes: [
771
+ { pattern: "assets/*.png", diff: "binary" },
772
+ { pattern: "secrets/**", diff: "none" },
773
+ ],
767
774
  });
768
775
  ```
769
776
 
777
+ Ignore patterns apply only to untracked paths:
778
+
779
+ - Ignored untracked files are skipped by `status()` and full `commit()`
780
+ - Files already tracked by VCS remain tracked even if they later match an ignore rule
781
+ - Full `checkout()` preserves ignored untracked files
782
+
783
+ Attribute rules are applied in declaration order, with later matches winning. Supported properties:
784
+
785
+ - `binary?: boolean`
786
+ - `diff?: "text" | "binary" | "none"`
787
+
770
788
  ### Committing Changes
771
789
 
772
790
  ```ts
@@ -784,10 +802,13 @@ await vcs.commit("update src only", { paths: ["/src/**"] });
784
802
  ```ts
785
803
  const changes = await vcs.status();
786
804
  for (const entry of changes) {
787
- console.log(entry.type, entry.path); // "add" | "modify" | "delete"
805
+ console.log(entry.type, entry.path, entry.diff, entry.binary);
806
+ // "add" | "modify" | "delete", "text" | "binary" | "none", boolean
788
807
  }
789
808
  ```
790
809
 
810
+ When `diff` is `"none"`, the entry still reports the path and change type, but omits `content` and `previousContent`.
811
+
791
812
  ### Checkout
792
813
 
793
814
  ```ts
@@ -826,7 +847,7 @@ const filtered = await vcs.log({ path: "src/index.ts", limit: 10 });
826
847
  // Diff between two revisions
827
848
  const diff = await vcs.diff(1, 2);
828
849
  for (const entry of diff) {
829
- console.log(entry.type, entry.path); // "add" | "modify" | "delete"
850
+ console.log(entry.type, entry.path, entry.diff);
830
851
  }
831
852
 
832
853
  // Current HEAD info
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "shell-dsl",
3
- "version": "0.0.33",
3
+ "version": "0.0.34",
4
4
  "type": "commonjs"
5
5
  }
@@ -2,7 +2,7 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../src/index.ts"],
4
4
  "sourcesContent": [
5
- "// Main class exports\nexport { ShellDSL, createShellDSL, type Program } from \"./shell-dsl.cjs\";\nexport { ShellPromise, type ShellPromiseOptions } from \"./shell-promise.cjs\";\n\n// Types\nexport type {\n VirtualFS,\n FileStat,\n Command,\n CommandContext,\n Stdin,\n Stdout,\n Stderr,\n OutputCollector,\n ExecResult,\n ShellConfig,\n RawValue,\n} from \"./types.cjs\";\nexport { isRawValue } from \"./types.cjs\";\n\n// Errors\nexport { ShellError, LexError, ParseError } from \"./errors.cjs\";\n\n// Lexer\nexport { Lexer, lex, tokenToString } from \"./lexer/index.cjs\";\nexport type { Token, RedirectMode } from \"./lexer/index.cjs\";\n\n// Parser\nexport { Parser, parse } from \"./parser/index.cjs\";\nexport type {\n ASTNode,\n Redirect,\n CommandNode,\n PipelineNode,\n AndNode,\n OrNode,\n SequenceNode,\n LiteralNode,\n VariableNode,\n SubstitutionNode,\n GlobNode,\n ConcatNode,\n IfNode,\n ForNode,\n WhileNode,\n UntilNode,\n CaseNode,\n CaseClause,\n ArithmeticNode,\n} from \"./parser/index.cjs\";\nexport {\n isCommandNode,\n isPipelineNode,\n isAndNode,\n isOrNode,\n isSequenceNode,\n isLiteralNode,\n isVariableNode,\n isSubstitutionNode,\n isGlobNode,\n isConcatNode,\n isIfNode,\n isForNode,\n isWhileNode,\n isUntilNode,\n isCaseNode,\n isArithmeticNode,\n} from \"./parser/index.cjs\";\n\n// Interpreter\nexport { Interpreter, type InterpreterOptions, BreakException, ContinueException } from \"./interpreter/index.cjs\";\n\n// Filesystem\nexport { createVirtualFS } from \"./fs/index.cjs\";\nexport {\n FileSystem,\n ReadOnlyFileSystem,\n WebFileSystem,\n createWebUnderlyingFS,\n type PathOps,\n type Permission,\n type PermissionRules,\n type UnderlyingFS,\n} from \"./fs/index.cjs\";\n\n// I/O\nexport { createStdin, StdinImpl } from \"./io/index.cjs\";\nexport { createStdout, createStderr, createPipe, OutputCollectorImpl, PipeBuffer } from \"./io/index.cjs\";\n\n// Utilities\nexport { escape, escapeForInterpolation, globVirtualFS } from \"./utils/index.cjs\";\nexport type { GlobVirtualFS, GlobOptions } from \"./utils/index.cjs\";\n\n// Version Control\nexport { VersionControlSystem } from \"./vcs/index.cjs\";\nexport type {\n VCSConfig,\n Revision,\n DiffEntry,\n TreeManifest,\n FileEntry,\n CommitOptions,\n CheckoutOptions,\n LogOptions,\n LogEntry,\n BranchInfo,\n} from \"./vcs/index.cjs\";\n"
5
+ "// Main class exports\nexport { ShellDSL, createShellDSL, type Program } from \"./shell-dsl.cjs\";\nexport { ShellPromise, type ShellPromiseOptions } from \"./shell-promise.cjs\";\n\n// Types\nexport type {\n VirtualFS,\n FileStat,\n Command,\n CommandContext,\n Stdin,\n Stdout,\n Stderr,\n OutputCollector,\n ExecResult,\n ShellConfig,\n RawValue,\n} from \"./types.cjs\";\nexport { isRawValue } from \"./types.cjs\";\n\n// Errors\nexport { ShellError, LexError, ParseError } from \"./errors.cjs\";\n\n// Lexer\nexport { Lexer, lex, tokenToString } from \"./lexer/index.cjs\";\nexport type { Token, RedirectMode } from \"./lexer/index.cjs\";\n\n// Parser\nexport { Parser, parse } from \"./parser/index.cjs\";\nexport type {\n ASTNode,\n Redirect,\n CommandNode,\n PipelineNode,\n AndNode,\n OrNode,\n SequenceNode,\n LiteralNode,\n VariableNode,\n SubstitutionNode,\n GlobNode,\n ConcatNode,\n IfNode,\n ForNode,\n WhileNode,\n UntilNode,\n CaseNode,\n CaseClause,\n ArithmeticNode,\n} from \"./parser/index.cjs\";\nexport {\n isCommandNode,\n isPipelineNode,\n isAndNode,\n isOrNode,\n isSequenceNode,\n isLiteralNode,\n isVariableNode,\n isSubstitutionNode,\n isGlobNode,\n isConcatNode,\n isIfNode,\n isForNode,\n isWhileNode,\n isUntilNode,\n isCaseNode,\n isArithmeticNode,\n} from \"./parser/index.cjs\";\n\n// Interpreter\nexport { Interpreter, type InterpreterOptions, BreakException, ContinueException } from \"./interpreter/index.cjs\";\n\n// Filesystem\nexport { createVirtualFS } from \"./fs/index.cjs\";\nexport {\n FileSystem,\n ReadOnlyFileSystem,\n WebFileSystem,\n createWebUnderlyingFS,\n type PathOps,\n type Permission,\n type PermissionRules,\n type UnderlyingFS,\n} from \"./fs/index.cjs\";\n\n// I/O\nexport { createStdin, StdinImpl } from \"./io/index.cjs\";\nexport { createStdout, createStderr, createPipe, OutputCollectorImpl, PipeBuffer } from \"./io/index.cjs\";\n\n// Utilities\nexport { escape, escapeForInterpolation, globVirtualFS } from \"./utils/index.cjs\";\nexport type { GlobVirtualFS, GlobOptions } from \"./utils/index.cjs\";\n\n// Version Control\nexport { VersionControlSystem } from \"./vcs/index.cjs\";\nexport type {\n VCSConfig,\n VCSAttributeRule,\n VCSResolvedAttributes,\n VCSDiffMode,\n Revision,\n DiffEntry,\n TreeManifest,\n TreeEntry,\n FileEntry,\n DirectoryEntry,\n CommitOptions,\n CheckoutOptions,\n LogOptions,\n LogEntry,\n BranchInfo,\n} from \"./vcs/index.cjs\";\n"
6
6
  ],
7
7
  "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACuD,IAAvD;AACuD,IAAvD;AAgB2B,IAA3B;AAGiD,IAAjD;AAG0C,IAA1C;AAI8B,IAA9B;AAuCO,IAjBP;AAoBwF,IAAxF;AAGgC,IAAhC;AAUO,IATP;AAYuC,IAAvC;AACwF,IAAxF;AAG8D,IAA9D;AAIqC,IAArC;",
8
8
  "debugId": "27F0973A44539CA064756E2164756E21",
@@ -43,60 +43,65 @@ __export(exports_diff, {
43
43
  diffManifests: () => diffManifests
44
44
  });
45
45
  module.exports = __toCommonJS(exports_diff);
46
- var import_walk = require("./walk.cjs");
47
- function diffManifests(before, after) {
46
+ var import_rules = require("./rules.cjs");
47
+ var import_snapshot = require("./snapshot.cjs");
48
+ function diffManifests(before, after, rules = new import_rules.VCSRules) {
48
49
  const entries = [];
49
50
  const allPaths = new Set([...Object.keys(before), ...Object.keys(after)]);
50
51
  for (const path of allPaths) {
51
52
  const prev = before[path];
52
53
  const curr = after[path];
53
54
  if (!prev && curr) {
54
- entries.push({ type: "add", path, content: curr.content });
55
+ entries.push(createDiffEntry("add", path, curr, undefined, rules));
55
56
  } else if (prev && !curr) {
56
- entries.push({ type: "delete", path, previousContent: prev.content });
57
- } else if (prev && curr && prev.content !== curr.content) {
58
- entries.push({
59
- type: "modify",
60
- path,
61
- content: curr.content,
62
- previousContent: prev.content
63
- });
57
+ entries.push(createDiffEntry("delete", path, undefined, prev, rules));
58
+ } else if (prev && curr && !entriesEqual(prev, curr)) {
59
+ entries.push(createDiffEntry("modify", path, curr, prev, rules));
64
60
  }
65
61
  }
66
62
  return entries.sort((a, b) => a.path.localeCompare(b.path));
67
63
  }
68
- async function diffWorkingTree(fs, rootPath, manifest, exclude = []) {
69
- const entries = [];
70
- const workingFiles = await import_walk.walkTree(fs, rootPath, exclude);
71
- const manifestPaths = new Set(Object.keys(manifest));
72
- const seenPaths = new Set;
73
- for (const relPath of workingFiles) {
74
- seenPaths.add(relPath);
75
- const fullPath = fs.resolve(rootPath, relPath);
76
- const content = await fs.readFile(fullPath);
77
- const b64 = Buffer.from(content).toString("base64");
78
- const prev = manifest[relPath];
79
- if (!prev) {
80
- entries.push({ type: "add", path: relPath, content: b64 });
81
- } else if (prev.content !== b64) {
82
- entries.push({
83
- type: "modify",
84
- path: relPath,
85
- content: b64,
86
- previousContent: prev.content
87
- });
64
+ async function diffWorkingTree(fs, rootPath, manifest, rules = new import_rules.VCSRules({ internalDirName: ".vcs" })) {
65
+ const workingManifest = await import_snapshot.buildTreeManifest(fs, rootPath, {
66
+ rules,
67
+ trackedPaths: Object.keys(manifest)
68
+ });
69
+ return diffManifests(manifest, workingManifest, rules);
70
+ }
71
+ function createDiffEntry(type, path, current, previous, rules) {
72
+ const attributes = rules.resolveAttributes(path);
73
+ const entryKind = getEntryKind(current ?? previous);
74
+ const previousEntryKind = previous ? getEntryKind(previous) : undefined;
75
+ const entry = {
76
+ type,
77
+ path,
78
+ binary: attributes.binary,
79
+ diff: attributes.diff,
80
+ entryKind,
81
+ previousEntryKind
82
+ };
83
+ if (attributes.diff !== "none") {
84
+ if (isFileEntry(current)) {
85
+ entry.content = current.content;
88
86
  }
89
- }
90
- for (const manifestPath of manifestPaths) {
91
- if (!seenPaths.has(manifestPath)) {
92
- entries.push({
93
- type: "delete",
94
- path: manifestPath,
95
- previousContent: manifest[manifestPath].content
96
- });
87
+ if (isFileEntry(previous)) {
88
+ entry.previousContent = previous.content;
97
89
  }
98
90
  }
99
- return entries.sort((a, b) => a.path.localeCompare(b.path));
91
+ return entry;
92
+ }
93
+ function entriesEqual(a, b) {
94
+ if (getEntryKind(a) !== getEntryKind(b))
95
+ return false;
96
+ if (!isFileEntry(a) || !isFileEntry(b))
97
+ return true;
98
+ return a.content === b.content;
99
+ }
100
+ function getEntryKind(entry) {
101
+ return entry?.kind === "directory" ? "directory" : "file";
102
+ }
103
+ function isFileEntry(entry) {
104
+ return !!entry && entry.kind !== "directory";
100
105
  }
101
106
 
102
- //# debugId=C182915799ABD69764756E2164756E21
107
+ //# debugId=F0849DE3546011FF64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/vcs/diff.ts"],
4
4
  "sourcesContent": [
5
- "import type { VirtualFS } from \"../types.cjs\";\nimport type { TreeManifest, DiffEntry } from \"./types.cjs\";\nimport { walkTree } from \"./walk.cjs\";\n\n/**\n * Compute diff entries between two tree manifests.\n */\nexport function diffManifests(\n before: TreeManifest,\n after: TreeManifest,\n): DiffEntry[] {\n const entries: DiffEntry[] = [];\n const allPaths = new Set([...Object.keys(before), ...Object.keys(after)]);\n\n for (const path of allPaths) {\n const prev = before[path];\n const curr = after[path];\n\n if (!prev && curr) {\n entries.push({ type: \"add\", path, content: curr.content });\n } else if (prev && !curr) {\n entries.push({ type: \"delete\", path, previousContent: prev.content });\n } else if (prev && curr && prev.content !== curr.content) {\n entries.push({\n type: \"modify\",\n path,\n content: curr.content,\n previousContent: prev.content,\n });\n }\n }\n\n return entries.sort((a, b) => a.path.localeCompare(b.path));\n}\n\n/**\n * Compute diff between a tree manifest and the current working tree.\n */\nexport async function diffWorkingTree(\n fs: VirtualFS,\n rootPath: string,\n manifest: TreeManifest,\n exclude: string[] = [],\n): Promise<DiffEntry[]> {\n const entries: DiffEntry[] = [];\n const workingFiles = await walkTree(fs, rootPath, exclude);\n const manifestPaths = new Set(Object.keys(manifest));\n const seenPaths = new Set<string>();\n\n for (const relPath of workingFiles) {\n seenPaths.add(relPath);\n const fullPath = fs.resolve(rootPath, relPath);\n const content = await fs.readFile(fullPath);\n const b64 = Buffer.from(content).toString(\"base64\");\n\n const prev = manifest[relPath];\n if (!prev) {\n entries.push({ type: \"add\", path: relPath, content: b64 });\n } else if (prev.content !== b64) {\n entries.push({\n type: \"modify\",\n path: relPath,\n content: b64,\n previousContent: prev.content,\n });\n }\n }\n\n for (const manifestPath of manifestPaths) {\n if (!seenPaths.has(manifestPath)) {\n entries.push({\n type: \"delete\",\n path: manifestPath,\n previousContent: manifest[manifestPath]!.content,\n });\n }\n }\n\n return entries.sort((a, b) => a.path.localeCompare(b.path));\n}\n"
5
+ "import type { VirtualFS } from \"../types.cjs\";\nimport type { TreeManifest, DiffEntry, TreeEntry } from \"./types.cjs\";\nimport { VCSRules } from \"./rules.cjs\";\nimport { buildTreeManifest } from \"./snapshot.cjs\";\n\n/**\n * Compute diff entries between two tree manifests.\n */\nexport function diffManifests(\n before: TreeManifest,\n after: TreeManifest,\n rules: VCSRules = new VCSRules(),\n): DiffEntry[] {\n const entries: DiffEntry[] = [];\n const allPaths = new Set([...Object.keys(before), ...Object.keys(after)]);\n\n for (const path of allPaths) {\n const prev = before[path];\n const curr = after[path];\n\n if (!prev && curr) {\n entries.push(createDiffEntry(\"add\", path, curr, undefined, rules));\n } else if (prev && !curr) {\n entries.push(createDiffEntry(\"delete\", path, undefined, prev, rules));\n } else if (prev && curr && !entriesEqual(prev, curr)) {\n entries.push(createDiffEntry(\"modify\", path, curr, prev, rules));\n }\n }\n\n return entries.sort((a, b) => a.path.localeCompare(b.path));\n}\n\n/**\n * Compute diff between a tree manifest and the current working tree.\n */\nexport async function diffWorkingTree(\n fs: VirtualFS,\n rootPath: string,\n manifest: TreeManifest,\n rules: VCSRules = new VCSRules({ internalDirName: \".vcs\" }),\n): Promise<DiffEntry[]> {\n const workingManifest = await buildTreeManifest(fs, rootPath, {\n rules,\n trackedPaths: Object.keys(manifest),\n });\n return diffManifests(manifest, workingManifest, rules);\n}\n\nfunction createDiffEntry(\n type: DiffEntry[\"type\"],\n path: string,\n current: TreeEntry | undefined,\n previous: TreeEntry | undefined,\n rules: VCSRules,\n): DiffEntry {\n const attributes = rules.resolveAttributes(path);\n const entryKind = getEntryKind(current ?? previous);\n const previousEntryKind = previous ? getEntryKind(previous) : undefined;\n const entry: DiffEntry = {\n type,\n path,\n binary: attributes.binary,\n diff: attributes.diff,\n entryKind,\n previousEntryKind,\n };\n\n if (attributes.diff !== \"none\") {\n if (isFileEntry(current)) {\n entry.content = current.content;\n }\n if (isFileEntry(previous)) {\n entry.previousContent = previous.content;\n }\n }\n\n return entry;\n}\n\nfunction entriesEqual(a: TreeEntry, b: TreeEntry): boolean {\n if (getEntryKind(a) !== getEntryKind(b)) return false;\n if (!isFileEntry(a) || !isFileEntry(b)) return true;\n return a.content === b.content;\n}\n\nfunction getEntryKind(entry: TreeEntry | undefined): \"file\" | \"directory\" {\n return entry?.kind === \"directory\" ? \"directory\" : \"file\";\n}\n\nfunction isFileEntry(entry: TreeEntry | undefined): entry is Extract<TreeEntry, { kind?: \"file\" }> {\n return !!entry && entry.kind !== \"directory\";\n}\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEyB,IAAzB;AAKO,SAAS,aAAa,CAC3B,QACA,OACa;AAAA,EACb,MAAM,UAAuB,CAAC;AAAA,EAC9B,MAAM,WAAW,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,CAAC,CAAC;AAAA,EAExE,WAAW,QAAQ,UAAU;AAAA,IAC3B,MAAM,OAAO,OAAO;AAAA,IACpB,MAAM,OAAO,MAAM;AAAA,IAEnB,IAAI,CAAC,QAAQ,MAAM;AAAA,MACjB,QAAQ,KAAK,EAAE,MAAM,OAAO,MAAM,SAAS,KAAK,QAAQ,CAAC;AAAA,IAC3D,EAAO,SAAI,QAAQ,CAAC,MAAM;AAAA,MACxB,QAAQ,KAAK,EAAE,MAAM,UAAU,MAAM,iBAAiB,KAAK,QAAQ,CAAC;AAAA,IACtE,EAAO,SAAI,QAAQ,QAAQ,KAAK,YAAY,KAAK,SAAS;AAAA,MACxD,QAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN;AAAA,QACA,SAAS,KAAK;AAAA,QACd,iBAAiB,KAAK;AAAA,MACxB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,OAAO,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAAA;AAM5D,eAAsB,eAAe,CACnC,IACA,UACA,UACA,UAAoB,CAAC,GACC;AAAA,EACtB,MAAM,UAAuB,CAAC;AAAA,EAC9B,MAAM,eAAe,MAAM,qBAAS,IAAI,UAAU,OAAO;AAAA,EACzD,MAAM,gBAAgB,IAAI,IAAI,OAAO,KAAK,QAAQ,CAAC;AAAA,EACnD,MAAM,YAAY,IAAI;AAAA,EAEtB,WAAW,WAAW,cAAc;AAAA,IAClC,UAAU,IAAI,OAAO;AAAA,IACrB,MAAM,WAAW,GAAG,QAAQ,UAAU,OAAO;AAAA,IAC7C,MAAM,UAAU,MAAM,GAAG,SAAS,QAAQ;AAAA,IAC1C,MAAM,MAAM,OAAO,KAAK,OAAO,EAAE,SAAS,QAAQ;AAAA,IAElD,MAAM,OAAO,SAAS;AAAA,IACtB,IAAI,CAAC,MAAM;AAAA,MACT,QAAQ,KAAK,EAAE,MAAM,OAAO,MAAM,SAAS,SAAS,IAAI,CAAC;AAAA,IAC3D,EAAO,SAAI,KAAK,YAAY,KAAK;AAAA,MAC/B,QAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,QACT,iBAAiB,KAAK;AAAA,MACxB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,WAAW,gBAAgB,eAAe;AAAA,IACxC,IAAI,CAAC,UAAU,IAAI,YAAY,GAAG;AAAA,MAChC,QAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,MAAM;AAAA,QACN,iBAAiB,SAAS,cAAe;AAAA,MAC3C,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,OAAO,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAAA;",
8
- "debugId": "C182915799ABD69764756E2164756E21",
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEyB,IAAzB;AACkC,IAAlC;AAKO,SAAS,aAAa,CAC3B,QACA,OACA,QAAkB,IAAI,uBACT;AAAA,EACb,MAAM,UAAuB,CAAC;AAAA,EAC9B,MAAM,WAAW,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,CAAC,CAAC;AAAA,EAExE,WAAW,QAAQ,UAAU;AAAA,IAC3B,MAAM,OAAO,OAAO;AAAA,IACpB,MAAM,OAAO,MAAM;AAAA,IAEnB,IAAI,CAAC,QAAQ,MAAM;AAAA,MACjB,QAAQ,KAAK,gBAAgB,OAAO,MAAM,MAAM,WAAW,KAAK,CAAC;AAAA,IACnE,EAAO,SAAI,QAAQ,CAAC,MAAM;AAAA,MACxB,QAAQ,KAAK,gBAAgB,UAAU,MAAM,WAAW,MAAM,KAAK,CAAC;AAAA,IACtE,EAAO,SAAI,QAAQ,QAAQ,CAAC,aAAa,MAAM,IAAI,GAAG;AAAA,MACpD,QAAQ,KAAK,gBAAgB,UAAU,MAAM,MAAM,MAAM,KAAK,CAAC;AAAA,IACjE;AAAA,EACF;AAAA,EAEA,OAAO,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAAA;AAM5D,eAAsB,eAAe,CACnC,IACA,UACA,UACA,QAAkB,IAAI,sBAAS,EAAE,iBAAiB,OAAO,CAAC,GACpC;AAAA,EACtB,MAAM,kBAAkB,MAAM,kCAAkB,IAAI,UAAU;AAAA,IAC5D;AAAA,IACA,cAAc,OAAO,KAAK,QAAQ;AAAA,EACpC,CAAC;AAAA,EACD,OAAO,cAAc,UAAU,iBAAiB,KAAK;AAAA;AAGvD,SAAS,eAAe,CACtB,MACA,MACA,SACA,UACA,OACW;AAAA,EACX,MAAM,aAAa,MAAM,kBAAkB,IAAI;AAAA,EAC/C,MAAM,YAAY,aAAa,WAAW,QAAQ;AAAA,EAClD,MAAM,oBAAoB,WAAW,aAAa,QAAQ,IAAI;AAAA,EAC9D,MAAM,QAAmB;AAAA,IACvB;AAAA,IACA;AAAA,IACA,QAAQ,WAAW;AAAA,IACnB,MAAM,WAAW;AAAA,IACjB;AAAA,IACA;AAAA,EACF;AAAA,EAEA,IAAI,WAAW,SAAS,QAAQ;AAAA,IAC9B,IAAI,YAAY,OAAO,GAAG;AAAA,MACxB,MAAM,UAAU,QAAQ;AAAA,IAC1B;AAAA,IACA,IAAI,YAAY,QAAQ,GAAG;AAAA,MACzB,MAAM,kBAAkB,SAAS;AAAA,IACnC;AAAA,EACF;AAAA,EAEA,OAAO;AAAA;AAGT,SAAS,YAAY,CAAC,GAAc,GAAuB;AAAA,EACzD,IAAI,aAAa,CAAC,MAAM,aAAa,CAAC;AAAA,IAAG,OAAO;AAAA,EAChD,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC;AAAA,IAAG,OAAO;AAAA,EAC/C,OAAO,EAAE,YAAY,EAAE;AAAA;AAGzB,SAAS,YAAY,CAAC,OAAoD;AAAA,EACxE,OAAO,OAAO,SAAS,cAAc,cAAc;AAAA;AAGrD,SAAS,WAAW,CAAC,OAA8E;AAAA,EACjG,OAAO,CAAC,CAAC,SAAS,MAAM,SAAS;AAAA;",
8
+ "debugId": "F0849DE3546011FF64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -2,7 +2,7 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/vcs/index.ts"],
4
4
  "sourcesContent": [
5
- "export { VersionControlSystem } from \"./vcs.cjs\";\nexport type {\n VCSConfig,\n Revision,\n DiffEntry,\n TreeManifest,\n FileEntry,\n CommitOptions,\n CheckoutOptions,\n LogOptions,\n LogEntry,\n BranchInfo,\n} from \"./types.cjs\";\n"
5
+ "export { VersionControlSystem } from \"./vcs.cjs\";\nexport type {\n VCSConfig,\n VCSAttributeRule,\n VCSResolvedAttributes,\n VCSDiffMode,\n Revision,\n DiffEntry,\n TreeManifest,\n TreeEntry,\n FileEntry,\n DirectoryEntry,\n CommitOptions,\n CheckoutOptions,\n LogOptions,\n LogEntry,\n BranchInfo,\n} from \"./types.cjs\";\n"
6
6
  ],
7
7
  "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAqC,IAArC;",
8
8
  "debugId": "882B2013CEEDB5D864756E2164756E21",
@@ -0,0 +1,180 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ function __accessProp(key) {
6
+ return this[key];
7
+ }
8
+ var __toCommonJS = (from) => {
9
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
10
+ if (entry)
11
+ return entry;
12
+ entry = __defProp({}, "__esModule", { value: true });
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (var key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(entry, key))
16
+ __defProp(entry, key, {
17
+ get: __accessProp.bind(from, key),
18
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
19
+ });
20
+ }
21
+ __moduleCache.set(from, entry);
22
+ return entry;
23
+ };
24
+ var __moduleCache;
25
+ var __returnValue = (v) => v;
26
+ function __exportSetter(name, newValue) {
27
+ this[name] = __returnValue.bind(null, newValue);
28
+ }
29
+ var __export = (target, all) => {
30
+ for (var name in all)
31
+ __defProp(target, name, {
32
+ get: all[name],
33
+ enumerable: true,
34
+ configurable: true,
35
+ set: __exportSetter.bind(all, name)
36
+ });
37
+ };
38
+
39
+ // src/vcs/rules.ts
40
+ var exports_rules = {};
41
+ __export(exports_rules, {
42
+ matchVCSPath: () => matchVCSPath,
43
+ VCSRules: () => VCSRules
44
+ });
45
+ module.exports = __toCommonJS(exports_rules);
46
+ var import_match = require("./match.cjs");
47
+
48
+ class VCSRules {
49
+ internalPath;
50
+ ignorePatterns;
51
+ attributeRules;
52
+ constructor(config = {}) {
53
+ this.internalPath = normalizePath(config.internalPath ?? config.internalDirName ?? "");
54
+ this.ignorePatterns = [...config.ignore ?? []];
55
+ this.attributeRules = [...config.attributes ?? []];
56
+ }
57
+ isInternalPath(relPath) {
58
+ if (!this.internalPath)
59
+ return false;
60
+ const normalizedPath = normalizePath(relPath);
61
+ if (!normalizedPath)
62
+ return false;
63
+ return normalizedPath === this.internalPath || normalizedPath.startsWith(`${this.internalPath}/`);
64
+ }
65
+ isIgnored(relPath) {
66
+ if (this.isInternalPath(relPath))
67
+ return true;
68
+ return this.ignorePatterns.some((pattern) => matchVCSPath(pattern, relPath));
69
+ }
70
+ shouldEnterDirectory(relPath, trackedPaths) {
71
+ if (this.isInternalPath(relPath))
72
+ return false;
73
+ if (!this.isIgnored(relPath))
74
+ return true;
75
+ return hasTrackedPathAtOrBelow(relPath, trackedPaths);
76
+ }
77
+ shouldIncludeWorkingFile(relPath, trackedPaths) {
78
+ if (this.isInternalPath(relPath))
79
+ return false;
80
+ if (trackedPaths.has(relPath))
81
+ return true;
82
+ return !this.isIgnored(relPath);
83
+ }
84
+ shouldIncludeEmptyDirectory(relPath, trackedPaths) {
85
+ if (this.isInternalPath(relPath))
86
+ return false;
87
+ if (trackedPaths.has(relPath))
88
+ return true;
89
+ return !this.isIgnored(relPath);
90
+ }
91
+ shouldIncludeRestoreScanFile(relPath) {
92
+ return !this.isInternalPath(relPath);
93
+ }
94
+ shouldPreserveUntrackedIgnored(relPath, trackedPaths) {
95
+ if (trackedPaths.has(relPath))
96
+ return false;
97
+ return this.isIgnored(relPath);
98
+ }
99
+ resolveAttributes(relPath) {
100
+ let binary = false;
101
+ let diff = "text";
102
+ for (const rule of this.attributeRules) {
103
+ if (!matchVCSPath(rule.pattern, relPath))
104
+ continue;
105
+ if (rule.binary !== undefined) {
106
+ binary = rule.binary;
107
+ }
108
+ if (rule.diff !== undefined) {
109
+ diff = rule.diff;
110
+ }
111
+ }
112
+ if (diff === "binary") {
113
+ binary = true;
114
+ }
115
+ return { binary, diff };
116
+ }
117
+ }
118
+ function matchVCSPath(pattern, relPath) {
119
+ const normalizedPattern = normalizePattern(pattern);
120
+ const normalizedPath = normalizePath(relPath);
121
+ if (!normalizedPattern || !normalizedPath)
122
+ return false;
123
+ if (normalizedPattern.mode === "root-path") {
124
+ return import_match.matchGlobPath(normalizedPattern.pattern, normalizedPath);
125
+ }
126
+ if (normalizedPattern.mode === "root-segment") {
127
+ const [firstSegment] = normalizedPath.split("/");
128
+ return firstSegment ? import_match.matchGlobPath(normalizedPattern.pattern, firstSegment) : false;
129
+ }
130
+ if (normalizedPattern.mode === "root-prefix") {
131
+ return import_match.matchGlobPath(normalizedPattern.pattern, normalizedPath) || import_match.matchGlobPath(`${normalizedPattern.pattern}/**`, normalizedPath);
132
+ }
133
+ const segments = normalizedPath.split("/");
134
+ return segments.some((segment) => import_match.matchGlobPath(normalizedPattern.pattern, segment));
135
+ }
136
+ function normalizePath(relPath) {
137
+ return relPath.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+$/, "");
138
+ }
139
+ function normalizePattern(pattern) {
140
+ let normalized = pattern.trim();
141
+ if (!normalized)
142
+ return null;
143
+ normalized = normalized.replace(/\\/g, "/");
144
+ const anchored = normalized.startsWith("/");
145
+ const directoryOnly = normalized.endsWith("/");
146
+ normalized = normalized.replace(/^\/+/, "").replace(/\/+$/, "");
147
+ if (!normalized)
148
+ return null;
149
+ if (normalized.includes("/")) {
150
+ return {
151
+ pattern: normalized,
152
+ mode: directoryOnly ? "root-prefix" : "root-path"
153
+ };
154
+ }
155
+ if (anchored) {
156
+ return {
157
+ pattern: normalized,
158
+ mode: "root-segment"
159
+ };
160
+ }
161
+ return {
162
+ pattern: normalized,
163
+ mode: "segment"
164
+ };
165
+ }
166
+ function hasTrackedPathAtOrBelow(relPath, trackedPaths) {
167
+ const normalizedPath = normalizePath(relPath);
168
+ if (!normalizedPath)
169
+ return false;
170
+ const prefix = normalizedPath + "/";
171
+ for (const trackedPath of trackedPaths) {
172
+ const normalizedTrackedPath = normalizePath(trackedPath);
173
+ if (normalizedTrackedPath === normalizedPath || normalizedTrackedPath.startsWith(prefix)) {
174
+ return true;
175
+ }
176
+ }
177
+ return false;
178
+ }
179
+
180
+ //# debugId=FD722B600498944364756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/vcs/rules.ts"],
4
+ "sourcesContent": [
5
+ "import { matchGlobPath } from \"./match.cjs\";\nimport type { VCSAttributeRule, VCSResolvedAttributes } from \"./types.cjs\";\n\ninterface VCSRulesConfig {\n internalPath?: string;\n internalDirName?: string;\n ignore?: string[];\n attributes?: VCSAttributeRule[];\n}\n\nexport class VCSRules {\n private readonly internalPath: string;\n private readonly ignorePatterns: string[];\n private readonly attributeRules: VCSAttributeRule[];\n\n constructor(config: VCSRulesConfig = {}) {\n this.internalPath = normalizePath(config.internalPath ?? config.internalDirName ?? \"\");\n this.ignorePatterns = [...(config.ignore ?? [])];\n this.attributeRules = [...(config.attributes ?? [])];\n }\n\n isInternalPath(relPath: string): boolean {\n if (!this.internalPath) return false;\n const normalizedPath = normalizePath(relPath);\n if (!normalizedPath) return false;\n return normalizedPath === this.internalPath || normalizedPath.startsWith(`${this.internalPath}/`);\n }\n\n isIgnored(relPath: string): boolean {\n if (this.isInternalPath(relPath)) return true;\n return this.ignorePatterns.some((pattern) => matchVCSPath(pattern, relPath));\n }\n\n shouldEnterDirectory(relPath: string, trackedPaths: Iterable<string>): boolean {\n if (this.isInternalPath(relPath)) return false;\n if (!this.isIgnored(relPath)) return true;\n return hasTrackedPathAtOrBelow(relPath, trackedPaths);\n }\n\n shouldIncludeWorkingFile(relPath: string, trackedPaths: ReadonlySet<string>): boolean {\n if (this.isInternalPath(relPath)) return false;\n if (trackedPaths.has(relPath)) return true;\n return !this.isIgnored(relPath);\n }\n\n shouldIncludeEmptyDirectory(relPath: string, trackedPaths: ReadonlySet<string>): boolean {\n if (this.isInternalPath(relPath)) return false;\n if (trackedPaths.has(relPath)) return true;\n return !this.isIgnored(relPath);\n }\n\n shouldIncludeRestoreScanFile(relPath: string): boolean {\n return !this.isInternalPath(relPath);\n }\n\n shouldPreserveUntrackedIgnored(relPath: string, trackedPaths: ReadonlySet<string>): boolean {\n if (trackedPaths.has(relPath)) return false;\n return this.isIgnored(relPath);\n }\n\n resolveAttributes(relPath: string): VCSResolvedAttributes {\n let binary = false;\n let diff: VCSResolvedAttributes[\"diff\"] = \"text\";\n\n for (const rule of this.attributeRules) {\n if (!matchVCSPath(rule.pattern, relPath)) continue;\n if (rule.binary !== undefined) {\n binary = rule.binary;\n }\n if (rule.diff !== undefined) {\n diff = rule.diff;\n }\n }\n\n if (diff === \"binary\") {\n binary = true;\n }\n\n return { binary, diff };\n }\n}\n\nexport function matchVCSPath(pattern: string, relPath: string): boolean {\n const normalizedPattern = normalizePattern(pattern);\n const normalizedPath = normalizePath(relPath);\n\n if (!normalizedPattern || !normalizedPath) return false;\n\n if (normalizedPattern.mode === \"root-path\") {\n return matchGlobPath(normalizedPattern.pattern, normalizedPath);\n }\n\n if (normalizedPattern.mode === \"root-segment\") {\n const [firstSegment] = normalizedPath.split(\"/\");\n return firstSegment ? matchGlobPath(normalizedPattern.pattern, firstSegment) : false;\n }\n\n if (normalizedPattern.mode === \"root-prefix\") {\n return (\n matchGlobPath(normalizedPattern.pattern, normalizedPath) ||\n matchGlobPath(`${normalizedPattern.pattern}/**`, normalizedPath)\n );\n }\n\n const segments = normalizedPath.split(\"/\");\n return segments.some((segment) => matchGlobPath(normalizedPattern.pattern, segment));\n}\n\nfunction normalizePath(relPath: string): string {\n return relPath.replace(/\\\\/g, \"/\").replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n}\n\nfunction normalizePattern(pattern: string): {\n pattern: string;\n mode: \"root-path\" | \"root-prefix\" | \"root-segment\" | \"segment\";\n} | null {\n let normalized = pattern.trim();\n if (!normalized) return null;\n\n normalized = normalized.replace(/\\\\/g, \"/\");\n const anchored = normalized.startsWith(\"/\");\n const directoryOnly = normalized.endsWith(\"/\");\n normalized = normalized.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n if (!normalized) return null;\n\n if (normalized.includes(\"/\")) {\n return {\n pattern: normalized,\n mode: directoryOnly ? \"root-prefix\" : \"root-path\",\n };\n }\n\n if (anchored) {\n return {\n pattern: normalized,\n mode: \"root-segment\",\n };\n }\n\n return {\n pattern: normalized,\n mode: \"segment\",\n };\n}\n\nfunction hasTrackedPathAtOrBelow(relPath: string, trackedPaths: Iterable<string>): boolean {\n const normalizedPath = normalizePath(relPath);\n if (!normalizedPath) return false;\n\n const prefix = normalizedPath + \"/\";\n for (const trackedPath of trackedPaths) {\n const normalizedTrackedPath = normalizePath(trackedPath);\n if (\n normalizedTrackedPath === normalizedPath ||\n normalizedTrackedPath.startsWith(prefix)\n ) {\n return true;\n }\n }\n return false;\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "FD722B600498944364756E2164756E21",
9
+ "names": []
10
+ }
@@ -44,15 +44,27 @@ __export(exports_snapshot, {
44
44
  buildPartialManifest: () => buildPartialManifest
45
45
  });
46
46
  module.exports = __toCommonJS(exports_snapshot);
47
+ var import_rules = require("./rules.cjs");
47
48
  var import_walk = require("./walk.cjs");
48
- async function buildTreeManifest(fs, rootPath, exclude = []) {
49
+ async function buildTreeManifest(fs, rootPath, options) {
49
50
  const manifest = {};
50
- const files = await import_walk.walkTree(fs, rootPath, exclude);
51
- for (const relPath of files) {
52
- const fullPath = fs.resolve(rootPath, relPath);
51
+ const rules = options?.rules ?? new import_rules.VCSRules({ internalDirName: ".vcs" });
52
+ const trackedPaths = new Set(options?.trackedPaths ?? []);
53
+ const entries = await import_walk.walkTreeEntries(fs, rootPath, {
54
+ enterDirectory: (relPath) => rules.shouldEnterDirectory(relPath, trackedPaths),
55
+ includeFile: (relPath) => rules.shouldIncludeWorkingFile(relPath, trackedPaths),
56
+ includeDirectory: (relPath, info) => info.empty && rules.shouldIncludeEmptyDirectory(relPath, trackedPaths)
57
+ });
58
+ for (const entry of entries) {
59
+ if (entry.kind === "directory") {
60
+ manifest[entry.path] = { kind: "directory", size: 0 };
61
+ continue;
62
+ }
63
+ const fullPath = fs.resolve(rootPath, entry.path);
53
64
  const content = await fs.readFile(fullPath);
54
65
  const buf = Buffer.from(content);
55
- manifest[relPath] = {
66
+ manifest[entry.path] = {
67
+ kind: "file",
56
68
  content: buf.toString("base64"),
57
69
  size: buf.length
58
70
  };
@@ -66,11 +78,19 @@ async function buildPartialManifest(fs, rootPath, paths) {
66
78
  if (!await fs.exists(fullPath))
67
79
  continue;
68
80
  const stat = await fs.stat(fullPath);
81
+ if (stat.isDirectory()) {
82
+ const entries = await fs.readdir(fullPath);
83
+ if (entries.length === 0) {
84
+ manifest[relPath] = { kind: "directory", size: 0 };
85
+ }
86
+ continue;
87
+ }
69
88
  if (!stat.isFile())
70
89
  continue;
71
90
  const content = await fs.readFile(fullPath);
72
91
  const buf = Buffer.from(content);
73
92
  manifest[relPath] = {
93
+ kind: "file",
74
94
  content: buf.toString("base64"),
75
95
  size: buf.length
76
96
  };
@@ -79,18 +99,55 @@ async function buildPartialManifest(fs, rootPath, paths) {
79
99
  }
80
100
  async function restoreTree(fs, rootPath, manifest, options) {
81
101
  const fullRestore = options?.fullRestore ?? false;
82
- const filterPaths = options?.paths ? new Set(options.paths) : null;
83
- for (const [relPath, entry] of Object.entries(manifest)) {
84
- if (filterPaths && !filterPaths.has(relPath))
102
+ const rules = options?.rules ?? new import_rules.VCSRules({ internalDirName: ".vcs" });
103
+ const trackedPaths = new Set(options?.trackedPaths ?? []);
104
+ const scopePatterns = options?.paths ?? null;
105
+ const scopedEntries = Object.entries(manifest).filter(([relPath]) => isPathInScope(relPath, scopePatterns)).sort(([a], [b]) => a.localeCompare(b));
106
+ const targetPaths = new Set(scopedEntries.map(([relPath]) => relPath));
107
+ const requiredDirectories = collectRequiredDirectories(scopedEntries);
108
+ const shouldDeleteExtras = fullRestore || scopePatterns !== null;
109
+ if (shouldDeleteExtras) {
110
+ const currentEntries = await import_walk.walkTreeEntries(fs, rootPath, {
111
+ enterDirectory: (relPath) => !rules.isInternalPath(relPath),
112
+ includeFile: (relPath) => rules.shouldIncludeRestoreScanFile(relPath),
113
+ includeDirectory: () => true
114
+ });
115
+ for (const current of currentEntries) {
116
+ if (current.kind !== "file")
117
+ continue;
118
+ if (!isPathInScope(current.path, scopePatterns))
119
+ continue;
120
+ if (targetPaths.has(current.path))
121
+ continue;
122
+ if (rules.shouldPreserveUntrackedIgnored(current.path, trackedPaths))
123
+ continue;
124
+ await fs.rm(fs.resolve(rootPath, current.path));
125
+ }
126
+ }
127
+ for (const directory of [...requiredDirectories].sort(comparePathDepth)) {
128
+ await ensureDirectoryExists(fs, fs.resolve(rootPath, directory));
129
+ }
130
+ for (const [relPath, entry] of scopedEntries) {
131
+ if (isDirectoryEntry(entry)) {
132
+ await ensureDirectoryExists(fs, fs.resolve(rootPath, relPath));
85
133
  continue;
134
+ }
86
135
  await writeFileFromEntry(fs, rootPath, relPath, entry);
87
136
  }
88
- if (fullRestore) {
89
- const exclude = getVcsDirName(rootPath);
90
- const currentFiles = await import_walk.walkTree(fs, rootPath, exclude ? [exclude] : []);
91
- for (const relPath of currentFiles) {
92
- if (!manifest[relPath]) {
93
- const fullPath = fs.resolve(rootPath, relPath);
137
+ if (shouldDeleteExtras) {
138
+ const currentEntries = await import_walk.walkTreeEntries(fs, rootPath, {
139
+ enterDirectory: (relPath) => !rules.isInternalPath(relPath),
140
+ includeFile: () => true,
141
+ includeDirectory: () => true
142
+ });
143
+ const directories = currentEntries.filter((entry) => entry.kind === "directory").map((entry) => entry.path).filter((relPath) => isPathInScope(relPath, scopePatterns)).sort((a, b) => comparePathDepth(b, a));
144
+ for (const relPath of directories) {
145
+ if (requiredDirectories.has(relPath))
146
+ continue;
147
+ if (rules.shouldPreserveUntrackedIgnored(relPath, trackedPaths))
148
+ continue;
149
+ const fullPath = fs.resolve(rootPath, relPath);
150
+ if (await isEmptyDirectory(fs, fullPath)) {
94
151
  await fs.rm(fullPath);
95
152
  }
96
153
  }
@@ -98,15 +155,74 @@ async function restoreTree(fs, rootPath, manifest, options) {
98
155
  }
99
156
  async function writeFileFromEntry(fs, rootPath, relPath, entry) {
100
157
  const fullPath = fs.resolve(rootPath, relPath);
101
- const dir = fs.dirname(fullPath);
102
- if (!await fs.exists(dir)) {
103
- await fs.mkdir(dir, { recursive: true });
104
- }
158
+ await ensureDirectoryExists(fs, fs.dirname(fullPath));
159
+ await removeDirectoryAtPath(fs, fullPath);
105
160
  const buf = Buffer.from(entry.content, "base64");
106
161
  await fs.writeFile(fullPath, buf);
107
162
  }
108
- function getVcsDirName(_rootPath) {
109
- return ".vcs";
163
+ function isDirectoryEntry(entry) {
164
+ return entry.kind === "directory";
165
+ }
166
+ function isPathInScope(relPath, patterns) {
167
+ if (!patterns || patterns.length === 0)
168
+ return true;
169
+ return patterns.some((pattern) => import_rules.matchVCSPath(pattern, relPath));
170
+ }
171
+ function collectRequiredDirectories(entries) {
172
+ const directories = new Set;
173
+ for (const [relPath, entry] of entries) {
174
+ if (isDirectoryEntry(entry)) {
175
+ directories.add(relPath);
176
+ }
177
+ for (const parent of parentDirectories(relPath)) {
178
+ directories.add(parent);
179
+ }
180
+ }
181
+ return directories;
182
+ }
183
+ function parentDirectories(relPath) {
184
+ const parts = relPath.split("/").filter(Boolean);
185
+ const parents = [];
186
+ for (let i = 1;i < parts.length; i++) {
187
+ parents.push(parts.slice(0, i).join("/"));
188
+ }
189
+ return parents;
190
+ }
191
+ function comparePathDepth(a, b) {
192
+ const depthA = a.split("/").filter(Boolean).length;
193
+ const depthB = b.split("/").filter(Boolean).length;
194
+ if (depthA !== depthB)
195
+ return depthA - depthB;
196
+ return a.localeCompare(b);
197
+ }
198
+ async function ensureDirectoryExists(fs, dirPath) {
199
+ const parent = fs.dirname(dirPath);
200
+ if (parent !== dirPath) {
201
+ await ensureDirectoryExists(fs, parent);
202
+ }
203
+ if (await fs.exists(dirPath)) {
204
+ const stat = await fs.stat(dirPath);
205
+ if (stat.isDirectory())
206
+ return;
207
+ await fs.rm(dirPath, { recursive: true, force: true });
208
+ }
209
+ await fs.mkdir(dirPath, { recursive: true });
210
+ }
211
+ async function removeDirectoryAtPath(fs, path) {
212
+ if (!await fs.exists(path))
213
+ return;
214
+ const stat = await fs.stat(path);
215
+ if (stat.isDirectory()) {
216
+ await fs.rm(path, { recursive: true, force: true });
217
+ }
218
+ }
219
+ async function isEmptyDirectory(fs, dirPath) {
220
+ try {
221
+ const entries = await fs.readdir(dirPath);
222
+ return entries.length === 0;
223
+ } catch {
224
+ return false;
225
+ }
110
226
  }
111
227
 
112
- //# debugId=E0FC26F624360D1C64756E2164756E21
228
+ //# debugId=7E15F1F035E56CC164756E2164756E21