@git-stunts/git-warp 10.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +16 -0
  3. package/README.md +480 -0
  4. package/SECURITY.md +30 -0
  5. package/bin/git-warp +24 -0
  6. package/bin/warp-graph.js +1574 -0
  7. package/index.d.ts +2366 -0
  8. package/index.js +180 -0
  9. package/package.json +129 -0
  10. package/scripts/install-git-warp.sh +258 -0
  11. package/scripts/uninstall-git-warp.sh +139 -0
  12. package/src/domain/WarpGraph.js +3157 -0
  13. package/src/domain/crdt/Dot.js +160 -0
  14. package/src/domain/crdt/LWW.js +154 -0
  15. package/src/domain/crdt/ORSet.js +371 -0
  16. package/src/domain/crdt/VersionVector.js +222 -0
  17. package/src/domain/entities/GraphNode.js +60 -0
  18. package/src/domain/errors/EmptyMessageError.js +47 -0
  19. package/src/domain/errors/ForkError.js +30 -0
  20. package/src/domain/errors/IndexError.js +23 -0
  21. package/src/domain/errors/OperationAbortedError.js +22 -0
  22. package/src/domain/errors/QueryError.js +39 -0
  23. package/src/domain/errors/SchemaUnsupportedError.js +17 -0
  24. package/src/domain/errors/ShardCorruptionError.js +56 -0
  25. package/src/domain/errors/ShardLoadError.js +57 -0
  26. package/src/domain/errors/ShardValidationError.js +61 -0
  27. package/src/domain/errors/StorageError.js +57 -0
  28. package/src/domain/errors/SyncError.js +30 -0
  29. package/src/domain/errors/TraversalError.js +23 -0
  30. package/src/domain/errors/WarpError.js +31 -0
  31. package/src/domain/errors/WormholeError.js +28 -0
  32. package/src/domain/errors/WriterError.js +39 -0
  33. package/src/domain/errors/index.js +21 -0
  34. package/src/domain/services/AnchorMessageCodec.js +99 -0
  35. package/src/domain/services/BitmapIndexBuilder.js +225 -0
  36. package/src/domain/services/BitmapIndexReader.js +435 -0
  37. package/src/domain/services/BoundaryTransitionRecord.js +463 -0
  38. package/src/domain/services/CheckpointMessageCodec.js +147 -0
  39. package/src/domain/services/CheckpointSerializerV5.js +281 -0
  40. package/src/domain/services/CheckpointService.js +384 -0
  41. package/src/domain/services/CommitDagTraversalService.js +156 -0
  42. package/src/domain/services/DagPathFinding.js +712 -0
  43. package/src/domain/services/DagTopology.js +239 -0
  44. package/src/domain/services/DagTraversal.js +245 -0
  45. package/src/domain/services/Frontier.js +108 -0
  46. package/src/domain/services/GCMetrics.js +101 -0
  47. package/src/domain/services/GCPolicy.js +122 -0
  48. package/src/domain/services/GitLogParser.js +205 -0
  49. package/src/domain/services/HealthCheckService.js +246 -0
  50. package/src/domain/services/HookInstaller.js +326 -0
  51. package/src/domain/services/HttpSyncServer.js +262 -0
  52. package/src/domain/services/IndexRebuildService.js +426 -0
  53. package/src/domain/services/IndexStalenessChecker.js +103 -0
  54. package/src/domain/services/JoinReducer.js +582 -0
  55. package/src/domain/services/KeyCodec.js +113 -0
  56. package/src/domain/services/LegacyAnchorDetector.js +67 -0
  57. package/src/domain/services/LogicalTraversal.js +351 -0
  58. package/src/domain/services/MessageCodecInternal.js +132 -0
  59. package/src/domain/services/MessageSchemaDetector.js +145 -0
  60. package/src/domain/services/MigrationService.js +55 -0
  61. package/src/domain/services/ObserverView.js +265 -0
  62. package/src/domain/services/PatchBuilderV2.js +669 -0
  63. package/src/domain/services/PatchMessageCodec.js +140 -0
  64. package/src/domain/services/ProvenanceIndex.js +337 -0
  65. package/src/domain/services/ProvenancePayload.js +242 -0
  66. package/src/domain/services/QueryBuilder.js +835 -0
  67. package/src/domain/services/StateDiff.js +300 -0
  68. package/src/domain/services/StateSerializerV5.js +156 -0
  69. package/src/domain/services/StreamingBitmapIndexBuilder.js +709 -0
  70. package/src/domain/services/SyncProtocol.js +593 -0
  71. package/src/domain/services/TemporalQuery.js +201 -0
  72. package/src/domain/services/TranslationCost.js +221 -0
  73. package/src/domain/services/TraversalService.js +8 -0
  74. package/src/domain/services/WarpMessageCodec.js +29 -0
  75. package/src/domain/services/WarpStateIndexBuilder.js +127 -0
  76. package/src/domain/services/WormholeService.js +353 -0
  77. package/src/domain/types/TickReceipt.js +285 -0
  78. package/src/domain/types/WarpTypes.js +209 -0
  79. package/src/domain/types/WarpTypesV2.js +200 -0
  80. package/src/domain/utils/CachedValue.js +140 -0
  81. package/src/domain/utils/EventId.js +89 -0
  82. package/src/domain/utils/LRUCache.js +112 -0
  83. package/src/domain/utils/MinHeap.js +114 -0
  84. package/src/domain/utils/RefLayout.js +280 -0
  85. package/src/domain/utils/WriterId.js +205 -0
  86. package/src/domain/utils/cancellation.js +33 -0
  87. package/src/domain/utils/canonicalStringify.js +42 -0
  88. package/src/domain/utils/defaultClock.js +20 -0
  89. package/src/domain/utils/defaultCodec.js +51 -0
  90. package/src/domain/utils/nullLogger.js +21 -0
  91. package/src/domain/utils/roaring.js +181 -0
  92. package/src/domain/utils/shardVersion.js +9 -0
  93. package/src/domain/warp/PatchSession.js +217 -0
  94. package/src/domain/warp/Writer.js +181 -0
  95. package/src/hooks/post-merge.sh +60 -0
  96. package/src/infrastructure/adapters/BunHttpAdapter.js +225 -0
  97. package/src/infrastructure/adapters/ClockAdapter.js +57 -0
  98. package/src/infrastructure/adapters/ConsoleLogger.js +150 -0
  99. package/src/infrastructure/adapters/DenoHttpAdapter.js +230 -0
  100. package/src/infrastructure/adapters/GitGraphAdapter.js +787 -0
  101. package/src/infrastructure/adapters/GlobalClockAdapter.js +5 -0
  102. package/src/infrastructure/adapters/NoOpLogger.js +62 -0
  103. package/src/infrastructure/adapters/NodeCryptoAdapter.js +32 -0
  104. package/src/infrastructure/adapters/NodeHttpAdapter.js +98 -0
  105. package/src/infrastructure/adapters/PerformanceClockAdapter.js +5 -0
  106. package/src/infrastructure/adapters/WebCryptoAdapter.js +121 -0
  107. package/src/infrastructure/codecs/CborCodec.js +384 -0
  108. package/src/ports/BlobPort.js +30 -0
  109. package/src/ports/ClockPort.js +25 -0
  110. package/src/ports/CodecPort.js +25 -0
  111. package/src/ports/CommitPort.js +114 -0
  112. package/src/ports/ConfigPort.js +31 -0
  113. package/src/ports/CryptoPort.js +38 -0
  114. package/src/ports/GraphPersistencePort.js +57 -0
  115. package/src/ports/HttpServerPort.js +25 -0
  116. package/src/ports/IndexStoragePort.js +39 -0
  117. package/src/ports/LoggerPort.js +68 -0
  118. package/src/ports/RefPort.js +51 -0
  119. package/src/ports/TreePort.js +51 -0
  120. package/src/visualization/index.js +26 -0
  121. package/src/visualization/layouts/converters.js +75 -0
  122. package/src/visualization/layouts/elkAdapter.js +86 -0
  123. package/src/visualization/layouts/elkLayout.js +95 -0
  124. package/src/visualization/layouts/index.js +29 -0
  125. package/src/visualization/renderers/ascii/box.js +16 -0
  126. package/src/visualization/renderers/ascii/check.js +271 -0
  127. package/src/visualization/renderers/ascii/colors.js +13 -0
  128. package/src/visualization/renderers/ascii/formatters.js +73 -0
  129. package/src/visualization/renderers/ascii/graph.js +344 -0
  130. package/src/visualization/renderers/ascii/history.js +335 -0
  131. package/src/visualization/renderers/ascii/index.js +14 -0
  132. package/src/visualization/renderers/ascii/info.js +245 -0
  133. package/src/visualization/renderers/ascii/materialize.js +255 -0
  134. package/src/visualization/renderers/ascii/path.js +240 -0
  135. package/src/visualization/renderers/ascii/progress.js +32 -0
  136. package/src/visualization/renderers/ascii/symbols.js +33 -0
  137. package/src/visualization/renderers/ascii/table.js +19 -0
  138. package/src/visualization/renderers/browser/index.js +1 -0
  139. package/src/visualization/renderers/svg/index.js +159 -0
  140. package/src/visualization/utils/ansi.js +14 -0
  141. package/src/visualization/utils/time.js +40 -0
  142. package/src/visualization/utils/truncate.js +40 -0
  143. package/src/visualization/utils/unicode.js +52 -0
