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/refs/branch.js
DELETED
|
@@ -1,897 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Git Branch Operations
|
|
3
|
-
*
|
|
4
|
-
* This module provides comprehensive branch management functionality including
|
|
5
|
-
* creation, deletion, renaming, listing, and upstream tracking configuration.
|
|
6
|
-
*
|
|
7
|
-
* Branches in Git are simply refs under `refs/heads/` that point to commits.
|
|
8
|
-
* This module provides a higher-level API that handles the ref manipulation
|
|
9
|
-
* and adds branch-specific features like tracking information.
|
|
10
|
-
*
|
|
11
|
-
* **Key Features**:
|
|
12
|
-
* - Branch CRUD operations (create, read, update, delete)
|
|
13
|
-
* - Upstream tracking configuration
|
|
14
|
-
* - Merge status checking
|
|
15
|
-
* - Branch name validation and normalization
|
|
16
|
-
*
|
|
17
|
-
* @module refs/branch
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```typescript
|
|
21
|
-
* import { BranchManager, createBranch, listBranches } from './refs/branch'
|
|
22
|
-
*
|
|
23
|
-
* // Using the manager
|
|
24
|
-
* const manager = new BranchManager(refStorage)
|
|
25
|
-
* const branch = await manager.createBranch('feature/new-thing', { startPoint: 'main' })
|
|
26
|
-
*
|
|
27
|
-
* // Or using convenience functions
|
|
28
|
-
* const branches = await listBranches(refStorage)
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
import { isValidSha } from './storage';
|
|
32
|
-
/**
|
|
33
|
-
* Error thrown when a branch operation fails.
|
|
34
|
-
*
|
|
35
|
-
* @description
|
|
36
|
-
* Provides structured error information with error code
|
|
37
|
-
* for programmatic error handling.
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* ```typescript
|
|
41
|
-
* try {
|
|
42
|
-
* await manager.deleteBranch('main')
|
|
43
|
-
* } catch (e) {
|
|
44
|
-
* if (e instanceof BranchError) {
|
|
45
|
-
* if (e.code === 'CANNOT_DELETE_CURRENT') {
|
|
46
|
-
* console.log('Cannot delete the branch you are on')
|
|
47
|
-
* }
|
|
48
|
-
* }
|
|
49
|
-
* }
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
export class BranchError extends Error {
|
|
53
|
-
code;
|
|
54
|
-
branchName;
|
|
55
|
-
/**
|
|
56
|
-
* Create a new BranchError.
|
|
57
|
-
*
|
|
58
|
-
* @param message - Human-readable error description
|
|
59
|
-
* @param code - Error code for programmatic handling
|
|
60
|
-
* @param branchName - The branch that caused the error
|
|
61
|
-
*/
|
|
62
|
-
constructor(message, code, branchName) {
|
|
63
|
-
super(message);
|
|
64
|
-
this.code = code;
|
|
65
|
-
this.branchName = branchName;
|
|
66
|
-
this.name = 'BranchError';
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// ============================================================================
|
|
70
|
-
// BranchManager Class
|
|
71
|
-
// ============================================================================
|
|
72
|
-
/**
|
|
73
|
-
* Branch manager for performing Git branch operations.
|
|
74
|
-
*
|
|
75
|
-
* @description
|
|
76
|
-
* Provides a comprehensive API for branch management. Uses RefStorage
|
|
77
|
-
* internally for ref manipulation.
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* ```typescript
|
|
81
|
-
* const manager = new BranchManager(refStorage)
|
|
82
|
-
*
|
|
83
|
-
* // Create a feature branch
|
|
84
|
-
* const branch = await manager.createBranch('feature/auth', {
|
|
85
|
-
* startPoint: 'main',
|
|
86
|
-
* track: true
|
|
87
|
-
* })
|
|
88
|
-
*
|
|
89
|
-
* // List all branches
|
|
90
|
-
* const branches = await manager.listBranches({ includeRemotes: true })
|
|
91
|
-
*
|
|
92
|
-
* // Delete a merged branch
|
|
93
|
-
* await manager.deleteBranch('feature/auth')
|
|
94
|
-
* ```
|
|
95
|
-
*/
|
|
96
|
-
export class BranchManager {
|
|
97
|
-
storage;
|
|
98
|
-
/** Storage for tracking information (simulated config) */
|
|
99
|
-
trackingInfo = new Map();
|
|
100
|
-
/**
|
|
101
|
-
* Create a new BranchManager.
|
|
102
|
-
*
|
|
103
|
-
* @param storage - RefStorage instance for ref operations
|
|
104
|
-
*/
|
|
105
|
-
constructor(storage) {
|
|
106
|
-
this.storage = storage;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Create a new branch.
|
|
110
|
-
*
|
|
111
|
-
* @description
|
|
112
|
-
* Creates a new branch pointing to the specified start point.
|
|
113
|
-
* By default, the branch starts at HEAD.
|
|
114
|
-
*
|
|
115
|
-
* @param name - Branch name (without refs/heads/ prefix)
|
|
116
|
-
* @param options - Creation options
|
|
117
|
-
* @returns The created branch
|
|
118
|
-
* @throws BranchError with code 'INVALID_NAME' if name is invalid
|
|
119
|
-
* @throws BranchError with code 'ALREADY_EXISTS' if branch exists and not forcing
|
|
120
|
-
* @throws BranchError with code 'INVALID_START_POINT' if start point is invalid
|
|
121
|
-
*
|
|
122
|
-
* @example
|
|
123
|
-
* ```typescript
|
|
124
|
-
* // Create from HEAD
|
|
125
|
-
* const branch = await manager.createBranch('feature')
|
|
126
|
-
*
|
|
127
|
-
* // Create from specific commit
|
|
128
|
-
* const branch = await manager.createBranch('hotfix', { startPoint: 'abc123' })
|
|
129
|
-
*
|
|
130
|
-
* // Force overwrite existing
|
|
131
|
-
* const branch = await manager.createBranch('main', { force: true, startPoint: 'HEAD' })
|
|
132
|
-
* ```
|
|
133
|
-
*/
|
|
134
|
-
async createBranch(name, options) {
|
|
135
|
-
// Validate branch name
|
|
136
|
-
const validation = validateBranchName(name);
|
|
137
|
-
if (!validation.valid) {
|
|
138
|
-
throw new BranchError(validation.error, 'INVALID_NAME', name);
|
|
139
|
-
}
|
|
140
|
-
const branchRef = getBranchRefName(name);
|
|
141
|
-
const normalizedName = normalizeBranchName(name);
|
|
142
|
-
// Check if branch already exists
|
|
143
|
-
if (!options?.force) {
|
|
144
|
-
const existing = await this.storage.getRef(branchRef);
|
|
145
|
-
if (existing) {
|
|
146
|
-
throw new BranchError(`Branch '${name}' already exists`, 'ALREADY_EXISTS', name);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
// Resolve start point to SHA
|
|
150
|
-
const startPoint = options?.startPoint ?? 'HEAD';
|
|
151
|
-
// Check for empty start point
|
|
152
|
-
if (startPoint === '') {
|
|
153
|
-
throw new BranchError('Start point cannot be empty', 'INVALID_START_POINT', name);
|
|
154
|
-
}
|
|
155
|
-
let sha;
|
|
156
|
-
// If startPoint is a valid SHA, use it directly
|
|
157
|
-
// Note: In a real implementation, we would verify the object exists in the object store
|
|
158
|
-
// For this implementation, we trust the caller that the SHA represents a valid commit
|
|
159
|
-
if (isValidSha(startPoint)) {
|
|
160
|
-
sha = startPoint;
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
// Try to resolve as ref
|
|
164
|
-
try {
|
|
165
|
-
// First try as branch name
|
|
166
|
-
let resolved;
|
|
167
|
-
try {
|
|
168
|
-
resolved = await this.storage.resolveRef(getBranchRefName(startPoint));
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
// Try as full ref path
|
|
172
|
-
try {
|
|
173
|
-
resolved = await this.storage.resolveRef(startPoint);
|
|
174
|
-
}
|
|
175
|
-
catch {
|
|
176
|
-
throw new BranchError(`Invalid start point: ${startPoint}`, 'INVALID_START_POINT', name);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
sha = resolved.sha;
|
|
180
|
-
}
|
|
181
|
-
catch (e) {
|
|
182
|
-
if (e instanceof BranchError)
|
|
183
|
-
throw e;
|
|
184
|
-
throw new BranchError(`Invalid start point: ${startPoint}`, 'INVALID_START_POINT', name);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
// Get current branch to check if new branch is current
|
|
188
|
-
const currentBranch = await this.getCurrentBranch();
|
|
189
|
-
const isCurrent = false; // New branch is never current
|
|
190
|
-
// Create tracking info if requested
|
|
191
|
-
let tracking;
|
|
192
|
-
if (options?.track) {
|
|
193
|
-
const remoteBranch = typeof options.track === 'string'
|
|
194
|
-
? options.track
|
|
195
|
-
: (startPoint.startsWith('refs/remotes/') ? startPoint : undefined);
|
|
196
|
-
if (remoteBranch) {
|
|
197
|
-
tracking = {
|
|
198
|
-
remote: remoteBranch.split('/')[2] || 'origin',
|
|
199
|
-
remoteBranch,
|
|
200
|
-
ahead: 0,
|
|
201
|
-
behind: 0,
|
|
202
|
-
gone: false
|
|
203
|
-
};
|
|
204
|
-
// Store tracking info
|
|
205
|
-
this.trackingInfo.set(normalizedName, tracking);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
// Build the branch object first (for dryRun)
|
|
209
|
-
const branch = {
|
|
210
|
-
name: normalizedName,
|
|
211
|
-
ref: branchRef,
|
|
212
|
-
sha,
|
|
213
|
-
isCurrent,
|
|
214
|
-
isRemote: false,
|
|
215
|
-
tracking
|
|
216
|
-
};
|
|
217
|
-
// If dryRun, return without actually creating
|
|
218
|
-
if (options?.dryRun) {
|
|
219
|
-
return branch;
|
|
220
|
-
}
|
|
221
|
-
// Create the ref
|
|
222
|
-
await this.storage.updateRef(branchRef, sha, { create: true, force: options?.force });
|
|
223
|
-
return branch;
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Delete a branch.
|
|
227
|
-
*
|
|
228
|
-
* @description
|
|
229
|
-
* Removes a branch ref. By default, refuses to delete unmerged branches.
|
|
230
|
-
*
|
|
231
|
-
* @param name - Branch name to delete
|
|
232
|
-
* @param options - Deletion options
|
|
233
|
-
* @throws BranchError with code 'NOT_FOUND' if branch doesn't exist
|
|
234
|
-
* @throws BranchError with code 'CANNOT_DELETE_CURRENT' if branch is checked out
|
|
235
|
-
* @throws BranchError with code 'NOT_FULLY_MERGED' if branch has unmerged commits
|
|
236
|
-
*
|
|
237
|
-
* @example
|
|
238
|
-
* ```typescript
|
|
239
|
-
* // Safe delete (only if merged)
|
|
240
|
-
* await manager.deleteBranch('feature')
|
|
241
|
-
*
|
|
242
|
-
* // Force delete
|
|
243
|
-
* await manager.deleteBranch('experiment', { force: true })
|
|
244
|
-
* ```
|
|
245
|
-
*/
|
|
246
|
-
async deleteBranch(name, options) {
|
|
247
|
-
const normalizedName = normalizeBranchName(name);
|
|
248
|
-
// Handle remote branch deletion
|
|
249
|
-
if (options?.remote) {
|
|
250
|
-
const remoteRef = `refs/remotes/${options.remote}/${normalizedName}`;
|
|
251
|
-
const remoteExists = await this.storage.getRef(remoteRef);
|
|
252
|
-
if (remoteExists) {
|
|
253
|
-
if (!options?.dryRun) {
|
|
254
|
-
await this.storage.deleteRef(remoteRef);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
const branchRef = getBranchRefName(normalizedName);
|
|
260
|
-
// Check if branch exists
|
|
261
|
-
const existing = await this.storage.getRef(branchRef);
|
|
262
|
-
if (!existing) {
|
|
263
|
-
throw new BranchError(`Branch '${name}' not found`, 'NOT_FOUND', name);
|
|
264
|
-
}
|
|
265
|
-
// Check if this is the current branch
|
|
266
|
-
const currentBranch = await this.getCurrentBranch();
|
|
267
|
-
if (currentBranch && currentBranch.name === normalizedName) {
|
|
268
|
-
throw new BranchError(`Cannot delete the checked out branch '${name}'`, 'CANNOT_DELETE_CURRENT', name);
|
|
269
|
-
}
|
|
270
|
-
// Check if branch is fully merged (unless force is true)
|
|
271
|
-
if (!options?.force) {
|
|
272
|
-
const isMerged = await this.isMerged(normalizedName);
|
|
273
|
-
if (!isMerged) {
|
|
274
|
-
throw new BranchError(`Branch '${name}' is not fully merged. Use force to delete anyway.`, 'NOT_FULLY_MERGED', name);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
// If dryRun, return without actually deleting
|
|
278
|
-
if (options?.dryRun) {
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
// Delete the ref
|
|
282
|
-
await this.storage.deleteRef(branchRef);
|
|
283
|
-
// Remove tracking info
|
|
284
|
-
this.trackingInfo.delete(normalizedName);
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Rename a branch.
|
|
288
|
-
*
|
|
289
|
-
* @description
|
|
290
|
-
* Renames a branch, updating the ref name. If the branch is the current
|
|
291
|
-
* branch, HEAD is updated accordingly.
|
|
292
|
-
*
|
|
293
|
-
* @param oldName - Current branch name
|
|
294
|
-
* @param newName - New branch name
|
|
295
|
-
* @param options - Rename options
|
|
296
|
-
* @returns The renamed branch
|
|
297
|
-
* @throws BranchError with code 'NOT_FOUND' if old branch doesn't exist
|
|
298
|
-
* @throws BranchError with code 'ALREADY_EXISTS' if new name exists and not forcing
|
|
299
|
-
* @throws BranchError with code 'INVALID_NAME' if new name is invalid
|
|
300
|
-
*
|
|
301
|
-
* @example
|
|
302
|
-
* ```typescript
|
|
303
|
-
* const branch = await manager.renameBranch('old-name', 'new-name')
|
|
304
|
-
* ```
|
|
305
|
-
*/
|
|
306
|
-
async renameBranch(oldName, newName, options) {
|
|
307
|
-
// Validate new branch name
|
|
308
|
-
const validation = validateBranchName(newName);
|
|
309
|
-
if (!validation.valid) {
|
|
310
|
-
throw new BranchError(validation.error, 'INVALID_NAME', newName);
|
|
311
|
-
}
|
|
312
|
-
const oldNormalized = normalizeBranchName(oldName);
|
|
313
|
-
const newNormalized = normalizeBranchName(newName);
|
|
314
|
-
const oldRef = getBranchRefName(oldNormalized);
|
|
315
|
-
const newRef = getBranchRefName(newNormalized);
|
|
316
|
-
// Check if old branch exists
|
|
317
|
-
const oldBranch = await this.storage.getRef(oldRef);
|
|
318
|
-
if (!oldBranch) {
|
|
319
|
-
throw new BranchError(`Branch '${oldName}' not found`, 'NOT_FOUND', oldName);
|
|
320
|
-
}
|
|
321
|
-
// Check if new branch already exists (unless force)
|
|
322
|
-
if (!options?.force) {
|
|
323
|
-
const existingNew = await this.storage.getRef(newRef);
|
|
324
|
-
if (existingNew) {
|
|
325
|
-
throw new BranchError(`Branch '${newName}' already exists`, 'ALREADY_EXISTS', newName);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
// Get the SHA from old branch
|
|
329
|
-
const sha = oldBranch.target;
|
|
330
|
-
// Check if this is the current branch
|
|
331
|
-
const currentBranch = await this.getCurrentBranch();
|
|
332
|
-
const wasCurrent = currentBranch && currentBranch.name === oldNormalized;
|
|
333
|
-
// Get tracking info from old branch
|
|
334
|
-
const oldTracking = this.trackingInfo.get(oldNormalized);
|
|
335
|
-
// Build result branch object
|
|
336
|
-
const branch = {
|
|
337
|
-
name: newNormalized,
|
|
338
|
-
ref: newRef,
|
|
339
|
-
sha,
|
|
340
|
-
isCurrent: wasCurrent ?? false,
|
|
341
|
-
isRemote: false,
|
|
342
|
-
tracking: oldTracking
|
|
343
|
-
};
|
|
344
|
-
// If dryRun, return without actually renaming
|
|
345
|
-
if (options?.dryRun) {
|
|
346
|
-
return branch;
|
|
347
|
-
}
|
|
348
|
-
// Create new ref with the same SHA
|
|
349
|
-
await this.storage.updateRef(newRef, sha, { create: true, force: options?.force });
|
|
350
|
-
// Delete old ref
|
|
351
|
-
await this.storage.deleteRef(oldRef);
|
|
352
|
-
// If this was the current branch, update HEAD to point to new branch
|
|
353
|
-
if (wasCurrent) {
|
|
354
|
-
await this.storage.updateHead(newRef, true);
|
|
355
|
-
}
|
|
356
|
-
// Transfer tracking info
|
|
357
|
-
if (oldTracking) {
|
|
358
|
-
this.trackingInfo.delete(oldNormalized);
|
|
359
|
-
this.trackingInfo.set(newNormalized, oldTracking);
|
|
360
|
-
}
|
|
361
|
-
return branch;
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* List all branches.
|
|
365
|
-
*
|
|
366
|
-
* @description
|
|
367
|
-
* Returns branches matching the specified criteria.
|
|
368
|
-
* By default, returns only local branches.
|
|
369
|
-
*
|
|
370
|
-
* @param options - Listing options
|
|
371
|
-
* @returns Array of branches matching criteria
|
|
372
|
-
*
|
|
373
|
-
* @example
|
|
374
|
-
* ```typescript
|
|
375
|
-
* // List local branches
|
|
376
|
-
* const local = await manager.listBranches()
|
|
377
|
-
*
|
|
378
|
-
* // List all branches including remotes
|
|
379
|
-
* const all = await manager.listBranches({ includeRemotes: true })
|
|
380
|
-
*
|
|
381
|
-
* // List merged branches
|
|
382
|
-
* const merged = await manager.listBranches({ mergedInto: 'main' })
|
|
383
|
-
* ```
|
|
384
|
-
*/
|
|
385
|
-
async listBranches(options) {
|
|
386
|
-
const branches = [];
|
|
387
|
-
// Get current branch for isCurrent flag
|
|
388
|
-
const currentBranch = await this.getCurrentBranch();
|
|
389
|
-
// List local branches
|
|
390
|
-
if (!options?.remotesOnly) {
|
|
391
|
-
const localRefs = await this.storage.listRefs({ pattern: 'refs/heads/*' });
|
|
392
|
-
for (const ref of localRefs) {
|
|
393
|
-
const name = normalizeBranchName(ref.name);
|
|
394
|
-
// Apply pattern filter if specified
|
|
395
|
-
if (options?.pattern) {
|
|
396
|
-
const regex = new RegExp('^' + options.pattern.replace(/\*/g, '.*') + '$');
|
|
397
|
-
if (!regex.test(name))
|
|
398
|
-
continue;
|
|
399
|
-
}
|
|
400
|
-
branches.push({
|
|
401
|
-
name,
|
|
402
|
-
ref: ref.name,
|
|
403
|
-
sha: ref.target,
|
|
404
|
-
isCurrent: currentBranch?.name === name,
|
|
405
|
-
isRemote: false,
|
|
406
|
-
tracking: options?.includeTracking ? this.trackingInfo.get(name) : undefined
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
// List remote branches if requested
|
|
411
|
-
if (options?.includeRemotes || options?.remotesOnly) {
|
|
412
|
-
const remoteRefs = await this.storage.listRefs({ pattern: 'refs/remotes/*' });
|
|
413
|
-
for (const ref of remoteRefs) {
|
|
414
|
-
const name = ref.name.replace(/^refs\/remotes\//, '');
|
|
415
|
-
branches.push({
|
|
416
|
-
name,
|
|
417
|
-
ref: ref.name,
|
|
418
|
-
sha: ref.target,
|
|
419
|
-
isCurrent: false,
|
|
420
|
-
isRemote: true
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
return branches;
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Get the current branch.
|
|
428
|
-
*
|
|
429
|
-
* @description
|
|
430
|
-
* Returns the branch that HEAD points to, or null if HEAD is detached.
|
|
431
|
-
*
|
|
432
|
-
* @returns Current branch or null if detached
|
|
433
|
-
*
|
|
434
|
-
* @example
|
|
435
|
-
* ```typescript
|
|
436
|
-
* const current = await manager.getCurrentBranch()
|
|
437
|
-
* if (current) {
|
|
438
|
-
* console.log(`On branch: ${current.name}`)
|
|
439
|
-
* } else {
|
|
440
|
-
* console.log('HEAD is detached')
|
|
441
|
-
* }
|
|
442
|
-
* ```
|
|
443
|
-
*/
|
|
444
|
-
async getCurrentBranch() {
|
|
445
|
-
const head = await this.storage.getRef('HEAD');
|
|
446
|
-
if (!head)
|
|
447
|
-
return null;
|
|
448
|
-
// If HEAD is direct (detached), there is no current branch
|
|
449
|
-
if (head.type === 'direct') {
|
|
450
|
-
return null;
|
|
451
|
-
}
|
|
452
|
-
// HEAD is symbolic, pointing to a branch
|
|
453
|
-
const branchRef = head.target;
|
|
454
|
-
if (!branchRef.startsWith('refs/heads/')) {
|
|
455
|
-
return null;
|
|
456
|
-
}
|
|
457
|
-
const ref = await this.storage.getRef(branchRef);
|
|
458
|
-
if (!ref)
|
|
459
|
-
return null;
|
|
460
|
-
const name = normalizeBranchName(branchRef);
|
|
461
|
-
return {
|
|
462
|
-
name,
|
|
463
|
-
ref: branchRef,
|
|
464
|
-
sha: ref.target,
|
|
465
|
-
isCurrent: true,
|
|
466
|
-
isRemote: false,
|
|
467
|
-
tracking: this.trackingInfo.get(name)
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
471
|
-
* Get a specific branch by name.
|
|
472
|
-
*
|
|
473
|
-
* @description
|
|
474
|
-
* Retrieves branch information for a specific branch.
|
|
475
|
-
*
|
|
476
|
-
* @param name - Branch name
|
|
477
|
-
* @returns Branch info or null if not found
|
|
478
|
-
*
|
|
479
|
-
* @example
|
|
480
|
-
* ```typescript
|
|
481
|
-
* const branch = await manager.getBranch('main')
|
|
482
|
-
* if (branch) {
|
|
483
|
-
* console.log(`main is at ${branch.sha}`)
|
|
484
|
-
* }
|
|
485
|
-
* ```
|
|
486
|
-
*/
|
|
487
|
-
async getBranch(name) {
|
|
488
|
-
const normalizedName = normalizeBranchName(name);
|
|
489
|
-
const branchRef = getBranchRefName(normalizedName);
|
|
490
|
-
const ref = await this.storage.getRef(branchRef);
|
|
491
|
-
if (!ref)
|
|
492
|
-
return null;
|
|
493
|
-
const currentBranch = await this.getCurrentBranch();
|
|
494
|
-
return {
|
|
495
|
-
name: normalizedName,
|
|
496
|
-
ref: branchRef,
|
|
497
|
-
sha: ref.target,
|
|
498
|
-
isCurrent: currentBranch?.name === normalizedName,
|
|
499
|
-
isRemote: false,
|
|
500
|
-
tracking: this.trackingInfo.get(normalizedName)
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
/**
|
|
504
|
-
* Check if a branch exists.
|
|
505
|
-
*
|
|
506
|
-
* @description
|
|
507
|
-
* Quick check for branch existence without fetching full info.
|
|
508
|
-
*
|
|
509
|
-
* @param name - Branch name
|
|
510
|
-
* @returns True if branch exists
|
|
511
|
-
*
|
|
512
|
-
* @example
|
|
513
|
-
* ```typescript
|
|
514
|
-
* if (await manager.branchExists('feature')) {
|
|
515
|
-
* console.log('Branch already exists')
|
|
516
|
-
* }
|
|
517
|
-
* ```
|
|
518
|
-
*/
|
|
519
|
-
async branchExists(name) {
|
|
520
|
-
// Handle remote branch names like 'origin/main'
|
|
521
|
-
if (name.includes('/') && !name.startsWith('refs/')) {
|
|
522
|
-
const parts = name.split('/');
|
|
523
|
-
if (parts.length >= 2) {
|
|
524
|
-
// Check if it's a remote reference
|
|
525
|
-
const remoteRef = `refs/remotes/${name}`;
|
|
526
|
-
const remoteExists = await this.storage.getRef(remoteRef);
|
|
527
|
-
if (remoteExists)
|
|
528
|
-
return true;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
const normalizedName = normalizeBranchName(name);
|
|
532
|
-
const branchRef = getBranchRefName(normalizedName);
|
|
533
|
-
const ref = await this.storage.getRef(branchRef);
|
|
534
|
-
return ref !== null;
|
|
535
|
-
}
|
|
536
|
-
/**
|
|
537
|
-
* Set upstream branch for tracking.
|
|
538
|
-
*
|
|
539
|
-
* @description
|
|
540
|
-
* Configures or removes upstream tracking for a branch.
|
|
541
|
-
*
|
|
542
|
-
* @param branchName - Local branch to configure
|
|
543
|
-
* @param options - Tracking options
|
|
544
|
-
* @throws BranchError with code 'NOT_FOUND' if branch doesn't exist
|
|
545
|
-
*
|
|
546
|
-
* @example
|
|
547
|
-
* ```typescript
|
|
548
|
-
* // Set upstream
|
|
549
|
-
* await manager.setUpstream('feature', {
|
|
550
|
-
* remote: 'origin',
|
|
551
|
-
* remoteBranch: 'feature'
|
|
552
|
-
* })
|
|
553
|
-
*
|
|
554
|
-
* // Remove upstream
|
|
555
|
-
* await manager.setUpstream('feature', { unset: true })
|
|
556
|
-
* ```
|
|
557
|
-
*/
|
|
558
|
-
async setUpstream(branchName, options) {
|
|
559
|
-
const normalizedName = normalizeBranchName(branchName);
|
|
560
|
-
const branchRef = getBranchRefName(normalizedName);
|
|
561
|
-
// Check if branch exists
|
|
562
|
-
const existing = await this.storage.getRef(branchRef);
|
|
563
|
-
if (!existing) {
|
|
564
|
-
throw new BranchError(`Branch '${branchName}' not found`, 'NOT_FOUND', branchName);
|
|
565
|
-
}
|
|
566
|
-
// If unset, remove tracking info
|
|
567
|
-
if (options.unset) {
|
|
568
|
-
this.trackingInfo.delete(normalizedName);
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
// Build the remote branch ref
|
|
572
|
-
const remote = options.remote || 'origin';
|
|
573
|
-
const remoteBranchName = options.remoteBranch || normalizedName;
|
|
574
|
-
const remoteBranch = `refs/remotes/${remote}/${remoteBranchName}`;
|
|
575
|
-
const tracking = {
|
|
576
|
-
remote,
|
|
577
|
-
remoteBranch,
|
|
578
|
-
ahead: 0,
|
|
579
|
-
behind: 0,
|
|
580
|
-
gone: false
|
|
581
|
-
};
|
|
582
|
-
this.trackingInfo.set(normalizedName, tracking);
|
|
583
|
-
}
|
|
584
|
-
/**
|
|
585
|
-
* Get tracking info for a branch.
|
|
586
|
-
*
|
|
587
|
-
* @description
|
|
588
|
-
* Returns upstream tracking information including ahead/behind counts.
|
|
589
|
-
*
|
|
590
|
-
* @param branchName - Branch to check
|
|
591
|
-
* @returns Tracking info or null if not tracking
|
|
592
|
-
*
|
|
593
|
-
* @example
|
|
594
|
-
* ```typescript
|
|
595
|
-
* const tracking = await manager.getTrackingInfo('main')
|
|
596
|
-
* if (tracking) {
|
|
597
|
-
* console.log(`${tracking.ahead} ahead, ${tracking.behind} behind ${tracking.remoteBranch}`)
|
|
598
|
-
* }
|
|
599
|
-
* ```
|
|
600
|
-
*/
|
|
601
|
-
async getTrackingInfo(branchName) {
|
|
602
|
-
const normalizedName = normalizeBranchName(branchName);
|
|
603
|
-
return this.trackingInfo.get(normalizedName) ?? null;
|
|
604
|
-
}
|
|
605
|
-
/**
|
|
606
|
-
* Check if a branch is fully merged into another branch.
|
|
607
|
-
*
|
|
608
|
-
* @description
|
|
609
|
-
* Determines if all commits on the branch are reachable from the target.
|
|
610
|
-
*
|
|
611
|
-
* @param branchName - Branch to check
|
|
612
|
-
* @param into - Target branch (defaults to current branch)
|
|
613
|
-
* @returns True if fully merged
|
|
614
|
-
*
|
|
615
|
-
* @example
|
|
616
|
-
* ```typescript
|
|
617
|
-
* if (await manager.isMerged('feature', 'main')) {
|
|
618
|
-
* console.log('Safe to delete feature branch')
|
|
619
|
-
* }
|
|
620
|
-
* ```
|
|
621
|
-
*/
|
|
622
|
-
async isMerged(branchName, into) {
|
|
623
|
-
const normalizedName = normalizeBranchName(branchName);
|
|
624
|
-
const branchRef = getBranchRefName(normalizedName);
|
|
625
|
-
// Get the branch SHA
|
|
626
|
-
const branchSha = await this.storage.getRef(branchRef);
|
|
627
|
-
if (!branchSha) {
|
|
628
|
-
throw new BranchError(`Branch '${branchName}' not found`, 'NOT_FOUND', branchName);
|
|
629
|
-
}
|
|
630
|
-
// Get the target branch SHA
|
|
631
|
-
let targetSha;
|
|
632
|
-
if (into) {
|
|
633
|
-
const targetRef = getBranchRefName(normalizeBranchName(into));
|
|
634
|
-
const resolved = await this.storage.getRef(targetRef);
|
|
635
|
-
if (!resolved) {
|
|
636
|
-
throw new BranchError(`Branch '${into}' not found`, 'NOT_FOUND', into);
|
|
637
|
-
}
|
|
638
|
-
targetSha = resolved.target;
|
|
639
|
-
}
|
|
640
|
-
else {
|
|
641
|
-
// Use current branch
|
|
642
|
-
const current = await this.getCurrentBranch();
|
|
643
|
-
if (!current) {
|
|
644
|
-
throw new BranchError('No current branch', 'DETACHED_HEAD');
|
|
645
|
-
}
|
|
646
|
-
targetSha = current.sha;
|
|
647
|
-
}
|
|
648
|
-
// Simple check: if the branch points to the same SHA as target, it's merged
|
|
649
|
-
// For a more accurate check, we'd need to walk the commit graph
|
|
650
|
-
return branchSha.target === targetSha;
|
|
651
|
-
}
|
|
652
|
-
/**
|
|
653
|
-
* Force delete an unmerged branch.
|
|
654
|
-
*
|
|
655
|
-
* @description
|
|
656
|
-
* Deletes a branch even if it has unmerged commits. Use with caution
|
|
657
|
-
* as this can result in lost commits.
|
|
658
|
-
*
|
|
659
|
-
* @param name - Branch name to delete
|
|
660
|
-
* @throws BranchError with code 'NOT_FOUND' if branch doesn't exist
|
|
661
|
-
* @throws BranchError with code 'CANNOT_DELETE_CURRENT' if branch is checked out
|
|
662
|
-
*
|
|
663
|
-
* @example
|
|
664
|
-
* ```typescript
|
|
665
|
-
* await manager.forceDeleteBranch('abandoned-feature')
|
|
666
|
-
* ```
|
|
667
|
-
*/
|
|
668
|
-
async forceDeleteBranch(name) {
|
|
669
|
-
return this.deleteBranch(name, { force: true });
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
// ============================================================================
|
|
673
|
-
// Validation Functions
|
|
674
|
-
// ============================================================================
|
|
675
|
-
/**
|
|
676
|
-
* Validate a branch name according to Git rules.
|
|
677
|
-
*
|
|
678
|
-
* @description
|
|
679
|
-
* Checks if a branch name is valid and returns detailed validation results
|
|
680
|
-
* including the normalized form of the name.
|
|
681
|
-
*
|
|
682
|
-
* @param name - Branch name to validate
|
|
683
|
-
* @returns Validation result with valid flag, error message, and normalized name
|
|
684
|
-
*
|
|
685
|
-
* @see https://git-scm.com/docs/git-check-ref-format
|
|
686
|
-
*
|
|
687
|
-
* @example
|
|
688
|
-
* ```typescript
|
|
689
|
-
* const result = validateBranchName('feature/auth')
|
|
690
|
-
* if (result.valid) {
|
|
691
|
-
* console.log(`Valid: ${result.normalized}`)
|
|
692
|
-
* } else {
|
|
693
|
-
* console.log(`Invalid: ${result.error}`)
|
|
694
|
-
* }
|
|
695
|
-
* ```
|
|
696
|
-
*/
|
|
697
|
-
export function validateBranchName(name) {
|
|
698
|
-
// Empty name is invalid
|
|
699
|
-
if (!name || name.length === 0) {
|
|
700
|
-
return { valid: false, error: 'Branch name cannot be empty' };
|
|
701
|
-
}
|
|
702
|
-
// HEAD is not a valid branch name
|
|
703
|
-
if (name === 'HEAD') {
|
|
704
|
-
return { valid: false, error: 'HEAD is not a valid branch name' };
|
|
705
|
-
}
|
|
706
|
-
// Cannot start with dash
|
|
707
|
-
if (name.startsWith('-')) {
|
|
708
|
-
return { valid: false, error: 'Branch name cannot start with a dash' };
|
|
709
|
-
}
|
|
710
|
-
// Cannot contain spaces
|
|
711
|
-
if (name.includes(' ')) {
|
|
712
|
-
return { valid: false, error: 'Branch name cannot contain spaces' };
|
|
713
|
-
}
|
|
714
|
-
// Cannot contain double dots
|
|
715
|
-
if (name.includes('..')) {
|
|
716
|
-
return { valid: false, error: 'Branch name cannot contain double dots (..)' };
|
|
717
|
-
}
|
|
718
|
-
// Cannot end with .lock
|
|
719
|
-
if (name.endsWith('.lock')) {
|
|
720
|
-
return { valid: false, error: 'Branch name cannot end with .lock' };
|
|
721
|
-
}
|
|
722
|
-
// Cannot contain control characters (ASCII 0-31, 127)
|
|
723
|
-
const controlCharRegex = /[\x00-\x1f\x7f]/;
|
|
724
|
-
if (controlCharRegex.test(name)) {
|
|
725
|
-
return { valid: false, error: 'Branch name cannot contain control characters' };
|
|
726
|
-
}
|
|
727
|
-
// Cannot contain ~, ^, :, ?, *, [, ], \
|
|
728
|
-
const invalidChars = /[~^:?*[\]\\]/;
|
|
729
|
-
if (invalidChars.test(name)) {
|
|
730
|
-
return { valid: false, error: 'Branch name contains invalid characters (~, ^, :, ?, *, [, ], \\)' };
|
|
731
|
-
}
|
|
732
|
-
// Normalize the name (strip refs/heads/ if present)
|
|
733
|
-
const normalized = normalizeBranchName(name);
|
|
734
|
-
return { valid: true, normalized };
|
|
735
|
-
}
|
|
736
|
-
/**
|
|
737
|
-
* Check if a string is a valid branch name.
|
|
738
|
-
*
|
|
739
|
-
* @description
|
|
740
|
-
* Simple boolean check for branch name validity.
|
|
741
|
-
*
|
|
742
|
-
* @param name - Branch name to check
|
|
743
|
-
* @returns True if valid
|
|
744
|
-
*
|
|
745
|
-
* @example
|
|
746
|
-
* ```typescript
|
|
747
|
-
* if (isValidBranchName('feature/new')) {
|
|
748
|
-
* // Create the branch
|
|
749
|
-
* }
|
|
750
|
-
* ```
|
|
751
|
-
*/
|
|
752
|
-
export function isValidBranchName(name) {
|
|
753
|
-
return validateBranchName(name).valid;
|
|
754
|
-
}
|
|
755
|
-
/**
|
|
756
|
-
* Normalize a branch name.
|
|
757
|
-
*
|
|
758
|
-
* @description
|
|
759
|
-
* Removes refs/heads/ prefix if present, cleans up the name.
|
|
760
|
-
*
|
|
761
|
-
* @param name - Branch name or ref
|
|
762
|
-
* @returns Normalized short branch name
|
|
763
|
-
*
|
|
764
|
-
* @example
|
|
765
|
-
* ```typescript
|
|
766
|
-
* normalizeBranchName('refs/heads/main') // 'main'
|
|
767
|
-
* normalizeBranchName('main') // 'main'
|
|
768
|
-
* ```
|
|
769
|
-
*/
|
|
770
|
-
export function normalizeBranchName(name) {
|
|
771
|
-
if (name.startsWith('refs/heads/')) {
|
|
772
|
-
return name.slice('refs/heads/'.length);
|
|
773
|
-
}
|
|
774
|
-
return name;
|
|
775
|
-
}
|
|
776
|
-
/**
|
|
777
|
-
* Get the full ref name for a branch.
|
|
778
|
-
*
|
|
779
|
-
* @description
|
|
780
|
-
* Adds refs/heads/ prefix if not present.
|
|
781
|
-
*
|
|
782
|
-
* @param name - Short branch name
|
|
783
|
-
* @returns Full ref name
|
|
784
|
-
*
|
|
785
|
-
* @example
|
|
786
|
-
* ```typescript
|
|
787
|
-
* getBranchRefName('main') // 'refs/heads/main'
|
|
788
|
-
* getBranchRefName('refs/heads/main') // 'refs/heads/main'
|
|
789
|
-
* ```
|
|
790
|
-
*/
|
|
791
|
-
export function getBranchRefName(name) {
|
|
792
|
-
if (name.startsWith('refs/heads/')) {
|
|
793
|
-
return name;
|
|
794
|
-
}
|
|
795
|
-
return `refs/heads/${name}`;
|
|
796
|
-
}
|
|
797
|
-
// ============================================================================
|
|
798
|
-
// Convenience Functions
|
|
799
|
-
// ============================================================================
|
|
800
|
-
/**
|
|
801
|
-
* Create a new branch.
|
|
802
|
-
*
|
|
803
|
-
* @description
|
|
804
|
-
* Convenience function that creates a BranchManager and calls createBranch.
|
|
805
|
-
*
|
|
806
|
-
* @param storage - RefStorage instance
|
|
807
|
-
* @param name - Branch name
|
|
808
|
-
* @param options - Creation options
|
|
809
|
-
* @returns Created branch
|
|
810
|
-
*
|
|
811
|
-
* @example
|
|
812
|
-
* ```typescript
|
|
813
|
-
* const branch = await createBranch(storage, 'feature', { startPoint: 'main' })
|
|
814
|
-
* ```
|
|
815
|
-
*/
|
|
816
|
-
export async function createBranch(storage, name, options) {
|
|
817
|
-
const manager = new BranchManager(storage);
|
|
818
|
-
return manager.createBranch(name, options);
|
|
819
|
-
}
|
|
820
|
-
/**
|
|
821
|
-
* Delete a branch.
|
|
822
|
-
*
|
|
823
|
-
* @description
|
|
824
|
-
* Convenience function that creates a BranchManager and calls deleteBranch.
|
|
825
|
-
*
|
|
826
|
-
* @param storage - RefStorage instance
|
|
827
|
-
* @param name - Branch name to delete
|
|
828
|
-
* @param options - Deletion options
|
|
829
|
-
*
|
|
830
|
-
* @example
|
|
831
|
-
* ```typescript
|
|
832
|
-
* await deleteBranch(storage, 'feature', { force: true })
|
|
833
|
-
* ```
|
|
834
|
-
*/
|
|
835
|
-
export async function deleteBranch(storage, name, options) {
|
|
836
|
-
const manager = new BranchManager(storage);
|
|
837
|
-
return manager.deleteBranch(name, options);
|
|
838
|
-
}
|
|
839
|
-
/**
|
|
840
|
-
* Rename a branch.
|
|
841
|
-
*
|
|
842
|
-
* @description
|
|
843
|
-
* Convenience function that creates a BranchManager and calls renameBranch.
|
|
844
|
-
*
|
|
845
|
-
* @param storage - RefStorage instance
|
|
846
|
-
* @param oldName - Current branch name
|
|
847
|
-
* @param newName - New branch name
|
|
848
|
-
* @param options - Rename options
|
|
849
|
-
* @returns Renamed branch
|
|
850
|
-
*
|
|
851
|
-
* @example
|
|
852
|
-
* ```typescript
|
|
853
|
-
* const branch = await renameBranch(storage, 'old', 'new')
|
|
854
|
-
* ```
|
|
855
|
-
*/
|
|
856
|
-
export async function renameBranch(storage, oldName, newName, options) {
|
|
857
|
-
const manager = new BranchManager(storage);
|
|
858
|
-
return manager.renameBranch(oldName, newName, options);
|
|
859
|
-
}
|
|
860
|
-
/**
|
|
861
|
-
* List all branches.
|
|
862
|
-
*
|
|
863
|
-
* @description
|
|
864
|
-
* Convenience function that creates a BranchManager and calls listBranches.
|
|
865
|
-
*
|
|
866
|
-
* @param storage - RefStorage instance
|
|
867
|
-
* @param options - Listing options
|
|
868
|
-
* @returns Array of branches
|
|
869
|
-
*
|
|
870
|
-
* @example
|
|
871
|
-
* ```typescript
|
|
872
|
-
* const branches = await listBranches(storage, { includeRemotes: true })
|
|
873
|
-
* ```
|
|
874
|
-
*/
|
|
875
|
-
export async function listBranches(storage, options) {
|
|
876
|
-
const manager = new BranchManager(storage);
|
|
877
|
-
return manager.listBranches(options);
|
|
878
|
-
}
|
|
879
|
-
/**
|
|
880
|
-
* Get the current branch.
|
|
881
|
-
*
|
|
882
|
-
* @description
|
|
883
|
-
* Convenience function that creates a BranchManager and calls getCurrentBranch.
|
|
884
|
-
*
|
|
885
|
-
* @param storage - RefStorage instance
|
|
886
|
-
* @returns Current branch or null if detached
|
|
887
|
-
*
|
|
888
|
-
* @example
|
|
889
|
-
* ```typescript
|
|
890
|
-
* const current = await getCurrentBranch(storage)
|
|
891
|
-
* ```
|
|
892
|
-
*/
|
|
893
|
-
export async function getCurrentBranch(storage) {
|
|
894
|
-
const manager = new BranchManager(storage);
|
|
895
|
-
return manager.getCurrentBranch();
|
|
896
|
-
}
|
|
897
|
-
//# sourceMappingURL=branch.js.map
|