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,88 @@
1
+ import { sha1 } from "../../../core/types.mjs";
2
+ import { tryReadObject } from "../../../objects/raw.mjs";
3
+ import { resolveRefHash } from "../../../refs/resolve.mjs";
4
+ import { encodeFlushPkt, encodePktLine } from "../../protocol/pkt-line.mjs";
5
+ import { CAPABILITIES_REF, SERVER_AGENT, ZERO_HASH } from "./types.mjs";
6
+ //#region src/transport/server/receive-pack/advertise.ts
7
+ /**
8
+ * receive-pack ref 广告生成
9
+ *
10
+ * 处理 GET /info/refs?service=git-receive-pack 请求,
11
+ * 生成 v1 风格的 ref 广告(含 capabilities)。
12
+ *
13
+ * @see https://git-scm.com/docs/pack-protocol#_git_gt_transport
14
+ */
15
+ /**
16
+ * 读取仓库中的所有引用
17
+ */
18
+ function readAllRefs(backend) {
19
+ const result = /* @__PURE__ */ new Map();
20
+ const headContent = backend.refs.read("HEAD");
21
+ if (headContent !== null) result.set("HEAD", headContent);
22
+ const refNames = backend.refs.listAll();
23
+ for (const ref of refNames) {
24
+ const content = backend.refs.read(ref);
25
+ if (content !== null) result.set(ref, content);
26
+ }
27
+ return result;
28
+ }
29
+ /**
30
+ * 生成 receive-pack 的 ref 广告
31
+ *
32
+ * 格式:
33
+ * ```
34
+ * 001e# service=git-receive-pack\n
35
+ * 0000
36
+ * <length><hash> <refname>\0<capabilities>\n ← 首行 ref 携带 capabilities
37
+ * <length><hash> <refname>\n ← 后续 ref
38
+ * ...
39
+ * 0000
40
+ * ```
41
+ *
42
+ * @param backend - 仓库后端
43
+ * @returns 完整 advertisement(pkt-line 编码)
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const buf = advertiseReceivePack(backend);
48
+ * // Response 的 Content-Type 应为 "application/x-git-receive-pack-advertisement"
49
+ * ```
50
+ */
51
+ function advertiseReceivePack(backend) {
52
+ const parts = [];
53
+ parts.push(encodePktLine("# service=git-receive-pack\n"));
54
+ parts.push(encodeFlushPkt());
55
+ const refs = readAllRefs(backend);
56
+ const capStr = [
57
+ "report-status",
58
+ "delete-refs",
59
+ "side-band-64k",
60
+ "ofs-delta",
61
+ "no-progress",
62
+ `agent=${SERVER_AGENT}`
63
+ ].join(" ");
64
+ let firstRef = true;
65
+ for (const [refName, content] of refs) if (content.startsWith("ref: ")) {
66
+ const resolved = resolveRefHash(backend.refs, refName);
67
+ if (resolved === null) continue;
68
+ const line = firstRef ? `${resolved} ${refName}\0${capStr}\n` : `${resolved} ${refName}\n`;
69
+ parts.push(encodePktLine(line));
70
+ const target = content.slice(5);
71
+ parts.push(encodePktLine(`${resolved} ${refName} symref-target:${target}\n`));
72
+ firstRef = false;
73
+ } else if (/^[0-9a-f]{40}$/.test(content)) {
74
+ const hash = sha1(content);
75
+ const line = firstRef ? `${hash} ${refName}\0${capStr}\n` : `${hash} ${refName}\n`;
76
+ parts.push(encodePktLine(line));
77
+ if (refName.startsWith("refs/tags/")) {
78
+ const obj = tryReadObject(backend.objects, hash);
79
+ if (obj?.type === "tag") parts.push(encodePktLine(`${obj.object} ${refName}^{}\n`));
80
+ }
81
+ firstRef = false;
82
+ }
83
+ if (firstRef) parts.push(encodePktLine(`${ZERO_HASH} ${CAPABILITIES_REF}\0${capStr}\n`));
84
+ parts.push(encodeFlushPkt());
85
+ return Buffer.concat(parts);
86
+ }
87
+ //#endregion
88
+ export { advertiseReceivePack };
@@ -0,0 +1,30 @@
1
+ import { RepositoryBackend } from "../../../backend/types.mjs";
2
+ import { ReceivePackOptions } from "./types.mjs";
3
+
4
+ //#region src/transport/server/receive-pack/handler.d.ts
5
+ /**
6
+ * 处理 receive-pack push 请求
7
+ *
8
+ * 完整流程:
9
+ * 1. 验证请求体非空
10
+ * 2. 解析客户端命令
11
+ * 3. 检查 delete-refs 能力(如需要删除)
12
+ * 4. 解包 packfile(如有)
13
+ * 5. 检查组删除 / 更新 / 创建条件
14
+ * 6. 批量应用 ref 更新
15
+ * 7. 返回 report-status
16
+ *
17
+ * @param backend - 仓库后端
18
+ * @param body - 完整的请求体
19
+ * @param options - 处理选项
20
+ * @returns report-status 响应(Buffer)
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const response = handleReceivePackRequest(backend, requestBody);
25
+ * // Response 的 Content-Type 应为 "application/x-git-receive-pack-result"
26
+ * ```
27
+ */
28
+ declare function handleReceivePackRequest(backend: RepositoryBackend, body: Buffer, options?: ReceivePackOptions): Buffer;
29
+ //#endregion
30
+ export { handleReceivePackRequest };
@@ -0,0 +1,156 @@
1
+ import { resolveRefHash } from "../../../refs/resolve.mjs";
2
+ import { encodeFlushPkt } from "../../protocol/pkt-line.mjs";
3
+ import { ReceivePackServiceError, ZERO_HASH } from "./types.mjs";
4
+ import { parseReceivePackRequest } from "./parse.mjs";
5
+ import { generateReceivePackReport } from "./report-status.mjs";
6
+ import { unpackPackfile } from "./unpack.mjs";
7
+ //#region src/transport/server/receive-pack/handler.ts
8
+ /**
9
+ * receive-pack 主处理函数
10
+ *
11
+ * 整合请求解析、packfile 解包、ref 校验与事务更新,
12
+ * 生成 report-status 响应。
13
+ */
14
+ /**
15
+ * 校验单个 ref 更新命令的合法性
16
+ *
17
+ * 检查:
18
+ * - oldHash 必须匹配 ref 当前值(新建时可为 000...0)
19
+ * - newHash 对象必须存在
20
+ * - 标签不可覆盖(允许 force — 但 v1 协议中 force 不在命令中体现)
21
+ * - 删除操作需要 delete-refs 能力
22
+ */
23
+ function checkRefUpdate(backend, cmd, _capabilities, _options) {
24
+ const { oldHash, newHash, refName } = cmd;
25
+ const isDelete = newHash === ZERO_HASH;
26
+ const isCreate = oldHash === ZERO_HASH;
27
+ const currentHash = resolveRefHash(backend.refs, refName);
28
+ if (isCreate) {
29
+ if (currentHash !== null) return {
30
+ ok: false,
31
+ error: `ref ${refName} already exists`
32
+ };
33
+ if (!backend.objects.exists(newHash)) return {
34
+ ok: false,
35
+ error: `object ${newHash} not found`
36
+ };
37
+ return { ok: true };
38
+ }
39
+ if (currentHash === null) return {
40
+ ok: false,
41
+ error: `ref ${refName} does not exist (expected ${oldHash})`
42
+ };
43
+ if (currentHash !== oldHash) return {
44
+ ok: false,
45
+ error: `ref ${refName} is at ${currentHash} but expected ${oldHash}`
46
+ };
47
+ if (isDelete) return { ok: true };
48
+ if (!backend.objects.exists(newHash)) return {
49
+ ok: false,
50
+ error: `object ${newHash} not found`
51
+ };
52
+ if (refName.startsWith("refs/tags/")) return {
53
+ ok: false,
54
+ error: `tag ${refName} already exists and cannot be overwritten without force`
55
+ };
56
+ return { ok: true };
57
+ }
58
+ /**
59
+ * 在事务中应用批量 ref 更新
60
+ */
61
+ function applyRefUpdates(backend, commands) {
62
+ const hooks = backend.refTransactionHooks;
63
+ const tx = backend.refs.beginTransaction(hooks);
64
+ try {
65
+ for (const cmd of commands) if (cmd.newHash === ZERO_HASH) tx.delete(cmd.refName);
66
+ else tx.write(cmd.refName, cmd.newHash);
67
+ tx.commit();
68
+ } catch (err) {
69
+ tx.rollback();
70
+ throw err;
71
+ }
72
+ }
73
+ /**
74
+ * 处理 receive-pack push 请求
75
+ *
76
+ * 完整流程:
77
+ * 1. 验证请求体非空
78
+ * 2. 解析客户端命令
79
+ * 3. 检查 delete-refs 能力(如需要删除)
80
+ * 4. 解包 packfile(如有)
81
+ * 5. 检查组删除 / 更新 / 创建条件
82
+ * 6. 批量应用 ref 更新
83
+ * 7. 返回 report-status
84
+ *
85
+ * @param backend - 仓库后端
86
+ * @param body - 完整的请求体
87
+ * @param options - 处理选项
88
+ * @returns report-status 响应(Buffer)
89
+ *
90
+ * @example
91
+ * ```ts
92
+ * const response = handleReceivePackRequest(backend, requestBody);
93
+ * // Response 的 Content-Type 应为 "application/x-git-receive-pack-result"
94
+ * ```
95
+ */
96
+ function handleReceivePackRequest(backend, body, options) {
97
+ let parsed;
98
+ try {
99
+ parsed = parseReceivePackRequest(body);
100
+ } catch (err) {
101
+ if (err instanceof ReceivePackServiceError) throw err;
102
+ throw new ReceivePackServiceError(`Failed to parse receive-pack request: ${err instanceof Error ? err.message : String(err)}`);
103
+ }
104
+ const { capabilities, commands, packfile } = parsed;
105
+ const hasReportStatus = capabilities.includes("report-status");
106
+ const hasSideBand = capabilities.includes("side-band-64k");
107
+ let unpackOk = true;
108
+ let unpackError;
109
+ if (packfile.length > 0) try {
110
+ unpackPackfile(backend.objects, packfile);
111
+ } catch (err) {
112
+ unpackOk = false;
113
+ unpackError = err instanceof Error ? err.message : String(err);
114
+ }
115
+ const successfulUpdates = [];
116
+ const refResults = [];
117
+ if (unpackOk) {
118
+ for (const cmd of commands) {
119
+ const check = checkRefUpdate(backend, cmd, capabilities, options);
120
+ if (check.ok) {
121
+ successfulUpdates.push({
122
+ refName: cmd.refName,
123
+ newHash: cmd.newHash
124
+ });
125
+ refResults.push({
126
+ refName: cmd.refName,
127
+ success: true
128
+ });
129
+ } else refResults.push({
130
+ refName: cmd.refName,
131
+ success: false,
132
+ error: check.error
133
+ });
134
+ }
135
+ if (successfulUpdates.length > 0) try {
136
+ applyRefUpdates(backend, successfulUpdates);
137
+ } catch (err) {
138
+ for (const up of successfulUpdates) {
139
+ const idx = refResults.findIndex((r) => r.refName === up.refName);
140
+ if (idx !== -1) refResults[idx] = {
141
+ refName: up.refName,
142
+ success: false,
143
+ error: `transaction failed: ${err instanceof Error ? err.message : String(err)}`
144
+ };
145
+ }
146
+ }
147
+ } else for (const cmd of commands) refResults.push({
148
+ refName: cmd.refName,
149
+ success: false,
150
+ error: "unpack error"
151
+ });
152
+ if (!hasReportStatus) return encodeFlushPkt();
153
+ return generateReceivePackReport(unpackOk, unpackError, refResults, hasSideBand);
154
+ }
155
+ //#endregion
156
+ export { handleReceivePackRequest };
@@ -0,0 +1,6 @@
1
+ import { advertiseReceivePack } from "./advertise.mjs";
2
+ import { ParsedReceivePackRequest, ReceivePackCommand, ReceivePackOptions, ReceivePackServiceError, ReceivePackUpdateResult } from "./types.mjs";
3
+ import { parseReceivePackRequest } from "./parse.mjs";
4
+ import { handleReceivePackRequest } from "./handler.mjs";
5
+ import { ReceivePackService, createReceivePackService } from "./service.mjs";
6
+ export { type ParsedReceivePackRequest, type ReceivePackCommand, type ReceivePackOptions, type ReceivePackService, ReceivePackServiceError, type ReceivePackUpdateResult, advertiseReceivePack, createReceivePackService, handleReceivePackRequest, parseReceivePackRequest };
@@ -0,0 +1,6 @@
1
+ import { ReceivePackServiceError } from "./types.mjs";
2
+ import { advertiseReceivePack } from "./advertise.mjs";
3
+ import { parseReceivePackRequest } from "./parse.mjs";
4
+ import { handleReceivePackRequest } from "./handler.mjs";
5
+ import { createReceivePackService } from "./service.mjs";
6
+ export { ReceivePackServiceError, advertiseReceivePack, createReceivePackService, handleReceivePackRequest, parseReceivePackRequest };
@@ -0,0 +1,17 @@
1
+ import { ParsedReceivePackRequest } from "./types.mjs";
2
+
3
+ //#region src/transport/server/receive-pack/parse.d.ts
4
+ /**
5
+ * 解析 receive-pack 请求 body
6
+ *
7
+ * @param body - 完整的请求 body
8
+ * @returns 解析后的命令、能力与 packfile
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const { commands, capabilities, packfile } = parseReceivePackRequest(body);
13
+ * ```
14
+ */
15
+ declare function parseReceivePackRequest(body: Buffer): ParsedReceivePackRequest;
16
+ //#endregion
17
+ export { parseReceivePackRequest };
@@ -0,0 +1,80 @@
1
+ import { sha1 } from "../../../core/types.mjs";
2
+ import { splitPktLinesFromBuffer } from "../../protocol/pkt-line.mjs";
3
+ import { ReceivePackServiceError } from "./types.mjs";
4
+ //#region src/transport/server/receive-pack/parse.ts
5
+ /**
6
+ * receive-pack 请求解析
7
+ *
8
+ * 解析客户端 POST 的 ref 命令与 capabilities。
9
+ *
10
+ * 请求格式:
11
+ * ```
12
+ * <old-hash> <new-hash> <refname>\0<capabilities>\n ← 首行
13
+ * <old-hash> <new-hash> <refname>\n ← 后续行
14
+ * ...
15
+ * 0000 ← flush
16
+ * <packfile data> ← packfile
17
+ * ```
18
+ *
19
+ * @see https://git-scm.com/docs/pack-protocol#_git_gt_transport
20
+ */
21
+ /**
22
+ * 解析单行命令
23
+ *
24
+ * 格式:<old-hash> SP <new-hash> SP <refname>
25
+ */
26
+ function parseCommandLine(text) {
27
+ const parts = text.split(" ");
28
+ if (parts.length < 3) return null;
29
+ const oldHash = parts[0];
30
+ const newHash = parts[1];
31
+ const refName = parts.slice(2).join(" ");
32
+ if (!/^[0-9a-f]{40}$/.test(oldHash)) return null;
33
+ if (!/^[0-9a-f]{40}$/.test(newHash)) return null;
34
+ return {
35
+ oldHash: sha1(oldHash),
36
+ newHash: sha1(newHash),
37
+ refName
38
+ };
39
+ }
40
+ /**
41
+ * 解析 receive-pack 请求 body
42
+ *
43
+ * @param body - 完整的请求 body
44
+ * @returns 解析后的命令、能力与 packfile
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const { commands, capabilities, packfile } = parseReceivePackRequest(body);
49
+ * ```
50
+ */
51
+ function parseReceivePackRequest(body) {
52
+ const { lines, trailing } = splitPktLinesFromBuffer(body);
53
+ const dataLines = lines.filter((l) => l.type === "data");
54
+ if (dataLines.length === 0) throw new ReceivePackServiceError("No commands in receive-pack request");
55
+ const commands = [];
56
+ let capabilities = [];
57
+ for (let i = 0; i < dataLines.length; i++) {
58
+ const text = dataLines[i].payload.toString("utf-8").replace(/\n$/, "");
59
+ if (i === 0) {
60
+ const nulIndex = text.indexOf("\0");
61
+ if (nulIndex !== -1) {
62
+ const cmdPart = text.substring(0, nulIndex);
63
+ capabilities = text.substring(nulIndex + 1).split(" ").filter(Boolean);
64
+ const cmd = parseCommandLine(cmdPart);
65
+ if (cmd) commands.push(cmd);
66
+ continue;
67
+ }
68
+ }
69
+ const cmd = parseCommandLine(text);
70
+ if (cmd) commands.push(cmd);
71
+ }
72
+ if (commands.length === 0) throw new ReceivePackServiceError("No valid ref update commands");
73
+ return {
74
+ capabilities,
75
+ commands,
76
+ packfile: trailing
77
+ };
78
+ }
79
+ //#endregion
80
+ export { parseReceivePackRequest };
@@ -0,0 +1,64 @@
1
+ import { encodeFlushPkt, encodePktLine } from "../../protocol/pkt-line.mjs";
2
+ //#region src/transport/server/receive-pack/report-status.ts
3
+ /**
4
+ * receive-pack report-status 响应生成
5
+ *
6
+ * 生成 unpack 状态和 ref 更新结果的 pkt-line 编码响应。
7
+ * 支持 side-band-64k 编码(progress 在 channel 2,report-status 在 channel 1)。
8
+ */
9
+ /**
10
+ * 编码 side-band 帧
11
+ */
12
+ function encodeSideBandFrame(channel, data) {
13
+ const frame = Buffer.alloc(1 + data.length);
14
+ frame[0] = channel;
15
+ data.copy(frame, 1);
16
+ return encodePktLine(frame);
17
+ }
18
+ /**
19
+ * 生成 receive-pack 的 report-status 响应
20
+ *
21
+ * 格式(无 side-band):
22
+ * ```
23
+ * unpack <ok|error>\n
24
+ * ok <refname>\n
25
+ * ng <refname> <error>\n
26
+ * ...
27
+ * 0000
28
+ * ```
29
+ *
30
+ * 格式(带 side-band-64k):
31
+ * ```
32
+ * <side-band channel 2: progress>
33
+ * <side-band channel 1: report-status lines>
34
+ * 0000
35
+ * ```
36
+ *
37
+ * @param unpackOk - 解包是否成功
38
+ * @param unpackError - 解包错误消息(unpackOk 为 false 时)
39
+ * @param refResults - 各 ref 的更新结果
40
+ * @param useSideBand - 是否使用 side-band-64k 编码
41
+ * @returns 完整的响应 Buffer
42
+ */
43
+ function generateReceivePackReport(unpackOk, unpackError, refResults, useSideBand) {
44
+ const statusLines = [];
45
+ if (unpackOk) statusLines.push(Buffer.from("unpack ok\n", "utf-8"));
46
+ else statusLines.push(Buffer.from(`unpack ${unpackError ?? "unknown error"}\n`, "utf-8"));
47
+ for (const result of refResults) if (result.success) statusLines.push(Buffer.from(`ok ${result.refName}\n`, "utf-8"));
48
+ else statusLines.push(Buffer.from(`ng ${result.refName} ${result.error ?? "unknown error"}\n`, "utf-8"));
49
+ const reportStatusData = Buffer.concat(statusLines);
50
+ const pktParts = [];
51
+ const lines = reportStatusData.toString("utf-8").split("\n");
52
+ for (const line of lines) if (line.length > 0) pktParts.push(encodePktLine(line + "\n"));
53
+ pktParts.push(encodeFlushPkt());
54
+ const reportPktSequence = Buffer.concat(pktParts);
55
+ if (!useSideBand) return reportPktSequence;
56
+ const parts = [];
57
+ const progressMsg = `Unpacking objects: 100% (${refResults.length}/${refResults.length})\n`;
58
+ parts.push(encodeSideBandFrame(2, Buffer.from(progressMsg, "utf-8")));
59
+ parts.push(encodeSideBandFrame(1, reportPktSequence));
60
+ parts.push(encodeFlushPkt());
61
+ return Buffer.concat(parts);
62
+ }
63
+ //#endregion
64
+ export { generateReceivePackReport };
@@ -0,0 +1,41 @@
1
+ import { RepositoryBackend } from "../../../backend/types.mjs";
2
+ import { ReceivePackOptions } from "./types.mjs";
3
+
4
+ //#region src/transport/server/receive-pack/service.d.ts
5
+ /**
6
+ * Receive-Pack 服务接口
7
+ *
8
+ * 提供协议无关的 receive-pack 能力:
9
+ * - advertise(): 生成服务 ref 广告
10
+ * - handleRequest(): 处理客户端请求
11
+ */
12
+ interface ReceivePackService {
13
+ /**
14
+ * 生成 ref 广告
15
+ */
16
+ advertise(): Buffer;
17
+ /**
18
+ * 处理请求
19
+ *
20
+ * @param body - 客户端请求体
21
+ * @returns 服务端响应
22
+ */
23
+ handleRequest(body: Buffer): Buffer;
24
+ }
25
+ /**
26
+ * 创建 Receive-Pack 服务实例
27
+ *
28
+ * @param backend - 仓库后端
29
+ * @param options - 处理选项
30
+ * @returns ReceivePackService 实例
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const service = createReceivePackService(backend);
35
+ * const advertise = service.advertise();
36
+ * const response = service.handleRequest(body);
37
+ * ```
38
+ */
39
+ declare function createReceivePackService(backend: RepositoryBackend, options?: ReceivePackOptions): ReceivePackService;
40
+ //#endregion
41
+ export { ReceivePackService, createReceivePackService };
@@ -0,0 +1,39 @@
1
+ import { advertiseReceivePack } from "./advertise.mjs";
2
+ import { handleReceivePackRequest } from "./handler.mjs";
3
+ //#region src/transport/server/receive-pack/service.ts
4
+ /**
5
+ * receive-pack 服务编排器
6
+ *
7
+ * 聚合 Git 协议 v1 receive-pack 的服务端能力:
8
+ * - ref 广告生成
9
+ * - push 请求处理
10
+ *
11
+ * 底层协议实现细节仍保留在各子模块中,
12
+ * 本文件仅提供协议无关的服务接口和工厂。
13
+ */
14
+ /**
15
+ * 创建 Receive-Pack 服务实例
16
+ *
17
+ * @param backend - 仓库后端
18
+ * @param options - 处理选项
19
+ * @returns ReceivePackService 实例
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const service = createReceivePackService(backend);
24
+ * const advertise = service.advertise();
25
+ * const response = service.handleRequest(body);
26
+ * ```
27
+ */
28
+ function createReceivePackService(backend, options) {
29
+ return {
30
+ advertise() {
31
+ return advertiseReceivePack(backend);
32
+ },
33
+ handleRequest(body) {
34
+ return handleReceivePackRequest(backend, body, options);
35
+ }
36
+ };
37
+ }
38
+ //#endregion
39
+ export { createReceivePackService };
@@ -0,0 +1,56 @@
1
+ import { SHA1 } from "../../../core/types.mjs";
2
+ import { GitError } from "../../../core/errors.mjs";
3
+
4
+ //#region src/transport/server/receive-pack/types.d.ts
5
+ /**
6
+ * receive-pack 服务错误
7
+ *
8
+ * 当请求解析、处理或响应生成过程中遇到可预见的错误时抛出。
9
+ */
10
+ declare class ReceivePackServiceError extends GitError {
11
+ constructor(message: string);
12
+ }
13
+ /**
14
+ * Receive-pack 命令(ref 更新)
15
+ *
16
+ * 表示客户端请求的一次 ref 变更。
17
+ */
18
+ interface ReceivePackCommand {
19
+ /** 客户端声称的服务端当前哈希(新建时为 000...0) */
20
+ readonly oldHash: SHA1;
21
+ /** 要设置的目标哈希(删除时为 000...0) */
22
+ readonly newHash: SHA1;
23
+ /** 引用完整名称,如 "refs/heads/main" */
24
+ readonly refName: string;
25
+ }
26
+ /**
27
+ * 解析后的 receive-pack 请求
28
+ */
29
+ interface ParsedReceivePackRequest {
30
+ /** 客户端能力列表(首行 NUL 后的内容) */
31
+ readonly capabilities: string[];
32
+ /** ref 更新命令列表 */
33
+ readonly commands: ReceivePackCommand[];
34
+ /** packfile 数据(可能为空) */
35
+ readonly packfile: Buffer;
36
+ }
37
+ /**
38
+ * 单个 ref 更新的处理结果
39
+ */
40
+ interface ReceivePackUpdateResult {
41
+ readonly refName: string;
42
+ readonly success: boolean;
43
+ readonly error?: string;
44
+ }
45
+ /**
46
+ * receive-pack 处理选项
47
+ */
48
+ interface ReceivePackOptions {
49
+ /**
50
+ * 是否拒绝非 fast-forward 推送(类似 receive.denyNonFastForwards)
51
+ * 默认 false
52
+ */
53
+ readonly denyNonFastForwards?: boolean;
54
+ }
55
+ //#endregion
56
+ export { ParsedReceivePackRequest, ReceivePackCommand, ReceivePackOptions, ReceivePackServiceError, ReceivePackUpdateResult };
@@ -0,0 +1,25 @@
1
+ import { GitError } from "../../../core/errors.mjs";
2
+ import { sha1 } from "../../../core/types.mjs";
3
+ //#region src/transport/server/receive-pack/types.ts
4
+ /**
5
+ * receive-pack 服务端类型定义与常量
6
+ */
7
+ /** 零哈希(表示新建或删除引用) */
8
+ const ZERO_HASH = sha1("0000000000000000000000000000000000000000");
9
+ /** 服务端 agent 字符串 */
10
+ const SERVER_AGENT = "nano-git/0.1";
11
+ /** v1 广告中 prefix-ref 的 magic 名称 */
12
+ const CAPABILITIES_REF = "capabilities^{}";
13
+ /**
14
+ * receive-pack 服务错误
15
+ *
16
+ * 当请求解析、处理或响应生成过程中遇到可预见的错误时抛出。
17
+ */
18
+ var ReceivePackServiceError = class extends GitError {
19
+ constructor(message) {
20
+ super(`receive-pack: ${message}`);
21
+ this.name = "ReceivePackServiceError";
22
+ }
23
+ };
24
+ //#endregion
25
+ export { CAPABILITIES_REF, ReceivePackServiceError, SERVER_AGENT, ZERO_HASH };