package/index.js ADDED
@@ -0,0 +1,180 @@
1
+ /**
2
+ * @fileoverview Empty Graph - A graph database substrate using Git commits pointing to the empty tree.
3
+ */
4
+
5
+ import GitGraphAdapter from './src/infrastructure/adapters/GitGraphAdapter.js';
6
+ import GraphNode from './src/domain/entities/GraphNode.js';
7
+ import BitmapIndexBuilder from './src/domain/services/BitmapIndexBuilder.js';
8
+ import BitmapIndexReader from './src/domain/services/BitmapIndexReader.js';
9
+ import IndexRebuildService from './src/domain/services/IndexRebuildService.js';
10
+ import HealthCheckService, { HealthStatus } from './src/domain/services/HealthCheckService.js';
11
+ import CommitDagTraversalService from './src/domain/services/CommitDagTraversalService.js';
12
+ import GraphPersistencePort from './src/ports/GraphPersistencePort.js';
13
+ import IndexStoragePort from './src/ports/IndexStoragePort.js';
14
+ import LoggerPort from './src/ports/LoggerPort.js';
15
+ import ClockPort from './src/ports/ClockPort.js';
16
+ import NoOpLogger from './src/infrastructure/adapters/NoOpLogger.js';
17
+ import ConsoleLogger, { LogLevel } from './src/infrastructure/adapters/ConsoleLogger.js';
18
+ import ClockAdapter from './src/infrastructure/adapters/ClockAdapter.js';
19
+ import PerformanceClockAdapter from './src/infrastructure/adapters/PerformanceClockAdapter.js';
20
+ import GlobalClockAdapter from './src/infrastructure/adapters/GlobalClockAdapter.js';
21
+ import {
22
+ ForkError,
23
+ IndexError,
24
+ QueryError,
25
+ SchemaUnsupportedError,
26
+ ShardLoadError,
27
+ ShardCorruptionError,
28
+ ShardValidationError,
29
+ StorageError,
30
+ TraversalError,
31
+ OperationAbortedError,
32
+ SyncError,
33
+ WormholeError,
34
+ } from './src/domain/errors/index.js';
35
+ import { checkAborted, createTimeoutSignal } from './src/domain/utils/cancellation.js';
36
+
37
+ // Multi-writer graph support (WARP)
38
+ import WarpGraph from './src/domain/WarpGraph.js';
39
+ import {
40
+ createNodeAdd,
41
+ createNodeTombstone,
42
+ createEdgeAdd,
43
+ createEdgeTombstone,
44
+ createPropSet,
45
+ createInlineValue,
46
+ createBlobValue,
47
+ createEventId,
48
+ } from './src/domain/types/WarpTypes.js';
49
+ import { migrateV4toV5 } from './src/domain/services/MigrationService.js';
50
+ import QueryBuilder from './src/domain/services/QueryBuilder.js';
51
+ import ObserverView from './src/domain/services/ObserverView.js';
52
+ import { computeTranslationCost } from './src/domain/services/TranslationCost.js';
53
+ import {
54
+ encodeEdgePropKey,
55
+ decodeEdgePropKey,
56
+ isEdgePropKey,
57
+ } from './src/domain/services/KeyCodec.js';
58
+ import {
59
+ createTickReceipt,
60
+ canonicalJson as tickReceiptCanonicalJson,
61
+ OP_TYPES as TICK_RECEIPT_OP_TYPES,
62
+ RESULT_TYPES as TICK_RECEIPT_RESULT_TYPES,
63
+ } from './src/domain/types/TickReceipt.js';
64
+
65
+ // Provenance payload (HOLOGRAM)
66
+ import ProvenancePayload from './src/domain/services/ProvenancePayload.js';
67
+
68
+ // Boundary Transition Records (HOLOGRAM)
69
+ import {
70
+ createBTR,
71
+ verifyBTR,
72
+ replayBTR,
73
+ serializeBTR,
74
+ deserializeBTR,
75
+ } from './src/domain/services/BoundaryTransitionRecord.js';
76
+
77
+ // Wormhole compression (HOLOGRAM)
78
+ import {
79
+ createWormhole,
80
+ composeWormholes,
81
+ replayWormhole,
82
+ serializeWormhole,
83
+ deserializeWormhole,
84
+ } from './src/domain/services/WormholeService.js';
85
+
86
+ const TraversalService = CommitDagTraversalService;
87
+
88
+ export {
89
+ GitGraphAdapter,
90
+ GraphNode,
91
+ BitmapIndexBuilder,
92
+ BitmapIndexReader,
93
+ IndexRebuildService,
94
+ HealthCheckService,
95
+ HealthStatus,
96
+ CommitDagTraversalService,
97
+ TraversalService,
98
+ GraphPersistencePort,
99
+ IndexStoragePort,
100
+
101
+ // Logging infrastructure
102
+ LoggerPort,
103
+ NoOpLogger,
104
+ ConsoleLogger,
105
+ LogLevel,
106
+
107
+ // Clock infrastructure
108
+ ClockPort,
109
+ ClockAdapter,
110
+ PerformanceClockAdapter,
111
+ GlobalClockAdapter,
112
+
113
+ // Error types for integrity failure handling
114
+ ForkError,
115
+ IndexError,
116
+ QueryError,
117
+ SchemaUnsupportedError,
118
+ ShardLoadError,
119
+ ShardCorruptionError,
120
+ ShardValidationError,
121
+ StorageError,
122
+ TraversalError,
123
+ OperationAbortedError,
124
+ SyncError,
125
+ WormholeError,
126
+
127
+ // Cancellation utilities
128
+ checkAborted,
129
+ createTimeoutSignal,
130
+
131
+ // Multi-writer graph support (WARP)
132
+ WarpGraph,
133
+ QueryBuilder,
134
+ ObserverView,
135
+ computeTranslationCost,
136
+
137
+ // WARP type creators
138
+ createNodeAdd,
139
+ createNodeTombstone,
140
+ createEdgeAdd,
141
+ createEdgeTombstone,
142
+ createPropSet,
143
+ createInlineValue,
144
+ createBlobValue,
145
+ createEventId,
146
+
147
+ // Key codec utilities (BULKHEAD)
148
+ encodeEdgePropKey,
149
+ decodeEdgePropKey,
150
+ isEdgePropKey,
151
+
152
+ // WARP migration
153
+ migrateV4toV5,
154
+
155
+ // Tick receipts (LIGHTHOUSE)
156
+ createTickReceipt,
157
+ tickReceiptCanonicalJson,
158
+ TICK_RECEIPT_OP_TYPES,
159
+ TICK_RECEIPT_RESULT_TYPES,
160
+
161
+ // Provenance payload (HOLOGRAM)
162
+ ProvenancePayload,
163
+
164
+ // Boundary Transition Records (HOLOGRAM)
165
+ createBTR,
166
+ verifyBTR,
167
+ replayBTR,
168
+ serializeBTR,
169
+ deserializeBTR,
170
+
171
+ // Wormhole compression (HOLOGRAM)
172
+ createWormhole,
173
+ composeWormholes,
174
+ replayWormhole,
175
+ serializeWormhole,
176
+ deserializeWormhole,
177
+ };
178
+
179
+ // WarpGraph is the primary API for V7
180
+ export default WarpGraph;
package/package.json ADDED
@@ -0,0 +1,129 @@
1
+ {
2
+ "name": "@git-stunts/git-warp",
3
+ "version": "10.1.1",
4
+ "description": "Deterministic WARP graph over Git: graph-native storage, traversal, and tooling.",
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "author": "James Ross <james@flyingrobots.dev>",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/git-stunts/git-warp.git"
11
+ },
12
+ "homepage": "https://github.com/git-stunts/git-warp#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/git-stunts/git-warp/issues"
15
+ },
16
+ "engines": {
17
+ "node": ">=20.0.0"
18
+ },
19
+ "sideEffects": false,
20
+ "main": "./index.js",
21
+ "types": "./index.d.ts",
22
+ "bin": {
23
+ "warp-graph": "./bin/warp-graph.js",
24
+ "git-warp": "./bin/git-warp"
25
+ },
26
+ "exports": {
27
+ ".": {
28
+ "types": "./index.d.ts",
29
+ "import": "./index.js",
30
+ "default": "./index.js"
31
+ },
32
+ "./node": {
33
+ "types": "./index.d.ts",
34
+ "import": "./src/domain/entities/GraphNode.js",
35
+ "default": "./src/domain/entities/GraphNode.js"
36
+ },
37
+ "./visualization": {
38
+ "import": "./src/visualization/index.js",
39
+ "default": "./src/visualization/index.js"
40
+ },
41
+ "./package.json": "./package.json"
42
+ },
43
+ "files": [
44
+ "bin/warp-graph.js",
45
+ "bin/git-warp",
46
+ "src",
47
+ "index.js",
48
+ "index.d.ts",
49
+ "README.md",
50
+ "LICENSE",
51
+ "NOTICE",
52
+ "SECURITY.md",
53
+ "scripts/install-git-warp.sh",
54
+ "scripts/uninstall-git-warp.sh"
55
+ ],
56
+ "publishConfig": {
57
+ "access": "public"
58
+ },
59
+ "scripts": {
60
+ "lint": "eslint .",
61
+ "lint:links": "lychee --config .lychee.toml '**/*.md'",
62
+ "format": "prettier --write .",
63
+ "test": "sh -c 'if [ \"$GIT_STUNTS_DOCKER\" = \"1\" ]; then vitest run test/unit \"$@\"; else docker compose run --build --rm test npm run test:local -- \"$@\"; fi' --",
64
+ "test:local": "vitest run test/unit",
65
+ "test:watch": "vitest",
66
+ "test:coverage": "vitest run --coverage test/unit",
67
+ "benchmark": "sh -c 'if [ \"$GIT_STUNTS_DOCKER\" = \"1\" ]; then vitest bench --run test/benchmark \"$@\"; else docker compose run --build --rm test npm run benchmark:local -- \"$@\"; fi' --",
68
+ "benchmark:local": "vitest bench --run test/benchmark",
69
+ "demo": "cd examples && docker compose up -d && docker compose exec demo bash",
70
+ "demo:setup": "cd examples && docker compose up -d && docker compose exec demo sh -c 'cd /app && npm install --silent && cd /demo && node /app/examples/setup.js'",
71
+ "demo:explore": "cd examples && docker compose exec demo node /app/examples/explore.js",
72
+ "demo:inspect": "cd examples && docker compose exec demo node /app/examples/inspect-index.js",
73
+ "demo:lagrangian": "cd examples && docker compose exec demo node /app/examples/lagrangian-path.js",
74
+ "demo:bench-streaming": "cd examples && docker compose up -d && docker compose exec demo node /app/examples/streaming-benchmark.js",
75
+ "demo:bench-traversal": "cd examples && docker compose up -d && docker compose exec demo node /app/examples/traversal-benchmark.js",
76
+ "demo:down": "cd examples && docker compose down -v",
77
+ "roadmap": "node scripts/roadmap.js",
78
+ "setup:hooks": "node scripts/setup-hooks.js",
79
+ "prepare": "patch-package && node scripts/setup-hooks.js",
80
+ "prepack": "npm run lint && npm run test:local",
81
+ "install:git-warp": "bash scripts/install-git-warp.sh",
82
+ "uninstall:git-warp": "bash scripts/uninstall-git-warp.sh"
83
+ },
84
+ "dependencies": {
85
+ "@git-stunts/alfred": "^0.4.0",
86
+ "@git-stunts/plumbing": "^2.8.0",
87
+ "@git-stunts/trailer-codec": "^2.1.1",
88
+ "boxen": "^7.1.1",
89
+ "cbor-x": "^1.6.0",
90
+ "chalk": "^5.3.0",
91
+ "cli-table3": "^0.6.3",
92
+ "elkjs": "^0.11.0",
93
+ "figures": "^6.0.1",
94
+ "roaring": "^2.7.0",
95
+ "string-width": "^7.1.0",
96
+ "wrap-ansi": "^9.0.0",
97
+ "zod": "^3.24.1"
98
+ },
99
+ "devDependencies": {
100
+ "@eslint/js": "^9.17.0",
101
+ "@git-stunts/docker-guard": "^0.1.0",
102
+ "@types/node": "^22.15.29",
103
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
104
+ "@typescript-eslint/parser": "^8.54.0",
105
+ "eslint": "^9.17.0",
106
+ "fast-check": "^4.5.3",
107
+ "patch-package": "^8.0.0",
108
+ "prettier": "^3.4.2",
109
+ "typescript": "^5.9.3",
110
+ "typescript-eslint": "^8.54.0",
111
+ "vitest": "^2.1.8"
112
+ },
113
+ "keywords": [
114
+ "git",
115
+ "git-stunts",
116
+ "warp",
117
+ "graph",
118
+ "graph-database",
119
+ "dag",
120
+ "merkle",
121
+ "commit-graph",
122
+ "content-addressable",
123
+ "hexagonal",
124
+ "ddd",
125
+ "invisible-storage",
126
+ "empty-tree"
127
+ ],
128
+ "packageManager": "npm@10"
129
+ }
@@ -0,0 +1,258 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ REPO_URL=${GIT_WARP_REPO_URL:-https://github.com/git-stunts/git-warp.git}
5
+ INSTALL_DIR=${GIT_WARP_HOME:-$HOME/.git-warp}
6
+ PROFILE_FILE=${GIT_WARP_PROFILE:-}
7
+ FORCE_CLONE=0
8
+ DRY_RUN=0
9
+ SILENT=0
10
+ SKIP_PROFILE=0
11
+
12
+ usage() {
13
+ cat <<'USAGE'
14
+ Git Warp bootstrap installer
15
+
16
+ Usage: install-git-warp.sh [options]
17
+
18
+ Options:
19
+ --force overwrite existing installation directory
20
+ --profile FILE shell profile to update (default: auto-detect)
21
+ --dry-run show what would happen without making changes
22
+ --silent reduce logging
23
+ --no-profile do not modify any shell profile (print instructions instead)
24
+ -h, --help show this help
25
+ USAGE
26
+ }
27
+
28
+ log() { [ "$SILENT" -eq 1 ] || echo "[git-warp-install] $*"; }
29
+ run() {
30
+ if [ "$DRY_RUN" -eq 1 ]; then
31
+ printf '+ '
32
+ printf '%q ' "$@"
33
+ printf '\n'
34
+ return 0
35
+ fi
36
+ "$@" || return $?
37
+ }
38
+
39
+ resolve_path() {
40
+ local raw="$1"
41
+ [ -n "$raw" ] || return 1
42
+
43
+ local path="$raw"
44
+ case "$path" in
45
+ ~) path="$HOME" ;;
46
+ ~/*) path="$HOME/${path#~/}" ;;
47
+ esac
48
+
49
+ if [ "${path#/}" = "$path" ]; then
50
+ path="$PWD/$path"
51
+ fi
52
+
53
+ local current="/"
54
+ local suffix="${path#/}"
55
+ local IFS='/'
56
+ # shellcheck disable=SC2206
57
+ local parts=( $suffix )
58
+ local part candidate
59
+
60
+ for part in "${parts[@]}"; do
61
+ case "$part" in
62
+ ''|.)
63
+ continue
64
+ ;;
65
+ ..)
66
+ current="${current%/*}"
67
+ [ -n "$current" ] || current="/"
68
+ continue
69
+ ;;
70
+ esac
71
+
72
+ if [ "$current" = "/" ]; then
73
+ candidate="/$part"
74
+ else
75
+ candidate="$current/$part"
76
+ fi
77
+
78
+ if [ -d "$candidate" ]; then
79
+ current="$(cd "$candidate" 2>/dev/null && pwd -P)" || return 1
80
+ else
81
+ current="$candidate"
82
+ fi
83
+ done
84
+
85
+ printf '%s' "$current"
86
+ }
87
+
88
+ while [ $# -gt 0 ]; do
89
+ case "$1" in
90
+ --force) FORCE_CLONE=1 ;;
91
+ --dry-run) DRY_RUN=1 ;;
92
+ --silent) SILENT=1 ;;
93
+ --no-profile) SKIP_PROFILE=1 ;;
94
+ --profile)
95
+ shift || { usage; exit 1; }
96
+ PROFILE_FILE="$1"
97
+ ;;
98
+ -h|--help)
99
+ usage
100
+ exit 0
101
+ ;;
102
+ *)
103
+ echo "Unknown flag: $1" >&2
104
+ usage
105
+ exit 1
106
+ ;;
107
+ esac
108
+ shift || break
109
+ done
110
+
111
+ if [ -z "$PROFILE_FILE" ]; then
112
+ if [ -n "${ZDOTDIR:-}" ] && [ -f "$ZDOTDIR/.zshrc" ]; then
113
+ PROFILE_FILE="$ZDOTDIR/.zshrc"
114
+ elif [ -f "$HOME/.zshrc" ]; then
115
+ PROFILE_FILE="$HOME/.zshrc"
116
+ elif [ -f "$HOME/.bashrc" ]; then
117
+ PROFILE_FILE="$HOME/.bashrc"
118
+ elif [ -f "$HOME/.bash_profile" ]; then
119
+ PROFILE_FILE="$HOME/.bash_profile"
120
+ elif [ -f "$HOME/.profile" ]; then
121
+ PROFILE_FILE="$HOME/.profile"
122
+ fi
123
+ fi
124
+
125
+ if [ -z "$INSTALL_DIR" ]; then
126
+ echo "Error: GIT_WARP_HOME (install directory) must not be empty" >&2
127
+ exit 1
128
+ fi
129
+
130
+ RESOLVED_INSTALL_DIR=$(resolve_path "$INSTALL_DIR")
131
+ if [ -z "$RESOLVED_INSTALL_DIR" ]; then
132
+ echo "Error: unable to resolve install path '$INSTALL_DIR'" >&2
133
+ exit 1
134
+ fi
135
+
136
+ case "$RESOLVED_INSTALL_DIR" in
137
+ /|/root|/root/*)
138
+ echo "Error: refusing to install into $RESOLVED_INSTALL_DIR" >&2
139
+ exit 1
140
+ ;;
141
+ esac
142
+
143
+ ALLOWED_BASES=${GIT_WARP_ALLOWED_BASES:-$HOME:/opt/git-warp}
144
+ IFS=':' read -r -a _git_warp_allowed <<< "$ALLOWED_BASES"
145
+ allowed_ok=0
146
+ for base in "${_git_warp_allowed[@]}"; do
147
+ [ -z "$base" ] && continue
148
+ resolved_base=$(resolve_path "$base" 2>/dev/null || true)
149
+ [ -z "$resolved_base" ] && continue
150
+ case "$RESOLVED_INSTALL_DIR" in
151
+ "$resolved_base"|"$resolved_base"/*)
152
+ allowed_ok=1
153
+ break
154
+ ;;
155
+ esac
156
+ done
157
+ unset _git_warp_allowed
158
+
159
+ if [ "$allowed_ok" -ne 1 ]; then
160
+ echo "Error: install directory $RESOLVED_INSTALL_DIR is outside permitted bases ($ALLOWED_BASES)" >&2
161
+ exit 1
162
+ fi
163
+
164
+ INSTALL_DIR="$RESOLVED_INSTALL_DIR"
165
+ log "Install dir: $INSTALL_DIR"
166
+ if [ -d "$INSTALL_DIR" ]; then
167
+ if [ "$FORCE_CLONE" -eq 1 ]; then
168
+ if [ -L "$INSTALL_DIR" ]; then
169
+ echo "Error: refusing to remove symlinked install dir ($INSTALL_DIR). Remove it manually first." >&2
170
+ exit 1
171
+ fi
172
+ log "Removing existing directory"
173
+ run rm -rf "$INSTALL_DIR"
174
+ else
175
+ log "Directory already exists; pulling latest"
176
+ if ! git -C "$INSTALL_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
177
+ echo "Error: $INSTALL_DIR exists but is not a git repository (use --force to replace it)" >&2
178
+ exit 1
179
+ fi
180
+ default_branch=$(git -C "$INSTALL_DIR" remote show origin 2>/dev/null | awk '/HEAD branch/ {print $NF}')
181
+ if [ -z "$default_branch" ]; then
182
+ for candidate in main master; do
183
+ if git -C "$INSTALL_DIR" show-ref --verify --quiet "refs/remotes/origin/$candidate"; then
184
+ default_branch="$candidate"
185
+ break
186
+ fi
187
+ done
188
+ fi
189
+ if [ -z "$default_branch" ]; then
190
+ echo "Error: unable to determine origin default branch for $INSTALL_DIR" >&2
191
+ exit 1
192
+ fi
193
+ run git -C "$INSTALL_DIR" fetch origin "$default_branch"
194
+ run git -C "$INSTALL_DIR" reset --hard "origin/$default_branch"
195
+ fi
196
+ else
197
+ log "Cloning repo from $REPO_URL"
198
+ run git clone "$REPO_URL" "$INSTALL_DIR"
199
+ fi
200
+
201
+ if [ "$DRY_RUN" -eq 0 ]; then
202
+ log "Installing dependencies"
203
+ if [ ! -f "$INSTALL_DIR/package.json" ]; then
204
+ log "Error: package.json not found in $INSTALL_DIR"
205
+ exit 1
206
+ fi
207
+ (cd "$INSTALL_DIR" && npm install ${SILENT:+--silent})
208
+ if [ ! -f "$INSTALL_DIR/bin/git-warp" ]; then
209
+ log "Error: expected $INSTALL_DIR/bin/git-warp to exist"
210
+ exit 1
211
+ fi
212
+ if [ ! -x "$INSTALL_DIR/bin/git-warp" ]; then
213
+ chmod +x "$INSTALL_DIR/bin/git-warp"
214
+ fi
215
+ else
216
+ log "Would install dependencies"
217
+ fi
218
+
219
+ BIN_LINE="export PATH=\"$INSTALL_DIR/bin:\$PATH\""
220
+ HOME_LINE="export GIT_WARP_HOME=\"$INSTALL_DIR\""
221
+ PROFILE_UPDATED=0
222
+
223
+ if [ "$SKIP_PROFILE" -eq 0 ] && [ -n "$PROFILE_FILE" ]; then
224
+ touch "$PROFILE_FILE"
225
+ if [ "$DRY_RUN" -eq 0 ]; then
226
+ if [ ! -f "$PROFILE_FILE.bak" ]; then
227
+ log "Creating backup $PROFILE_FILE.bak"
228
+ cp "$PROFILE_FILE" "$PROFILE_FILE.bak"
229
+ fi
230
+ if ! grep -E "^[[:space:]]*export[[:space:]]+GIT_WARP_HOME=" "$PROFILE_FILE" >/dev/null 2>&1; then
231
+ log "Updating $PROFILE_FILE"
232
+ printf '\n# Git Warp\nexport GIT_WARP_HOME="%s"\nexport PATH="%s/bin:$PATH"\n' "$INSTALL_DIR" "$INSTALL_DIR" >> "$PROFILE_FILE"
233
+ PROFILE_UPDATED=1
234
+ else
235
+ log "$PROFILE_FILE already references GIT_WARP_HOME; skipping"
236
+ fi
237
+ else
238
+ log "Would append env vars to $PROFILE_FILE"
239
+ fi
240
+ else
241
+ log "No shell profile updated; export vars manually:"
242
+ log " $HOME_LINE"
243
+ log " $BIN_LINE"
244
+ fi
245
+
246
+ cat <<INFO
247
+
248
+ Git Warp installed!
249
+ Reload your shell or run:
250
+ $HOME_LINE
251
+ $BIN_LINE
252
+ Then test with:
253
+ git warp --help
254
+ INFO
255
+
256
+ if [ "$PROFILE_UPDATED" -eq 1 ]; then
257
+ echo "(Appended PATH setup to $PROFILE_FILE)"
258
+ fi
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ INSTALL_DIR=${GIT_WARP_HOME:-$HOME/.git-warp}
5
+ PROFILE_FILE=${GIT_WARP_PROFILE:-}
6
+ DRY_RUN=0
7
+ SILENT=0
8
+ NO_BACKUP=0
9
+
10
+ usage() {
11
+ cat <<'USAGE'
12
+ Git Warp uninstaller
13
+
14
+ Usage: uninstall-git-warp.sh [options]
15
+
16
+ Options:
17
+ --dry-run show actions without performing them
18
+ --silent reduce logging
19
+ --no-backup do not create profile backups before editing
20
+ --profile FILE explicit profile to edit (default: auto-detect)
21
+ -h, --help show this help
22
+ USAGE
23
+ }
24
+
25
+ log() { [ "$SILENT" -eq 1 ] || echo "[git-warp-uninstall] $*"; }
26
+ run() {
27
+ if [ "$DRY_RUN" -eq 1 ]; then
28
+ printf '+ '
29
+ printf '%q ' "$@"
30
+ printf '\n'
31
+ return 0
32
+ fi
33
+ "$@" || return $?
34
+ }
35
+
36
+ sanitize_profile() {
37
+ local profile="$1" install_dir="$2"
38
+ local tmp
39
+ tmp=$(mktemp) || return 1
40
+ if ! awk -v dir="$install_dir" '
41
+ {
42
+ stripped=$0
43
+ sub(/^[[:space:]]+/, "", stripped)
44
+ sub(/[[:space:]]+$/, "", stripped)
45
+ if (stripped == "# Git Warp") next
46
+ if (index($0, dir) > 0) {
47
+ if (stripped ~ /^export[[:space:]]+GIT_WARP_HOME=/) next
48
+ if (stripped ~ /^export[[:space:]]+PATH=/ && index($0, dir "/bin") > 0) next
49
+ }
50
+ print $0
51
+ }
52
+ ' "$profile" > "$tmp"; then
53
+ rm -f "$tmp"
54
+ return 1
55
+ fi
56
+ mv "$tmp" "$profile"
57
+ }
58
+
59
+ while [ $# -gt 0 ]; do
60
+ case "$1" in
61
+ --dry-run) DRY_RUN=1 ;;
62
+ --silent) SILENT=1 ;;
63
+ --no-backup) NO_BACKUP=1 ;;
64
+ --profile)
65
+ shift || { usage; exit 1; }
66
+ PROFILE_FILE="$1"
67
+ ;;
68
+ -h|--help)
69
+ usage
70
+ exit 0
71
+ ;;
72
+ *)
73
+ echo "Unknown flag: $1" >&2
74
+ usage
75
+ exit 1
76
+ ;;
77
+ esac
78
+ shift || break
79
+ done
80
+
81
+ if [ -z "$PROFILE_FILE" ]; then
82
+ if [ -n "${ZDOTDIR:-}" ] && [ -f "$ZDOTDIR/.zshrc" ]; then
83
+ PROFILE_FILE="$ZDOTDIR/.zshrc"
84
+ elif [ -f "$HOME/.zshrc" ]; then
85
+ PROFILE_FILE="$HOME/.zshrc"
86
+ elif [ -f "$HOME/.bashrc" ]; then
87
+ PROFILE_FILE="$HOME/.bashrc"
88
+ elif [ -f "$HOME/.bash_profile" ]; then
89
+ PROFILE_FILE="$HOME/.bash_profile"
90
+ elif [ -f "$HOME/.profile" ]; then
91
+ PROFILE_FILE="$HOME/.profile"
92
+ fi
93
+ fi
94
+
95
+ GIT_WARP_TARGET="$INSTALL_DIR/bin/git-warp"
96
+
97
+ if [ -e "/usr/local/bin/git-warp" ]; then
98
+ if [ -L "/usr/local/bin/git-warp" ] && [ "$(readlink "/usr/local/bin/git-warp")" = "$GIT_WARP_TARGET" ]; then
99
+ log "Removing /usr/local/bin/git-warp"
100
+ run rm -f /usr/local/bin/git-warp
101
+ elif cmp -s "/usr/local/bin/git-warp" "$GIT_WARP_TARGET" 2>/dev/null; then
102
+ log "Removing /usr/local/bin/git-warp (matching installed copy)"
103
+ run rm -f /usr/local/bin/git-warp
104
+ else
105
+ log "Skipping removal of /usr/local/bin/git-warp (custom install detected)"
106
+ fi
107
+ fi
108
+
109
+ if [ -d "$INSTALL_DIR" ]; then
110
+ if [ -L "$INSTALL_DIR" ]; then
111
+ log "Refusing to remove symlinked install dir $INSTALL_DIR; remove manually"
112
+ else
113
+ log "Removing $INSTALL_DIR"
114
+ run rm -rf "$INSTALL_DIR"
115
+ fi
116
+ else
117
+ log "Install directory $INSTALL_DIR not found"
118
+ fi
119
+
120
+ if [ -n "$PROFILE_FILE" ] && [ -f "$PROFILE_FILE" ]; then
121
+ if [ "$DRY_RUN" -eq 0 ] && [ "$NO_BACKUP" -eq 0 ]; then
122
+ if [ ! -f "$PROFILE_FILE.git-warp.bak" ]; then
123
+ log "Creating backup $PROFILE_FILE.git-warp.bak"
124
+ cp "$PROFILE_FILE" "$PROFILE_FILE.git-warp.bak"
125
+ fi
126
+ fi
127
+ log "Cleaning Git Warp lines from $PROFILE_FILE"
128
+ if [ "$DRY_RUN" -eq 1 ]; then
129
+ echo "+ removing Git Warp entries from $PROFILE_FILE"
130
+ else
131
+ if ! sanitize_profile "$PROFILE_FILE" "$INSTALL_DIR"; then
132
+ log "Error: failed to clean $PROFILE_FILE; leaving original in place."
133
+ fi
134
+ fi
135
+ else
136
+ log "Profile file not found or unspecified; remove PATH/GIT_WARP_HOME entries manually if needed."
137
+ fi
138
+
139
+ log "Git Warp uninstall complete"