gitx.do 0.1.0 → 0.1.2
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 +40 -353
- package/dist/do/logger.d.ts +50 -0
- package/dist/do/logger.d.ts.map +1 -0
- package/dist/do/logger.js +122 -0
- package/dist/do/logger.js.map +1 -0
- package/dist/{durable-object → do}/schema.d.ts +3 -3
- package/dist/do/schema.d.ts.map +1 -0
- package/dist/{durable-object → do}/schema.js +4 -3
- package/dist/do/schema.js.map +1 -0
- package/dist/do/types.d.ts +267 -0
- package/dist/do/types.d.ts.map +1 -0
- package/dist/do/types.js +62 -0
- package/dist/do/types.js.map +1 -0
- package/dist/index.d.ts +15 -415
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -483
- package/dist/index.js.map +1 -1
- package/package.json +13 -21
- package/dist/cli/commands/add.d.ts +0 -174
- package/dist/cli/commands/add.d.ts.map +0 -1
- package/dist/cli/commands/add.js +0 -131
- package/dist/cli/commands/add.js.map +0 -1
- package/dist/cli/commands/blame.d.ts +0 -259
- package/dist/cli/commands/blame.d.ts.map +0 -1
- package/dist/cli/commands/blame.js +0 -609
- package/dist/cli/commands/blame.js.map +0 -1
- package/dist/cli/commands/branch.d.ts +0 -249
- package/dist/cli/commands/branch.d.ts.map +0 -1
- package/dist/cli/commands/branch.js +0 -693
- package/dist/cli/commands/branch.js.map +0 -1
- package/dist/cli/commands/commit.d.ts +0 -182
- package/dist/cli/commands/commit.d.ts.map +0 -1
- package/dist/cli/commands/commit.js +0 -437
- package/dist/cli/commands/commit.js.map +0 -1
- package/dist/cli/commands/diff.d.ts +0 -464
- package/dist/cli/commands/diff.d.ts.map +0 -1
- package/dist/cli/commands/diff.js +0 -958
- package/dist/cli/commands/diff.js.map +0 -1
- package/dist/cli/commands/log.d.ts +0 -239
- package/dist/cli/commands/log.d.ts.map +0 -1
- package/dist/cli/commands/log.js +0 -535
- package/dist/cli/commands/log.js.map +0 -1
- package/dist/cli/commands/merge.d.ts +0 -106
- package/dist/cli/commands/merge.d.ts.map +0 -1
- package/dist/cli/commands/merge.js +0 -55
- package/dist/cli/commands/merge.js.map +0 -1
- package/dist/cli/commands/review.d.ts +0 -457
- package/dist/cli/commands/review.d.ts.map +0 -1
- package/dist/cli/commands/review.js +0 -533
- package/dist/cli/commands/review.js.map +0 -1
- package/dist/cli/commands/status.d.ts +0 -269
- package/dist/cli/commands/status.d.ts.map +0 -1
- package/dist/cli/commands/status.js +0 -493
- package/dist/cli/commands/status.js.map +0 -1
- package/dist/cli/commands/web.d.ts +0 -199
- package/dist/cli/commands/web.d.ts.map +0 -1
- package/dist/cli/commands/web.js +0 -696
- package/dist/cli/commands/web.js.map +0 -1
- package/dist/cli/fs-adapter.d.ts +0 -656
- package/dist/cli/fs-adapter.d.ts.map +0 -1
- package/dist/cli/fs-adapter.js +0 -1179
- package/dist/cli/fs-adapter.js.map +0 -1
- package/dist/cli/fsx-cli-adapter.d.ts +0 -359
- package/dist/cli/fsx-cli-adapter.d.ts.map +0 -1
- package/dist/cli/fsx-cli-adapter.js +0 -619
- package/dist/cli/fsx-cli-adapter.js.map +0 -1
- package/dist/cli/index.d.ts +0 -387
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -523
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/ui/components/DiffView.d.ts +0 -7
- package/dist/cli/ui/components/DiffView.d.ts.map +0 -1
- package/dist/cli/ui/components/DiffView.js +0 -11
- package/dist/cli/ui/components/DiffView.js.map +0 -1
- package/dist/cli/ui/components/ErrorDisplay.d.ts +0 -6
- package/dist/cli/ui/components/ErrorDisplay.d.ts.map +0 -1
- package/dist/cli/ui/components/ErrorDisplay.js +0 -11
- package/dist/cli/ui/components/ErrorDisplay.js.map +0 -1
- package/dist/cli/ui/components/FuzzySearch.d.ts +0 -9
- package/dist/cli/ui/components/FuzzySearch.d.ts.map +0 -1
- package/dist/cli/ui/components/FuzzySearch.js +0 -12
- package/dist/cli/ui/components/FuzzySearch.js.map +0 -1
- package/dist/cli/ui/components/LoadingSpinner.d.ts +0 -6
- package/dist/cli/ui/components/LoadingSpinner.d.ts.map +0 -1
- package/dist/cli/ui/components/LoadingSpinner.js +0 -10
- package/dist/cli/ui/components/LoadingSpinner.js.map +0 -1
- package/dist/cli/ui/components/NavigationList.d.ts +0 -9
- package/dist/cli/ui/components/NavigationList.d.ts.map +0 -1
- package/dist/cli/ui/components/NavigationList.js +0 -11
- package/dist/cli/ui/components/NavigationList.js.map +0 -1
- package/dist/cli/ui/components/ScrollableContent.d.ts +0 -8
- package/dist/cli/ui/components/ScrollableContent.d.ts.map +0 -1
- package/dist/cli/ui/components/ScrollableContent.js +0 -11
- package/dist/cli/ui/components/ScrollableContent.js.map +0 -1
- package/dist/cli/ui/components/index.d.ts +0 -7
- package/dist/cli/ui/components/index.d.ts.map +0 -1
- package/dist/cli/ui/components/index.js +0 -9
- package/dist/cli/ui/components/index.js.map +0 -1
- package/dist/cli/ui/terminal-ui.d.ts +0 -52
- package/dist/cli/ui/terminal-ui.d.ts.map +0 -1
- package/dist/cli/ui/terminal-ui.js +0 -121
- package/dist/cli/ui/terminal-ui.js.map +0 -1
- package/dist/do/BashModule.d.ts +0 -871
- package/dist/do/BashModule.d.ts.map +0 -1
- package/dist/do/BashModule.js +0 -1143
- package/dist/do/BashModule.js.map +0 -1
- package/dist/do/FsModule.d.ts +0 -601
- package/dist/do/FsModule.d.ts.map +0 -1
- package/dist/do/FsModule.js +0 -1120
- package/dist/do/FsModule.js.map +0 -1
- package/dist/do/GitModule.d.ts +0 -635
- package/dist/do/GitModule.d.ts.map +0 -1
- package/dist/do/GitModule.js +0 -781
- package/dist/do/GitModule.js.map +0 -1
- package/dist/do/GitRepoDO.d.ts +0 -281
- package/dist/do/GitRepoDO.d.ts.map +0 -1
- package/dist/do/GitRepoDO.js +0 -479
- package/dist/do/GitRepoDO.js.map +0 -1
- package/dist/do/bash-ast.d.ts +0 -246
- package/dist/do/bash-ast.d.ts.map +0 -1
- package/dist/do/bash-ast.js +0 -888
- package/dist/do/bash-ast.js.map +0 -1
- package/dist/do/container-executor.d.ts +0 -491
- package/dist/do/container-executor.d.ts.map +0 -1
- package/dist/do/container-executor.js +0 -730
- package/dist/do/container-executor.js.map +0 -1
- package/dist/do/index.d.ts +0 -53
- package/dist/do/index.d.ts.map +0 -1
- package/dist/do/index.js +0 -91
- package/dist/do/index.js.map +0 -1
- package/dist/do/tiered-storage.d.ts +0 -403
- package/dist/do/tiered-storage.d.ts.map +0 -1
- package/dist/do/tiered-storage.js +0 -689
- package/dist/do/tiered-storage.js.map +0 -1
- package/dist/do/withBash.d.ts +0 -231
- package/dist/do/withBash.d.ts.map +0 -1
- package/dist/do/withBash.js +0 -244
- package/dist/do/withBash.js.map +0 -1
- package/dist/do/withFs.d.ts +0 -237
- package/dist/do/withFs.d.ts.map +0 -1
- package/dist/do/withFs.js +0 -387
- package/dist/do/withFs.js.map +0 -1
- package/dist/do/withGit.d.ts +0 -180
- package/dist/do/withGit.d.ts.map +0 -1
- package/dist/do/withGit.js +0 -271
- package/dist/do/withGit.js.map +0 -1
- package/dist/durable-object/object-store.d.ts +0 -633
- package/dist/durable-object/object-store.d.ts.map +0 -1
- package/dist/durable-object/object-store.js +0 -1161
- package/dist/durable-object/object-store.js.map +0 -1
- package/dist/durable-object/schema.d.ts.map +0 -1
- package/dist/durable-object/schema.js.map +0 -1
- package/dist/durable-object/wal.d.ts +0 -416
- package/dist/durable-object/wal.d.ts.map +0 -1
- package/dist/durable-object/wal.js +0 -445
- package/dist/durable-object/wal.js.map +0 -1
- package/dist/mcp/adapter.d.ts +0 -772
- package/dist/mcp/adapter.d.ts.map +0 -1
- package/dist/mcp/adapter.js +0 -895
- package/dist/mcp/adapter.js.map +0 -1
- package/dist/mcp/sandbox/miniflare-evaluator.d.ts +0 -22
- package/dist/mcp/sandbox/miniflare-evaluator.d.ts.map +0 -1
- package/dist/mcp/sandbox/miniflare-evaluator.js +0 -140
- package/dist/mcp/sandbox/miniflare-evaluator.js.map +0 -1
- package/dist/mcp/sandbox/object-store-proxy.d.ts +0 -32
- package/dist/mcp/sandbox/object-store-proxy.d.ts.map +0 -1
- package/dist/mcp/sandbox/object-store-proxy.js +0 -30
- package/dist/mcp/sandbox/object-store-proxy.js.map +0 -1
- package/dist/mcp/sandbox/template.d.ts +0 -17
- package/dist/mcp/sandbox/template.d.ts.map +0 -1
- package/dist/mcp/sandbox/template.js +0 -71
- package/dist/mcp/sandbox/template.js.map +0 -1
- package/dist/mcp/sandbox.d.ts +0 -764
- package/dist/mcp/sandbox.d.ts.map +0 -1
- package/dist/mcp/sandbox.js +0 -1362
- package/dist/mcp/sandbox.js.map +0 -1
- package/dist/mcp/sdk-adapter.d.ts +0 -835
- package/dist/mcp/sdk-adapter.d.ts.map +0 -1
- package/dist/mcp/sdk-adapter.js +0 -974
- package/dist/mcp/sdk-adapter.js.map +0 -1
- package/dist/mcp/tools/do.d.ts +0 -32
- package/dist/mcp/tools/do.d.ts.map +0 -1
- package/dist/mcp/tools/do.js +0 -115
- package/dist/mcp/tools/do.js.map +0 -1
- package/dist/mcp/tools.d.ts +0 -548
- package/dist/mcp/tools.d.ts.map +0 -1
- package/dist/mcp/tools.js +0 -1934
- package/dist/mcp/tools.js.map +0 -1
- package/dist/ops/blame.d.ts +0 -551
- package/dist/ops/blame.d.ts.map +0 -1
- package/dist/ops/blame.js +0 -1037
- package/dist/ops/blame.js.map +0 -1
- package/dist/ops/branch.d.ts +0 -766
- package/dist/ops/branch.d.ts.map +0 -1
- package/dist/ops/branch.js +0 -950
- package/dist/ops/branch.js.map +0 -1
- package/dist/ops/commit-traversal.d.ts +0 -349
- package/dist/ops/commit-traversal.d.ts.map +0 -1
- package/dist/ops/commit-traversal.js +0 -821
- package/dist/ops/commit-traversal.js.map +0 -1
- package/dist/ops/commit.d.ts +0 -555
- package/dist/ops/commit.d.ts.map +0 -1
- package/dist/ops/commit.js +0 -826
- package/dist/ops/commit.js.map +0 -1
- package/dist/ops/merge-base.d.ts +0 -397
- package/dist/ops/merge-base.d.ts.map +0 -1
- package/dist/ops/merge-base.js +0 -691
- package/dist/ops/merge-base.js.map +0 -1
- package/dist/ops/merge.d.ts +0 -855
- package/dist/ops/merge.d.ts.map +0 -1
- package/dist/ops/merge.js +0 -1551
- package/dist/ops/merge.js.map +0 -1
- package/dist/ops/tag.d.ts +0 -247
- package/dist/ops/tag.d.ts.map +0 -1
- package/dist/ops/tag.js +0 -649
- package/dist/ops/tag.js.map +0 -1
- package/dist/ops/tree-builder.d.ts +0 -178
- package/dist/ops/tree-builder.d.ts.map +0 -1
- package/dist/ops/tree-builder.js +0 -271
- package/dist/ops/tree-builder.js.map +0 -1
- package/dist/ops/tree-diff.d.ts +0 -291
- package/dist/ops/tree-diff.d.ts.map +0 -1
- package/dist/ops/tree-diff.js +0 -705
- package/dist/ops/tree-diff.js.map +0 -1
- package/dist/pack/delta.d.ts +0 -248
- package/dist/pack/delta.d.ts.map +0 -1
- package/dist/pack/delta.js +0 -736
- package/dist/pack/delta.js.map +0 -1
- package/dist/pack/format.d.ts +0 -446
- package/dist/pack/format.d.ts.map +0 -1
- package/dist/pack/format.js +0 -572
- package/dist/pack/format.js.map +0 -1
- package/dist/pack/full-generation.d.ts +0 -612
- package/dist/pack/full-generation.d.ts.map +0 -1
- package/dist/pack/full-generation.js +0 -1378
- package/dist/pack/full-generation.js.map +0 -1
- package/dist/pack/generation.d.ts +0 -441
- package/dist/pack/generation.d.ts.map +0 -1
- package/dist/pack/generation.js +0 -707
- package/dist/pack/generation.js.map +0 -1
- package/dist/pack/index.d.ts +0 -502
- package/dist/pack/index.d.ts.map +0 -1
- package/dist/pack/index.js +0 -833
- package/dist/pack/index.js.map +0 -1
- package/dist/refs/branch.d.ts +0 -668
- package/dist/refs/branch.d.ts.map +0 -1
- package/dist/refs/branch.js +0 -897
- package/dist/refs/branch.js.map +0 -1
- package/dist/refs/storage.d.ts +0 -833
- package/dist/refs/storage.d.ts.map +0 -1
- package/dist/refs/storage.js +0 -1023
- package/dist/refs/storage.js.map +0 -1
- package/dist/refs/tag.d.ts +0 -860
- package/dist/refs/tag.d.ts.map +0 -1
- package/dist/refs/tag.js +0 -996
- package/dist/refs/tag.js.map +0 -1
- package/dist/storage/backend.d.ts +0 -425
- package/dist/storage/backend.d.ts.map +0 -1
- package/dist/storage/backend.js +0 -41
- package/dist/storage/backend.js.map +0 -1
- package/dist/storage/fsx-adapter.d.ts +0 -204
- package/dist/storage/fsx-adapter.d.ts.map +0 -1
- package/dist/storage/fsx-adapter.js +0 -470
- package/dist/storage/fsx-adapter.js.map +0 -1
- package/dist/storage/lru-cache.d.ts +0 -691
- package/dist/storage/lru-cache.d.ts.map +0 -1
- package/dist/storage/lru-cache.js +0 -813
- package/dist/storage/lru-cache.js.map +0 -1
- package/dist/storage/object-index.d.ts +0 -585
- package/dist/storage/object-index.d.ts.map +0 -1
- package/dist/storage/object-index.js +0 -532
- package/dist/storage/object-index.js.map +0 -1
- package/dist/storage/r2-pack.d.ts +0 -1257
- package/dist/storage/r2-pack.d.ts.map +0 -1
- package/dist/storage/r2-pack.js +0 -1770
- package/dist/storage/r2-pack.js.map +0 -1
- package/dist/tiered/cdc-pipeline.d.ts +0 -1888
- package/dist/tiered/cdc-pipeline.d.ts.map +0 -1
- package/dist/tiered/cdc-pipeline.js +0 -1880
- package/dist/tiered/cdc-pipeline.js.map +0 -1
- package/dist/tiered/migration.d.ts +0 -1104
- package/dist/tiered/migration.d.ts.map +0 -1
- package/dist/tiered/migration.js +0 -1214
- package/dist/tiered/migration.js.map +0 -1
- package/dist/tiered/parquet-writer.d.ts +0 -1145
- package/dist/tiered/parquet-writer.d.ts.map +0 -1
- package/dist/tiered/parquet-writer.js +0 -1183
- package/dist/tiered/parquet-writer.js.map +0 -1
- package/dist/tiered/read-path.d.ts +0 -835
- package/dist/tiered/read-path.d.ts.map +0 -1
- package/dist/tiered/read-path.js +0 -487
- package/dist/tiered/read-path.js.map +0 -1
- package/dist/types/capability.d.ts +0 -1385
- package/dist/types/capability.d.ts.map +0 -1
- package/dist/types/capability.js +0 -36
- package/dist/types/capability.js.map +0 -1
- package/dist/types/index.d.ts +0 -13
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -18
- package/dist/types/index.js.map +0 -1
- package/dist/types/objects.d.ts +0 -692
- package/dist/types/objects.d.ts.map +0 -1
- package/dist/types/objects.js +0 -837
- package/dist/types/objects.js.map +0 -1
- package/dist/types/storage.d.ts +0 -603
- package/dist/types/storage.d.ts.map +0 -1
- package/dist/types/storage.js +0 -191
- package/dist/types/storage.js.map +0 -1
- package/dist/types/worker-loader.d.ts +0 -60
- package/dist/types/worker-loader.d.ts.map +0 -1
- package/dist/types/worker-loader.js +0 -62
- package/dist/types/worker-loader.js.map +0 -1
- package/dist/utils/hash.d.ts +0 -197
- package/dist/utils/hash.d.ts.map +0 -1
- package/dist/utils/hash.js +0 -268
- package/dist/utils/hash.js.map +0 -1
- package/dist/utils/sha1.d.ts +0 -290
- package/dist/utils/sha1.d.ts.map +0 -1
- package/dist/utils/sha1.js +0 -582
- package/dist/utils/sha1.js.map +0 -1
- package/dist/wire/capabilities.d.ts +0 -1044
- package/dist/wire/capabilities.d.ts.map +0 -1
- package/dist/wire/capabilities.js +0 -941
- package/dist/wire/capabilities.js.map +0 -1
- package/dist/wire/path-security.d.ts +0 -157
- package/dist/wire/path-security.d.ts.map +0 -1
- package/dist/wire/path-security.js +0 -307
- package/dist/wire/path-security.js.map +0 -1
- package/dist/wire/pkt-line.d.ts +0 -345
- package/dist/wire/pkt-line.d.ts.map +0 -1
- package/dist/wire/pkt-line.js +0 -381
- package/dist/wire/pkt-line.js.map +0 -1
- package/dist/wire/receive-pack.d.ts +0 -1059
- package/dist/wire/receive-pack.d.ts.map +0 -1
- package/dist/wire/receive-pack.js +0 -1414
- package/dist/wire/receive-pack.js.map +0 -1
- package/dist/wire/smart-http.d.ts +0 -799
- package/dist/wire/smart-http.d.ts.map +0 -1
- package/dist/wire/smart-http.js +0 -945
- package/dist/wire/smart-http.js.map +0 -1
- package/dist/wire/upload-pack.d.ts +0 -727
- package/dist/wire/upload-pack.d.ts.map +0 -1
- package/dist/wire/upload-pack.js +0 -1138
- package/dist/wire/upload-pack.js.map +0 -1
package/dist/pack/generation.js
DELETED
|
@@ -1,707 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Git Packfile Generation
|
|
3
|
-
*
|
|
4
|
-
* This module provides comprehensive packfile generation capabilities for creating
|
|
5
|
-
* Git packfiles programmatically. It supports both full object storage and delta
|
|
6
|
-
* compression for efficient packing.
|
|
7
|
-
*
|
|
8
|
-
* ## Features
|
|
9
|
-
*
|
|
10
|
-
* - **Full Object Packing**: Store objects without delta compression
|
|
11
|
-
* - **Delta Compression**: Automatic OFS_DELTA generation for similar objects
|
|
12
|
-
* - **REF_DELTA Support**: Reference-based deltas for thin packs
|
|
13
|
-
* - **Configurable Options**: Control delta depth, window size, compression level
|
|
14
|
-
* - **Thin Pack Generation**: Create packs that reference external objects
|
|
15
|
-
*
|
|
16
|
-
* ## Pack Structure
|
|
17
|
-
*
|
|
18
|
-
* Generated packfiles follow the Git packfile v2 format:
|
|
19
|
-
* - 12-byte header (signature + version + object count)
|
|
20
|
-
* - Sequence of packed objects (header + compressed data)
|
|
21
|
-
* - 20-byte SHA-1 trailer
|
|
22
|
-
*
|
|
23
|
-
* ## Delta Compression
|
|
24
|
-
*
|
|
25
|
-
* When enabled, the generator uses a sliding window approach to find similar
|
|
26
|
-
* objects and create delta chains. OFS_DELTA is preferred as it's more efficient
|
|
27
|
-
* than REF_DELTA for local storage.
|
|
28
|
-
*
|
|
29
|
-
* @module pack/generation
|
|
30
|
-
* @see {@link https://git-scm.com/docs/pack-format} Git Pack Format Documentation
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* // Simple packfile generation
|
|
34
|
-
* import { generatePackfile, PackableObject, PackObjectType } from './generation';
|
|
35
|
-
*
|
|
36
|
-
* const objects: PackableObject[] = [
|
|
37
|
-
* { sha: 'abc123...', type: PackObjectType.OBJ_BLOB, data: blobData }
|
|
38
|
-
* ];
|
|
39
|
-
* const packfile = generatePackfile(objects);
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* // Using PackfileGenerator with options
|
|
43
|
-
* import { PackfileGenerator, PackObjectType } from './generation';
|
|
44
|
-
*
|
|
45
|
-
* const generator = new PackfileGenerator({
|
|
46
|
-
* enableDeltaCompression: true,
|
|
47
|
-
* maxDeltaDepth: 50,
|
|
48
|
-
* windowSize: 10
|
|
49
|
-
* });
|
|
50
|
-
*
|
|
51
|
-
* generator.addObject({ sha: '...', type: PackObjectType.OBJ_BLOB, data: ... });
|
|
52
|
-
* const result = generator.generate();
|
|
53
|
-
*/
|
|
54
|
-
import pako from 'pako';
|
|
55
|
-
import { PackObjectType, encodeTypeAndSize } from './format';
|
|
56
|
-
import { createDelta } from './delta';
|
|
57
|
-
import { sha1 } from '../utils/sha1';
|
|
58
|
-
// ============================================================================
|
|
59
|
-
// Helper Functions
|
|
60
|
-
// ============================================================================
|
|
61
|
-
/**
|
|
62
|
-
* Computes the SHA-1 checksum of pack content.
|
|
63
|
-
*
|
|
64
|
-
* @description Calculates the 20-byte SHA-1 hash that serves as the pack's
|
|
65
|
-
* checksum/trailer. This checksum is appended to the pack and also used
|
|
66
|
-
* in the corresponding .idx file.
|
|
67
|
-
*
|
|
68
|
-
* @param {Uint8Array} data - The pack data to checksum
|
|
69
|
-
* @returns {Uint8Array} 20-byte SHA-1 checksum
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* const packWithoutChecksum = generatePackContent(objects);
|
|
73
|
-
* const checksum = computePackChecksum(packWithoutChecksum);
|
|
74
|
-
* // Append checksum to create complete packfile
|
|
75
|
-
*/
|
|
76
|
-
export function computePackChecksum(data) {
|
|
77
|
-
return sha1(data);
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Creates the 12-byte pack file header.
|
|
81
|
-
*
|
|
82
|
-
* @param {number} objectCount - Number of objects in the pack
|
|
83
|
-
* @returns {Uint8Array} 12-byte header (signature + version + count)
|
|
84
|
-
* @internal
|
|
85
|
-
*/
|
|
86
|
-
function createPackHeader(objectCount) {
|
|
87
|
-
const header = new Uint8Array(12);
|
|
88
|
-
// Signature: "PACK"
|
|
89
|
-
header[0] = 0x50; // P
|
|
90
|
-
header[1] = 0x41; // A
|
|
91
|
-
header[2] = 0x43; // C
|
|
92
|
-
header[3] = 0x4b; // K
|
|
93
|
-
// Version: 2 (big-endian)
|
|
94
|
-
header[4] = 0;
|
|
95
|
-
header[5] = 0;
|
|
96
|
-
header[6] = 0;
|
|
97
|
-
header[7] = 2;
|
|
98
|
-
// Object count (big-endian)
|
|
99
|
-
header[8] = (objectCount >> 24) & 0xff;
|
|
100
|
-
header[9] = (objectCount >> 16) & 0xff;
|
|
101
|
-
header[10] = (objectCount >> 8) & 0xff;
|
|
102
|
-
header[11] = objectCount & 0xff;
|
|
103
|
-
return header;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Encodes an offset for OFS_DELTA using Git's variable-length format.
|
|
107
|
-
*
|
|
108
|
-
* @description Uses a special encoding where each byte after the first
|
|
109
|
-
* must subtract 1 before shifting to avoid ambiguity.
|
|
110
|
-
*
|
|
111
|
-
* @param {number} offset - The byte offset to encode
|
|
112
|
-
* @returns {Uint8Array} Encoded offset bytes
|
|
113
|
-
* @internal
|
|
114
|
-
*/
|
|
115
|
-
function encodeOffset(offset) {
|
|
116
|
-
const bytes = [];
|
|
117
|
-
// First byte: 7 bits of offset (no continuation)
|
|
118
|
-
bytes.push(offset & 0x7f);
|
|
119
|
-
offset >>>= 7;
|
|
120
|
-
// Subsequent bytes: continuation bit + 7 bits, but we need to subtract 1
|
|
121
|
-
// to avoid ambiguity
|
|
122
|
-
while (offset > 0) {
|
|
123
|
-
offset -= 1;
|
|
124
|
-
bytes.unshift((offset & 0x7f) | 0x80);
|
|
125
|
-
offset >>>= 7;
|
|
126
|
-
}
|
|
127
|
-
return new Uint8Array(bytes);
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Converts a hexadecimal string to bytes.
|
|
131
|
-
*
|
|
132
|
-
* @param {string} hex - Hex string to convert
|
|
133
|
-
* @returns {Uint8Array} Decoded bytes
|
|
134
|
-
* @internal
|
|
135
|
-
*/
|
|
136
|
-
function hexToBytes(hex) {
|
|
137
|
-
const bytes = new Uint8Array(hex.length / 2);
|
|
138
|
-
for (let i = 0; i < hex.length; i += 2) {
|
|
139
|
-
bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
|
|
140
|
-
}
|
|
141
|
-
return bytes;
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Concatenates multiple Uint8Arrays into one.
|
|
145
|
-
*
|
|
146
|
-
* @param {Uint8Array[]} arrays - Arrays to concatenate
|
|
147
|
-
* @returns {Uint8Array} Combined array
|
|
148
|
-
* @internal
|
|
149
|
-
*/
|
|
150
|
-
function concatArrays(arrays) {
|
|
151
|
-
let totalLength = 0;
|
|
152
|
-
for (const arr of arrays) {
|
|
153
|
-
totalLength += arr.length;
|
|
154
|
-
}
|
|
155
|
-
const result = new Uint8Array(totalLength);
|
|
156
|
-
let offset = 0;
|
|
157
|
-
for (const arr of arrays) {
|
|
158
|
-
result.set(arr, offset);
|
|
159
|
-
offset += arr.length;
|
|
160
|
-
}
|
|
161
|
-
return result;
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Calculates similarity between two byte arrays using hash-based comparison.
|
|
165
|
-
*
|
|
166
|
-
* @description Uses a simple approach: counts matching 4-byte sequences between
|
|
167
|
-
* the arrays. For small arrays, falls back to byte-by-byte comparison.
|
|
168
|
-
*
|
|
169
|
-
* @param {Uint8Array} a - First byte array
|
|
170
|
-
* @param {Uint8Array} b - Second byte array
|
|
171
|
-
* @returns {number} Similarity score between 0 and 1
|
|
172
|
-
* @internal
|
|
173
|
-
*/
|
|
174
|
-
function calculateSimilarity(a, b) {
|
|
175
|
-
if (a.length === 0 || b.length === 0)
|
|
176
|
-
return 0;
|
|
177
|
-
// Use a simple approach: count matching 4-byte sequences
|
|
178
|
-
const windowSize = 4;
|
|
179
|
-
if (a.length < windowSize || b.length < windowSize) {
|
|
180
|
-
// For small objects, compare byte by byte
|
|
181
|
-
let matches = 0;
|
|
182
|
-
const minLen = Math.min(a.length, b.length);
|
|
183
|
-
for (let i = 0; i < minLen; i++) {
|
|
184
|
-
if (a[i] === b[i])
|
|
185
|
-
matches++;
|
|
186
|
-
}
|
|
187
|
-
return matches / Math.max(a.length, b.length);
|
|
188
|
-
}
|
|
189
|
-
// Build hash set from 'a'
|
|
190
|
-
const hashes = new Set();
|
|
191
|
-
for (let i = 0; i <= a.length - windowSize; i++) {
|
|
192
|
-
let hash = 0;
|
|
193
|
-
for (let j = 0; j < windowSize; j++) {
|
|
194
|
-
hash = ((hash << 5) - hash + a[i + j]) | 0;
|
|
195
|
-
}
|
|
196
|
-
hashes.add(hash);
|
|
197
|
-
}
|
|
198
|
-
// Count matches in 'b'
|
|
199
|
-
let matches = 0;
|
|
200
|
-
for (let i = 0; i <= b.length - windowSize; i++) {
|
|
201
|
-
let hash = 0;
|
|
202
|
-
for (let j = 0; j < windowSize; j++) {
|
|
203
|
-
hash = ((hash << 5) - hash + b[i + j]) | 0;
|
|
204
|
-
}
|
|
205
|
-
if (hashes.has(hash))
|
|
206
|
-
matches++;
|
|
207
|
-
}
|
|
208
|
-
return matches / Math.max(a.length - windowSize + 1, b.length - windowSize + 1);
|
|
209
|
-
}
|
|
210
|
-
// ============================================================================
|
|
211
|
-
// Object Ordering
|
|
212
|
-
// ============================================================================
|
|
213
|
-
/**
|
|
214
|
-
* Orders objects for optimal delta compression.
|
|
215
|
-
*
|
|
216
|
-
* @description Sorts objects to maximize delta compression efficiency by:
|
|
217
|
-
* 1. Grouping by type (commits, trees, blobs, tags)
|
|
218
|
-
* 2. Within each type, grouping by path (similar files together)
|
|
219
|
-
* 3. Within path groups, sorting by size (larger first as better bases)
|
|
220
|
-
*
|
|
221
|
-
* This ordering ensures that similar objects are adjacent, improving the
|
|
222
|
-
* chances of finding good delta bases within the sliding window.
|
|
223
|
-
*
|
|
224
|
-
* @param {PackableObject[]} objects - Objects to order
|
|
225
|
-
* @returns {PackableObject[]} New array with objects in optimal order
|
|
226
|
-
*
|
|
227
|
-
* @example
|
|
228
|
-
* const ordered = orderObjectsForCompression(objects);
|
|
229
|
-
* // Use ordered array for pack generation
|
|
230
|
-
*/
|
|
231
|
-
export function orderObjectsForCompression(objects) {
|
|
232
|
-
// Define type order: commits, trees, blobs, tags
|
|
233
|
-
const typeOrder = {
|
|
234
|
-
[PackObjectType.OBJ_COMMIT]: 0,
|
|
235
|
-
[PackObjectType.OBJ_TREE]: 1,
|
|
236
|
-
[PackObjectType.OBJ_BLOB]: 2,
|
|
237
|
-
[PackObjectType.OBJ_TAG]: 3,
|
|
238
|
-
[PackObjectType.OBJ_OFS_DELTA]: 4,
|
|
239
|
-
[PackObjectType.OBJ_REF_DELTA]: 5
|
|
240
|
-
};
|
|
241
|
-
return [...objects].sort((a, b) => {
|
|
242
|
-
// First, sort by type
|
|
243
|
-
const typeCompare = typeOrder[a.type] - typeOrder[b.type];
|
|
244
|
-
if (typeCompare !== 0)
|
|
245
|
-
return typeCompare;
|
|
246
|
-
// Within same type, sort by path if available (groups similar files)
|
|
247
|
-
if (a.path && b.path) {
|
|
248
|
-
const pathCompare = a.path.localeCompare(b.path);
|
|
249
|
-
if (pathCompare !== 0)
|
|
250
|
-
return pathCompare;
|
|
251
|
-
}
|
|
252
|
-
// Then by size (larger first - better delta bases)
|
|
253
|
-
return b.data.length - a.data.length;
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* Selects the best delta base from a set of candidates.
|
|
258
|
-
*
|
|
259
|
-
* @description Evaluates each candidate by computing similarity with the target
|
|
260
|
-
* and returns the most similar object if it exceeds the threshold.
|
|
261
|
-
*
|
|
262
|
-
* **Selection Criteria:**
|
|
263
|
-
* - Must be same type as target
|
|
264
|
-
* - Must not be the target itself
|
|
265
|
-
* - Similarity must exceed 30% threshold
|
|
266
|
-
*
|
|
267
|
-
* @param {DeltaCandidate} target - The object to find a base for
|
|
268
|
-
* @param {DeltaCandidate[]} candidates - Potential base objects
|
|
269
|
-
* @returns {DeltaCandidate | null} Best candidate or null if none suitable
|
|
270
|
-
*
|
|
271
|
-
* @example
|
|
272
|
-
* const base = selectDeltaBase(targetObj, windowObjects);
|
|
273
|
-
* if (base) {
|
|
274
|
-
* const delta = createDelta(base.data, targetObj.data);
|
|
275
|
-
* }
|
|
276
|
-
*/
|
|
277
|
-
export function selectDeltaBase(target, candidates) {
|
|
278
|
-
if (candidates.length === 0)
|
|
279
|
-
return null;
|
|
280
|
-
let bestCandidate = null;
|
|
281
|
-
let bestSimilarity = 0;
|
|
282
|
-
for (const candidate of candidates) {
|
|
283
|
-
// Only consider same type for delta
|
|
284
|
-
if (candidate.type !== target.type)
|
|
285
|
-
continue;
|
|
286
|
-
// Don't delta against self
|
|
287
|
-
if (candidate.sha === target.sha)
|
|
288
|
-
continue;
|
|
289
|
-
const similarity = calculateSimilarity(candidate.data, target.data);
|
|
290
|
-
if (similarity > bestSimilarity) {
|
|
291
|
-
bestSimilarity = similarity;
|
|
292
|
-
bestCandidate = candidate;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
// Only return if similarity is good enough
|
|
296
|
-
return bestSimilarity > 0.3 ? bestCandidate : null;
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Generator class for creating Git packfiles.
|
|
300
|
-
*
|
|
301
|
-
* @description Provides a fluent API for building packfiles with support for:
|
|
302
|
-
* - Adding objects incrementally
|
|
303
|
-
* - Optional delta compression with configurable parameters
|
|
304
|
-
* - Both OFS_DELTA and REF_DELTA encoding
|
|
305
|
-
* - Statistics collection during generation
|
|
306
|
-
*
|
|
307
|
-
* **Usage Pattern:**
|
|
308
|
-
* 1. Create generator with desired options
|
|
309
|
-
* 2. Add objects using addObject() or addDeltaObject()
|
|
310
|
-
* 3. Call generate() to produce the packfile
|
|
311
|
-
* 4. Optionally call reset() to reuse the generator
|
|
312
|
-
*
|
|
313
|
-
* @class PackfileGenerator
|
|
314
|
-
*
|
|
315
|
-
* @example
|
|
316
|
-
* // Basic usage
|
|
317
|
-
* const generator = new PackfileGenerator();
|
|
318
|
-
* generator.addObject({ sha: '...', type: PackObjectType.OBJ_BLOB, data: ... });
|
|
319
|
-
* generator.addObject({ sha: '...', type: PackObjectType.OBJ_TREE, data: ... });
|
|
320
|
-
* const result = generator.generate();
|
|
321
|
-
*
|
|
322
|
-
* @example
|
|
323
|
-
* // With delta compression
|
|
324
|
-
* const generator = new PackfileGenerator({
|
|
325
|
-
* enableDeltaCompression: true,
|
|
326
|
-
* maxDeltaDepth: 50,
|
|
327
|
-
* windowSize: 10
|
|
328
|
-
* });
|
|
329
|
-
*
|
|
330
|
-
* for (const obj of objects) {
|
|
331
|
-
* generator.addObject(obj);
|
|
332
|
-
* }
|
|
333
|
-
*
|
|
334
|
-
* const { packData, checksum, stats } = generator.generate();
|
|
335
|
-
* console.log(`Compressed ${stats.deltaObjects} objects as deltas`);
|
|
336
|
-
*/
|
|
337
|
-
export class PackfileGenerator {
|
|
338
|
-
objects = new Map();
|
|
339
|
-
deltaObjects = [];
|
|
340
|
-
options;
|
|
341
|
-
/**
|
|
342
|
-
* Creates a new PackfileGenerator with the specified options.
|
|
343
|
-
*
|
|
344
|
-
* @param {GeneratorOptions} [options={}] - Configuration options
|
|
345
|
-
*/
|
|
346
|
-
constructor(options = {}) {
|
|
347
|
-
this.options = {
|
|
348
|
-
enableDeltaCompression: options.enableDeltaCompression ?? false,
|
|
349
|
-
maxDeltaDepth: options.maxDeltaDepth ?? 50,
|
|
350
|
-
windowSize: options.windowSize ?? 10,
|
|
351
|
-
compressionLevel: options.compressionLevel ?? 6,
|
|
352
|
-
useRefDelta: options.useRefDelta ?? false,
|
|
353
|
-
minDeltaSize: options.minDeltaSize ?? 0 // Default to 0, use caller-specified value if set
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
/**
|
|
357
|
-
* Gets the total number of objects added to the generator.
|
|
358
|
-
* @returns {number} Count of regular objects plus delta objects
|
|
359
|
-
*/
|
|
360
|
-
get objectCount() {
|
|
361
|
-
return this.objects.size + this.deltaObjects.length;
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* Adds an object to be included in the packfile.
|
|
365
|
-
*
|
|
366
|
-
* @description Objects are deduplicated by SHA. If an object with the same SHA
|
|
367
|
-
* has already been added, this call is a no-op.
|
|
368
|
-
*
|
|
369
|
-
* @param {PackableObject} object - The object to add
|
|
370
|
-
*/
|
|
371
|
-
addObject(object) {
|
|
372
|
-
// Skip duplicates
|
|
373
|
-
if (this.objects.has(object.sha))
|
|
374
|
-
return;
|
|
375
|
-
this.objects.set(object.sha, {
|
|
376
|
-
sha: object.sha,
|
|
377
|
-
type: object.type,
|
|
378
|
-
data: object.data,
|
|
379
|
-
path: object.path,
|
|
380
|
-
isDelta: false,
|
|
381
|
-
depth: 0
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* Adds a pre-computed delta object for thin pack generation.
|
|
386
|
-
*
|
|
387
|
-
* @description Use this for REF_DELTA objects that reference external bases.
|
|
388
|
-
* The delta must already be computed.
|
|
389
|
-
*
|
|
390
|
-
* @param {DeltaObject} deltaObj - The delta object to add
|
|
391
|
-
*/
|
|
392
|
-
addDeltaObject(deltaObj) {
|
|
393
|
-
this.deltaObjects.push(deltaObj);
|
|
394
|
-
}
|
|
395
|
-
/**
|
|
396
|
-
* Resets the generator to its initial state.
|
|
397
|
-
*
|
|
398
|
-
* @description Clears all added objects and delta objects, allowing the
|
|
399
|
-
* generator to be reused for a new packfile.
|
|
400
|
-
*/
|
|
401
|
-
reset() {
|
|
402
|
-
this.objects.clear();
|
|
403
|
-
this.deltaObjects = [];
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* Generates the packfile from all added objects.
|
|
407
|
-
*
|
|
408
|
-
* @description Produces a complete packfile including:
|
|
409
|
-
* - 12-byte header
|
|
410
|
-
* - All objects (with optional delta compression)
|
|
411
|
-
* - Generation statistics
|
|
412
|
-
*
|
|
413
|
-
* Note: The returned packData does NOT include the trailing checksum.
|
|
414
|
-
* Concatenate packData with checksum to create the complete packfile.
|
|
415
|
-
*
|
|
416
|
-
* @returns {GeneratedPackfile} Pack data, checksum, and statistics
|
|
417
|
-
*
|
|
418
|
-
* @example
|
|
419
|
-
* const result = generator.generate();
|
|
420
|
-
* // Write complete packfile
|
|
421
|
-
* const complete = new Uint8Array(result.packData.length + 20);
|
|
422
|
-
* complete.set(result.packData);
|
|
423
|
-
* complete.set(result.checksum, result.packData.length);
|
|
424
|
-
*/
|
|
425
|
-
generate() {
|
|
426
|
-
const startTime = Date.now();
|
|
427
|
-
let totalSize = 0;
|
|
428
|
-
let compressedSize = 0;
|
|
429
|
-
let deltaCount = 0;
|
|
430
|
-
let maxDeltaDepth = 0;
|
|
431
|
-
// Get all objects and order them
|
|
432
|
-
const objectList = Array.from(this.objects.values());
|
|
433
|
-
const orderedObjects = orderObjectsForCompression(objectList.map(o => ({ sha: o.sha, type: o.type, data: o.data, path: o.path })));
|
|
434
|
-
// Calculate total size
|
|
435
|
-
for (const obj of orderedObjects) {
|
|
436
|
-
totalSize += obj.data.length;
|
|
437
|
-
}
|
|
438
|
-
// Build offset map for OFS_DELTA
|
|
439
|
-
const offsetMap = new Map();
|
|
440
|
-
const parts = [];
|
|
441
|
-
// Create header
|
|
442
|
-
const header = createPackHeader(orderedObjects.length + this.deltaObjects.length);
|
|
443
|
-
parts.push(header);
|
|
444
|
-
let currentOffset = 12; // After header
|
|
445
|
-
// Compute delta chains if enabled
|
|
446
|
-
const deltaChains = new Map();
|
|
447
|
-
if (this.options.enableDeltaCompression) {
|
|
448
|
-
// Window of recent objects for delta comparison
|
|
449
|
-
const window = [];
|
|
450
|
-
const depthMap = new Map();
|
|
451
|
-
for (const obj of orderedObjects) {
|
|
452
|
-
const internalObj = this.objects.get(obj.sha);
|
|
453
|
-
// Skip small objects
|
|
454
|
-
if (obj.data.length < (this.options.minDeltaSize ?? 50)) {
|
|
455
|
-
window.push(internalObj);
|
|
456
|
-
if (window.length > (this.options.windowSize ?? 10)) {
|
|
457
|
-
window.shift();
|
|
458
|
-
}
|
|
459
|
-
continue;
|
|
460
|
-
}
|
|
461
|
-
// Look for a good base in the window
|
|
462
|
-
let bestBase = null;
|
|
463
|
-
let bestDelta = null;
|
|
464
|
-
let bestSavings = 0;
|
|
465
|
-
for (const candidate of window) {
|
|
466
|
-
if (candidate.type !== obj.type)
|
|
467
|
-
continue;
|
|
468
|
-
// Check depth limit
|
|
469
|
-
const candidateDepth = depthMap.get(candidate.sha) ?? 0;
|
|
470
|
-
if (candidateDepth >= (this.options.maxDeltaDepth ?? 50))
|
|
471
|
-
continue;
|
|
472
|
-
const delta = createDelta(candidate.data, obj.data);
|
|
473
|
-
const savings = obj.data.length - delta.length;
|
|
474
|
-
if (savings > bestSavings && delta.length < obj.data.length * 0.9) {
|
|
475
|
-
bestBase = candidate;
|
|
476
|
-
bestDelta = delta;
|
|
477
|
-
bestSavings = savings;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
if (bestBase && bestDelta) {
|
|
481
|
-
const depth = (depthMap.get(bestBase.sha) ?? 0) + 1;
|
|
482
|
-
deltaChains.set(obj.sha, { base: bestBase, delta: bestDelta, depth });
|
|
483
|
-
depthMap.set(obj.sha, depth);
|
|
484
|
-
if (depth > maxDeltaDepth)
|
|
485
|
-
maxDeltaDepth = depth;
|
|
486
|
-
}
|
|
487
|
-
window.push(internalObj);
|
|
488
|
-
if (window.length > (this.options.windowSize ?? 10)) {
|
|
489
|
-
window.shift();
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
// Write objects
|
|
494
|
-
for (const obj of orderedObjects) {
|
|
495
|
-
const objStart = currentOffset;
|
|
496
|
-
offsetMap.set(obj.sha, objStart);
|
|
497
|
-
const deltaInfo = deltaChains.get(obj.sha);
|
|
498
|
-
if (deltaInfo && offsetMap.has(deltaInfo.base.sha)) {
|
|
499
|
-
// Write as OFS_DELTA
|
|
500
|
-
const baseOffset = offsetMap.get(deltaInfo.base.sha);
|
|
501
|
-
const relativeOffset = objStart - baseOffset;
|
|
502
|
-
// OFS_DELTA header
|
|
503
|
-
const typeAndSize = encodeTypeAndSize(PackObjectType.OBJ_OFS_DELTA, deltaInfo.delta.length);
|
|
504
|
-
const offsetEncoded = encodeOffset(relativeOffset);
|
|
505
|
-
const compressed = pako.deflate(deltaInfo.delta, { level: this.options.compressionLevel });
|
|
506
|
-
parts.push(typeAndSize);
|
|
507
|
-
parts.push(offsetEncoded);
|
|
508
|
-
parts.push(compressed);
|
|
509
|
-
currentOffset += typeAndSize.length + offsetEncoded.length + compressed.length;
|
|
510
|
-
compressedSize += compressed.length;
|
|
511
|
-
deltaCount++;
|
|
512
|
-
}
|
|
513
|
-
else {
|
|
514
|
-
// Write as full object
|
|
515
|
-
const typeAndSize = encodeTypeAndSize(obj.type, obj.data.length);
|
|
516
|
-
const compressed = pako.deflate(obj.data, { level: this.options.compressionLevel });
|
|
517
|
-
parts.push(typeAndSize);
|
|
518
|
-
parts.push(compressed);
|
|
519
|
-
currentOffset += typeAndSize.length + compressed.length;
|
|
520
|
-
compressedSize += compressed.length;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
// Write REF_DELTA objects
|
|
524
|
-
for (const deltaObj of this.deltaObjects) {
|
|
525
|
-
// REF_DELTA header
|
|
526
|
-
const typeAndSize = encodeTypeAndSize(PackObjectType.OBJ_REF_DELTA, deltaObj.delta.length);
|
|
527
|
-
const baseShaBytes = hexToBytes(deltaObj.baseSha);
|
|
528
|
-
const compressed = pako.deflate(deltaObj.delta, { level: this.options.compressionLevel });
|
|
529
|
-
parts.push(typeAndSize);
|
|
530
|
-
parts.push(baseShaBytes);
|
|
531
|
-
parts.push(compressed);
|
|
532
|
-
currentOffset += typeAndSize.length + baseShaBytes.length + compressed.length;
|
|
533
|
-
compressedSize += compressed.length;
|
|
534
|
-
totalSize += deltaObj.delta.length;
|
|
535
|
-
}
|
|
536
|
-
// Combine all parts (this is the pack data without trailing checksum)
|
|
537
|
-
const packData = concatArrays(parts);
|
|
538
|
-
// Calculate checksum of the pack data
|
|
539
|
-
const checksum = computePackChecksum(packData);
|
|
540
|
-
const generationTimeMs = Date.now() - startTime;
|
|
541
|
-
// packData does NOT include the trailing checksum
|
|
542
|
-
// To get a complete packfile, concatenate packData + checksum
|
|
543
|
-
// This allows the caller to verify or manipulate the pack before finalizing
|
|
544
|
-
return {
|
|
545
|
-
packData,
|
|
546
|
-
checksum,
|
|
547
|
-
stats: {
|
|
548
|
-
totalObjects: orderedObjects.length + this.deltaObjects.length,
|
|
549
|
-
deltaObjects: deltaCount,
|
|
550
|
-
totalSize,
|
|
551
|
-
compressedSize,
|
|
552
|
-
maxDeltaDepth,
|
|
553
|
-
generationTimeMs
|
|
554
|
-
}
|
|
555
|
-
};
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
// ============================================================================
|
|
559
|
-
// Main Functions
|
|
560
|
-
// ============================================================================
|
|
561
|
-
/**
|
|
562
|
-
* Generates a complete packfile from an array of objects.
|
|
563
|
-
*
|
|
564
|
-
* @description Convenience function that creates a PackfileGenerator, adds all
|
|
565
|
-
* objects, generates the pack, and returns a complete packfile with the trailing
|
|
566
|
-
* SHA-1 checksum appended.
|
|
567
|
-
*
|
|
568
|
-
* This function does not use delta compression. For delta compression, use
|
|
569
|
-
* the PackfileGenerator class directly with enableDeltaCompression option.
|
|
570
|
-
*
|
|
571
|
-
* @param {PackableObject[]} objects - Array of objects to pack
|
|
572
|
-
* @returns {Uint8Array} Complete packfile with header, objects, and checksum
|
|
573
|
-
*
|
|
574
|
-
* @example
|
|
575
|
-
* const objects: PackableObject[] = [
|
|
576
|
-
* { sha: 'abc...', type: PackObjectType.OBJ_BLOB, data: blobData },
|
|
577
|
-
* { sha: 'def...', type: PackObjectType.OBJ_TREE, data: treeData },
|
|
578
|
-
* { sha: 'ghi...', type: PackObjectType.OBJ_COMMIT, data: commitData }
|
|
579
|
-
* ];
|
|
580
|
-
*
|
|
581
|
-
* const packfile = generatePackfile(objects);
|
|
582
|
-
* await fs.writeFile('pack-abc123.pack', packfile);
|
|
583
|
-
*/
|
|
584
|
-
export function generatePackfile(objects) {
|
|
585
|
-
const generator = new PackfileGenerator();
|
|
586
|
-
for (const obj of objects) {
|
|
587
|
-
generator.addObject(obj);
|
|
588
|
-
}
|
|
589
|
-
const result = generator.generate();
|
|
590
|
-
// Combine packData with checksum to form complete packfile
|
|
591
|
-
const completePackfile = new Uint8Array(result.packData.length + result.checksum.length);
|
|
592
|
-
completePackfile.set(result.packData, 0);
|
|
593
|
-
completePackfile.set(result.checksum, result.packData.length);
|
|
594
|
-
return completePackfile;
|
|
595
|
-
}
|
|
596
|
-
/**
|
|
597
|
-
* Generates a thin pack that can reference external base objects.
|
|
598
|
-
*
|
|
599
|
-
* @description Creates a packfile where objects can be stored as REF_DELTA
|
|
600
|
-
* referencing base objects not included in the pack. This is typically used
|
|
601
|
-
* for network transfers where the receiver already has some objects.
|
|
602
|
-
*
|
|
603
|
-
* **Thin Pack Behavior:**
|
|
604
|
-
* - Attempts to delta-compress objects against external bases
|
|
605
|
-
* - Uses REF_DELTA format (base referenced by SHA-1)
|
|
606
|
-
* - Falls back to full objects when delta is not beneficial
|
|
607
|
-
* - Tracks which external bases are referenced
|
|
608
|
-
*
|
|
609
|
-
* @param {PackableObject[]} objects - Array of objects to pack
|
|
610
|
-
* @param {ThinPackOptions} options - Configuration including external object set
|
|
611
|
-
* @returns {ThinPackResult} Pack data, checksum, and metadata about external refs
|
|
612
|
-
*
|
|
613
|
-
* @example
|
|
614
|
-
* // Generate thin pack for git push
|
|
615
|
-
* const externalObjects = new Set(['abc123...', 'def456...']); // Objects server has
|
|
616
|
-
* const baseData = new Map([['abc123...', baseObjData]]); // Data for delta computation
|
|
617
|
-
*
|
|
618
|
-
* const result = generateThinPack(objectsToSend, {
|
|
619
|
-
* externalObjects,
|
|
620
|
-
* baseData
|
|
621
|
-
* });
|
|
622
|
-
*
|
|
623
|
-
* console.log(`Created thin pack with ${result.missingBases.length} external refs`);
|
|
624
|
-
*/
|
|
625
|
-
export function generateThinPack(objects, options) {
|
|
626
|
-
const startTime = Date.now();
|
|
627
|
-
const missingBases = [];
|
|
628
|
-
let deltaCount = 0;
|
|
629
|
-
let totalSize = 0;
|
|
630
|
-
let compressedSize = 0;
|
|
631
|
-
// Check if any objects can use external bases
|
|
632
|
-
const hasExternalBases = options.externalObjects.size > 0;
|
|
633
|
-
const parts = [];
|
|
634
|
-
// Create header
|
|
635
|
-
const header = createPackHeader(objects.length);
|
|
636
|
-
parts.push(header);
|
|
637
|
-
// Process objects
|
|
638
|
-
for (const obj of objects) {
|
|
639
|
-
totalSize += obj.data.length;
|
|
640
|
-
// Try to find an external base for delta
|
|
641
|
-
let usedExternalBase = false;
|
|
642
|
-
if (hasExternalBases && options.baseData) {
|
|
643
|
-
for (const externalSha of options.externalObjects) {
|
|
644
|
-
const baseData = options.baseData.get(externalSha);
|
|
645
|
-
if (baseData) {
|
|
646
|
-
// Calculate similarity
|
|
647
|
-
const similarity = calculateSimilarity(baseData, obj.data);
|
|
648
|
-
if (similarity > 0.3) {
|
|
649
|
-
// Create delta
|
|
650
|
-
const delta = createDelta(baseData, obj.data);
|
|
651
|
-
if (delta.length < obj.data.length * 0.9) {
|
|
652
|
-
// Use REF_DELTA
|
|
653
|
-
const typeAndSize = encodeTypeAndSize(PackObjectType.OBJ_REF_DELTA, delta.length);
|
|
654
|
-
const baseShaBytes = hexToBytes(externalSha);
|
|
655
|
-
const compressed = pako.deflate(delta);
|
|
656
|
-
parts.push(typeAndSize);
|
|
657
|
-
parts.push(baseShaBytes);
|
|
658
|
-
parts.push(compressed);
|
|
659
|
-
compressedSize += compressed.length;
|
|
660
|
-
deltaCount++;
|
|
661
|
-
usedExternalBase = true;
|
|
662
|
-
if (!missingBases.includes(externalSha)) {
|
|
663
|
-
missingBases.push(externalSha);
|
|
664
|
-
}
|
|
665
|
-
break;
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
if (!usedExternalBase) {
|
|
672
|
-
// Write as full object
|
|
673
|
-
const typeAndSize = encodeTypeAndSize(obj.type, obj.data.length);
|
|
674
|
-
const compressed = pako.deflate(obj.data);
|
|
675
|
-
parts.push(typeAndSize);
|
|
676
|
-
parts.push(compressed);
|
|
677
|
-
compressedSize += compressed.length;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
// Combine all parts
|
|
681
|
-
const packData = concatArrays(parts);
|
|
682
|
-
// Calculate checksum
|
|
683
|
-
const checksum = computePackChecksum(packData);
|
|
684
|
-
// Create final packfile with checksum
|
|
685
|
-
const finalPack = new Uint8Array(packData.length + 20);
|
|
686
|
-
finalPack.set(packData, 0);
|
|
687
|
-
finalPack.set(checksum, packData.length);
|
|
688
|
-
const generationTimeMs = Date.now() - startTime;
|
|
689
|
-
// A pack is considered "thin" if it's generated with the capability to reference
|
|
690
|
-
// external objects, even if no actual external references were used
|
|
691
|
-
const isThin = hasExternalBases;
|
|
692
|
-
return {
|
|
693
|
-
packData: finalPack,
|
|
694
|
-
checksum,
|
|
695
|
-
isThin,
|
|
696
|
-
missingBases,
|
|
697
|
-
stats: {
|
|
698
|
-
totalObjects: objects.length,
|
|
699
|
-
deltaObjects: deltaCount,
|
|
700
|
-
totalSize,
|
|
701
|
-
compressedSize,
|
|
702
|
-
maxDeltaDepth: deltaCount > 0 ? 1 : 0,
|
|
703
|
-
generationTimeMs
|
|
704
|
-
}
|
|
705
|
-
};
|
|
706
|
-
}
|
|
707
|
-
//# sourceMappingURL=generation.js.map
|