gitx.do 0.0.2 → 0.0.3
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/dist/cli/commands/blame.d.ts +259 -0
- package/dist/cli/commands/blame.d.ts.map +1 -0
- package/dist/cli/commands/blame.js +609 -0
- package/dist/cli/commands/blame.js.map +1 -0
- package/dist/cli/commands/branch.d.ts +249 -0
- package/dist/cli/commands/branch.d.ts.map +1 -0
- package/dist/cli/commands/branch.js +693 -0
- package/dist/cli/commands/branch.js.map +1 -0
- package/dist/cli/commands/commit.d.ts +182 -0
- package/dist/cli/commands/commit.d.ts.map +1 -0
- package/dist/cli/commands/commit.js +437 -0
- package/dist/cli/commands/commit.js.map +1 -0
- package/dist/cli/commands/diff.d.ts +464 -0
- package/dist/cli/commands/diff.d.ts.map +1 -0
- package/dist/cli/commands/diff.js +958 -0
- package/dist/cli/commands/diff.js.map +1 -0
- package/dist/cli/commands/log.d.ts +239 -0
- package/dist/cli/commands/log.d.ts.map +1 -0
- package/dist/cli/commands/log.js +535 -0
- package/dist/cli/commands/log.js.map +1 -0
- package/dist/cli/commands/review.d.ts +457 -0
- package/dist/cli/commands/review.d.ts.map +1 -0
- package/dist/cli/commands/review.js +533 -0
- package/dist/cli/commands/review.js.map +1 -0
- package/dist/cli/commands/status.d.ts +269 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +493 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/web.d.ts +199 -0
- package/dist/cli/commands/web.d.ts.map +1 -0
- package/dist/cli/commands/web.js +696 -0
- package/dist/cli/commands/web.js.map +1 -0
- package/dist/cli/fs-adapter.d.ts +656 -0
- package/dist/cli/fs-adapter.d.ts.map +1 -0
- package/dist/cli/fs-adapter.js +1179 -0
- package/dist/cli/fs-adapter.js.map +1 -0
- package/dist/cli/index.d.ts +387 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +523 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/ui/components/DiffView.d.ts +7 -0
- package/dist/cli/ui/components/DiffView.d.ts.map +1 -0
- package/dist/cli/ui/components/DiffView.js +11 -0
- package/dist/cli/ui/components/DiffView.js.map +1 -0
- package/dist/cli/ui/components/ErrorDisplay.d.ts +6 -0
- package/dist/cli/ui/components/ErrorDisplay.d.ts.map +1 -0
- package/dist/cli/ui/components/ErrorDisplay.js +11 -0
- package/dist/cli/ui/components/ErrorDisplay.js.map +1 -0
- package/dist/cli/ui/components/FuzzySearch.d.ts +9 -0
- package/dist/cli/ui/components/FuzzySearch.d.ts.map +1 -0
- package/dist/cli/ui/components/FuzzySearch.js +12 -0
- package/dist/cli/ui/components/FuzzySearch.js.map +1 -0
- package/dist/cli/ui/components/LoadingSpinner.d.ts +6 -0
- package/dist/cli/ui/components/LoadingSpinner.d.ts.map +1 -0
- package/dist/cli/ui/components/LoadingSpinner.js +10 -0
- package/dist/cli/ui/components/LoadingSpinner.js.map +1 -0
- package/dist/cli/ui/components/NavigationList.d.ts +9 -0
- package/dist/cli/ui/components/NavigationList.d.ts.map +1 -0
- package/dist/cli/ui/components/NavigationList.js +11 -0
- package/dist/cli/ui/components/NavigationList.js.map +1 -0
- package/dist/cli/ui/components/ScrollableContent.d.ts +8 -0
- package/dist/cli/ui/components/ScrollableContent.d.ts.map +1 -0
- package/dist/cli/ui/components/ScrollableContent.js +11 -0
- package/dist/cli/ui/components/ScrollableContent.js.map +1 -0
- package/dist/cli/ui/components/index.d.ts +7 -0
- package/dist/cli/ui/components/index.d.ts.map +1 -0
- package/dist/cli/ui/components/index.js +9 -0
- package/dist/cli/ui/components/index.js.map +1 -0
- package/dist/cli/ui/terminal-ui.d.ts +52 -0
- package/dist/cli/ui/terminal-ui.d.ts.map +1 -0
- package/dist/cli/ui/terminal-ui.js +121 -0
- package/dist/cli/ui/terminal-ui.js.map +1 -0
- package/dist/durable-object/object-store.d.ts +401 -23
- package/dist/durable-object/object-store.d.ts.map +1 -1
- package/dist/durable-object/object-store.js +414 -25
- package/dist/durable-object/object-store.js.map +1 -1
- package/dist/durable-object/schema.d.ts +188 -0
- package/dist/durable-object/schema.d.ts.map +1 -1
- package/dist/durable-object/schema.js +160 -0
- package/dist/durable-object/schema.js.map +1 -1
- package/dist/durable-object/wal.d.ts +336 -31
- package/dist/durable-object/wal.d.ts.map +1 -1
- package/dist/durable-object/wal.js +272 -27
- package/dist/durable-object/wal.js.map +1 -1
- package/dist/index.d.ts +379 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +379 -7
- package/dist/index.js.map +1 -1
- package/dist/mcp/adapter.d.ts +579 -38
- package/dist/mcp/adapter.d.ts.map +1 -1
- package/dist/mcp/adapter.js +426 -33
- package/dist/mcp/adapter.js.map +1 -1
- package/dist/mcp/sandbox.d.ts +532 -29
- package/dist/mcp/sandbox.d.ts.map +1 -1
- package/dist/mcp/sandbox.js +389 -22
- package/dist/mcp/sandbox.js.map +1 -1
- package/dist/mcp/sdk-adapter.d.ts +478 -56
- package/dist/mcp/sdk-adapter.d.ts.map +1 -1
- package/dist/mcp/sdk-adapter.js +346 -44
- package/dist/mcp/sdk-adapter.js.map +1 -1
- package/dist/mcp/tools.d.ts +445 -30
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +363 -33
- package/dist/mcp/tools.js.map +1 -1
- package/dist/ops/blame.d.ts +424 -21
- package/dist/ops/blame.d.ts.map +1 -1
- package/dist/ops/blame.js +303 -20
- package/dist/ops/blame.js.map +1 -1
- package/dist/ops/branch.d.ts +583 -32
- package/dist/ops/branch.d.ts.map +1 -1
- package/dist/ops/branch.js +365 -23
- package/dist/ops/branch.js.map +1 -1
- package/dist/ops/commit-traversal.d.ts +164 -24
- package/dist/ops/commit-traversal.d.ts.map +1 -1
- package/dist/ops/commit-traversal.js +68 -2
- package/dist/ops/commit-traversal.js.map +1 -1
- package/dist/ops/commit.d.ts +387 -53
- package/dist/ops/commit.d.ts.map +1 -1
- package/dist/ops/commit.js +249 -29
- package/dist/ops/commit.js.map +1 -1
- package/dist/ops/merge-base.d.ts +195 -21
- package/dist/ops/merge-base.d.ts.map +1 -1
- package/dist/ops/merge-base.js +122 -12
- package/dist/ops/merge-base.js.map +1 -1
- package/dist/ops/merge.d.ts +600 -130
- package/dist/ops/merge.d.ts.map +1 -1
- package/dist/ops/merge.js +408 -60
- package/dist/ops/merge.js.map +1 -1
- package/dist/ops/tag.d.ts +67 -2
- package/dist/ops/tag.d.ts.map +1 -1
- package/dist/ops/tag.js +42 -1
- package/dist/ops/tag.js.map +1 -1
- package/dist/ops/tree-builder.d.ts +102 -6
- package/dist/ops/tree-builder.d.ts.map +1 -1
- package/dist/ops/tree-builder.js +30 -5
- package/dist/ops/tree-builder.js.map +1 -1
- package/dist/ops/tree-diff.d.ts +50 -2
- package/dist/ops/tree-diff.d.ts.map +1 -1
- package/dist/ops/tree-diff.js +50 -2
- package/dist/ops/tree-diff.js.map +1 -1
- package/dist/pack/delta.d.ts +211 -39
- package/dist/pack/delta.d.ts.map +1 -1
- package/dist/pack/delta.js +232 -46
- package/dist/pack/delta.js.map +1 -1
- package/dist/pack/format.d.ts +390 -28
- package/dist/pack/format.d.ts.map +1 -1
- package/dist/pack/format.js +344 -33
- package/dist/pack/format.js.map +1 -1
- package/dist/pack/full-generation.d.ts +313 -28
- package/dist/pack/full-generation.d.ts.map +1 -1
- package/dist/pack/full-generation.js +238 -19
- package/dist/pack/full-generation.js.map +1 -1
- package/dist/pack/generation.d.ts +346 -23
- package/dist/pack/generation.d.ts.map +1 -1
- package/dist/pack/generation.js +269 -21
- package/dist/pack/generation.js.map +1 -1
- package/dist/pack/index.d.ts +407 -86
- package/dist/pack/index.d.ts.map +1 -1
- package/dist/pack/index.js +351 -70
- package/dist/pack/index.js.map +1 -1
- package/dist/refs/branch.d.ts +517 -71
- package/dist/refs/branch.d.ts.map +1 -1
- package/dist/refs/branch.js +410 -26
- package/dist/refs/branch.js.map +1 -1
- package/dist/refs/storage.d.ts +610 -57
- package/dist/refs/storage.d.ts.map +1 -1
- package/dist/refs/storage.js +481 -29
- package/dist/refs/storage.js.map +1 -1
- package/dist/refs/tag.d.ts +677 -67
- package/dist/refs/tag.d.ts.map +1 -1
- package/dist/refs/tag.js +497 -30
- package/dist/refs/tag.js.map +1 -1
- package/dist/storage/lru-cache.d.ts +556 -53
- package/dist/storage/lru-cache.d.ts.map +1 -1
- package/dist/storage/lru-cache.js +439 -36
- package/dist/storage/lru-cache.js.map +1 -1
- package/dist/storage/object-index.d.ts +483 -38
- package/dist/storage/object-index.d.ts.map +1 -1
- package/dist/storage/object-index.js +388 -22
- package/dist/storage/object-index.js.map +1 -1
- package/dist/storage/r2-pack.d.ts +957 -94
- package/dist/storage/r2-pack.d.ts.map +1 -1
- package/dist/storage/r2-pack.js +756 -48
- package/dist/storage/r2-pack.js.map +1 -1
- package/dist/tiered/cdc-pipeline.d.ts +1610 -38
- package/dist/tiered/cdc-pipeline.d.ts.map +1 -1
- package/dist/tiered/cdc-pipeline.js +1131 -22
- package/dist/tiered/cdc-pipeline.js.map +1 -1
- package/dist/tiered/migration.d.ts +903 -41
- package/dist/tiered/migration.d.ts.map +1 -1
- package/dist/tiered/migration.js +646 -24
- package/dist/tiered/migration.js.map +1 -1
- package/dist/tiered/parquet-writer.d.ts +944 -47
- package/dist/tiered/parquet-writer.d.ts.map +1 -1
- package/dist/tiered/parquet-writer.js +667 -39
- package/dist/tiered/parquet-writer.js.map +1 -1
- package/dist/tiered/read-path.d.ts +728 -34
- package/dist/tiered/read-path.d.ts.map +1 -1
- package/dist/tiered/read-path.js +310 -27
- package/dist/tiered/read-path.js.map +1 -1
- package/dist/types/objects.d.ts +457 -0
- package/dist/types/objects.d.ts.map +1 -1
- package/dist/types/objects.js +305 -4
- package/dist/types/objects.js.map +1 -1
- package/dist/types/storage.d.ts +407 -35
- package/dist/types/storage.d.ts.map +1 -1
- package/dist/types/storage.js +27 -3
- package/dist/types/storage.js.map +1 -1
- package/dist/utils/hash.d.ts +133 -12
- package/dist/utils/hash.d.ts.map +1 -1
- package/dist/utils/hash.js +133 -12
- package/dist/utils/hash.js.map +1 -1
- package/dist/utils/sha1.d.ts +102 -9
- package/dist/utils/sha1.d.ts.map +1 -1
- package/dist/utils/sha1.js +114 -11
- package/dist/utils/sha1.js.map +1 -1
- package/dist/wire/capabilities.d.ts +896 -88
- package/dist/wire/capabilities.d.ts.map +1 -1
- package/dist/wire/capabilities.js +566 -62
- package/dist/wire/capabilities.js.map +1 -1
- package/dist/wire/pkt-line.d.ts +293 -15
- package/dist/wire/pkt-line.d.ts.map +1 -1
- package/dist/wire/pkt-line.js +251 -15
- package/dist/wire/pkt-line.js.map +1 -1
- package/dist/wire/receive-pack.d.ts +814 -64
- package/dist/wire/receive-pack.d.ts.map +1 -1
- package/dist/wire/receive-pack.js +542 -41
- package/dist/wire/receive-pack.js.map +1 -1
- package/dist/wire/smart-http.d.ts +575 -97
- package/dist/wire/smart-http.d.ts.map +1 -1
- package/dist/wire/smart-http.js +337 -46
- package/dist/wire/smart-http.js.map +1 -1
- package/dist/wire/upload-pack.d.ts +492 -98
- package/dist/wire/upload-pack.d.ts.map +1 -1
- package/dist/wire/upload-pack.js +347 -59
- package/dist/wire/upload-pack.js.map +1 -1
- package/package.json +1 -1
package/dist/ops/merge.js
CHANGED
|
@@ -1,35 +1,111 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Three-way
|
|
2
|
+
* @fileoverview Three-way Merge Implementation for Git
|
|
3
3
|
*
|
|
4
|
-
* This module provides
|
|
5
|
-
*
|
|
4
|
+
* This module provides a complete implementation of Git's three-way merge algorithm,
|
|
5
|
+
* enabling branch merging with automatic conflict detection and resolution capabilities.
|
|
6
|
+
*
|
|
7
|
+
* ## Overview
|
|
8
|
+
*
|
|
9
|
+
* The three-way merge algorithm works by:
|
|
10
|
+
* 1. Finding the common ancestor (merge base) of two commits
|
|
11
|
+
* 2. Comparing both branches against this base to identify changes
|
|
12
|
+
* 3. Automatically merging non-conflicting changes
|
|
13
|
+
* 4. Detecting and reporting conflicts for manual resolution
|
|
14
|
+
*
|
|
15
|
+
* ## Supported Features
|
|
16
|
+
*
|
|
17
|
+
* - Fast-forward merges when possible
|
|
18
|
+
* - Three-way content merging for text files
|
|
19
|
+
* - Binary file detection and handling
|
|
20
|
+
* - Multiple conflict types (content, add-add, modify-delete, etc.)
|
|
21
|
+
* - Conflict resolution strategies (ours, theirs, custom)
|
|
22
|
+
* - Merge state persistence for multi-step conflict resolution
|
|
23
|
+
*
|
|
24
|
+
* ## Usage Example
|
|
25
|
+
*
|
|
26
|
+
* ```typescript
|
|
27
|
+
* import { merge, resolveConflict, continueMerge } from './ops/merge'
|
|
28
|
+
*
|
|
29
|
+
* // Perform a merge
|
|
30
|
+
* const result = await merge(storage, currentBranchSha, featureBranchSha, {
|
|
31
|
+
* message: 'Merge feature branch',
|
|
32
|
+
* allowFastForward: true
|
|
33
|
+
* })
|
|
34
|
+
*
|
|
35
|
+
* if (result.status === 'conflicted') {
|
|
36
|
+
* // Resolve conflicts
|
|
37
|
+
* for (const conflict of result.conflicts) {
|
|
38
|
+
* await resolveConflict(storage, conflict.path, { resolution: 'ours' })
|
|
39
|
+
* }
|
|
40
|
+
* // Complete the merge
|
|
41
|
+
* await continueMerge(storage)
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @module ops/merge
|
|
6
46
|
*/
|
|
7
47
|
/**
|
|
8
48
|
* Performs a three-way merge between the current branch and another commit.
|
|
9
49
|
*
|
|
50
|
+
* @description
|
|
10
51
|
* This function implements Git's three-way merge algorithm:
|
|
11
52
|
* 1. Find the common ancestor (merge base) of the two commits
|
|
12
53
|
* 2. Compare both sides against the base to identify changes
|
|
13
54
|
* 3. Apply non-conflicting changes automatically
|
|
14
55
|
* 4. Identify and report conflicts for manual resolution
|
|
15
56
|
*
|
|
16
|
-
*
|
|
57
|
+
* The merge can result in several outcomes:
|
|
58
|
+
* - **fast-forward**: If the current branch is an ancestor of the target,
|
|
59
|
+
* the branch pointer is simply moved forward
|
|
60
|
+
* - **merged**: Changes were successfully combined into a merge commit
|
|
61
|
+
* - **conflicted**: Some changes conflict and require manual resolution
|
|
62
|
+
* - **up-to-date**: The target is already merged; nothing to do
|
|
63
|
+
*
|
|
64
|
+
* @param storage - The storage interface for reading/writing Git objects
|
|
17
65
|
* @param oursSha - SHA of the current branch's HEAD commit
|
|
18
|
-
* @param theirsSha - SHA of the commit to merge
|
|
19
|
-
* @param options -
|
|
20
|
-
*
|
|
66
|
+
* @param theirsSha - SHA of the commit to merge into the current branch
|
|
67
|
+
* @param options - Configuration options for the merge operation
|
|
68
|
+
*
|
|
69
|
+
* @returns A promise resolving to the merge result with status and any conflicts
|
|
70
|
+
*
|
|
71
|
+
* @throws {Error} When commit objects cannot be read
|
|
72
|
+
* @throws {Error} When tree objects cannot be parsed
|
|
73
|
+
* @throws {Error} When fastForwardOnly is true but fast-forward is not possible
|
|
21
74
|
*
|
|
22
75
|
* @example
|
|
23
76
|
* ```typescript
|
|
77
|
+
* // Basic merge
|
|
24
78
|
* const result = await merge(storage, 'abc123', 'def456', {
|
|
25
|
-
* message: 'Merge feature branch'
|
|
26
|
-
* allowFastForward: true
|
|
79
|
+
* message: 'Merge feature branch'
|
|
27
80
|
* })
|
|
28
81
|
*
|
|
29
|
-
* if (result.status === '
|
|
30
|
-
* console.log('
|
|
82
|
+
* if (result.status === 'merged') {
|
|
83
|
+
* console.log('Merge successful:', result.commitSha)
|
|
31
84
|
* }
|
|
32
85
|
* ```
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* // Fast-forward only merge
|
|
90
|
+
* try {
|
|
91
|
+
* const result = await merge(storage, 'abc123', 'def456', {
|
|
92
|
+
* fastForwardOnly: true
|
|
93
|
+
* })
|
|
94
|
+
* console.log('Fast-forwarded to:', result.treeSha)
|
|
95
|
+
* } catch (error) {
|
|
96
|
+
* console.log('Cannot fast-forward, branches have diverged')
|
|
97
|
+
* }
|
|
98
|
+
* ```
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* // Merge with auto-resolve conflicts using 'ours' strategy
|
|
103
|
+
* const result = await merge(storage, 'abc123', 'def456', {
|
|
104
|
+
* autoResolve: true,
|
|
105
|
+
* conflictStrategy: 'ours',
|
|
106
|
+
* message: 'Merge with our changes taking precedence'
|
|
107
|
+
* })
|
|
108
|
+
* ```
|
|
33
109
|
*/
|
|
34
110
|
export async function merge(storage, oursSha, theirsSha, options = {}) {
|
|
35
111
|
// Check if merging with self
|
|
@@ -210,7 +286,16 @@ export async function merge(storage, oursSha, theirsSha, options = {}) {
|
|
|
210
286
|
};
|
|
211
287
|
}
|
|
212
288
|
/**
|
|
213
|
-
*
|
|
289
|
+
* Generates a deterministic 40-character hex SHA from a seed string.
|
|
290
|
+
*
|
|
291
|
+
* @description
|
|
292
|
+
* Creates a SHA-like string for internal use. This is a simplified
|
|
293
|
+
* implementation for testing; production code should use proper SHA-1.
|
|
294
|
+
*
|
|
295
|
+
* @param seed - Input string to generate SHA from
|
|
296
|
+
* @returns 40-character hexadecimal string
|
|
297
|
+
*
|
|
298
|
+
* @internal
|
|
214
299
|
*/
|
|
215
300
|
function generateHexSha(seed) {
|
|
216
301
|
// Generate a proper 40-character hex string
|
|
@@ -225,7 +310,18 @@ function generateHexSha(seed) {
|
|
|
225
310
|
return hex.padStart(8, '0').repeat(5).slice(0, 40);
|
|
226
311
|
}
|
|
227
312
|
/**
|
|
228
|
-
*
|
|
313
|
+
* Recursively retrieves all entries from a tree object.
|
|
314
|
+
*
|
|
315
|
+
* @description
|
|
316
|
+
* Walks the tree structure recursively, collecting all file entries
|
|
317
|
+
* with their full paths from the repository root.
|
|
318
|
+
*
|
|
319
|
+
* @param storage - Storage interface for reading tree objects
|
|
320
|
+
* @param treeSha - SHA of the tree to read
|
|
321
|
+
* @param prefix - Path prefix for nested entries
|
|
322
|
+
* @returns Map of full path to tree entry info
|
|
323
|
+
*
|
|
324
|
+
* @internal
|
|
229
325
|
*/
|
|
230
326
|
async function getTreeEntries(storage, treeSha, prefix = '') {
|
|
231
327
|
const entries = new Map();
|
|
@@ -262,7 +358,16 @@ async function getTreeEntries(storage, treeSha, prefix = '') {
|
|
|
262
358
|
return entries;
|
|
263
359
|
}
|
|
264
360
|
/**
|
|
265
|
-
*
|
|
361
|
+
* Parses tree entries from raw Git tree object data.
|
|
362
|
+
*
|
|
363
|
+
* @description
|
|
364
|
+
* Git tree format is: mode SP name NUL sha (20 bytes binary)
|
|
365
|
+
* This function parses that binary format into structured entries.
|
|
366
|
+
*
|
|
367
|
+
* @param data - Raw tree object content
|
|
368
|
+
* @returns Array of parsed tree entries
|
|
369
|
+
*
|
|
370
|
+
* @internal
|
|
266
371
|
*/
|
|
267
372
|
function parseTreeEntries(data) {
|
|
268
373
|
const entries = [];
|
|
@@ -291,7 +396,24 @@ function parseTreeEntries(data) {
|
|
|
291
396
|
return entries;
|
|
292
397
|
}
|
|
293
398
|
/**
|
|
294
|
-
*
|
|
399
|
+
* Merges a single file entry using three-way merge logic.
|
|
400
|
+
*
|
|
401
|
+
* @description
|
|
402
|
+
* Compares the base, ours, and theirs versions of a single file
|
|
403
|
+
* and determines the merge result. Handles various cases:
|
|
404
|
+
* - File unchanged in one or both sides
|
|
405
|
+
* - File added/deleted on one or both sides
|
|
406
|
+
* - File modified on one or both sides (with content merge)
|
|
407
|
+
*
|
|
408
|
+
* @param storage - Storage interface for reading blob content
|
|
409
|
+
* @param path - Path of the file being merged
|
|
410
|
+
* @param baseEntry - Entry from the base (common ancestor)
|
|
411
|
+
* @param oursEntry - Entry from our branch
|
|
412
|
+
* @param theirsEntry - Entry from their branch
|
|
413
|
+
* @param stats - Statistics object to update
|
|
414
|
+
* @returns Merge result with either an entry or a conflict
|
|
415
|
+
*
|
|
416
|
+
* @internal
|
|
295
417
|
*/
|
|
296
418
|
async function mergeEntry(storage, path, baseEntry, oursEntry, theirsEntry, stats) {
|
|
297
419
|
// Case 1: File unchanged in both (same SHA and mode)
|
|
@@ -468,7 +590,13 @@ async function mergeEntry(storage, path, baseEntry, oursEntry, theirsEntry, stat
|
|
|
468
590
|
return {};
|
|
469
591
|
}
|
|
470
592
|
/**
|
|
471
|
-
*
|
|
593
|
+
* Retrieves blob content from storage.
|
|
594
|
+
*
|
|
595
|
+
* @param storage - Storage interface
|
|
596
|
+
* @param sha - SHA of the blob to read
|
|
597
|
+
* @returns Blob content or null if not found
|
|
598
|
+
*
|
|
599
|
+
* @internal
|
|
472
600
|
*/
|
|
473
601
|
async function getBlobContent(storage, sha) {
|
|
474
602
|
const obj = await storage.readObject(sha);
|
|
@@ -478,7 +606,17 @@ async function getBlobContent(storage, sha) {
|
|
|
478
606
|
return obj.data;
|
|
479
607
|
}
|
|
480
608
|
/**
|
|
481
|
-
*
|
|
609
|
+
* Builds a tree object from entries and writes it to storage.
|
|
610
|
+
*
|
|
611
|
+
* @description
|
|
612
|
+
* Takes a flat map of paths to entries and constructs the nested
|
|
613
|
+
* tree structure required by Git, writing subtrees as needed.
|
|
614
|
+
*
|
|
615
|
+
* @param storage - Storage interface for writing tree objects
|
|
616
|
+
* @param entries - Map of full paths to tree entries
|
|
617
|
+
* @returns SHA of the root tree object
|
|
618
|
+
*
|
|
619
|
+
* @internal
|
|
482
620
|
*/
|
|
483
621
|
async function buildAndWriteTree(storage, entries) {
|
|
484
622
|
// Group entries by top-level directory
|
|
@@ -553,7 +691,12 @@ async function buildAndWriteTree(storage, entries) {
|
|
|
553
691
|
return storage.writeObject('tree', treeData);
|
|
554
692
|
}
|
|
555
693
|
/**
|
|
556
|
-
*
|
|
694
|
+
* Converts a hex string to a 20-byte Uint8Array.
|
|
695
|
+
*
|
|
696
|
+
* @param hex - 40-character hexadecimal string
|
|
697
|
+
* @returns 20-byte array
|
|
698
|
+
*
|
|
699
|
+
* @internal
|
|
557
700
|
*/
|
|
558
701
|
function hexToBytes(hex) {
|
|
559
702
|
const bytes = new Uint8Array(20);
|
|
@@ -563,26 +706,54 @@ function hexToBytes(hex) {
|
|
|
563
706
|
return bytes;
|
|
564
707
|
}
|
|
565
708
|
/**
|
|
566
|
-
* Resolves a single merge conflict.
|
|
709
|
+
* Resolves a single merge conflict with the specified strategy.
|
|
567
710
|
*
|
|
711
|
+
* @description
|
|
568
712
|
* After a merge results in conflicts, use this function to resolve
|
|
569
|
-
* individual files.
|
|
570
|
-
*
|
|
713
|
+
* individual files. The resolution can use one of the three versions
|
|
714
|
+
* (ours, theirs, base) or provide custom merged content.
|
|
571
715
|
*
|
|
572
|
-
*
|
|
573
|
-
*
|
|
574
|
-
*
|
|
575
|
-
* @
|
|
716
|
+
* Once all conflicts are resolved, use {@link continueMerge} to create
|
|
717
|
+
* the merge commit and complete the operation.
|
|
718
|
+
*
|
|
719
|
+
* @param storage - The storage interface for reading/writing objects
|
|
720
|
+
* @param path - Path to the conflicted file to resolve
|
|
721
|
+
* @param options - Resolution options specifying which version to use
|
|
722
|
+
*
|
|
723
|
+
* @returns A promise resolving to the resolution result
|
|
724
|
+
*
|
|
725
|
+
* @throws {Error} When no merge is in progress
|
|
726
|
+
* @throws {Error} When the specified path has no conflict
|
|
576
727
|
*
|
|
577
728
|
* @example
|
|
578
729
|
* ```typescript
|
|
579
|
-
* // Resolve using
|
|
580
|
-
* await resolveConflict(storage, 'src/file.ts', {
|
|
730
|
+
* // Resolve using our version
|
|
731
|
+
* const result = await resolveConflict(storage, 'src/file.ts', {
|
|
732
|
+
* resolution: 'ours'
|
|
733
|
+
* })
|
|
734
|
+
* console.log(`${result.remainingConflicts} conflicts remaining`)
|
|
735
|
+
* ```
|
|
581
736
|
*
|
|
582
|
-
*
|
|
583
|
-
*
|
|
737
|
+
* @example
|
|
738
|
+
* ```typescript
|
|
739
|
+
* // Resolve using their version
|
|
740
|
+
* await resolveConflict(storage, 'config.json', {
|
|
741
|
+
* resolution: 'theirs'
|
|
742
|
+
* })
|
|
743
|
+
* ```
|
|
744
|
+
*
|
|
745
|
+
* @example
|
|
746
|
+
* ```typescript
|
|
747
|
+
* // Resolve with manually merged content
|
|
748
|
+
* const mergedContent = new TextEncoder().encode(`
|
|
749
|
+
* // Manually resolved: kept both features
|
|
750
|
+
* export function feature1() { ... }
|
|
751
|
+
* export function feature2() { ... }
|
|
752
|
+
* `)
|
|
753
|
+
*
|
|
754
|
+
* await resolveConflict(storage, 'src/features.ts', {
|
|
584
755
|
* resolution: 'custom',
|
|
585
|
-
* customContent:
|
|
756
|
+
* customContent: mergedContent
|
|
586
757
|
* })
|
|
587
758
|
* ```
|
|
588
759
|
*/
|
|
@@ -691,17 +862,28 @@ export async function resolveConflict(storage, path, options) {
|
|
|
691
862
|
/**
|
|
692
863
|
* Aborts an in-progress merge operation.
|
|
693
864
|
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
865
|
+
* @description
|
|
866
|
+
* Cancels the current merge and restores the repository to its state
|
|
867
|
+
* before the merge began. Any conflict resolutions or staged changes
|
|
868
|
+
* from the merge will be discarded.
|
|
869
|
+
*
|
|
870
|
+
* This is equivalent to `git merge --abort`.
|
|
696
871
|
*
|
|
697
872
|
* @param storage - The storage interface
|
|
698
|
-
*
|
|
873
|
+
*
|
|
874
|
+
* @returns A promise resolving to the operation result
|
|
875
|
+
*
|
|
876
|
+
* @throws {Error} When no merge is in progress
|
|
699
877
|
*
|
|
700
878
|
* @example
|
|
701
879
|
* ```typescript
|
|
880
|
+
* // User decides to cancel the merge
|
|
702
881
|
* const result = await abortMerge(storage)
|
|
882
|
+
*
|
|
703
883
|
* if (result.success) {
|
|
704
|
-
* console.log('Merge aborted, HEAD
|
|
884
|
+
* console.log('Merge aborted, HEAD restored to', result.headSha)
|
|
885
|
+
* } else {
|
|
886
|
+
* console.error('Failed to abort:', result.error)
|
|
705
887
|
* }
|
|
706
888
|
* ```
|
|
707
889
|
*/
|
|
@@ -728,21 +910,39 @@ export async function abortMerge(storage) {
|
|
|
728
910
|
/**
|
|
729
911
|
* Continues a merge after all conflicts have been resolved.
|
|
730
912
|
*
|
|
731
|
-
*
|
|
732
|
-
*
|
|
913
|
+
* @description
|
|
914
|
+
* After resolving all conflicts using {@link resolveConflict}, call this
|
|
915
|
+
* function to create the merge commit and complete the merge operation.
|
|
916
|
+
* The merge state will be cleaned up automatically.
|
|
917
|
+
*
|
|
918
|
+
* This is equivalent to `git merge --continue` or `git commit` after
|
|
919
|
+
* resolving conflicts.
|
|
733
920
|
*
|
|
734
921
|
* @param storage - The storage interface
|
|
735
|
-
* @param message - Optional commit message (overrides stored message)
|
|
736
|
-
*
|
|
922
|
+
* @param message - Optional commit message (overrides the stored message)
|
|
923
|
+
*
|
|
924
|
+
* @returns A promise resolving to the operation result with the new commit SHA
|
|
925
|
+
*
|
|
926
|
+
* @throws {Error} When no merge is in progress
|
|
927
|
+
* @throws {Error} When unresolved conflicts remain
|
|
737
928
|
*
|
|
738
929
|
* @example
|
|
739
930
|
* ```typescript
|
|
740
931
|
* // After resolving all conflicts
|
|
741
932
|
* const result = await continueMerge(storage)
|
|
933
|
+
*
|
|
742
934
|
* if (result.success) {
|
|
743
|
-
* console.log('Merge completed
|
|
935
|
+
* console.log('Merge completed:', result.headSha)
|
|
936
|
+
* } else {
|
|
937
|
+
* console.error('Cannot continue:', result.error)
|
|
744
938
|
* }
|
|
745
939
|
* ```
|
|
940
|
+
*
|
|
941
|
+
* @example
|
|
942
|
+
* ```typescript
|
|
943
|
+
* // Continue with a custom commit message
|
|
944
|
+
* const result = await continueMerge(storage, 'Merge feature-x with conflict resolution')
|
|
945
|
+
* ```
|
|
746
946
|
*/
|
|
747
947
|
export async function continueMerge(storage, message) {
|
|
748
948
|
// Get current merge state
|
|
@@ -777,18 +977,42 @@ export async function continueMerge(storage, message) {
|
|
|
777
977
|
};
|
|
778
978
|
}
|
|
779
979
|
/**
|
|
780
|
-
*
|
|
980
|
+
* Creates a SHA-like string from a prefix.
|
|
981
|
+
*
|
|
982
|
+
* @param prefix - String to use as the basis for the SHA
|
|
983
|
+
* @returns 40-character string
|
|
984
|
+
*
|
|
985
|
+
* @internal
|
|
781
986
|
*/
|
|
782
987
|
function makeSha(prefix) {
|
|
783
988
|
return prefix.padEnd(40, '0');
|
|
784
989
|
}
|
|
785
990
|
/**
|
|
786
|
-
* Finds the best common ancestor (merge base)
|
|
991
|
+
* Finds the best common ancestor (merge base) of two commits.
|
|
787
992
|
*
|
|
788
|
-
* @
|
|
789
|
-
*
|
|
790
|
-
*
|
|
791
|
-
*
|
|
993
|
+
* @description
|
|
994
|
+
* Implements the merge base algorithm by finding the most recent commit
|
|
995
|
+
* that is an ancestor of both input commits. This is the commit from
|
|
996
|
+
* which both branches diverged.
|
|
997
|
+
*
|
|
998
|
+
* Uses a breadth-first search from both commits to find their
|
|
999
|
+
* intersection in the commit graph.
|
|
1000
|
+
*
|
|
1001
|
+
* @param storage - The storage interface for reading commit objects
|
|
1002
|
+
* @param commit1 - SHA of the first commit
|
|
1003
|
+
* @param commit2 - SHA of the second commit
|
|
1004
|
+
*
|
|
1005
|
+
* @returns A promise resolving to the merge base SHA, or null if no common ancestor exists
|
|
1006
|
+
*
|
|
1007
|
+
* @example
|
|
1008
|
+
* ```typescript
|
|
1009
|
+
* const base = await findMergeBase(storage, 'feature-sha', 'main-sha')
|
|
1010
|
+
* if (base) {
|
|
1011
|
+
* console.log('Common ancestor:', base)
|
|
1012
|
+
* } else {
|
|
1013
|
+
* console.log('No common history')
|
|
1014
|
+
* }
|
|
1015
|
+
* ```
|
|
792
1016
|
*/
|
|
793
1017
|
export async function findMergeBase(storage, commit1, commit2) {
|
|
794
1018
|
// Get all ancestors of commit1 (including itself)
|
|
@@ -836,7 +1060,13 @@ export async function findMergeBase(storage, commit1, commit2) {
|
|
|
836
1060
|
return null;
|
|
837
1061
|
}
|
|
838
1062
|
/**
|
|
839
|
-
*
|
|
1063
|
+
* Parses parent commit SHAs from raw commit data.
|
|
1064
|
+
*
|
|
1065
|
+
* @param data - Raw commit object content
|
|
1066
|
+
* @param extendedParents - Pre-parsed parents if available
|
|
1067
|
+
* @returns Array of parent commit SHAs
|
|
1068
|
+
*
|
|
1069
|
+
* @internal
|
|
840
1070
|
*/
|
|
841
1071
|
function parseCommitParents(data, extendedParents) {
|
|
842
1072
|
// If extended parents are provided, use them directly
|
|
@@ -857,7 +1087,13 @@ function parseCommitParents(data, extendedParents) {
|
|
|
857
1087
|
return parents;
|
|
858
1088
|
}
|
|
859
1089
|
/**
|
|
860
|
-
*
|
|
1090
|
+
* Parses the tree SHA from raw commit data.
|
|
1091
|
+
*
|
|
1092
|
+
* @param data - Raw commit object content
|
|
1093
|
+
* @param treeSha - Pre-parsed tree SHA if available
|
|
1094
|
+
* @returns Tree SHA or null if not found
|
|
1095
|
+
*
|
|
1096
|
+
* @internal
|
|
861
1097
|
*/
|
|
862
1098
|
function parseCommitTree(data, treeSha) {
|
|
863
1099
|
// If extended tree SHA is provided, use it directly
|
|
@@ -876,7 +1112,12 @@ function parseCommitTree(data, treeSha) {
|
|
|
876
1112
|
const encoder = new TextEncoder();
|
|
877
1113
|
const decoder = new TextDecoder();
|
|
878
1114
|
/**
|
|
879
|
-
*
|
|
1115
|
+
* Splits content into lines while preserving line endings.
|
|
1116
|
+
*
|
|
1117
|
+
* @param content - Binary content to split
|
|
1118
|
+
* @returns Array of lines (without line ending characters)
|
|
1119
|
+
*
|
|
1120
|
+
* @internal
|
|
880
1121
|
*/
|
|
881
1122
|
function splitLines(content) {
|
|
882
1123
|
const text = decoder.decode(content);
|
|
@@ -888,7 +1129,18 @@ function splitLines(content) {
|
|
|
888
1129
|
return text.split(/\r?\n/);
|
|
889
1130
|
}
|
|
890
1131
|
/**
|
|
891
|
-
*
|
|
1132
|
+
* Computes the longest common subsequence of two arrays.
|
|
1133
|
+
*
|
|
1134
|
+
* @description
|
|
1135
|
+
* Uses dynamic programming to find the longest subsequence common
|
|
1136
|
+
* to both arrays. Used as a building block for the diff algorithm.
|
|
1137
|
+
*
|
|
1138
|
+
* @param a - First array
|
|
1139
|
+
* @param b - Second array
|
|
1140
|
+
* @param equals - Function to compare elements for equality
|
|
1141
|
+
* @returns Array containing the longest common subsequence
|
|
1142
|
+
*
|
|
1143
|
+
* @internal
|
|
892
1144
|
*/
|
|
893
1145
|
function lcs(a, b, equals) {
|
|
894
1146
|
const m = a.length;
|
|
@@ -925,7 +1177,13 @@ function lcs(a, b, equals) {
|
|
|
925
1177
|
return result;
|
|
926
1178
|
}
|
|
927
1179
|
/**
|
|
928
|
-
*
|
|
1180
|
+
* Computes diff hunks between base and target line arrays.
|
|
1181
|
+
*
|
|
1182
|
+
* @param base - Original lines
|
|
1183
|
+
* @param target - Modified lines
|
|
1184
|
+
* @returns Array of hunks describing the differences
|
|
1185
|
+
*
|
|
1186
|
+
* @internal
|
|
929
1187
|
*/
|
|
930
1188
|
function computeHunks(base, target) {
|
|
931
1189
|
const hunks = [];
|
|
@@ -966,7 +1224,13 @@ function computeHunks(base, target) {
|
|
|
966
1224
|
return hunks;
|
|
967
1225
|
}
|
|
968
1226
|
/**
|
|
969
|
-
*
|
|
1227
|
+
* Checks if two hunks overlap in their base ranges.
|
|
1228
|
+
*
|
|
1229
|
+
* @param h1 - First hunk
|
|
1230
|
+
* @param h2 - Second hunk
|
|
1231
|
+
* @returns true if the hunks overlap
|
|
1232
|
+
*
|
|
1233
|
+
* @internal
|
|
970
1234
|
*/
|
|
971
1235
|
function hunksOverlap(h1, h2) {
|
|
972
1236
|
// Hunks overlap if their base ranges intersect
|
|
@@ -975,7 +1239,13 @@ function hunksOverlap(h1, h2) {
|
|
|
975
1239
|
return !(end1 <= h2.baseStart || end2 <= h1.baseStart);
|
|
976
1240
|
}
|
|
977
1241
|
/**
|
|
978
|
-
*
|
|
1242
|
+
* Checks if two hunks represent the same change.
|
|
1243
|
+
*
|
|
1244
|
+
* @param h1 - First hunk
|
|
1245
|
+
* @param h2 - Second hunk
|
|
1246
|
+
* @returns true if the hunks are identical
|
|
1247
|
+
*
|
|
1248
|
+
* @internal
|
|
979
1249
|
*/
|
|
980
1250
|
function hunksSameChange(h1, h2) {
|
|
981
1251
|
if (h1.baseStart !== h2.baseStart || h1.baseCount !== h2.baseCount) {
|
|
@@ -994,10 +1264,39 @@ function hunksSameChange(h1, h2) {
|
|
|
994
1264
|
/**
|
|
995
1265
|
* Performs a content-level three-way merge on text files.
|
|
996
1266
|
*
|
|
1267
|
+
* @description
|
|
1268
|
+
* Takes three versions of a file (base, ours, theirs) and attempts to
|
|
1269
|
+
* automatically merge them. Non-conflicting changes are combined
|
|
1270
|
+
* automatically. Conflicting changes are marked with standard Git
|
|
1271
|
+
* conflict markers.
|
|
1272
|
+
*
|
|
1273
|
+
* The algorithm:
|
|
1274
|
+
* 1. Compute the diff hunks from base to ours
|
|
1275
|
+
* 2. Compute the diff hunks from base to theirs
|
|
1276
|
+
* 3. Process hunks in order, detecting overlaps
|
|
1277
|
+
* 4. Non-overlapping hunks are applied automatically
|
|
1278
|
+
* 5. Overlapping hunks with identical changes are deduplicated
|
|
1279
|
+
* 6. Overlapping hunks with different changes create conflict markers
|
|
1280
|
+
*
|
|
997
1281
|
* @param base - Content of the base (common ancestor) version
|
|
998
|
-
* @param ours - Content of our (current) version
|
|
999
|
-
* @param theirs - Content of their (merged) version
|
|
1000
|
-
*
|
|
1282
|
+
* @param ours - Content of our (current branch) version
|
|
1283
|
+
* @param theirs - Content of their (merged branch) version
|
|
1284
|
+
*
|
|
1285
|
+
* @returns Object containing merged content, conflict flag, and marker locations
|
|
1286
|
+
*
|
|
1287
|
+
* @example
|
|
1288
|
+
* ```typescript
|
|
1289
|
+
* const result = mergeContent(baseContent, oursContent, theirsContent)
|
|
1290
|
+
*
|
|
1291
|
+
* if (result.hasConflicts) {
|
|
1292
|
+
* console.log('Content has conflicts at:', result.markers)
|
|
1293
|
+
* // Write file with conflict markers for manual resolution
|
|
1294
|
+
* await writeFile(path, result.merged)
|
|
1295
|
+
* } else {
|
|
1296
|
+
* console.log('Content merged cleanly')
|
|
1297
|
+
* await writeFile(path, result.merged)
|
|
1298
|
+
* }
|
|
1299
|
+
* ```
|
|
1001
1300
|
*/
|
|
1002
1301
|
export function mergeContent(base, ours, theirs) {
|
|
1003
1302
|
const baseLines = splitLines(base);
|
|
@@ -1143,10 +1442,27 @@ export function mergeContent(base, ours, theirs) {
|
|
|
1143
1442
|
};
|
|
1144
1443
|
}
|
|
1145
1444
|
/**
|
|
1146
|
-
*
|
|
1445
|
+
* Determines if a file is binary (non-text) based on its content.
|
|
1446
|
+
*
|
|
1447
|
+
* @description
|
|
1448
|
+
* Uses Git's heuristic: a file is considered binary if it contains
|
|
1449
|
+
* null bytes (0x00) within the first 8000 bytes, or if it has
|
|
1450
|
+
* specific binary file magic numbers (PNG, JPEG, GIF).
|
|
1147
1451
|
*
|
|
1148
|
-
*
|
|
1149
|
-
*
|
|
1452
|
+
* Binary files cannot be automatically merged and always result
|
|
1453
|
+
* in conflicts when both sides modify them.
|
|
1454
|
+
*
|
|
1455
|
+
* @param content - File content to analyze
|
|
1456
|
+
*
|
|
1457
|
+
* @returns true if the file appears to be binary, false for text files
|
|
1458
|
+
*
|
|
1459
|
+
* @example
|
|
1460
|
+
* ```typescript
|
|
1461
|
+
* const content = await readFile('image.png')
|
|
1462
|
+
* if (isBinaryFile(content)) {
|
|
1463
|
+
* console.log('Cannot perform text merge on binary file')
|
|
1464
|
+
* }
|
|
1465
|
+
* ```
|
|
1150
1466
|
*/
|
|
1151
1467
|
export function isBinaryFile(content) {
|
|
1152
1468
|
// Empty files are considered text
|
|
@@ -1184,8 +1500,25 @@ export function isBinaryFile(content) {
|
|
|
1184
1500
|
/**
|
|
1185
1501
|
* Gets the current merge state if a merge is in progress.
|
|
1186
1502
|
*
|
|
1503
|
+
* @description
|
|
1504
|
+
* Returns the persisted merge state, which includes information about
|
|
1505
|
+
* the merge in progress, any unresolved conflicts, and the original
|
|
1506
|
+
* merge options.
|
|
1507
|
+
*
|
|
1187
1508
|
* @param storage - The storage interface
|
|
1188
|
-
*
|
|
1509
|
+
*
|
|
1510
|
+
* @returns A promise resolving to the merge state, or null if no merge is in progress
|
|
1511
|
+
*
|
|
1512
|
+
* @example
|
|
1513
|
+
* ```typescript
|
|
1514
|
+
* const state = await getMergeState(storage)
|
|
1515
|
+
* if (state) {
|
|
1516
|
+
* console.log('Merging', state.mergeHead, 'into', state.origHead)
|
|
1517
|
+
* console.log('Unresolved conflicts:', state.unresolvedConflicts.length)
|
|
1518
|
+
* } else {
|
|
1519
|
+
* console.log('No merge in progress')
|
|
1520
|
+
* }
|
|
1521
|
+
* ```
|
|
1189
1522
|
*/
|
|
1190
1523
|
export async function getMergeState(storage) {
|
|
1191
1524
|
return storage.readMergeState();
|
|
@@ -1193,8 +1526,23 @@ export async function getMergeState(storage) {
|
|
|
1193
1526
|
/**
|
|
1194
1527
|
* Checks if a merge is currently in progress.
|
|
1195
1528
|
*
|
|
1529
|
+
* @description
|
|
1530
|
+
* Quick check to determine if there's an active merge that hasn't
|
|
1531
|
+
* been completed or aborted. Useful for UI state and command validation.
|
|
1532
|
+
*
|
|
1196
1533
|
* @param storage - The storage interface
|
|
1197
|
-
*
|
|
1534
|
+
*
|
|
1535
|
+
* @returns A promise resolving to true if a merge is in progress
|
|
1536
|
+
*
|
|
1537
|
+
* @example
|
|
1538
|
+
* ```typescript
|
|
1539
|
+
* if (await isMergeInProgress(storage)) {
|
|
1540
|
+
* console.log('Please complete or abort the current merge first')
|
|
1541
|
+
* } else {
|
|
1542
|
+
* // Safe to start a new merge
|
|
1543
|
+
* await merge(storage, oursSha, theirsSha, options)
|
|
1544
|
+
* }
|
|
1545
|
+
* ```
|
|
1198
1546
|
*/
|
|
1199
1547
|
export async function isMergeInProgress(storage) {
|
|
1200
1548
|
const state = await storage.readMergeState();
|