nano-git 0.3.4 → 0.3.6

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.
@@ -0,0 +1,20 @@
1
+ import { Database, Statement } from "bun:sqlite";
2
+
3
+ //#region src/backend/sqlite-pool.d.ts
4
+ /** 连接池获取结果 */
5
+ interface SqliteConnectionHandle {
6
+ readonly db: Database;
7
+ /** 释放连接(引用计数减一,归零时关闭数据库) */
8
+ readonly release: () => void;
9
+ /**
10
+ * 获取缓存的 prepared statement
11
+ *
12
+ * 相同 SQL 文本返回同一实例,避免重复编译。
13
+ * 绑定参数类型由调用方按 SQL 保证一致,此处不做泛型约束。
14
+ */
15
+ readonly prepare: <T>(sql: string) => Statement<T, any[]>;
16
+ /** 支持 `using` 语法自动释放 */
17
+ [Symbol.dispose](): void;
18
+ }
19
+ //#endregion
20
+ export { SqliteConnectionHandle };
@@ -0,0 +1,95 @@
1
+ import { Database } from "bun:sqlite";
2
+ //#region src/backend/sqlite-pool.ts
3
+ /**
4
+ * SQLite 连接池(引用计数)
5
+ *
6
+ * 缓存 Database 实例,相同 dbPath 复用同一连接,
7
+ * 防止反复打开同一数据库文件带来的开销。
8
+ *
9
+ * 每个 acquire 对应一个 release,当引用计数归零时自动关闭连接。
10
+ * 返回的 handle 实现了 [Symbol.dispose](),可用 `using` 语法自动释放。
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * using _conn = acquireConnection("/tmp/repo.sqlite");
15
+ * // 作用域结束时自动释放
16
+ * ```
17
+ */
18
+ const pool = /* @__PURE__ */ new Map();
19
+ /** 构造连接句柄 */
20
+ function makeHandle(db, stmts, onRelease) {
21
+ return {
22
+ db,
23
+ release: onRelease,
24
+ prepare(sql) {
25
+ let stmt = stmts.get(sql);
26
+ if (stmt === void 0) {
27
+ stmt = db.query(sql);
28
+ stmts.set(sql, stmt);
29
+ }
30
+ return stmt;
31
+ },
32
+ [Symbol.dispose]() {
33
+ onRelease();
34
+ }
35
+ };
36
+ }
37
+ /**
38
+ * 从全局连接池获取或创建 SQLite 数据库连接
39
+ *
40
+ * 相同 dbPath 返回同一个 Database 实例,通过引用计数管理生命周期。
41
+ * 首次打开时根据 walMode 执行 PRAGMA journal_mode = WAL。
42
+ * 后续打开(缓存命中)忽略 walMode 参数。
43
+ *
44
+ * 注意:`:memory:` 和 `file:xxx?mode=memory` 等内存数据库不会被缓存,
45
+ * 每次调用都会创建独立连接(SQLite 的 :memory: 为每个连接独立)。
46
+ */
47
+ function acquireConnection(dbPath, walMode = true) {
48
+ if (dbPath === ":memory:" || dbPath.startsWith("file:") && dbPath.includes("mode=memory")) {
49
+ const db = new Database(dbPath);
50
+ if (walMode) db.run("PRAGMA journal_mode = WAL");
51
+ const localCache = /* @__PURE__ */ new Map();
52
+ let released = false;
53
+ return makeHandle(db, localCache, () => {
54
+ if (released) return;
55
+ released = true;
56
+ db.close();
57
+ });
58
+ }
59
+ const existing = pool.get(dbPath);
60
+ if (existing !== void 0) {
61
+ existing.refCount++;
62
+ let released = false;
63
+ return makeHandle(existing.db, existing.stmts, () => {
64
+ if (released) return;
65
+ released = true;
66
+ releaseConnection(dbPath);
67
+ });
68
+ }
69
+ const db = new Database(dbPath);
70
+ if (walMode) db.run("PRAGMA journal_mode = WAL");
71
+ const stmts = /* @__PURE__ */ new Map();
72
+ pool.set(dbPath, {
73
+ db,
74
+ refCount: 1,
75
+ stmts
76
+ });
77
+ let released = false;
78
+ return makeHandle(db, stmts, () => {
79
+ if (released) return;
80
+ released = true;
81
+ releaseConnection(dbPath);
82
+ });
83
+ }
84
+ /** 内部释放逻辑 */
85
+ function releaseConnection(dbPath) {
86
+ const entry = pool.get(dbPath);
87
+ if (entry === void 0) return;
88
+ entry.refCount--;
89
+ if (entry.refCount <= 0) {
90
+ pool.delete(dbPath);
91
+ entry.db.close();
92
+ }
93
+ }
94
+ //#endregion
95
+ export { acquireConnection };
@@ -19,7 +19,8 @@ interface SqliteRepositoryBackend extends RepositoryBackend {
19
19
  /**
20
20
  * 创建基于 SQLite 文件的完整仓库后端
21
21
  *
22
- * 内部创建 Database 实例并组合三个子 store。
22
+ * 内部通过连接池获取 Database 实例并组合三个子 store。
23
+ * 相同 dbPath 复用同一连接,引用计数归零时自动关闭。
23
24
  * 返回的 backend 实现了 [Symbol.dispose](),可使用 `using` 语法
24
25
  * 或在不再使用时手动调用 `backend[Symbol.dispose]()`。
25
26
  *
@@ -2,7 +2,7 @@ import { HEADS_PREFIX, HEAD_REF } from "../core/types/refs.mjs";
2
2
  import { createSqliteObjectStore } from "../odb/sqlite.mjs";
3
3
  import { createSqliteRefStore } from "../refs/sqlite.mjs";
4
4
  import { createSqliteShallowStore } from "../refs/shallow/sqlite.mjs";
5
- import { Database } from "bun:sqlite";
5
+ import { acquireConnection } from "./sqlite-pool.mjs";
6
6
  //#region src/backend/sqlite.ts
7
7
  /**
8
8
  * 基于 SQLite 的仓库后端
@@ -12,11 +12,15 @@ import { Database } from "bun:sqlite";
12
12
  *
13
13
  * 支持 [Symbol.dispose]() 释放数据库连接,
14
14
  * 可使用 `using` 语法管理生命周期。
15
+ *
16
+ * 数据库连接通过全局连接池管理(引用计数),
17
+ * 相同 dbPath 复用同一连接,避免反复打开同一个数据库文件。
15
18
  */
16
19
  /**
17
20
  * 创建基于 SQLite 文件的完整仓库后端
18
21
  *
19
- * 内部创建 Database 实例并组合三个子 store。
22
+ * 内部通过连接池获取 Database 实例并组合三个子 store。
23
+ * 相同 dbPath 复用同一连接,引用计数归零时自动关闭。
20
24
  * 返回的 backend 实现了 [Symbol.dispose](),可使用 `using` 语法
21
25
  * 或在不再使用时手动调用 `backend[Symbol.dispose]()`。
22
26
  *
@@ -37,21 +41,20 @@ import { Database } from "bun:sqlite";
37
41
  * ```
38
42
  */
39
43
  function createSqliteRepositoryBackend(dbPath, options = {}) {
40
- const db = new Database(dbPath);
41
- if (options.walMode !== false) db.run("PRAGMA journal_mode = WAL");
42
- db.run("CREATE TABLE IF NOT EXISTS objects (hash TEXT PRIMARY KEY, type TEXT NOT NULL, content BLOB NOT NULL)");
43
- db.run("CREATE TABLE IF NOT EXISTS refs (name TEXT PRIMARY KEY, target TEXT NOT NULL)");
44
- db.run("CREATE TABLE IF NOT EXISTS shallow (hash TEXT PRIMARY KEY)");
45
- db.run("INSERT OR IGNORE INTO refs (name, target) VALUES (?, ?)", [HEAD_REF, `ref: ${HEADS_PREFIX}main`]);
44
+ const conn = acquireConnection(dbPath, options.walMode !== false);
45
+ conn.db.run("CREATE TABLE IF NOT EXISTS objects (hash TEXT PRIMARY KEY, type TEXT NOT NULL, content BLOB NOT NULL)");
46
+ conn.db.run("CREATE TABLE IF NOT EXISTS refs (name TEXT PRIMARY KEY, target TEXT NOT NULL)");
47
+ conn.db.run("CREATE TABLE IF NOT EXISTS shallow (hash TEXT PRIMARY KEY)");
48
+ conn.db.run("INSERT OR IGNORE INTO refs (name, target) VALUES (?, ?)", [HEAD_REF, `ref: ${HEADS_PREFIX}main`]);
46
49
  return {
47
50
  gitDir: dbPath,
48
- objects: createSqliteObjectStore(db),
49
- refs: createSqliteRefStore(db),
50
- shallow: createSqliteShallowStore(db),
51
+ objects: createSqliteObjectStore(conn),
52
+ refs: createSqliteRefStore(conn),
53
+ shallow: createSqliteShallowStore(conn),
51
54
  packs: null,
52
- /** 释放 SQLite 数据库连接 */
55
+ /** 释放 SQLite 数据库连接(引用计数减一) */
53
56
  [Symbol.dispose]() {
54
- db.close();
57
+ conn.release();
55
58
  }
56
59
  };
57
60
  }
@@ -1,23 +1,23 @@
1
1
  import { ObjectDatabase } from "../core/types/odb.mjs";
2
- import { Database } from "bun:sqlite";
2
+ import { SqliteConnectionHandle } from "../backend/sqlite-pool.mjs";
3
3
 
4
4
  //#region src/odb/sqlite.d.ts
5
5
  /**
6
6
  * 创建基于 SQLite 的对象数据库
7
7
  *
8
- * @param db - 已打开的 bun:sqlite Database 实例
8
+ * @param conn - SQLite 连接池句柄(含 statement 缓存)
9
9
  * @returns 符合 ObjectDatabase 接口的存储后端
10
10
  *
11
11
  * @example
12
12
  * ```ts
13
- * import { Database } from "bun:sqlite";
14
- * const db = new Database("/tmp/repo.sqlite");
15
- * const store = createSqliteObjectStore(db);
13
+ * import { acquireConnection } from "nano-git/backend/sqlite";
14
+ * using conn = acquireConnection("/tmp/repo.sqlite");
15
+ * const store = createSqliteObjectStore(conn);
16
16
  *
17
17
  * store.ingest(raw);
18
18
  * const obj = store.read(hash);
19
19
  * ```
20
20
  */
21
- declare function createSqliteObjectStore(db: Database): ObjectDatabase;
21
+ declare function createSqliteObjectStore(conn: SqliteConnectionHandle): ObjectDatabase;
22
22
  //#endregion
23
23
  export { createSqliteObjectStore };
@@ -14,27 +14,27 @@ import { hashObject } from "../core/hash-digest.mjs";
14
14
  /**
15
15
  * 创建基于 SQLite 的对象数据库
16
16
  *
17
- * @param db - 已打开的 bun:sqlite Database 实例
17
+ * @param conn - SQLite 连接池句柄(含 statement 缓存)
18
18
  * @returns 符合 ObjectDatabase 接口的存储后端
19
19
  *
20
20
  * @example
21
21
  * ```ts
22
- * import { Database } from "bun:sqlite";
23
- * const db = new Database("/tmp/repo.sqlite");
24
- * const store = createSqliteObjectStore(db);
22
+ * import { acquireConnection } from "nano-git/backend/sqlite";
23
+ * using conn = acquireConnection("/tmp/repo.sqlite");
24
+ * const store = createSqliteObjectStore(conn);
25
25
  *
26
26
  * store.ingest(raw);
27
27
  * const obj = store.read(hash);
28
28
  * ```
29
29
  */
30
- function createSqliteObjectStore(db) {
31
- const selectStmt = db.query("SELECT hash, type, content FROM objects WHERE hash = ?");
32
- const existsStmt = db.query("SELECT 1 FROM objects WHERE hash = ?");
33
- const insertStmt = db.query("INSERT OR IGNORE INTO objects (hash, type, content) VALUES (?, ?, ?)");
34
- const deleteStmt = db.query("DELETE FROM objects WHERE hash = ?");
35
- const listStmt = db.query("SELECT hash FROM objects ORDER BY hash");
30
+ function createSqliteObjectStore(conn) {
31
+ const selectStmt = conn.prepare("SELECT hash, type, content FROM objects WHERE hash = ?");
32
+ const existsStmt = conn.prepare("SELECT 1 FROM objects WHERE hash = ?");
33
+ const insertStmt = conn.prepare("INSERT OR IGNORE INTO objects (hash, type, content) VALUES (?, ?, ?)");
34
+ const deleteStmt = conn.prepare("DELETE FROM objects WHERE hash = ?");
35
+ const listStmt = conn.prepare("SELECT hash FROM objects ORDER BY hash");
36
36
  /** 批量插入的事务包装 */
37
- const ingestManyTx = db.transaction((objects) => {
37
+ const ingestManyTx = conn.db.transaction((objects) => {
38
38
  for (const raw of objects) {
39
39
  const expectedHash = hashObject(raw.type, raw.content);
40
40
  if (expectedHash !== raw.hash) throw new Error(`RawGitObject hash mismatch: expected ${expectedHash}, got ${raw.hash}`);
@@ -1,23 +1,23 @@
1
1
  import { ShallowStore } from "../../core/types/shallow.mjs";
2
- import { Database } from "bun:sqlite";
2
+ import { SqliteConnectionHandle } from "../../backend/sqlite-pool.mjs";
3
3
 
4
4
  //#region src/refs/shallow/sqlite.d.ts
5
5
  /**
6
6
  * 创建基于 SQLite 的 shallow 边界存储
7
7
  *
8
- * @param db - 已打开的 bun:sqlite Database 实例
8
+ * @param conn - SQLite 连接池句柄(含 statement 缓存)
9
9
  * @returns 符合 ShallowStore 接口的存储后端
10
10
  *
11
11
  * @example
12
12
  * ```ts
13
- * import { Database } from "bun:sqlite";
14
- * const db = new Database("/tmp/repo.sqlite");
15
- * const store = createSqliteShallowStore(db);
13
+ * import { acquireConnection } from "nano-git/backend/sqlite";
14
+ * using conn = acquireConnection("/tmp/repo.sqlite");
15
+ * const store = createSqliteShallowStore(conn);
16
16
  *
17
17
  * store.write([hashA, hashB]);
18
18
  * console.log(store.isShallow(hashA)); // true
19
19
  * ```
20
20
  */
21
- declare function createSqliteShallowStore(db: Database): ShallowStore;
21
+ declare function createSqliteShallowStore(conn: SqliteConnectionHandle): ShallowStore;
22
22
  //#endregion
23
23
  export { createSqliteShallowStore };
@@ -13,32 +13,32 @@ import { sha1 } from "../../core/types.mjs";
13
13
  /**
14
14
  * 创建基于 SQLite 的 shallow 边界存储
15
15
  *
16
- * @param db - 已打开的 bun:sqlite Database 实例
16
+ * @param conn - SQLite 连接池句柄(含 statement 缓存)
17
17
  * @returns 符合 ShallowStore 接口的存储后端
18
18
  *
19
19
  * @example
20
20
  * ```ts
21
- * import { Database } from "bun:sqlite";
22
- * const db = new Database("/tmp/repo.sqlite");
23
- * const store = createSqliteShallowStore(db);
21
+ * import { acquireConnection } from "nano-git/backend/sqlite";
22
+ * using conn = acquireConnection("/tmp/repo.sqlite");
23
+ * const store = createSqliteShallowStore(conn);
24
24
  *
25
25
  * store.write([hashA, hashB]);
26
26
  * console.log(store.isShallow(hashA)); // true
27
27
  * ```
28
28
  */
29
- function createSqliteShallowStore(db) {
30
- const selectAllStmt = db.query("SELECT hash FROM shallow ORDER BY hash");
31
- const selectExistsStmt = db.query("SELECT 1 FROM shallow WHERE hash = ?");
32
- const deleteAllStmt = db.query("DELETE FROM shallow");
33
- const insertStmt = db.query("INSERT OR IGNORE INTO shallow (hash) VALUES (?)");
34
- const deleteOneStmt = db.query("DELETE FROM shallow WHERE hash = ?");
29
+ function createSqliteShallowStore(conn) {
30
+ const selectAllStmt = conn.prepare("SELECT hash FROM shallow ORDER BY hash");
31
+ const selectExistsStmt = conn.prepare("SELECT 1 FROM shallow WHERE hash = ?");
32
+ const deleteAllStmt = conn.prepare("DELETE FROM shallow");
33
+ const insertStmt = conn.prepare("INSERT OR IGNORE INTO shallow (hash) VALUES (?)");
34
+ const deleteOneStmt = conn.prepare("DELETE FROM shallow WHERE hash = ?");
35
35
  /** 全量替换事务 */
36
- const replaceAllTx = db.transaction((boundaries) => {
36
+ const replaceAllTx = conn.db.transaction((boundaries) => {
37
37
  deleteAllStmt.run();
38
38
  for (const hash of boundaries) insertStmt.run(hash);
39
39
  });
40
40
  /** 增量更新事务 */
41
- const applyUpdateTx = db.transaction((update) => {
41
+ const applyUpdateTx = conn.db.transaction((update) => {
42
42
  for (const hash of update.unshallow) deleteOneStmt.run(hash);
43
43
  for (const hash of update.shallow) insertStmt.run(hash);
44
44
  });
@@ -1,23 +1,23 @@
1
1
  import { RefStore } from "../core/types/refs.mjs";
2
- import { Database } from "bun:sqlite";
2
+ import { SqliteConnectionHandle } from "../backend/sqlite-pool.mjs";
3
3
 
4
4
  //#region src/refs/sqlite.d.ts
5
5
  /**
6
6
  * 创建基于 SQLite 的 RefStore
7
7
  *
8
- * @param db - 已打开的 bun:sqlite Database 实例(生命周期由调用方管理)
8
+ * @param conn - SQLite 连接池句柄(含 statement 缓存)
9
9
  * @returns 符合 RefStore 接口的存储后端(含事务支持)
10
10
  *
11
11
  * @example
12
12
  * ```ts
13
- * import { Database } from "bun:sqlite";
14
- * const db = new Database("/tmp/repo.sqlite");
15
- * const store = createSqliteRefStore(db);
13
+ * import { acquireConnection } from "nano-git/backend/sqlite";
14
+ * using conn = acquireConnection("/tmp/repo.sqlite");
15
+ * const store = createSqliteRefStore(conn);
16
16
  *
17
17
  * store.write("refs/heads/main", "abc123");
18
18
  * const content = store.read("refs/heads/main");
19
19
  * ```
20
20
  */
21
- declare function createSqliteRefStore(db: Database): RefStore;
21
+ declare function createSqliteRefStore(conn: SqliteConnectionHandle): RefStore;
22
22
  //#endregion
23
23
  export { createSqliteRefStore };
@@ -13,26 +13,27 @@ import { validateRefName, validateRefPrefix } from "./names.mjs";
13
13
  /**
14
14
  * 创建基于 SQLite 的 RefStore
15
15
  *
16
- * @param db - 已打开的 bun:sqlite Database 实例(生命周期由调用方管理)
16
+ * @param conn - SQLite 连接池句柄(含 statement 缓存)
17
17
  * @returns 符合 RefStore 接口的存储后端(含事务支持)
18
18
  *
19
19
  * @example
20
20
  * ```ts
21
- * import { Database } from "bun:sqlite";
22
- * const db = new Database("/tmp/repo.sqlite");
23
- * const store = createSqliteRefStore(db);
21
+ * import { acquireConnection } from "nano-git/backend/sqlite";
22
+ * using conn = acquireConnection("/tmp/repo.sqlite");
23
+ * const store = createSqliteRefStore(conn);
24
24
  *
25
25
  * store.write("refs/heads/main", "abc123");
26
26
  * const content = store.read("refs/heads/main");
27
27
  * ```
28
28
  */
29
- function createSqliteRefStore(db) {
30
- const selectStmt = db.query("SELECT target FROM refs WHERE name = ?");
31
- const selectExistsStmt = db.query("SELECT 1 FROM refs WHERE name = ?");
32
- const insertStmt = db.query("INSERT OR REPLACE INTO refs (name, target) VALUES (?, ?)");
33
- const deleteStmt = db.query("DELETE FROM refs WHERE name = ?");
34
- const listPrefixStmt = db.query("SELECT name FROM refs WHERE name >= ? AND name < ? ORDER BY name");
35
- const listAllStmt = db.query("SELECT name FROM refs WHERE name LIKE 'refs/%' ORDER BY name");
29
+ function createSqliteRefStore(conn) {
30
+ const selectStmt = conn.prepare("SELECT target FROM refs WHERE name = ?");
31
+ const selectExistsStmt = conn.prepare("SELECT 1 FROM refs WHERE name = ?");
32
+ const insertStmt = conn.prepare("INSERT OR REPLACE INTO refs (name, target) VALUES (?, ?)");
33
+ const deleteStmt = conn.prepare("DELETE FROM refs WHERE name = ?");
34
+ const listPrefixStmt = conn.prepare("SELECT name FROM refs WHERE name >= ? AND name < ? ORDER BY name");
35
+ const listAllStmt = conn.prepare("SELECT name FROM refs WHERE name LIKE 'refs/%' ORDER BY name");
36
+ const db = conn.db;
36
37
  /**
37
38
  * 开启一个新的事务
38
39
  *
@@ -6,6 +6,18 @@ import { patchTree } from "../tree/tree-patch.mjs";
6
6
  * 仓库对象操作组装
7
7
  */
8
8
  /**
9
+ * 空 tree 的规范原始对象
10
+ *
11
+ * 空 tree 序列化后内容为空,header 为 "tree 0\0",
12
+ * 其 SHA-1 是 Git 通用已知常数。
13
+ * 预计算一份避免每次重复序列化和哈希。
14
+ */
15
+ const EMPTY_TREE_RAW = {
16
+ hash: hashObject("tree", Buffer.alloc(0)),
17
+ type: "tree",
18
+ content: Buffer.alloc(0)
19
+ };
20
+ /**
9
21
  * 创建仓库对象相关操作
10
22
  *
11
23
  * @example
@@ -36,6 +48,10 @@ function createObjectRepositoryOperations(objects) {
36
48
  return objects.list();
37
49
  },
38
50
  createTree(entries) {
51
+ if (entries.length === 0) {
52
+ objects.ingest(EMPTY_TREE_RAW);
53
+ return EMPTY_TREE_RAW.hash;
54
+ }
39
55
  return writeObject(objects, {
40
56
  type: "tree",
41
57
  entries
@@ -14,7 +14,7 @@ interface NormalizedChangeRecord {
14
14
  readonly previous: VirtualDiffObject | null;
15
15
  /** 变更后对象 */
16
16
  readonly current: VirtualDiffObject | null;
17
- /** rename/copy 来源 */
17
+ /** move/copy 来源 */
18
18
  readonly source: VirtualDiffSource | null;
19
19
  }
20
20
  //#endregion
@@ -76,7 +76,7 @@ function rebuildNormalizedChangeIndex(source, state, cache) {
76
76
  previous: renameFrom.previous,
77
77
  current: addRecord.current,
78
78
  source: {
79
- kind: "rename",
79
+ kind: "move",
80
80
  path: renameFrom.path
81
81
  }
82
82
  });
@@ -155,17 +155,17 @@ function refreshChangeRecordForPath(source, state, path, cache) {
155
155
  state.setChangeRecord(nextRecord);
156
156
  }
157
157
  /**
158
- * 将单一路径的变更记录折叠为 rename 目标路径。
158
+ * 将单一路径的变更记录折叠为 move 目标路径。
159
159
  *
160
- * 仅适用于叶子节点 rename
160
+ * 仅适用于叶子节点 move
161
161
  * 目录及无法判定来源的情况应由调用方回退到全量重建。
162
162
  */
163
163
  function rewriteChangeRecordForRename(source, state, from, to, cache) {
164
164
  const previousRecord = state.getChangeRecord(from);
165
165
  const currentTarget = snapshotCurrentEntryAtPath(source, state, to, cache);
166
- if (currentTarget === null) throw new Error(`Cannot rewrite rename change record for missing path: ${to}`);
166
+ if (currentTarget === null) throw new Error(`Cannot rewrite move change record for missing path: ${to}`);
167
167
  const nextRecord = computeRenameRecordForPath(source, state, from, to, previousRecord, currentTarget);
168
- if (nextRecord === null) throw new Error(`Cannot rewrite rename change record from '${from}' to '${to}'`);
168
+ if (nextRecord === null) throw new Error(`Cannot rewrite move change record from '${from}' to '${to}'`);
169
169
  state.deleteChangeRecord(from);
170
170
  state.setChangeRecord(nextRecord);
171
171
  }
@@ -279,7 +279,7 @@ function computeRenameRecordForPath(source, state, from, to, previousRecord, cur
279
279
  if (derivedFromPrevious !== void 0) return derivedFromPrevious;
280
280
  const baseEntry = baseSnapshotEntryAtPath(source, state.readBaseTree(), from);
281
281
  if (baseEntry === null) return null;
282
- return createNormalizedChangeRecord(to, baseEntry.object, currentTarget.object, createDiffSource("rename", from));
282
+ return createNormalizedChangeRecord(to, baseEntry.object, currentTarget.object, createDiffSource("move", from));
283
283
  }
284
284
  function deriveCopySource(from, sourceRecord, source, state) {
285
285
  const fromRecordSource = sourceRecord?.source;
@@ -288,7 +288,7 @@ function deriveCopySource(from, sourceRecord, source, state) {
288
288
  return null;
289
289
  }
290
290
  function preserveLineageRecordForPath(path, previousRecord, currentEntry) {
291
- if (previousRecord?.source?.kind === "rename" && previousRecord.previous !== null) {
291
+ if (previousRecord?.source?.kind === "move" && previousRecord.previous !== null) {
292
292
  if (currentEntry === null) return null;
293
293
  return createNormalizedChangeRecord(path, previousRecord.previous, currentEntry.object, previousRecord.source);
294
294
  }
@@ -302,7 +302,7 @@ function deriveRenameRecordFromPreviousRecord(from, to, previousRecord, currentT
302
302
  if (previousRecord.current === null) return null;
303
303
  if (previousRecord.source !== null) return createNormalizedChangeRecord(to, previousRecord.previous, currentTarget.object, previousRecord.source);
304
304
  if (previousRecord.previous === null) return createNormalizedChangeRecord(to, null, currentTarget.object);
305
- return createNormalizedChangeRecord(to, previousRecord.previous, currentTarget.object, createDiffSource("rename", from));
305
+ return createNormalizedChangeRecord(to, previousRecord.previous, currentTarget.object, createDiffSource("move", from));
306
306
  }
307
307
  function snapshotCurrentEntryAtPath(source, state, path, cache) {
308
308
  const resolved = resolveCurrentLeafAtPath(source, state, path);
@@ -50,11 +50,11 @@ interface VirtualDiffObject {
50
50
  readonly hash: SHA1;
51
51
  }
52
52
  /**
53
- * rename/copy 来源描述
53
+ * move/copy 来源描述
54
54
  */
55
55
  interface VirtualDiffSource {
56
56
  /** 来源类型 */
57
- readonly kind: "rename" | "copy";
57
+ readonly kind: "move" | "copy";
58
58
  /** 来源路径 */
59
59
  readonly path: string;
60
60
  }
@@ -77,7 +77,7 @@ interface VirtualDiffChanges {
77
77
  type VirtualDiffEntry = {
78
78
  /** 新建路径 */readonly kind: "create"; /** 当前路径 */
79
79
  readonly path: string; /** 当前对象 */
80
- readonly current: VirtualDiffObject; /** rename/copy 的来源 */
80
+ readonly current: VirtualDiffObject; /** move/copy 的来源 */
81
81
  readonly source?: VirtualDiffSource;
82
82
  } | {
83
83
  /** 删除路径 */readonly kind: "remove"; /** 当前路径 */
@@ -88,7 +88,7 @@ type VirtualDiffEntry = {
88
88
  readonly path: string; /** 更新前对象 */
89
89
  readonly previous: VirtualDiffObject; /** 更新后对象 */
90
90
  readonly current: VirtualDiffObject; /** 变化维度 */
91
- readonly changes: VirtualDiffChanges; /** rename/copy 的来源 */
91
+ readonly changes: VirtualDiffChanges; /** move/copy 的来源 */
92
92
  readonly source?: VirtualDiffSource;
93
93
  };
94
94
  /**
@@ -158,17 +158,19 @@ interface VirtualWorkdir {
158
158
  readonly force?: boolean;
159
159
  }): void;
160
160
  /**
161
- * 重命名路径
161
+ * 移动路径(可跨目录树)
162
162
  *
163
163
  * 只做路径重绑定,不退化为 delete + write。
164
- * 目录重命名后,子项保持懒加载。
164
+ * 目标父目录不存在时会自动创建中间目录。
165
+ * 目录移动后,子项保持懒加载。
165
166
  */
166
- rename(from: string, to: string): void;
167
+ move(from: string, to: string): void;
167
168
  /**
168
169
  * 复制路径
169
170
  *
170
171
  * 新建 workdir node,共享 origin,不共享 node 身份。
171
- * 目录复制为浅复制,子项保持懒加载。
172
+ * 目录复制采用 CoW(写时复制):子树节点共享同一份 origin 引用,
173
+ * 任一副本下的子项被修改时才会真正分裂出独立副本。
172
174
  */
173
175
  copy(from: string, to: string): void;
174
176
  /**