nano-git 0.1.0

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 (227) hide show
  1. package/README.md +407 -0
  2. package/dist/backend/file-backend.d.mts +26 -0
  3. package/dist/backend/file-backend.mjs +83 -0
  4. package/dist/backend/file.d.mts +2 -0
  5. package/dist/backend/file.mjs +2 -0
  6. package/dist/backend/index.d.mts +2 -0
  7. package/dist/backend/index.mjs +1 -0
  8. package/dist/backend/memory-backend.d.mts +25 -0
  9. package/dist/backend/memory-backend.mjs +33 -0
  10. package/dist/backend/memory.d.mts +2 -0
  11. package/dist/backend/memory.mjs +2 -0
  12. package/dist/backend/types.d.mts +90 -0
  13. package/dist/core/errors.d.mts +124 -0
  14. package/dist/core/errors.mjs +168 -0
  15. package/dist/core/hash-digest.d.mts +35 -0
  16. package/dist/core/hash-digest.mjs +50 -0
  17. package/dist/core/hash-file.d.mts +20 -0
  18. package/dist/core/hash-file.mjs +27 -0
  19. package/dist/core/hash-path.d.mts +17 -0
  20. package/dist/core/hash-path.mjs +35 -0
  21. package/dist/core/types/odb.d.mts +63 -0
  22. package/dist/core/types/refs.d.mts +140 -0
  23. package/dist/core/types/refs.mjs +19 -0
  24. package/dist/core/types/shallow.d.mts +52 -0
  25. package/dist/core/types.d.mts +154 -0
  26. package/dist/core/types.mjs +43 -0
  27. package/dist/errors.d.mts +2 -0
  28. package/dist/errors.mjs +2 -0
  29. package/dist/hash-file.d.mts +2 -0
  30. package/dist/hash-file.mjs +2 -0
  31. package/dist/index.d.mts +16 -0
  32. package/dist/index.mjs +14 -0
  33. package/dist/objects/author.d.mts +25 -0
  34. package/dist/objects/author.mjs +45 -0
  35. package/dist/objects/blob.d.mts +15 -0
  36. package/dist/objects/blob.mjs +20 -0
  37. package/dist/objects/codec.d.mts +46 -0
  38. package/dist/objects/codec.mjs +84 -0
  39. package/dist/objects/commit.d.mts +26 -0
  40. package/dist/objects/commit.mjs +160 -0
  41. package/dist/objects/index.d.mts +8 -0
  42. package/dist/objects/index.mjs +8 -0
  43. package/dist/objects/raw.d.mts +85 -0
  44. package/dist/objects/raw.mjs +111 -0
  45. package/dist/objects/tag.d.mts +26 -0
  46. package/dist/objects/tag.mjs +148 -0
  47. package/dist/objects/tree.d.mts +22 -0
  48. package/dist/objects/tree.mjs +66 -0
  49. package/dist/odb/file-utils.mjs +136 -0
  50. package/dist/odb/file.d.mts +22 -0
  51. package/dist/odb/file.mjs +66 -0
  52. package/dist/odb/memory.d.mts +22 -0
  53. package/dist/odb/memory.mjs +54 -0
  54. package/dist/pack/composite-store.d.mts +76 -0
  55. package/dist/pack/composite-store.mjs +133 -0
  56. package/dist/pack/constants.mjs +48 -0
  57. package/dist/pack/crc32.mjs +38 -0
  58. package/dist/pack/delta-apply.d.mts +21 -0
  59. package/dist/pack/delta-apply.mjs +71 -0
  60. package/dist/pack/delta-create.d.mts +28 -0
  61. package/dist/pack/delta-create.mjs +151 -0
  62. package/dist/pack/index.d.mts +16 -0
  63. package/dist/pack/index.mjs +13 -0
  64. package/dist/pack/object-header.d.mts +36 -0
  65. package/dist/pack/object-header.mjs +72 -0
  66. package/dist/pack/ofs-delta-offset.d.mts +35 -0
  67. package/dist/pack/ofs-delta-offset.mjs +59 -0
  68. package/dist/pack/pack-builder-types.d.mts +19 -0
  69. package/dist/pack/pack-builder.d.mts +52 -0
  70. package/dist/pack/pack-builder.mjs +80 -0
  71. package/dist/pack/pack-encoding.mjs +76 -0
  72. package/dist/pack/pack-index-reader.d.mts +57 -0
  73. package/dist/pack/pack-index-reader.mjs +90 -0
  74. package/dist/pack/pack-index-types.d.mts +16 -0
  75. package/dist/pack/pack-index-writer.d.mts +45 -0
  76. package/dist/pack/pack-index-writer.mjs +106 -0
  77. package/dist/pack/pack-reader-resolver.mjs +104 -0
  78. package/dist/pack/pack-reader-types.d.mts +31 -0
  79. package/dist/pack/pack-reader-types.mjs +22 -0
  80. package/dist/pack/pack-reader-utils.mjs +61 -0
  81. package/dist/pack/pack-reader.d.mts +81 -0
  82. package/dist/pack/pack-reader.mjs +171 -0
  83. package/dist/pack/pack-store-loader.mjs +79 -0
  84. package/dist/pack/pack-store-types.d.mts +16 -0
  85. package/dist/pack/pack-store.d.mts +55 -0
  86. package/dist/pack/pack-store.mjs +114 -0
  87. package/dist/pack/pack-writer.mjs +96 -0
  88. package/dist/pack/varint.d.mts +34 -0
  89. package/dist/pack/varint.mjs +57 -0
  90. package/dist/refs/file.d.mts +6 -0
  91. package/dist/refs/file.mjs +222 -0
  92. package/dist/refs/fs-utils.mjs +30 -0
  93. package/dist/refs/memory.d.mts +14 -0
  94. package/dist/refs/memory.mjs +104 -0
  95. package/dist/refs/names.d.mts +57 -0
  96. package/dist/refs/names.mjs +80 -0
  97. package/dist/refs/resolve.d.mts +33 -0
  98. package/dist/refs/resolve.mjs +55 -0
  99. package/dist/refs/shallow/file.d.mts +17 -0
  100. package/dist/refs/shallow/file.mjs +61 -0
  101. package/dist/refs/shallow/memory.d.mts +18 -0
  102. package/dist/refs/shallow/memory.mjs +33 -0
  103. package/dist/repository/core.d.mts +3 -0
  104. package/dist/repository/core.mjs +2 -0
  105. package/dist/repository/create.d.mts +22 -0
  106. package/dist/repository/create.mjs +43 -0
  107. package/dist/repository/file.d.mts +43 -0
  108. package/dist/repository/file.mjs +82 -0
  109. package/dist/repository/import/import-glob.mjs +44 -0
  110. package/dist/repository/import/import-plan-builder.mjs +625 -0
  111. package/dist/repository/import/import-session-types.d.mts +280 -0
  112. package/dist/repository/import/import-session.mjs +96 -0
  113. package/dist/repository/import/import-view.mjs +133 -0
  114. package/dist/repository/memory.d.mts +17 -0
  115. package/dist/repository/memory.mjs +33 -0
  116. package/dist/repository/ops/fetch-operations.mjs +20 -0
  117. package/dist/repository/ops/fetch-types.d.mts +82 -0
  118. package/dist/repository/ops/fetch-url.mjs +82 -0
  119. package/dist/repository/ops/fs-object-operations.mjs +31 -0
  120. package/dist/repository/ops/maintenance-operations.mjs +62 -0
  121. package/dist/repository/ops/maintenance-types.d.mts +42 -0
  122. package/dist/repository/ops/object-operations.mjs +65 -0
  123. package/dist/repository/ops/object-types.d.mts +109 -0
  124. package/dist/repository/ops/push-operations.mjs +16 -0
  125. package/dist/repository/ops/push-resolution.mjs +17 -0
  126. package/dist/repository/ops/push-types.d.mts +72 -0
  127. package/dist/repository/ops/push-url.mjs +45 -0
  128. package/dist/repository/ops/reachability.mjs +60 -0
  129. package/dist/repository/ops/ref-operations.mjs +86 -0
  130. package/dist/repository/ops/ref-types.d.mts +70 -0
  131. package/dist/repository/tree/tree-patch.d.mts +64 -0
  132. package/dist/repository/tree/tree-patch.mjs +268 -0
  133. package/dist/repository/tree/tree-walk.d.mts +46 -0
  134. package/dist/repository/tree/tree-walk.mjs +65 -0
  135. package/dist/repository/tree/tree-writer.mjs +68 -0
  136. package/dist/repository/types.d.mts +36 -0
  137. package/dist/sha1.d.mts +4 -0
  138. package/dist/sha1.mjs +4 -0
  139. package/dist/transport/client/receive-pack/http.d.mts +33 -0
  140. package/dist/transport/client/receive-pack/http.mjs +99 -0
  141. package/dist/transport/client/receive-pack/push-error.d.mts +23 -0
  142. package/dist/transport/client/receive-pack/push-error.mjs +32 -0
  143. package/dist/transport/client/receive-pack/push-pack-plan.d.mts +28 -0
  144. package/dist/transport/client/receive-pack/push-pack-plan.mjs +60 -0
  145. package/dist/transport/client/receive-pack/push-policy.d.mts +19 -0
  146. package/dist/transport/client/receive-pack/push-policy.mjs +64 -0
  147. package/dist/transport/client/receive-pack/push-ref-plan.d.mts +45 -0
  148. package/dist/transport/client/receive-pack/push-ref-plan.mjs +108 -0
  149. package/dist/transport/client/receive-pack/push-report.d.mts +28 -0
  150. package/dist/transport/client/receive-pack/push-report.mjs +84 -0
  151. package/dist/transport/client/receive-pack/push-request-plan.mjs +52 -0
  152. package/dist/transport/client/receive-pack/push.d.mts +32 -0
  153. package/dist/transport/client/receive-pack/push.mjs +97 -0
  154. package/dist/transport/client/receive-pack/request.d.mts +39 -0
  155. package/dist/transport/client/receive-pack/request.mjs +46 -0
  156. package/dist/transport/client/receive-pack/response.d.mts +26 -0
  157. package/dist/transport/client/receive-pack/response.mjs +52 -0
  158. package/dist/transport/client/receive-pack/result.d.mts +34 -0
  159. package/dist/transport/client/receive-pack/result.mjs +100 -0
  160. package/dist/transport/client/upload-pack/capability-advertisement.d.mts +56 -0
  161. package/dist/transport/client/upload-pack/capability-advertisement.mjs +130 -0
  162. package/dist/transport/client/upload-pack/fetch.d.mts +109 -0
  163. package/dist/transport/client/upload-pack/fetch.mjs +392 -0
  164. package/dist/transport/client/upload-pack/http.d.mts +29 -0
  165. package/dist/transport/client/upload-pack/http.mjs +79 -0
  166. package/dist/transport/client/upload-pack/ls-refs.d.mts +75 -0
  167. package/dist/transport/client/upload-pack/ls-refs.mjs +150 -0
  168. package/dist/transport/client/upload-pack/object-info.d.mts +65 -0
  169. package/dist/transport/client/upload-pack/object-info.mjs +111 -0
  170. package/dist/transport/client/upload-pack/types.d.mts +153 -0
  171. package/dist/transport/http/index.d.mts +3 -0
  172. package/dist/transport/http/index.mjs +2 -0
  173. package/dist/transport/http/smart-http.d.mts +46 -0
  174. package/dist/transport/http/smart-http.mjs +176 -0
  175. package/dist/transport/http/types.d.mts +27 -0
  176. package/dist/transport/index.d.mts +9 -0
  177. package/dist/transport/index.mjs +8 -0
  178. package/dist/transport/protocol/object-graph.d.mts +63 -0
  179. package/dist/transport/protocol/object-graph.mjs +149 -0
  180. package/dist/transport/protocol/pkt-line.d.mts +109 -0
  181. package/dist/transport/protocol/pkt-line.mjs +195 -0
  182. package/dist/transport/protocol/ref-advertisement.mjs +185 -0
  183. package/dist/transport/protocol/ref-collection.d.mts +38 -0
  184. package/dist/transport/protocol/ref-collection.mjs +63 -0
  185. package/dist/transport/protocol/ref-match.d.mts +39 -0
  186. package/dist/transport/protocol/ref-match.mjs +42 -0
  187. package/dist/transport/protocol/refspec.d.mts +44 -0
  188. package/dist/transport/protocol/refspec.mjs +79 -0
  189. package/dist/transport/protocol/side-band.d.mts +65 -0
  190. package/dist/transport/protocol/side-band.mjs +142 -0
  191. package/dist/transport/protocol/transport-capabilities.mjs +46 -0
  192. package/dist/transport/protocol/types.d.mts +148 -0
  193. package/dist/transport/protocol/update-refs.d.mts +68 -0
  194. package/dist/transport/protocol/update-refs.mjs +126 -0
  195. package/dist/transport/receive-pack.d.mts +11 -0
  196. package/dist/transport/receive-pack.mjs +11 -0
  197. package/dist/transport/server/receive-pack/advertise.d.mts +28 -0
  198. package/dist/transport/server/receive-pack/advertise.mjs +88 -0
  199. package/dist/transport/server/receive-pack/handler.d.mts +30 -0
  200. package/dist/transport/server/receive-pack/handler.mjs +156 -0
  201. package/dist/transport/server/receive-pack/index.d.mts +6 -0
  202. package/dist/transport/server/receive-pack/index.mjs +6 -0
  203. package/dist/transport/server/receive-pack/parse.d.mts +17 -0
  204. package/dist/transport/server/receive-pack/parse.mjs +80 -0
  205. package/dist/transport/server/receive-pack/report-status.mjs +64 -0
  206. package/dist/transport/server/receive-pack/service.d.mts +41 -0
  207. package/dist/transport/server/receive-pack/service.mjs +39 -0
  208. package/dist/transport/server/receive-pack/types.d.mts +56 -0
  209. package/dist/transport/server/receive-pack/types.mjs +25 -0
  210. package/dist/transport/server/receive-pack/unpack.mjs +119 -0
  211. package/dist/transport/server/upload-pack/advertise.d.mts +20 -0
  212. package/dist/transport/server/upload-pack/advertise.mjs +30 -0
  213. package/dist/transport/server/upload-pack/command.d.mts +43 -0
  214. package/dist/transport/server/upload-pack/command.mjs +56 -0
  215. package/dist/transport/server/upload-pack/fetch.d.mts +43 -0
  216. package/dist/transport/server/upload-pack/fetch.mjs +217 -0
  217. package/dist/transport/server/upload-pack/index.d.mts +7 -0
  218. package/dist/transport/server/upload-pack/index.mjs +7 -0
  219. package/dist/transport/server/upload-pack/ls-refs.d.mts +38 -0
  220. package/dist/transport/server/upload-pack/ls-refs.mjs +113 -0
  221. package/dist/transport/server/upload-pack/service.d.mts +40 -0
  222. package/dist/transport/server/upload-pack/service.mjs +51 -0
  223. package/dist/transport/server/upload-pack/types.d.mts +11 -0
  224. package/dist/transport/server/upload-pack/types.mjs +21 -0
  225. package/dist/transport/upload-pack.d.mts +7 -0
  226. package/dist/transport/upload-pack.mjs +6 -0
  227. package/package.json +98 -0
