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.
- package/README.md +407 -0
- package/dist/backend/file-backend.d.mts +26 -0
- package/dist/backend/file-backend.mjs +83 -0
- package/dist/backend/file.d.mts +2 -0
- package/dist/backend/file.mjs +2 -0
- package/dist/backend/index.d.mts +2 -0
- package/dist/backend/index.mjs +1 -0
- package/dist/backend/memory-backend.d.mts +25 -0
- package/dist/backend/memory-backend.mjs +33 -0
- package/dist/backend/memory.d.mts +2 -0
- package/dist/backend/memory.mjs +2 -0
- package/dist/backend/types.d.mts +90 -0
- package/dist/core/errors.d.mts +124 -0
- package/dist/core/errors.mjs +168 -0
- package/dist/core/hash-digest.d.mts +35 -0
- package/dist/core/hash-digest.mjs +50 -0
- package/dist/core/hash-file.d.mts +20 -0
- package/dist/core/hash-file.mjs +27 -0
- package/dist/core/hash-path.d.mts +17 -0
- package/dist/core/hash-path.mjs +35 -0
- package/dist/core/types/odb.d.mts +63 -0
- package/dist/core/types/refs.d.mts +140 -0
- package/dist/core/types/refs.mjs +19 -0
- package/dist/core/types/shallow.d.mts +52 -0
- package/dist/core/types.d.mts +154 -0
- package/dist/core/types.mjs +43 -0
- package/dist/errors.d.mts +2 -0
- package/dist/errors.mjs +2 -0
- package/dist/hash-file.d.mts +2 -0
- package/dist/hash-file.mjs +2 -0
- package/dist/index.d.mts +16 -0
- package/dist/index.mjs +14 -0
- package/dist/objects/author.d.mts +25 -0
- package/dist/objects/author.mjs +45 -0
- package/dist/objects/blob.d.mts +15 -0
- package/dist/objects/blob.mjs +20 -0
- package/dist/objects/codec.d.mts +46 -0
- package/dist/objects/codec.mjs +84 -0
- package/dist/objects/commit.d.mts +26 -0
- package/dist/objects/commit.mjs +160 -0
- package/dist/objects/index.d.mts +8 -0
- package/dist/objects/index.mjs +8 -0
- package/dist/objects/raw.d.mts +85 -0
- package/dist/objects/raw.mjs +111 -0
- package/dist/objects/tag.d.mts +26 -0
- package/dist/objects/tag.mjs +148 -0
- package/dist/objects/tree.d.mts +22 -0
- package/dist/objects/tree.mjs +66 -0
- package/dist/odb/file-utils.mjs +136 -0
- package/dist/odb/file.d.mts +22 -0
- package/dist/odb/file.mjs +66 -0
- package/dist/odb/memory.d.mts +22 -0
- package/dist/odb/memory.mjs +54 -0
- package/dist/pack/composite-store.d.mts +76 -0
- package/dist/pack/composite-store.mjs +133 -0
- package/dist/pack/constants.mjs +48 -0
- package/dist/pack/crc32.mjs +38 -0
- package/dist/pack/delta-apply.d.mts +21 -0
- package/dist/pack/delta-apply.mjs +71 -0
- package/dist/pack/delta-create.d.mts +28 -0
- package/dist/pack/delta-create.mjs +151 -0
- package/dist/pack/index.d.mts +16 -0
- package/dist/pack/index.mjs +13 -0
- package/dist/pack/object-header.d.mts +36 -0
- package/dist/pack/object-header.mjs +72 -0
- package/dist/pack/ofs-delta-offset.d.mts +35 -0
- package/dist/pack/ofs-delta-offset.mjs +59 -0
- package/dist/pack/pack-builder-types.d.mts +19 -0
- package/dist/pack/pack-builder.d.mts +52 -0
- package/dist/pack/pack-builder.mjs +80 -0
- package/dist/pack/pack-encoding.mjs +76 -0
- package/dist/pack/pack-index-reader.d.mts +57 -0
- package/dist/pack/pack-index-reader.mjs +90 -0
- package/dist/pack/pack-index-types.d.mts +16 -0
- package/dist/pack/pack-index-writer.d.mts +45 -0
- package/dist/pack/pack-index-writer.mjs +106 -0
- package/dist/pack/pack-reader-resolver.mjs +104 -0
- package/dist/pack/pack-reader-types.d.mts +31 -0
- package/dist/pack/pack-reader-types.mjs +22 -0
- package/dist/pack/pack-reader-utils.mjs +61 -0
- package/dist/pack/pack-reader.d.mts +81 -0
- package/dist/pack/pack-reader.mjs +171 -0
- package/dist/pack/pack-store-loader.mjs +79 -0
- package/dist/pack/pack-store-types.d.mts +16 -0
- package/dist/pack/pack-store.d.mts +55 -0
- package/dist/pack/pack-store.mjs +114 -0
- package/dist/pack/pack-writer.mjs +96 -0
- package/dist/pack/varint.d.mts +34 -0
- package/dist/pack/varint.mjs +57 -0
- package/dist/refs/file.d.mts +6 -0
- package/dist/refs/file.mjs +222 -0
- package/dist/refs/fs-utils.mjs +30 -0
- package/dist/refs/memory.d.mts +14 -0
- package/dist/refs/memory.mjs +104 -0
- package/dist/refs/names.d.mts +57 -0
- package/dist/refs/names.mjs +80 -0
- package/dist/refs/resolve.d.mts +33 -0
- package/dist/refs/resolve.mjs +55 -0
- package/dist/refs/shallow/file.d.mts +17 -0
- package/dist/refs/shallow/file.mjs +61 -0
- package/dist/refs/shallow/memory.d.mts +18 -0
- package/dist/refs/shallow/memory.mjs +33 -0
- package/dist/repository/core.d.mts +3 -0
- package/dist/repository/core.mjs +2 -0
- package/dist/repository/create.d.mts +22 -0
- package/dist/repository/create.mjs +43 -0
- package/dist/repository/file.d.mts +43 -0
- package/dist/repository/file.mjs +82 -0
- package/dist/repository/import/import-glob.mjs +44 -0
- package/dist/repository/import/import-plan-builder.mjs +625 -0
- package/dist/repository/import/import-session-types.d.mts +280 -0
- package/dist/repository/import/import-session.mjs +96 -0
- package/dist/repository/import/import-view.mjs +133 -0
- package/dist/repository/memory.d.mts +17 -0
- package/dist/repository/memory.mjs +33 -0
- package/dist/repository/ops/fetch-operations.mjs +20 -0
- package/dist/repository/ops/fetch-types.d.mts +82 -0
- package/dist/repository/ops/fetch-url.mjs +82 -0
- package/dist/repository/ops/fs-object-operations.mjs +31 -0
- package/dist/repository/ops/maintenance-operations.mjs +62 -0
- package/dist/repository/ops/maintenance-types.d.mts +42 -0
- package/dist/repository/ops/object-operations.mjs +65 -0
- package/dist/repository/ops/object-types.d.mts +109 -0
- package/dist/repository/ops/push-operations.mjs +16 -0
- package/dist/repository/ops/push-resolution.mjs +17 -0
- package/dist/repository/ops/push-types.d.mts +72 -0
- package/dist/repository/ops/push-url.mjs +45 -0
- package/dist/repository/ops/reachability.mjs +60 -0
- package/dist/repository/ops/ref-operations.mjs +86 -0
- package/dist/repository/ops/ref-types.d.mts +70 -0
- package/dist/repository/tree/tree-patch.d.mts +64 -0
- package/dist/repository/tree/tree-patch.mjs +268 -0
- package/dist/repository/tree/tree-walk.d.mts +46 -0
- package/dist/repository/tree/tree-walk.mjs +65 -0
- package/dist/repository/tree/tree-writer.mjs +68 -0
- package/dist/repository/types.d.mts +36 -0
- package/dist/sha1.d.mts +4 -0
- package/dist/sha1.mjs +4 -0
- package/dist/transport/client/receive-pack/http.d.mts +33 -0
- package/dist/transport/client/receive-pack/http.mjs +99 -0
- package/dist/transport/client/receive-pack/push-error.d.mts +23 -0
- package/dist/transport/client/receive-pack/push-error.mjs +32 -0
- package/dist/transport/client/receive-pack/push-pack-plan.d.mts +28 -0
- package/dist/transport/client/receive-pack/push-pack-plan.mjs +60 -0
- package/dist/transport/client/receive-pack/push-policy.d.mts +19 -0
- package/dist/transport/client/receive-pack/push-policy.mjs +64 -0
- package/dist/transport/client/receive-pack/push-ref-plan.d.mts +45 -0
- package/dist/transport/client/receive-pack/push-ref-plan.mjs +108 -0
- package/dist/transport/client/receive-pack/push-report.d.mts +28 -0
- package/dist/transport/client/receive-pack/push-report.mjs +84 -0
- package/dist/transport/client/receive-pack/push-request-plan.mjs +52 -0
- package/dist/transport/client/receive-pack/push.d.mts +32 -0
- package/dist/transport/client/receive-pack/push.mjs +97 -0
- package/dist/transport/client/receive-pack/request.d.mts +39 -0
- package/dist/transport/client/receive-pack/request.mjs +46 -0
- package/dist/transport/client/receive-pack/response.d.mts +26 -0
- package/dist/transport/client/receive-pack/response.mjs +52 -0
- package/dist/transport/client/receive-pack/result.d.mts +34 -0
- package/dist/transport/client/receive-pack/result.mjs +100 -0
- package/dist/transport/client/upload-pack/capability-advertisement.d.mts +56 -0
- package/dist/transport/client/upload-pack/capability-advertisement.mjs +130 -0
- package/dist/transport/client/upload-pack/fetch.d.mts +109 -0
- package/dist/transport/client/upload-pack/fetch.mjs +392 -0
- package/dist/transport/client/upload-pack/http.d.mts +29 -0
- package/dist/transport/client/upload-pack/http.mjs +79 -0
- package/dist/transport/client/upload-pack/ls-refs.d.mts +75 -0
- package/dist/transport/client/upload-pack/ls-refs.mjs +150 -0
- package/dist/transport/client/upload-pack/object-info.d.mts +65 -0
- package/dist/transport/client/upload-pack/object-info.mjs +111 -0
- package/dist/transport/client/upload-pack/types.d.mts +153 -0
- package/dist/transport/http/index.d.mts +3 -0
- package/dist/transport/http/index.mjs +2 -0
- package/dist/transport/http/smart-http.d.mts +46 -0
- package/dist/transport/http/smart-http.mjs +176 -0
- package/dist/transport/http/types.d.mts +27 -0
- package/dist/transport/index.d.mts +9 -0
- package/dist/transport/index.mjs +8 -0
- package/dist/transport/protocol/object-graph.d.mts +63 -0
- package/dist/transport/protocol/object-graph.mjs +149 -0
- package/dist/transport/protocol/pkt-line.d.mts +109 -0
- package/dist/transport/protocol/pkt-line.mjs +195 -0
- package/dist/transport/protocol/ref-advertisement.mjs +185 -0
- package/dist/transport/protocol/ref-collection.d.mts +38 -0
- package/dist/transport/protocol/ref-collection.mjs +63 -0
- package/dist/transport/protocol/ref-match.d.mts +39 -0
- package/dist/transport/protocol/ref-match.mjs +42 -0
- package/dist/transport/protocol/refspec.d.mts +44 -0
- package/dist/transport/protocol/refspec.mjs +79 -0
- package/dist/transport/protocol/side-band.d.mts +65 -0
- package/dist/transport/protocol/side-band.mjs +142 -0
- package/dist/transport/protocol/transport-capabilities.mjs +46 -0
- package/dist/transport/protocol/types.d.mts +148 -0
- package/dist/transport/protocol/update-refs.d.mts +68 -0
- package/dist/transport/protocol/update-refs.mjs +126 -0
- package/dist/transport/receive-pack.d.mts +11 -0
- package/dist/transport/receive-pack.mjs +11 -0
- package/dist/transport/server/receive-pack/advertise.d.mts +28 -0
- package/dist/transport/server/receive-pack/advertise.mjs +88 -0
- package/dist/transport/server/receive-pack/handler.d.mts +30 -0
- package/dist/transport/server/receive-pack/handler.mjs +156 -0
- package/dist/transport/server/receive-pack/index.d.mts +6 -0
- package/dist/transport/server/receive-pack/index.mjs +6 -0
- package/dist/transport/server/receive-pack/parse.d.mts +17 -0
- package/dist/transport/server/receive-pack/parse.mjs +80 -0
- package/dist/transport/server/receive-pack/report-status.mjs +64 -0
- package/dist/transport/server/receive-pack/service.d.mts +41 -0
- package/dist/transport/server/receive-pack/service.mjs +39 -0
- package/dist/transport/server/receive-pack/types.d.mts +56 -0
- package/dist/transport/server/receive-pack/types.mjs +25 -0
- package/dist/transport/server/receive-pack/unpack.mjs +119 -0
- package/dist/transport/server/upload-pack/advertise.d.mts +20 -0
- package/dist/transport/server/upload-pack/advertise.mjs +30 -0
- package/dist/transport/server/upload-pack/command.d.mts +43 -0
- package/dist/transport/server/upload-pack/command.mjs +56 -0
- package/dist/transport/server/upload-pack/fetch.d.mts +43 -0
- package/dist/transport/server/upload-pack/fetch.mjs +217 -0
- package/dist/transport/server/upload-pack/index.d.mts +7 -0
- package/dist/transport/server/upload-pack/index.mjs +7 -0
- package/dist/transport/server/upload-pack/ls-refs.d.mts +38 -0
- package/dist/transport/server/upload-pack/ls-refs.mjs +113 -0
- package/dist/transport/server/upload-pack/service.d.mts +40 -0
- package/dist/transport/server/upload-pack/service.mjs +51 -0
- package/dist/transport/server/upload-pack/types.d.mts +11 -0
- package/dist/transport/server/upload-pack/types.mjs +21 -0
- package/dist/transport/upload-pack.d.mts +7 -0
- package/dist/transport/upload-pack.mjs +6 -0
- package/package.json +98 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { InvalidPackError } from "../core/errors.mjs";
|
|
2
|
+
import { decodeObjectHeader } from "./object-header.mjs";
|
|
3
|
+
import { decodeOfsDeltaOffset } from "./ofs-delta-offset.mjs";
|
|
4
|
+
import { parsePackHeader } from "./pack-reader-utils.mjs";
|
|
5
|
+
import { resolveOfsDeltaPackObject, resolvePlainPackObject, resolveRefDeltaPackObject } from "./pack-reader-resolver.mjs";
|
|
6
|
+
import "./pack-reader-types.mjs";
|
|
7
|
+
//#region src/pack/pack-reader.ts
|
|
8
|
+
/**
|
|
9
|
+
* Packfile 读取
|
|
10
|
+
*
|
|
11
|
+
* Git Packfile 格式:
|
|
12
|
+
* - 头部:4 字节签名 "PACK" + 4 字节版本 + 4 字节对象数
|
|
13
|
+
* - 对象序列:每个对象包含头部 + zlib 压缩数据
|
|
14
|
+
* - 尾部:20 字节 SHA-1 校验和
|
|
15
|
+
*
|
|
16
|
+
* 对象类型:
|
|
17
|
+
* - 非 delta 对象:直接存储压缩后的对象数据
|
|
18
|
+
* - ofs_delta:基于偏移量的 delta,引用同一 packfile 中的其他对象
|
|
19
|
+
* - ref_delta:基于 SHA-1 的 delta,可引用任意对象
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const reader = createPackReader(packData);
|
|
24
|
+
* for (const obj of reader.objects()) {
|
|
25
|
+
* console.log(obj.type, obj.hash);
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* 拆分共享类型与底层辅助函数后,
|
|
30
|
+
* 当前文件只保留对象遍历与 delta 解析主流程。
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* 创建 Packfile 读取器
|
|
34
|
+
*
|
|
35
|
+
* @param data - 完整的 packfile 数据
|
|
36
|
+
* @returns Packfile 读取器实例
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const reader = createPackReader(packData);
|
|
41
|
+
* console.log(`Packfile 包含 ${reader.objectCount} 个对象`);
|
|
42
|
+
*
|
|
43
|
+
* // 遍历所有对象
|
|
44
|
+
* for (const obj of reader.objects()) {
|
|
45
|
+
* console.log(`${obj.type} ${obj.hash}`);
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
function createPackReader(data) {
|
|
50
|
+
return new PackReader(data);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Packfile 读取器类
|
|
54
|
+
*
|
|
55
|
+
* 使用惰性解析策略:对象在首次被访问时才解析。
|
|
56
|
+
* - getByHash() / getByOffset() / has() 只解析到找到目标为止
|
|
57
|
+
* - objects() / listHashes() 解析全部对象
|
|
58
|
+
*/
|
|
59
|
+
var PackReader = class {
|
|
60
|
+
data;
|
|
61
|
+
_objectCount;
|
|
62
|
+
objectsByOffset = /* @__PURE__ */ new Map();
|
|
63
|
+
objectsByHash = /* @__PURE__ */ new Map();
|
|
64
|
+
parseOffset;
|
|
65
|
+
parsedCount = 0;
|
|
66
|
+
fullyParsed = false;
|
|
67
|
+
constructor(data) {
|
|
68
|
+
this.data = data;
|
|
69
|
+
this._objectCount = parsePackHeader(data);
|
|
70
|
+
this.parseOffset = 12;
|
|
71
|
+
}
|
|
72
|
+
/** 对象数量 */
|
|
73
|
+
get objectCount() {
|
|
74
|
+
return this._objectCount;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 解析下一个对象
|
|
78
|
+
*/
|
|
79
|
+
parseNext() {
|
|
80
|
+
if (this.fullyParsed) return;
|
|
81
|
+
const endOffset = this.data.length - 20;
|
|
82
|
+
if (this.parsedCount >= this._objectCount || this.parseOffset >= endOffset) {
|
|
83
|
+
this.fullyParsed = true;
|
|
84
|
+
if (this.parsedCount < this._objectCount) throw new InvalidPackError(`Unexpected end of packfile at object ${this.parsedCount}`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const objOffset = this.parseOffset;
|
|
88
|
+
const [typeNum, _size, headerBytes] = decodeObjectHeader(this.data, this.parseOffset);
|
|
89
|
+
this.parseOffset += headerBytes;
|
|
90
|
+
let obj;
|
|
91
|
+
if (typeNum === 6) {
|
|
92
|
+
const [negOffset, offsetBytes] = decodeOfsDeltaOffset(this.data, this.parseOffset);
|
|
93
|
+
this.parseOffset += offsetBytes;
|
|
94
|
+
const resolved = resolveOfsDeltaPackObject(this.data, this.parseOffset, objOffset, negOffset, this.objectsByOffset);
|
|
95
|
+
obj = resolved.object;
|
|
96
|
+
this.parseOffset = resolved.nextOffset;
|
|
97
|
+
} else if (typeNum === 7) {
|
|
98
|
+
const baseHash = this.data.subarray(this.parseOffset, this.parseOffset + 20).toString("hex");
|
|
99
|
+
this.parseOffset += 20;
|
|
100
|
+
const resolved = resolveRefDeltaPackObject(this.data, this.parseOffset, objOffset, baseHash, this.objectsByHash);
|
|
101
|
+
obj = resolved.object;
|
|
102
|
+
this.parseOffset = resolved.nextOffset;
|
|
103
|
+
} else {
|
|
104
|
+
const resolved = resolvePlainPackObject(this.data, this.parseOffset, objOffset, typeNum);
|
|
105
|
+
obj = resolved.object;
|
|
106
|
+
this.parseOffset = resolved.nextOffset;
|
|
107
|
+
}
|
|
108
|
+
this.objectsByOffset.set(objOffset, obj);
|
|
109
|
+
this.objectsByHash.set(obj.hash, obj);
|
|
110
|
+
this.parsedCount++;
|
|
111
|
+
if (this.parsedCount >= this._objectCount) this.fullyParsed = true;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 按需解析直到条件满足或解析全部
|
|
115
|
+
*/
|
|
116
|
+
parseUntil(condition) {
|
|
117
|
+
while (!this.fullyParsed && !condition()) this.parseNext();
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* 解析所有剩余对象
|
|
121
|
+
*/
|
|
122
|
+
parseAllRemaining() {
|
|
123
|
+
this.parseUntil(() => this.fullyParsed);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* 遍历所有对象
|
|
127
|
+
*
|
|
128
|
+
* 按 offset 顺序解析并迭代所有对象。
|
|
129
|
+
*/
|
|
130
|
+
*objects() {
|
|
131
|
+
this.parseAllRemaining();
|
|
132
|
+
yield* this.objectsByOffset.values();
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* 根据哈希获取对象(惰性解析)
|
|
136
|
+
*
|
|
137
|
+
* 只解析到找到目标对象为止,不解析全部。
|
|
138
|
+
*/
|
|
139
|
+
getByHash(hash) {
|
|
140
|
+
if (this.objectsByHash.has(hash)) return this.objectsByHash.get(hash);
|
|
141
|
+
this.parseUntil(() => this.objectsByHash.has(hash));
|
|
142
|
+
return this.objectsByHash.get(hash);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* 根据偏移量获取对象(惰性解析)
|
|
146
|
+
*
|
|
147
|
+
* 只解析到目标偏移位置为止。
|
|
148
|
+
*/
|
|
149
|
+
getByOffset(offset) {
|
|
150
|
+
if (this.objectsByOffset.has(offset)) return this.objectsByOffset.get(offset);
|
|
151
|
+
this.parseUntil(() => this.objectsByOffset.has(offset) || this.fullyParsed);
|
|
152
|
+
return this.objectsByOffset.get(offset);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* 检查对象是否存在(惰性查找)
|
|
156
|
+
*/
|
|
157
|
+
has(hash) {
|
|
158
|
+
if (this.objectsByHash.has(hash)) return true;
|
|
159
|
+
this.parseUntil(() => this.objectsByHash.has(hash));
|
|
160
|
+
return this.objectsByHash.has(hash);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* 获取所有对象的哈希列表(解析全部)
|
|
164
|
+
*/
|
|
165
|
+
listHashes() {
|
|
166
|
+
this.parseAllRemaining();
|
|
167
|
+
return Array.from(this.objectsByHash.keys());
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
//#endregion
|
|
171
|
+
export { PackReader, createPackReader };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { PackReader } from "./pack-reader.mjs";
|
|
2
|
+
import { createPackIndexReader } from "./pack-index-reader.mjs";
|
|
3
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
//#region src/pack/pack-store-loader.ts
|
|
6
|
+
/**
|
|
7
|
+
* Pack 对象存储加载辅助函数
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* 扫描 pack 目录并加载索引信息
|
|
11
|
+
*
|
|
12
|
+
* @param packDir - `.git/objects/pack` 目录
|
|
13
|
+
* @returns 已发现的 pack 文件对
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const pairs = loadPackPairs("/tmp/repo/.git/objects/pack");
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
function loadPackPairs(packDir) {
|
|
21
|
+
if (!existsSync(packDir)) return [];
|
|
22
|
+
const pairs = [];
|
|
23
|
+
const idxFiles = readdirSync(packDir).filter((file) => file.endsWith(".idx"));
|
|
24
|
+
for (const idxFile of idxFiles) {
|
|
25
|
+
const match = idxFile.match(/^pack-([0-9a-f]{40})\.idx$/);
|
|
26
|
+
if (!match) continue;
|
|
27
|
+
const checksum = match[1];
|
|
28
|
+
if (!existsSync(join(packDir, `pack-${checksum}.pack`))) continue;
|
|
29
|
+
const idxData = readFileSync(join(packDir, idxFile));
|
|
30
|
+
pairs.push({
|
|
31
|
+
checksum,
|
|
32
|
+
index: createPackIndexReader(idxData),
|
|
33
|
+
reader: null,
|
|
34
|
+
packData: null
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return pairs;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 按需加载 packfile 读取器
|
|
41
|
+
*
|
|
42
|
+
* @param packDir - `.git/objects/pack` 目录
|
|
43
|
+
* @param pair - pack 文件对
|
|
44
|
+
* @returns PackReader 实例
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const reader = getPackReader(packDir, pair);
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
function getPackReader(packDir, pair) {
|
|
52
|
+
if (!pair.reader) {
|
|
53
|
+
pair.packData = readFileSync(join(packDir, `pack-${pair.checksum}.pack`));
|
|
54
|
+
pair.reader = new PackReader(pair.packData);
|
|
55
|
+
}
|
|
56
|
+
return pair.reader;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 将 pack 文件对转换为对外展示信息
|
|
60
|
+
*
|
|
61
|
+
* @param packDir - `.git/objects/pack` 目录
|
|
62
|
+
* @param pair - pack 文件对
|
|
63
|
+
* @returns pack 文件信息
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* const info = toPackFileInfo(packDir, pair);
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
function toPackFileInfo(packDir, pair) {
|
|
71
|
+
return {
|
|
72
|
+
checksum: pair.checksum,
|
|
73
|
+
packPath: join(packDir, `pack-${pair.checksum}.pack`),
|
|
74
|
+
idxPath: join(packDir, `pack-${pair.checksum}.idx`),
|
|
75
|
+
objectCount: pair.index.objectCount
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
export { getPackReader, loadPackPairs, toPackFileInfo };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/pack/pack-store-types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* 一个已发现的 pack 文件对
|
|
4
|
+
*/
|
|
5
|
+
interface PackFileInfo {
|
|
6
|
+
/** pack 文件校验和 */
|
|
7
|
+
checksum: string;
|
|
8
|
+
/** .pack 文件路径 */
|
|
9
|
+
packPath: string;
|
|
10
|
+
/** .idx 文件路径 */
|
|
11
|
+
idxPath: string;
|
|
12
|
+
/** 索引中的对象数量 */
|
|
13
|
+
objectCount: number;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { PackFileInfo };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { SHA1 } from "../core/types.mjs";
|
|
2
|
+
import { ObjectSource } from "../core/types/odb.mjs";
|
|
3
|
+
import { PackFileInfo } from "./pack-store-types.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/pack/pack-store.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* 基于 Packfile 的对象源接口
|
|
8
|
+
*/
|
|
9
|
+
interface PackObjectStore extends ObjectSource {
|
|
10
|
+
/**
|
|
11
|
+
* 刷新 pack 目录缓存
|
|
12
|
+
*
|
|
13
|
+
* 当外部新增或删除 packfile 后,需要调用此方法重新扫描。
|
|
14
|
+
*/
|
|
15
|
+
refresh(): void;
|
|
16
|
+
/**
|
|
17
|
+
* 列出当前可见的 pack 文件对
|
|
18
|
+
*/
|
|
19
|
+
listPacks(): PackFileInfo[];
|
|
20
|
+
/**
|
|
21
|
+
* 获取所有 packfile 中的对象哈希列表
|
|
22
|
+
*
|
|
23
|
+
* 保留此方法作为更明确的命名别名。
|
|
24
|
+
*/
|
|
25
|
+
listHashes(): SHA1[];
|
|
26
|
+
/** 获取 packfile 数量 */
|
|
27
|
+
readonly packCount: number;
|
|
28
|
+
/** 获取所有对象数量 */
|
|
29
|
+
readonly objectCount: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 创建基于 Packfile 的对象源
|
|
33
|
+
*
|
|
34
|
+
* 扫描 .git/objects/pack/ 目录,加载所有 .idx 文件。
|
|
35
|
+
* packfile 数据按需加载(首次读取时才加载)。
|
|
36
|
+
*
|
|
37
|
+
* @param gitDir - .git 目录的路径
|
|
38
|
+
* @returns 基于 Packfile 的对象源
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const store = createPackObjectStore("/path/to/.git");
|
|
43
|
+
*
|
|
44
|
+
* // 读取原始对象
|
|
45
|
+
* const raw = store.read(hash);
|
|
46
|
+
*
|
|
47
|
+
* // 检查对象是否存在
|
|
48
|
+
* if (store.exists(hash)) {
|
|
49
|
+
* console.log("对象在 packfile 中");
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
declare function createPackObjectStore(gitDir: string): PackObjectStore;
|
|
54
|
+
//#endregion
|
|
55
|
+
export { PackObjectStore, createPackObjectStore };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { ObjectNotFoundError } from "../core/errors.mjs";
|
|
2
|
+
import { packObjectToRaw } from "./pack-reader-types.mjs";
|
|
3
|
+
import { getPackReader, loadPackPairs, toPackFileInfo } from "./pack-store-loader.mjs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
//#region src/pack/pack-store.ts
|
|
6
|
+
/**
|
|
7
|
+
* 基于 Packfile 的对象源(raw-first)
|
|
8
|
+
*
|
|
9
|
+
* 从 .git/objects/pack/ 目录中读取 packfile 和索引文件,
|
|
10
|
+
* 提供只读的原始对象读取接口。
|
|
11
|
+
*
|
|
12
|
+
* Git 的 pack 目录结构:
|
|
13
|
+
* - pack-<checksum>.pack — 打包的对象数据
|
|
14
|
+
* - pack-<checksum>.idx — 对应的索引文件
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const store = createPackObjectStore("/path/to/.git");
|
|
19
|
+
* const raw = store.read(hash);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* 创建基于 Packfile 的对象源
|
|
24
|
+
*
|
|
25
|
+
* 扫描 .git/objects/pack/ 目录,加载所有 .idx 文件。
|
|
26
|
+
* packfile 数据按需加载(首次读取时才加载)。
|
|
27
|
+
*
|
|
28
|
+
* @param gitDir - .git 目录的路径
|
|
29
|
+
* @returns 基于 Packfile 的对象源
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* const store = createPackObjectStore("/path/to/.git");
|
|
34
|
+
*
|
|
35
|
+
* // 读取原始对象
|
|
36
|
+
* const raw = store.read(hash);
|
|
37
|
+
*
|
|
38
|
+
* // 检查对象是否存在
|
|
39
|
+
* if (store.exists(hash)) {
|
|
40
|
+
* console.log("对象在 packfile 中");
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
function createPackObjectStore(gitDir) {
|
|
45
|
+
const packDir = join(gitDir, "objects", "pack");
|
|
46
|
+
const pairs = [];
|
|
47
|
+
let loaded = false;
|
|
48
|
+
/**
|
|
49
|
+
* 扫描 pack 目录并加载索引
|
|
50
|
+
*/
|
|
51
|
+
function ensureLoaded() {
|
|
52
|
+
if (loaded) return;
|
|
53
|
+
loaded = true;
|
|
54
|
+
pairs.push(...loadPackPairs(packDir));
|
|
55
|
+
}
|
|
56
|
+
function read(hash) {
|
|
57
|
+
ensureLoaded();
|
|
58
|
+
for (const pair of pairs) if (pair.index.lookup(hash)) {
|
|
59
|
+
const obj = getPackReader(packDir, pair).getByHash(hash);
|
|
60
|
+
if (obj) return packObjectToRaw(obj);
|
|
61
|
+
}
|
|
62
|
+
throw new ObjectNotFoundError(hash);
|
|
63
|
+
}
|
|
64
|
+
function tryRead(hash) {
|
|
65
|
+
ensureLoaded();
|
|
66
|
+
for (const pair of pairs) if (pair.index.lookup(hash)) {
|
|
67
|
+
const obj = getPackReader(packDir, pair).getByHash(hash);
|
|
68
|
+
if (obj) return packObjectToRaw(obj);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function exists(hash) {
|
|
72
|
+
ensureLoaded();
|
|
73
|
+
for (const pair of pairs) if (pair.index.has(hash)) return true;
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
function list() {
|
|
77
|
+
ensureLoaded();
|
|
78
|
+
const hashes = [];
|
|
79
|
+
for (const pair of pairs) hashes.push(...pair.index.listHashes());
|
|
80
|
+
return hashes;
|
|
81
|
+
}
|
|
82
|
+
function listHashes() {
|
|
83
|
+
return list();
|
|
84
|
+
}
|
|
85
|
+
function listPacks() {
|
|
86
|
+
ensureLoaded();
|
|
87
|
+
return pairs.map((pair) => toPackFileInfo(packDir, pair));
|
|
88
|
+
}
|
|
89
|
+
function refresh() {
|
|
90
|
+
loaded = false;
|
|
91
|
+
pairs.length = 0;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
refresh,
|
|
95
|
+
read,
|
|
96
|
+
tryRead,
|
|
97
|
+
exists,
|
|
98
|
+
list,
|
|
99
|
+
listHashes,
|
|
100
|
+
listPacks,
|
|
101
|
+
get packCount() {
|
|
102
|
+
ensureLoaded();
|
|
103
|
+
return pairs.length;
|
|
104
|
+
},
|
|
105
|
+
get objectCount() {
|
|
106
|
+
ensureLoaded();
|
|
107
|
+
let count = 0;
|
|
108
|
+
for (const pair of pairs) count += pair.index.objectCount;
|
|
109
|
+
return count;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { createPackObjectStore };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { buildEncodedPack, toEncodedPackObject } from "./pack-encoding.mjs";
|
|
2
|
+
//#region src/pack/pack-writer.ts
|
|
3
|
+
/**
|
|
4
|
+
* Packfile 写入
|
|
5
|
+
*
|
|
6
|
+
* 将多个 Git 对象打包成一个 packfile。
|
|
7
|
+
*
|
|
8
|
+
* Packfile 格式:
|
|
9
|
+
* - 头部:4 字节签名 "PACK" + 4 字节版本 + 4 字节对象数
|
|
10
|
+
* - 对象序列:每个对象包含头部 + zlib 压缩数据
|
|
11
|
+
* - 尾部:20 字节 SHA-1 校验和(对整个 packfile 的哈希)
|
|
12
|
+
*
|
|
13
|
+
* 当前实现不生成 delta 对象,所有对象都以非 delta 形式存储。
|
|
14
|
+
* 这简化了实现,但牺牲了一些压缩效率。
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const writer = createPackWriter();
|
|
19
|
+
* writer.addObject({ type: "blob", content: Buffer.from("hello") });
|
|
20
|
+
* const packData = writer.build();
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* 创建 Packfile 写入器
|
|
25
|
+
*
|
|
26
|
+
* @returns Packfile 写入器实例
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const writer = createPackWriter();
|
|
31
|
+
*
|
|
32
|
+
* // 添加对象
|
|
33
|
+
* writer.addObject({ type: "blob", content: Buffer.from("hello") });
|
|
34
|
+
* writer.addObject({ type: "blob", content: Buffer.from("world") });
|
|
35
|
+
*
|
|
36
|
+
* // 构建 packfile
|
|
37
|
+
* const packData = writer.build();
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
function createPackWriter() {
|
|
41
|
+
return new PackWriter();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Packfile 写入器类
|
|
45
|
+
*/
|
|
46
|
+
var PackWriter = class {
|
|
47
|
+
entries = [];
|
|
48
|
+
hashes = /* @__PURE__ */ new Set();
|
|
49
|
+
/**
|
|
50
|
+
* 添加一个原始对象到 packfile
|
|
51
|
+
*
|
|
52
|
+
* @param raw - 原始 Git 对象
|
|
53
|
+
* @returns 对象的 SHA-1 哈希
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* const hash = writer.addRaw(raw);
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
addRaw(raw) {
|
|
61
|
+
const entry = toEncodedPackObject(raw);
|
|
62
|
+
const hash = entry.hash;
|
|
63
|
+
if (this.hashes.has(hash)) return hash;
|
|
64
|
+
this.entries.push(entry);
|
|
65
|
+
this.hashes.add(hash);
|
|
66
|
+
return hash;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 获取已添加的对象数量
|
|
70
|
+
*/
|
|
71
|
+
get objectCount() {
|
|
72
|
+
return this.entries.length;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 构建 packfile 数据
|
|
76
|
+
*
|
|
77
|
+
* @returns 完整的 packfile 二进制数据
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* const packData = writer.build();
|
|
82
|
+
* writeFileSync("objects/pack/pack-xxx.pack", packData);
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
build() {
|
|
86
|
+
return buildEncodedPack(this.entries).packData;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 获取所有已添加对象的哈希列表
|
|
90
|
+
*/
|
|
91
|
+
listHashes() {
|
|
92
|
+
return this.entries.map((e) => e.hash);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
//#endregion
|
|
96
|
+
export { createPackWriter };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/pack/varint.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Packfile 通用变长整数编码/解码
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 解码变长整数(通用版本)
|
|
7
|
+
*
|
|
8
|
+
* 用于 delta 指令中的变长整数,每个字节高 1 位是继续标志,
|
|
9
|
+
* 低 7 位是数据,小端序。
|
|
10
|
+
*
|
|
11
|
+
* @param buf - 数据缓冲区
|
|
12
|
+
* @param offset - 起始偏移量
|
|
13
|
+
* @returns [值, 消耗的字节数]
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const [value, bytesRead] = decodeVarint(buf, 0);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare function decodeVarint(buf: Buffer, offset: number): [value: number, bytesRead: number];
|
|
21
|
+
/**
|
|
22
|
+
* 编码变长整数(通用版本)
|
|
23
|
+
*
|
|
24
|
+
* @param value - 要编码的值
|
|
25
|
+
* @returns 编码后的字节缓冲区
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const encoded = encodeVarint(123);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare function encodeVarint(value: number): Buffer;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { decodeVarint, encodeVarint };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { InvalidPackError } from "../core/errors.mjs";
|
|
2
|
+
//#region src/pack/varint.ts
|
|
3
|
+
/**
|
|
4
|
+
* Packfile 通用变长整数编码/解码
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* 解码变长整数(通用版本)
|
|
8
|
+
*
|
|
9
|
+
* 用于 delta 指令中的变长整数,每个字节高 1 位是继续标志,
|
|
10
|
+
* 低 7 位是数据,小端序。
|
|
11
|
+
*
|
|
12
|
+
* @param buf - 数据缓冲区
|
|
13
|
+
* @param offset - 起始偏移量
|
|
14
|
+
* @returns [值, 消耗的字节数]
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const [value, bytesRead] = decodeVarint(buf, 0);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
function decodeVarint(buf, offset) {
|
|
22
|
+
let value = 0;
|
|
23
|
+
let shift = 0;
|
|
24
|
+
let bytesRead = 0;
|
|
25
|
+
let byte;
|
|
26
|
+
do {
|
|
27
|
+
if (offset + bytesRead >= buf.length) throw new InvalidPackError("Unexpected end of data in varint");
|
|
28
|
+
byte = buf[offset + bytesRead];
|
|
29
|
+
value |= (byte & 127) << shift;
|
|
30
|
+
shift += 7;
|
|
31
|
+
bytesRead++;
|
|
32
|
+
} while (byte & 128);
|
|
33
|
+
return [value, bytesRead];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 编码变长整数(通用版本)
|
|
37
|
+
*
|
|
38
|
+
* @param value - 要编码的值
|
|
39
|
+
* @returns 编码后的字节缓冲区
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const encoded = encodeVarint(123);
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
function encodeVarint(value) {
|
|
47
|
+
const bytes = [];
|
|
48
|
+
do {
|
|
49
|
+
let byte = value & 127;
|
|
50
|
+
value >>>= 7;
|
|
51
|
+
if (value > 0) byte |= 128;
|
|
52
|
+
bytes.push(byte);
|
|
53
|
+
} while (value > 0);
|
|
54
|
+
return Buffer.from(bytes);
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { decodeVarint, encodeVarint };
|