@@ -0,0 +1,136 @@
1
+ import { InvalidObjectError } from "../core/errors.mjs";
2
+ import { assertObjectType, sha1 } from "../core/types.mjs";
3
+ import { hashToPath } from "../core/hash-path.mjs";
4
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
5
+ import { deflateSync, inflateSync } from "node:zlib";
6
+ import { join } from "node:path";
7
+ //#region src/odb/file-utils.ts
8
+ /**
9
+ * loose object 文件系统辅助函数
10
+ *
11
+ * 负责对象路径计算、压缩读写与对象目录枚举。
12
+ */
13
+ /**
14
+ * 计算 loose object 文件路径
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const path = getLooseObjectPath("/repo/.git/objects", hash);
19
+ * console.log(path);
20
+ * ```
21
+ */
22
+ function getLooseObjectPath(objectsDir, hash) {
23
+ return join(objectsDir, hashToPath(hash));
24
+ }
25
+ /**
26
+ * 检查 loose object 是否存在
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * if (hasLooseObject(objectsDir, hash)) {
31
+ * console.log("对象已存在");
32
+ * }
33
+ * ```
34
+ */
35
+ function hasLooseObject(objectsDir, hash) {
36
+ return existsSync(getLooseObjectPath(objectsDir, hash));
37
+ }
38
+ /**
39
+ * 删除 loose object 文件
40
+ *
41
+ * 删除不存在的对象时静默成功(no-op)。
42
+ *
43
+ * @param objectsDir - .git/objects 目录路径
44
+ * @param hash - 要删除的对象哈希
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * deleteLooseObject(objectsDir, hash);
49
+ * ```
50
+ */
51
+ function deleteLooseObject(objectsDir, hash) {
52
+ const objectPath = getLooseObjectPath(objectsDir, hash);
53
+ if (existsSync(objectPath)) unlinkSync(objectPath);
54
+ }
55
+ /**
56
+ * 枚举所有 loose object 哈希
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * const hashes = listLooseObjects(objectsDir);
61
+ * console.log(hashes.length);
62
+ * ```
63
+ */
64
+ function listLooseObjects(objectsDir) {
65
+ if (!existsSync(objectsDir)) return [];
66
+ const hashes = [];
67
+ const dirs = readdirSync(objectsDir).sort();
68
+ for (const dirName of dirs) {
69
+ if (dirName === "info" || dirName === "pack" || dirName.length !== 2) continue;
70
+ const dirPath = join(objectsDir, dirName);
71
+ if (!statSync(dirPath).isDirectory()) continue;
72
+ const files = readdirSync(dirPath).sort();
73
+ for (const fileName of files) {
74
+ if (fileName.length !== 38) continue;
75
+ hashes.push(sha1(`${dirName}${fileName}`));
76
+ }
77
+ }
78
+ return hashes;
79
+ }
80
+ /**
81
+ * 写入 raw loose object 文件(使用 canonical bytes)
82
+ *
83
+ * 直接以 "<type> <size>\0<content>" 格式 deflate 写入,
84
+ * 不经过语义序列化。
85
+ *
86
+ * @param objectsDir - .git/objects 目录路径
87
+ * @param raw - 原始对象
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * writeRawLooseObject(objectsDir, raw);
92
+ * ```
93
+ */
94
+ function writeRawLooseObject(objectsDir, raw) {
95
+ const objectPath = getLooseObjectPath(objectsDir, raw.hash);
96
+ mkdirSync(join(objectsDir, raw.hash.slice(0, 2)), { recursive: true });
97
+ const header = `${raw.type} ${raw.content.length}\0`;
98
+ writeFileSync(objectPath, deflateSync(Buffer.concat([Buffer.from(header), raw.content])));
99
+ }
100
+ /**
101
+ * 读取 raw loose object 文件
102
+ *
103
+ * 读取后直接返回 { hash, type, content },
104
+ * 不经过语义反序列化。
105
+ *
106
+ * @param objectsDir - .git/objects 目录路径
107
+ * @param hash - 对象哈希
108
+ * @returns 原始对象
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * const raw = readRawLooseObject(objectsDir, hash);
113
+ * console.log(raw.type, raw.content.length);
114
+ * ```
115
+ */
116
+ function readRawLooseObject(objectsDir, hash) {
117
+ const objectPath = getLooseObjectPath(objectsDir, hash);
118
+ if (!existsSync(objectPath)) throw new Error(`Object not found: ${hash}`);
119
+ const data = inflateSync(readFileSync(objectPath));
120
+ const nullIndex = data.indexOf(0);
121
+ if (nullIndex === -1) throw new InvalidObjectError("missing null byte in loose object");
122
+ const header = data.subarray(0, nullIndex).toString("utf-8");
123
+ const match = header.match(/^(blob|tree|commit|tag) (\d+)$/);
124
+ if (!match) throw new InvalidObjectError(`invalid loose object header: ${header}`);
125
+ const type = assertObjectType(match[1]);
126
+ const size = parseInt(match[2], 10);
127
+ const content = data.subarray(nullIndex + 1);
128
+ if (content.length !== size) throw new InvalidObjectError(`size mismatch: header says ${size}, got ${content.length}`);
129
+ return {
130
+ hash,
131
+ type,
132
+ content
133
+ };
134
+ }
135
+ //#endregion
136
+ export { deleteLooseObject, hasLooseObject, listLooseObjects, readRawLooseObject, writeRawLooseObject };
@@ -0,0 +1,22 @@
1
+ import { ObjectDatabase } from "../core/types/odb.mjs";
2
+
3
+ //#region src/odb/file.d.ts
4
+ /**
5
+ * 创建基于文件系统的对象数据库
6
+ *
7
+ * @param gitDir - .git 目录的路径
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const db = createFileObjectStore("/path/to/repo/.git");
12
+ *
13
+ * // 摄入一个原始对象
14
+ * db.ingest(raw);
15
+ *
16
+ * // 读取回来
17
+ * const obj = db.read(hash);
18
+ * ```
19
+ */
20
+ declare function createFileObjectStore(gitDir: string): ObjectDatabase;
21
+ //#endregion
22
+ export { createFileObjectStore };
@@ -0,0 +1,66 @@
1
+ import { hashObject } from "../core/hash-digest.mjs";
2
+ import { deleteLooseObject, hasLooseObject, listLooseObjects, readRawLooseObject, writeRawLooseObject } from "./file-utils.mjs";
3
+ import { join } from "node:path";
4
+ //#region src/odb/file.ts
5
+ /**
6
+ * 基于文件系统的对象数据库(raw-first)
7
+ *
8
+ * Git 将对象存储在 .git/objects/ 目录下:
9
+ * - 每个对象以 zlib 压缩格式存储
10
+ * - 路径格式: .git/objects/<前2字符>/<剩余38字符>
11
+ * - 例如: .git/objects/95/d09f2b10159347eece71399a7e2e907ea3df4f
12
+ *
13
+ * ODB 的真实边界是 RawGitObject,不是 GitObject。
14
+ * 写入路径直接接收 RawGitObject,不再经过语义序列化。
15
+ */
16
+ /**
17
+ * 创建基于文件系统的对象数据库
18
+ *
19
+ * @param gitDir - .git 目录的路径
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const db = createFileObjectStore("/path/to/repo/.git");
24
+ *
25
+ * // 摄入一个原始对象
26
+ * db.ingest(raw);
27
+ *
28
+ * // 读取回来
29
+ * const obj = db.read(hash);
30
+ * ```
31
+ */
32
+ function createFileObjectStore(gitDir) {
33
+ const objectsDir = join(gitDir, "objects");
34
+ return {
35
+ ingest(raw) {
36
+ const expectedHash = hashObject(raw.type, raw.content);
37
+ if (expectedHash !== raw.hash) throw new Error(`RawGitObject hash mismatch: expected ${expectedHash}, got ${raw.hash}`);
38
+ if (hasLooseObject(objectsDir, raw.hash)) return;
39
+ writeRawLooseObject(objectsDir, raw);
40
+ },
41
+ ingestMany(objects) {
42
+ for (const raw of objects) this.ingest(raw);
43
+ },
44
+ read(hash) {
45
+ return readRawLooseObject(objectsDir, hash);
46
+ },
47
+ tryRead(hash) {
48
+ try {
49
+ return readRawLooseObject(objectsDir, hash);
50
+ } catch {
51
+ return;
52
+ }
53
+ },
54
+ exists(hash) {
55
+ return hasLooseObject(objectsDir, hash);
56
+ },
57
+ list() {
58
+ return listLooseObjects(objectsDir);
59
+ },
60
+ delete(hash) {
61
+ deleteLooseObject(objectsDir, hash);
62
+ }
63
+ };
64
+ }
65
+ //#endregion
66
+ export { createFileObjectStore };
@@ -0,0 +1,22 @@
1
+ import { ObjectDatabase } from "../core/types/odb.mjs";
2
+
3
+ //#region src/odb/memory.d.ts
4
+ /**
5
+ * 内存对象数据库接口
6
+ */
7
+ type MemoryObjectDatabase = ObjectDatabase;
8
+ /**
9
+ * 创建基于内存的对象数据库
10
+ *
11
+ * 所有对象存储在内存中,程序退出后丢失。
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const db = createMemoryObjectStore();
16
+ * db.ingest(raw);
17
+ * const obj = db.read(hash);
18
+ * ```
19
+ */
20
+ declare function createMemoryObjectStore(): MemoryObjectDatabase;
21
+ //#endregion
22
+ export { MemoryObjectDatabase, type ObjectDatabase, createMemoryObjectStore };
@@ -0,0 +1,54 @@
1
+ import { hashObject } from "../core/hash-digest.mjs";
2
+ //#region src/odb/memory.ts
3
+ /**
4
+ * 基于内存的对象数据库(raw-first)
5
+ *
6
+ * 所有对象以 RawGitObject 形式存储在内存 Map 中,程序退出后丢失。
7
+ * 适用于单元测试和临时操作场景。
8
+ *
9
+ * ODB 的真实边界是 RawGitObject,不是 GitObject。
10
+ */
11
+ /**
12
+ * 创建基于内存的对象数据库
13
+ *
14
+ * 所有对象存储在内存中,程序退出后丢失。
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const db = createMemoryObjectStore();
19
+ * db.ingest(raw);
20
+ * const obj = db.read(hash);
21
+ * ```
22
+ */
23
+ function createMemoryObjectStore() {
24
+ const store = /* @__PURE__ */ new Map();
25
+ return {
26
+ ingest(raw) {
27
+ const expectedHash = hashObject(raw.type, raw.content);
28
+ if (expectedHash !== raw.hash) throw new Error(`RawGitObject hash mismatch: expected ${expectedHash}, got ${raw.hash}`);
29
+ if (!store.has(raw.hash)) store.set(raw.hash, raw);
30
+ },
31
+ ingestMany(objects) {
32
+ for (const raw of objects) this.ingest(raw);
33
+ },
34
+ read(hash) {
35
+ const obj = store.get(hash);
36
+ if (!obj) throw new Error(`Object not found: ${hash}`);
37
+ return obj;
38
+ },
39
+ tryRead(hash) {
40
+ return store.get(hash);
41
+ },
42
+ exists(hash) {
43
+ return store.has(hash);
44
+ },
45
+ list() {
46
+ return Array.from(store.keys());
47
+ },
48
+ delete(hash) {
49
+ store.delete(hash);
50
+ }
51
+ };
52
+ }
53
+ //#endregion
54
+ export { createMemoryObjectStore };
@@ -0,0 +1,76 @@
1
+ import { RawGitObject, SHA1 } from "../core/types.mjs";
2
+ import { ObjectDatabase, ObjectSource } from "../core/types/odb.mjs";
3
+
4
+ //#region src/pack/composite-store.d.ts
5
+ /**
6
+ * 创建组合对象数据库
7
+ *
8
+ * @param primary - 主数据库(用于摄入)
9
+ * @param secondary - 辅助源列表(只读,按顺序查找)
10
+ * @returns 组合对象数据库
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const looseDb = createFileObjectStore(gitDir);
15
+ * const packSource = createPackObjectStore(gitDir);
16
+ * const db = createCompositeObjectDatabase(looseDb, packSource);
17
+ *
18
+ * // 读取时自动在所有存储中查找
19
+ * const raw = db.read(hash);
20
+ *
21
+ * // 摄入时只写入主数据库
22
+ * db.ingest(raw);
23
+ * ```
24
+ */
25
+ declare function createCompositeObjectDatabase(primary: ObjectDatabase, ...secondary: ObjectSource[]): CompositeObjectDatabase;
26
+ /**
27
+ * 组合对象数据库类
28
+ */
29
+ declare class CompositeObjectDatabase implements ObjectDatabase {
30
+ private readonly primary;
31
+ private readonly secondary;
32
+ constructor(primary: ObjectDatabase, secondary: ObjectSource[]);
33
+ /**
34
+ * 摄入原始对象到主数据库
35
+ */
36
+ ingest(raw: RawGitObject): void;
37
+ /**
38
+ * 批量摄入原始对象到主数据库
39
+ */
40
+ ingestMany(objects: Iterable<RawGitObject>): void;
41
+ /**
42
+ * 删除对象
43
+ *
44
+ * 委托给主数据库的 delete。
45
+ */
46
+ delete(hash: SHA1): void;
47
+ /**
48
+ * 尝试读取原始对象,不存在时返回 undefined
49
+ */
50
+ tryRead(hash: SHA1): RawGitObject | undefined;
51
+ /**
52
+ * 尝试从指定源中读取对象,不存在时返回 undefined
53
+ */
54
+ private tryReadSource;
55
+ /**
56
+ * 读取原始对象
57
+ *
58
+ * 按顺序在所有存储中查找,返回第一个找到的对象。
59
+ * (跳过 exists() 前置检查,直接尝试 read() 以消除 N+1 双重调用)
60
+ *
61
+ * @throws ObjectNotFoundError 如果对象在所有存储中都不存在
62
+ */
63
+ read(hash: SHA1): RawGitObject;
64
+ /**
65
+ * 检查对象是否存在
66
+ */
67
+ exists(hash: SHA1): boolean;
68
+ /**
69
+ * 列出所有对象哈希
70
+ *
71
+ * 主数据库排在前面,重复哈希自动去重。
72
+ */
73
+ list(): SHA1[];
74
+ }
75
+ //#endregion
76
+ export { CompositeObjectDatabase, createCompositeObjectDatabase };
@@ -0,0 +1,133 @@
1
+ import { ObjectNotFoundError } from "../core/errors.mjs";
2
+ //#region src/pack/composite-store.ts
3
+ /**
4
+ * 组合对象源/数据库
5
+ *
6
+ * 将多个对象源/数据库组合在一起,按优先级顺序查找对象。
7
+ * 典型用法:loose objects(文件系统)+ packfile 存储。
8
+ *
9
+ * 查找顺序:
10
+ * 1. 先在主数据库中查找(最新写入/摄入的对象)
11
+ * 2. 再在辅助源中查找(已打包的对象)
12
+ *
13
+ * 摄入操作始终写入到主数据库。
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const db = createCompositeObjectDatabase(fileDb, packSource);
18
+ * const raw = db.read(hash); // 自动在所有存储中查找
19
+ * ```
20
+ */
21
+ /**
22
+ * 创建组合对象数据库
23
+ *
24
+ * @param primary - 主数据库(用于摄入)
25
+ * @param secondary - 辅助源列表(只读,按顺序查找)
26
+ * @returns 组合对象数据库
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const looseDb = createFileObjectStore(gitDir);
31
+ * const packSource = createPackObjectStore(gitDir);
32
+ * const db = createCompositeObjectDatabase(looseDb, packSource);
33
+ *
34
+ * // 读取时自动在所有存储中查找
35
+ * const raw = db.read(hash);
36
+ *
37
+ * // 摄入时只写入主数据库
38
+ * db.ingest(raw);
39
+ * ```
40
+ */
41
+ function createCompositeObjectDatabase(primary, ...secondary) {
42
+ return new CompositeObjectDatabase(primary, secondary);
43
+ }
44
+ /**
45
+ * 组合对象数据库类
46
+ */
47
+ var CompositeObjectDatabase = class {
48
+ primary;
49
+ secondary;
50
+ constructor(primary, secondary) {
51
+ this.primary = primary;
52
+ this.secondary = secondary;
53
+ }
54
+ /**
55
+ * 摄入原始对象到主数据库
56
+ */
57
+ ingest(raw) {
58
+ return this.primary.ingest(raw);
59
+ }
60
+ /**
61
+ * 批量摄入原始对象到主数据库
62
+ */
63
+ ingestMany(objects) {
64
+ for (const raw of objects) this.ingest(raw);
65
+ }
66
+ /**
67
+ * 删除对象
68
+ *
69
+ * 委托给主数据库的 delete。
70
+ */
71
+ delete(hash) {
72
+ this.primary.delete(hash);
73
+ }
74
+ /**
75
+ * 尝试读取原始对象,不存在时返回 undefined
76
+ */
77
+ tryRead(hash) {
78
+ const primaryResult = this.tryReadSource(this.primary, hash);
79
+ if (primaryResult !== void 0) return primaryResult;
80
+ for (const source of this.secondary) {
81
+ const result = this.tryReadSource(source, hash);
82
+ if (result !== void 0) return result;
83
+ }
84
+ }
85
+ /**
86
+ * 尝试从指定源中读取对象,不存在时返回 undefined
87
+ */
88
+ tryReadSource(source, hash) {
89
+ try {
90
+ return source.read(hash);
91
+ } catch {
92
+ return;
93
+ }
94
+ }
95
+ /**
96
+ * 读取原始对象
97
+ *
98
+ * 按顺序在所有存储中查找,返回第一个找到的对象。
99
+ * (跳过 exists() 前置检查,直接尝试 read() 以消除 N+1 双重调用)
100
+ *
101
+ * @throws ObjectNotFoundError 如果对象在所有存储中都不存在
102
+ */
103
+ read(hash) {
104
+ const primaryResult = this.tryReadSource(this.primary, hash);
105
+ if (primaryResult !== void 0) return primaryResult;
106
+ for (const source of this.secondary) {
107
+ const result = this.tryReadSource(source, hash);
108
+ if (result !== void 0) return result;
109
+ }
110
+ throw new ObjectNotFoundError(hash);
111
+ }
112
+ /**
113
+ * 检查对象是否存在
114
+ */
115
+ exists(hash) {
116
+ if (this.primary.exists(hash)) return true;
117
+ for (const source of this.secondary) if (source.exists(hash)) return true;
118
+ return false;
119
+ }
120
+ /**
121
+ * 列出所有对象哈希
122
+ *
123
+ * 主数据库排在前面,重复哈希自动去重。
124
+ */
125
+ list() {
126
+ const hashes = /* @__PURE__ */ new Set();
127
+ for (const hash of this.primary.list()) hashes.add(hash);
128
+ for (const source of this.secondary) for (const hash of source.list()) hashes.add(hash);
129
+ return Array.from(hashes).sort();
130
+ }
131
+ };
132
+ //#endregion
133
+ export { CompositeObjectDatabase, createCompositeObjectDatabase };
@@ -0,0 +1,48 @@
1
+ /** Packfile 魔数 "PACK" */
2
+ const PACK_SIGNATURE = Buffer.from("PACK");
3
+ /** idx v2 文件魔数 */
4
+ const IDX_V2_SIGNATURE = Buffer.from([
5
+ 255,
6
+ 116,
7
+ 79,
8
+ 99
9
+ ]);
10
+ /** idx v2 扇出表大小:256 个 4 字节整数 */
11
+ const IDX_V2_FANOUT_SIZE = 256 * 4;
12
+ /**
13
+ * 将 Git 对象类型字符串转换为 Packfile 类型编号
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * objectTypeToNumber("commit") // => 1
18
+ * objectTypeToNumber("blob") // => 3
19
+ * ```
20
+ */
21
+ function objectTypeToNumber(type) {
22
+ switch (type) {
23
+ case "commit": return 1;
24
+ case "tree": return 2;
25
+ case "blob": return 3;
26
+ case "tag": return 4;
27
+ }
28
+ }
29
+ /**
30
+ * 将 Packfile 类型编号转换为 Git 对象类型字符串
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * numberToObjectType(1) // => "commit"
35
+ * numberToObjectType(3) // => "blob"
36
+ * ```
37
+ */
38
+ function numberToObjectType(n) {
39
+ switch (n) {
40
+ case 1: return "commit";
41
+ case 2: return "tree";
42
+ case 3: return "blob";
43
+ case 4: return "tag";
44
+ default: throw new Error(`Unknown object type number: ${n}`);
45
+ }
46
+ }
47
+ //#endregion
48
+ export { IDX_V2_FANOUT_SIZE, IDX_V2_SIGNATURE, PACK_SIGNATURE, numberToObjectType, objectTypeToNumber };
@@ -0,0 +1,38 @@
1
+ //#region src/pack/crc32.ts
2
+ /**
3
+ * CRC32 计算工具
4
+ */
5
+ /** CRC32 查找表缓存 */
6
+ let crc32Table = null;
7
+ /**
8
+ * 计算数据的 CRC32 校验和
9
+ *
10
+ * 使用标准 CRC32 算法(与 Git 兼容)。
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const crc = crc32Value(Buffer.from("hello"));
15
+ * ```
16
+ */
17
+ function crc32Value(data) {
18
+ const table = getCRC32Table();
19
+ let crc = 4294967295;
20
+ for (let i = 0; i < data.length; i++) crc = crc >>> 8 ^ table[(crc ^ data[i]) & 255];
21
+ return (crc ^ 4294967295) >>> 0;
22
+ }
23
+ /**
24
+ * 获取或生成 CRC32 查找表
25
+ */
26
+ function getCRC32Table() {
27
+ if (crc32Table) return crc32Table;
28
+ crc32Table = /* @__PURE__ */ new Uint32Array(256);
29
+ for (let i = 0; i < 256; i++) {
30
+ let crc = i;
31
+ for (let j = 0; j < 8; j++) if (crc & 1) crc = crc >>> 1 ^ 3988292384;
32
+ else crc = crc >>> 1;
33
+ crc32Table[i] = crc;
34
+ }
35
+ return crc32Table;
36
+ }
37
+ //#endregion
38
+ export { crc32Value };
@@ -0,0 +1,21 @@
1
+ //#region src/pack/delta-apply.d.ts
2
+ /**
3
+ * Delta 应用(解码)
4
+ */
5
+ /**
6
+ * 将 delta 数据应用到 base object,生成目标对象
7
+ *
8
+ * @param base - base object 的原始数据
9
+ * @param delta - delta 数据
10
+ * @returns 目标对象的原始数据
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const base = Buffer.from("hello world");
15
+ * const delta = createDelta(base, Buffer.from("hello git"));
16
+ * const result = applyDelta(base, delta);
17
+ * ```
18
+ */
19
+ declare function applyDelta(base: Buffer, delta: Buffer): Buffer;
20
+ //#endregion
21
+ export { applyDelta };
@@ -0,0 +1,71 @@
1
+ import { DeltaError } from "../core/errors.mjs";
2
+ import { decodeVarint } from "./varint.mjs";
3
+ //#region src/pack/delta-apply.ts
4
+ /**
5
+ * Delta 应用(解码)
6
+ */
7
+ /**
8
+ * 将 delta 数据应用到 base object,生成目标对象
9
+ *
10
+ * @param base - base object 的原始数据
11
+ * @param delta - delta 数据
12
+ * @returns 目标对象的原始数据
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const base = Buffer.from("hello world");
17
+ * const delta = createDelta(base, Buffer.from("hello git"));
18
+ * const result = applyDelta(base, delta);
19
+ * ```
20
+ */
21
+ function applyDelta(base, delta) {
22
+ let offset = 0;
23
+ const [_srcSize, srcBytes] = decodeVarint(delta, offset);
24
+ offset += srcBytes;
25
+ const [destSize, destBytes] = decodeVarint(delta, offset);
26
+ offset += destBytes;
27
+ const result = Buffer.alloc(destSize);
28
+ let destOffset = 0;
29
+ while (offset < delta.length) {
30
+ const cmd = delta[offset];
31
+ offset++;
32
+ if (cmd & 128) {
33
+ const copy = decodeCopyInstruction(delta, offset, cmd);
34
+ offset += copy.bytesRead;
35
+ if (copy.offset + copy.size > base.length) throw new DeltaError(`Copy out of bounds: offset=${copy.offset}, size=${copy.size}, base.length=${base.length}`);
36
+ base.copy(result, destOffset, copy.offset, copy.offset + copy.size);
37
+ destOffset += copy.size;
38
+ continue;
39
+ }
40
+ if (cmd > 0) {
41
+ if (offset + cmd > delta.length) throw new DeltaError("Insert out of bounds");
42
+ delta.copy(result, destOffset, offset, offset + cmd);
43
+ destOffset += cmd;
44
+ offset += cmd;
45
+ continue;
46
+ }
47
+ throw new DeltaError("Unexpected delta command: 0");
48
+ }
49
+ if (destOffset !== destSize) throw new DeltaError(`Delta size mismatch: expected ${destSize}, got ${destOffset}`);
50
+ return result;
51
+ }
52
+ function decodeCopyInstruction(delta, offset, cmd) {
53
+ const start = offset;
54
+ let copyOffset = 0;
55
+ let copySize = 0;
56
+ if (cmd & 1) copyOffset = delta[offset++];
57
+ if (cmd & 2) copyOffset |= delta[offset++] << 8;
58
+ if (cmd & 4) copyOffset |= delta[offset++] << 16;
59
+ if (cmd & 8) copyOffset |= delta[offset++] << 24;
60
+ if (cmd & 16) copySize = delta[offset++];
61
+ if (cmd & 32) copySize |= delta[offset++] << 8;
62
+ if (cmd & 64) copySize |= delta[offset++] << 16;
63
+ if (copySize === 0) copySize = 65536;
64
+ return {
65
+ offset: copyOffset,
66
+ size: copySize,
67
+ bytesRead: offset - start
68
+ };
69
+ }
70
+ //#endregion
71
+ export { applyDelta };