isomorphic-jj 1.4.3 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +226 -0
- package/README.md +9 -11
- package/bin/isojj.js +270 -102
- package/dist/index.cjs +2910 -1666
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +2910 -1666
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
- package/src/api/repository.js +1099 -739
- package/src/backend/isomorphic-git-backend.js +183 -136
- package/src/backend/lazy-git-backend.js +12 -19
- package/src/browser/helpers.js +6 -2
- package/src/core/background-ops.js +58 -41
- package/src/core/bookmark-store.js +12 -8
- package/src/core/change-graph.js +26 -29
- package/src/core/conflict-model.js +66 -26
- package/src/core/jj-checkout.js +8 -4
- package/src/core/jj-operation-store.js +16 -12
- package/src/core/jj-tree-state.js +13 -9
- package/src/core/jj-view-store.js +11 -5
- package/src/core/merge-driver-registry.js +92 -46
- package/src/core/operation-log.js +30 -33
- package/src/core/revset-engine.js +1069 -485
- package/src/core/storage-manager.js +25 -19
- package/src/core/tag-store.js +2 -2
- package/src/core/user-config.js +3 -1
- package/src/core/working-copy.js +125 -9
- package/src/core/workspace-manager.js +2 -2
- package/src/drivers/built-in-drivers.js +66 -33
- package/src/types.d.ts +67 -1
- package/src/utils/errors.js +3 -3
- package/src/utils/id-generation.js +4 -4
- package/src/utils/validation.js +6 -10
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to isomorphic-jj are documented here. The project tracks the
|
|
4
|
+
[Jujutsu (jj)](https://github.com/jj-vcs/jj) CLI; each release notes the upstream
|
|
5
|
+
jj version whose semantics it targets.
|
|
6
|
+
|
|
7
|
+
## [1.7.0] — Real revset parser, testable/UX-improved CLI, background-ops fix
|
|
8
|
+
|
|
9
|
+
Closes out the remaining known-issue backlog.
|
|
10
|
+
|
|
11
|
+
### Changed — revset engine rewritten around a real tokenizer + parser + AST
|
|
12
|
+
|
|
13
|
+
`RevsetEngine.evaluate()` previously matched the whole trimmed expression
|
|
14
|
+
against one long chain of regexes, one per construct. This had two real bugs:
|
|
15
|
+
|
|
16
|
+
- **Nested function-call arguments silently mis-parsed.** A non-greedy regex
|
|
17
|
+
like `/^roots\((.+?)\)$/` truncates at the *first* `)` it sees, so
|
|
18
|
+
`roots(ancestors(x))` never worked — the previous "fix" for this was a
|
|
19
|
+
one-off hand-rolled paren-counter used only by `reachable()`.
|
|
20
|
+
- **No real operator precedence.** Mixed `&`/`|`/`~` expressions were
|
|
21
|
+
resolved by checking `.includes(' & ')` before `' | '` before `' ~ '`
|
|
22
|
+
regardless of the expression's actual structure.
|
|
23
|
+
|
|
24
|
+
The expression is now tokenized and parsed into a small AST via a real
|
|
25
|
+
recursive-descent / precedence-climbing parser (`&`/`~` at one precedence
|
|
26
|
+
tier, `|` lower, both left-associative), which is then evaluated by walking
|
|
27
|
+
the tree. This is purely a parsing-layer replacement — every per-function
|
|
28
|
+
filter/traversal method (`filterByAuthor`, `getAncestors`, `findForkPoint`,
|
|
29
|
+
etc.) is untouched, and **all 183 pre-existing revset tests pass unchanged**.
|
|
30
|
+
|
|
31
|
+
New capabilities (all strictly additive — nothing that worked before changed
|
|
32
|
+
behavior):
|
|
33
|
+
- Function-call arguments can nest arbitrarily deep: `roots(ancestors(x))`,
|
|
34
|
+
`heads(roots(ancestors(x)))`, etc.
|
|
35
|
+
- Parenthesized grouping: `(a | b) & c`.
|
|
36
|
+
- Quote-aware argument splitting: `description("a, b, and c")` no longer
|
|
37
|
+
mis-splits on the comma inside the quoted string.
|
|
38
|
+
- Whitespace around `&`/`|`/`~` is now optional (`a|b` works, not just `a | b`).
|
|
39
|
+
|
|
40
|
+
### Fixed — background-ops (td-488842)
|
|
41
|
+
|
|
42
|
+
`enableAutoSnapshot()`'s timer callback awaited only `queue()`'s immediate
|
|
43
|
+
`{ id, promise }` handle, not the operation's own settling `promise`. Since
|
|
44
|
+
`queue()` resolves as soon as the operation is *enqueued* (not once it
|
|
45
|
+
finishes), a later `describe()` rejection landed on the unobserved promise
|
|
46
|
+
and became an unhandled promise rejection instead of reaching the
|
|
47
|
+
surrounding `catch`. Now awaits the returned `promise` too.
|
|
48
|
+
|
|
49
|
+
### Changed — CLI (`bin/isojj.js`) rewritten to be testable, with real bugs fixed
|
|
50
|
+
|
|
51
|
+
The CLI had zero tests (calling `process.exit()` at import time made it
|
|
52
|
+
untestable) and several real bugs:
|
|
53
|
+
- **Made testable**: `parseArgs`/`formatOutput`/`findRepoRoot`/
|
|
54
|
+
`loadGitBackend`/`run` are now exported; `run()` returns an exit code
|
|
55
|
+
instead of calling `process.exit()` directly, guarded behind an
|
|
56
|
+
is-this-the-entrypoint check.
|
|
57
|
+
- **Fixed a parsing bug**: boolean flags (`dryRun`, `json`, `force`,
|
|
58
|
+
`interactive`, etc.) used to swallow the next token as a fake "value",
|
|
59
|
+
silently dropping a following positional — `describe --dryRun "fix typo"`
|
|
60
|
+
lost the commit message. Boolean flags never consume the next token now;
|
|
61
|
+
`--flag=true`/`--flag=false` is coerced to a real boolean.
|
|
62
|
+
- **Fixed a latent bug**: the CLI's own error message promised searching
|
|
63
|
+
"any parent up to mount point /" for a `.jj` repo but never actually did
|
|
64
|
+
so. Added `findRepoRoot()` to walk up parent directories like git/jj, so
|
|
65
|
+
the CLI now works from any subdirectory of a repo.
|
|
66
|
+
- **Wired up the real Git backend**: the CLI never passed `git`/`http` to
|
|
67
|
+
`createJJ()`, so `isojj init` silently ran in storage-only "mock" mode
|
|
68
|
+
with no real `.git` directory. It now dynamically imports isomorphic-git
|
|
69
|
+
(an optional peer dependency) when present, falling back gracefully when
|
|
70
|
+
it isn't.
|
|
71
|
+
- Added a full test suite (`tests/integration/cli.test.js`) and included
|
|
72
|
+
`bin/**/*.js` in coverage collection.
|
|
73
|
+
|
|
74
|
+
### Testing
|
|
75
|
+
|
|
76
|
+
1701 tests passing (was 1671); lint, format:check, typecheck, coverage
|
|
77
|
+
(91.2% branches), and build all green.
|
|
78
|
+
|
|
79
|
+
## [1.6.1] — Bugfix batch
|
|
80
|
+
|
|
81
|
+
Fixes nine real, pre-existing defects that the v1.5/v1.6 coverage push
|
|
82
|
+
surfaced and documented (each had a test asserting the buggy behavior with a
|
|
83
|
+
`// BUG:` note — those tests now assert the corrected behavior instead).
|
|
84
|
+
|
|
85
|
+
### Fixed
|
|
86
|
+
|
|
87
|
+
- **`bookmarks([pattern])` revset returned `[undefined]`.** `filterBookmarks()`
|
|
88
|
+
read `bookmark.target`, but `BookmarkStore.list()` returns objects keyed
|
|
89
|
+
`changeId`. It now reads `bookmark.changeId`.
|
|
90
|
+
- **`show()` never attributed bookmarks to a change**, for the same
|
|
91
|
+
`.target` vs. `.changeId` mismatch. Fixed the same way.
|
|
92
|
+
- **`bookmark.delete()`'s not-found guard was dead code.** It called
|
|
93
|
+
`bookmarks.get(name)` without `await`, so the "truthy Promise" always
|
|
94
|
+
passed the guard; the store's own `BOOKMARK_NOT_FOUND` was reached
|
|
95
|
+
instead of the friendlier method-level `NOT_FOUND`. Added the missing
|
|
96
|
+
`await`.
|
|
97
|
+
- **`operations.revert()` on a bookmark-move operation always threw
|
|
98
|
+
`BOOKMARK_EXISTS`.** Its "moved bookmark" branch called `bookmarks.set()`
|
|
99
|
+
(create-only) to move the bookmark back, but the bookmark still exists
|
|
100
|
+
during a move-revert. Changed to `bookmarks.move()`.
|
|
101
|
+
- **`ChangeGraph.getAncestors()` returned duplicate ancestors on
|
|
102
|
+
diamond-shaped history** (e.g. `a<-b`, `a<-c`, `b,c<-d`). A node was only
|
|
103
|
+
marked visited when dequeued, so a shared ancestor reachable via two
|
|
104
|
+
parents could be enqueued twice. Nodes are now marked visited the moment
|
|
105
|
+
they're enqueued.
|
|
106
|
+
- **`matchesPattern()` (merge driver registry) broke `**` glob patterns.**
|
|
107
|
+
Literal dots were escaped *after* `**` had already been converted to
|
|
108
|
+
`.*`, corrupting it into `\.*` (zero-or-more literal dots) instead of "any
|
|
109
|
+
characters". Dots are now escaped before the glob substitutions.
|
|
110
|
+
- **`IsomorphicGitBackend.getCurrentTree()` always threw
|
|
111
|
+
`TREE_READ_FAILED`.** It called `git.writeTree({ fs, dir })` without the
|
|
112
|
+
library's required `tree` argument (`git.writeTree` writes a single,
|
|
113
|
+
already-built tree — it doesn't build one from the working directory).
|
|
114
|
+
Reimplemented to walk the git index (`STAGE`) and build the nested tree
|
|
115
|
+
bottom-up, verified to produce the same oid as an equivalent real commit.
|
|
116
|
+
- **`ConflictModel._tryMergeDriver()`'s "no custom driver" guard never
|
|
117
|
+
matched.** It compared `driver === this.mergeDriverRegistry.defaultDriver`,
|
|
118
|
+
but `MergeDriverRegistry` never set a `defaultDriver` property (always
|
|
119
|
+
`undefined`), so every file was routed through the generic three-way
|
|
120
|
+
merge driver instead of falling back to the richer path-based conflict-type
|
|
121
|
+
detection. `MergeDriverRegistry` now exposes `defaultDriver`.
|
|
122
|
+
- **`IsomorphicGitBackend.stageAll()` double-wrapped per-file errors.** A
|
|
123
|
+
specific `STAGE_FILE_FAILED` thrown for one file was immediately re-wrapped
|
|
124
|
+
by the surrounding `catch` as the generic `STAGE_FAILED`, hiding the more
|
|
125
|
+
useful code. The outer catch now re-throws an already-categorized
|
|
126
|
+
`JJError` as-is.
|
|
127
|
+
|
|
128
|
+
## [1.6.0] — Automatic working-copy snapshotting
|
|
129
|
+
|
|
130
|
+
Closes a long-standing fidelity gap: the library now **walks the working
|
|
131
|
+
directory on disk** and reconciles tracked state before read/commit operations,
|
|
132
|
+
so files created, modified, or deleted **out-of-band** (an editor, the shell,
|
|
133
|
+
`git checkout`) are picked up — matching jj's "snapshot before every command".
|
|
134
|
+
Previously only files written through `jj.write()` were ever tracked, so
|
|
135
|
+
`status`/`describe`/`diff`/`file.search`/`read` couldn't see anything else.
|
|
136
|
+
|
|
137
|
+
### Added
|
|
138
|
+
|
|
139
|
+
- `WorkingCopy.walk()` — recursively lists working-directory files (excluding
|
|
140
|
+
`.git`, `.jj`, `node_modules`), robust across Node fs and in-memory fses.
|
|
141
|
+
- `WorkingCopy.snapshot()` — reconciles tracked file state with disk, returning
|
|
142
|
+
`{ added, modified, deleted }`; honors sparse patterns.
|
|
143
|
+
- `jj.snapshot()` — public method to trigger a snapshot explicitly.
|
|
144
|
+
- `createJJ({ autoSnapshot })` option (default `true`) to opt out of the
|
|
145
|
+
automatic behavior.
|
|
146
|
+
|
|
147
|
+
### Changed
|
|
148
|
+
|
|
149
|
+
- `describe()`, `status()`, `diff()`, `read()`, `file.list()`, and
|
|
150
|
+
`file.search()` now auto-snapshot the working copy first (when operating on
|
|
151
|
+
`@`), so they reflect on-disk reality. `status()` now returns real `added` and
|
|
152
|
+
`removed` lists (previously always empty).
|
|
153
|
+
- A tracked file deleted on disk is now gracefully untracked on the next
|
|
154
|
+
snapshot instead of making `describe()` throw `SNAPSHOT_FILE_FAILED` (that
|
|
155
|
+
path still applies under `{ autoSnapshot: false }`).
|
|
156
|
+
|
|
157
|
+
## [1.5.0] — Parity refresh (tracks jj through v0.43)
|
|
158
|
+
|
|
159
|
+
This release brings isomorphic-jj up to date with Jujutsu releases v0.31–v0.43,
|
|
160
|
+
adds a batch of revset functions and commands, and cleans up the toolchain.
|
|
161
|
+
|
|
162
|
+
### Added — revset functions (jj v0.31–v0.43)
|
|
163
|
+
|
|
164
|
+
- `change_id(prefix)` / `commit_id(prefix)` — resolve a change by a hex prefix (jj v0.31).
|
|
165
|
+
- `subject(pattern)` — match only the first line of a description (jj v0.26).
|
|
166
|
+
- `author_name(x)`, `author_email(x)`, `committer(x)`, `committer_name(x)`,
|
|
167
|
+
`committer_email(x)` — fine-grained signature filters (jj v0.26).
|
|
168
|
+
- `signed()` — cryptographically signed changes (jj v0.29).
|
|
169
|
+
- `divergent()` — changes flagged as divergent (jj v0.38).
|
|
170
|
+
- `merges()` — canonical alias of the existing `merge()`.
|
|
171
|
+
- `forks()` — changes with more than one child.
|
|
172
|
+
- `first_parent(x)` / `first_ancestors(x)` — first-parent navigation (jj v0.32).
|
|
173
|
+
- `fork_point(x)` — youngest common ancestor of a set (jj v0.32).
|
|
174
|
+
- `merge_point(x)` — youngest common descendant of a set.
|
|
175
|
+
- `exactly(x, n)` — the set, but errors unless it has exactly `n` elements (jj v0.34).
|
|
176
|
+
- `present(x)` — evaluate without erroring on unknown symbols.
|
|
177
|
+
- `coalesce(a, b, …)` — the first argument that resolves to a non-empty set.
|
|
178
|
+
- `remote_tags([pattern])` — remote (slash-qualified) tag targets (jj v0.38).
|
|
179
|
+
- `ancestors(x, depth)` — generalized to accept any nested revset plus an
|
|
180
|
+
optional depth limit (previously accepted only a bare 32-hex change ID).
|
|
181
|
+
|
|
182
|
+
### Added — commands
|
|
183
|
+
|
|
184
|
+
- `revert({ revision })` — canonical replacement for `backout()` (jj renamed
|
|
185
|
+
`backout` → `revert` and removed `backout` in v0.35). `backout()` remains as a
|
|
186
|
+
deprecated alias.
|
|
187
|
+
- `redo()` — progressively re-applies operations reverted by `undo()` (jj v0.33).
|
|
188
|
+
- `sign()` / `unsign()` — record and clear signature metadata on a change (jj
|
|
189
|
+
v0.27). Previously hard `UNSUPPORTED_OPERATION` stubs; now implemented as
|
|
190
|
+
metadata operations that light up the `signed()` revset. (A pure-JS library
|
|
191
|
+
cannot verify GPG/SSH keys, so cryptographic verification is still delegated to
|
|
192
|
+
the Git layer.)
|
|
193
|
+
- `file.search({ pattern })` — search tracked file contents, regex by default
|
|
194
|
+
with `{ kind: 'substring' }` for literal matches (jj v0.37 / v0.41).
|
|
195
|
+
- `bookmark.advance({ name, to })` — move a bookmark forward only; refuses
|
|
196
|
+
non-descendant targets (jj v0.39).
|
|
197
|
+
- `tag.set({ name, changeId })` — create-or-move (upsert) a tag (jj v0.35).
|
|
198
|
+
|
|
199
|
+
### Fixed
|
|
200
|
+
|
|
201
|
+
- **`tags()` revset now works.** It was a stub returning `[]` even though a
|
|
202
|
+
`TagStore` existed; the `RevsetEngine` is now wired to the tag store and
|
|
203
|
+
`tags()` / `tags(pattern)` return real results.
|
|
204
|
+
- **Duplicate `conflicts` key in the API object.** The raw `ConflictModel` was
|
|
205
|
+
exposed under the same key as the public `conflicts` namespace and silently
|
|
206
|
+
shadowed. The raw model is now available as `jj.conflictModel`.
|
|
207
|
+
- **`npm run lint` was completely broken** — the ESLint config was authored as an
|
|
208
|
+
ES module in a `"type": "module"` package. Renamed to `.eslintrc.cjs` and
|
|
209
|
+
fixed; the source tree now lints clean (0 errors).
|
|
210
|
+
- Latent `fail()` calls in tests (removed from jest-circus) replaced with `throw`.
|
|
211
|
+
- Removed an unnecessary try/catch, an empty catch block, an unused import, and
|
|
212
|
+
several unused variables flagged by the newly-working linter.
|
|
213
|
+
|
|
214
|
+
### Changed
|
|
215
|
+
|
|
216
|
+
- `tsconfig.json` now includes the DOM/WebWorker libs and Node types so the
|
|
217
|
+
type-check reflects the library's real isomorphic runtime.
|
|
218
|
+
- Expanded the `isojj` CLI: `--version`, a far more complete `help`, positional
|
|
219
|
+
argument handling for `log`/`describe`/`file.*`, and generic pass-through of
|
|
220
|
+
all `--flags` so every API method is reachable from the CLI.
|
|
221
|
+
|
|
222
|
+
## [1.4.3] and earlier
|
|
223
|
+
|
|
224
|
+
See the git history and [ROADMAP.md](./ROADMAP.md) for the v0.1–v1.4 development
|
|
225
|
+
record (core model, history editing, Git backend, first-class conflicts, revset
|
|
226
|
+
query language, workspaces, browser support, events, and full v1.0 JJ CLI parity).
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# isomorphic-jj
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/isomorphic-jj)
|
|
4
|
-
[](https://github.com/johnhenry/isomorphic-jj)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
7
|
> **Jujutsu version control for JavaScript**—stable change IDs, fearless undo, and no staging area. Works in Node.js and browsers.
|
|
@@ -88,7 +88,7 @@ isomorphic-jj v1.0 now implements virtually all commonly-used JJ CLI commands:
|
|
|
88
88
|
See [JJ_CLI_PARITY.md](./JJ_CLI_PARITY.md) for complete feature comparison.
|
|
89
89
|
|
|
90
90
|
### Testing & Quality
|
|
91
|
-
- **
|
|
91
|
+
- **1701 tests** passing — ~97% statement / 91% branch coverage (v1.0–v1.7)
|
|
92
92
|
- **100% backward compatible** - All existing code continues to work
|
|
93
93
|
- **Zero breaking changes** - Deprecated features show warnings but still function
|
|
94
94
|
|
|
@@ -898,8 +898,9 @@ repo/
|
|
|
898
898
|
|
|
899
899
|
## Project Status
|
|
900
900
|
|
|
901
|
-
**Current Version**:
|
|
902
|
-
**Test Coverage**:
|
|
901
|
+
**Current Version**: v1.7.0
|
|
902
|
+
**Test Coverage**: 1701 tests, ~97% statements / 91% branches
|
|
903
|
+
**JJ CLI parity**: tracks Jujutsu through v0.43
|
|
903
904
|
**Status**: Ready for experimentation and prototyping
|
|
904
905
|
|
|
905
906
|
**Completed:**
|
|
@@ -908,13 +909,10 @@ repo/
|
|
|
908
909
|
- ✅ v0.3: Git backend, conflicts, workspaces, browser support
|
|
909
910
|
- ✅ v0.4: Shallow clones, advanced revsets, event system
|
|
910
911
|
- ✅ v0.5: Custom merge drivers, enhanced revsets (time-based, graph analytics), conflict resolution enhancements
|
|
912
|
+
- ✅ v1.0: Full JJ CLI semantic parity, complete `file.*`/`operations.*`/`git.*` namespaces
|
|
913
|
+
- ✅ v1.5: Parity refresh tracking jj through **v0.43** — new revsets (`change_id`, `commit_id`, `subject`, `author_name`/`email`, `committer*`, `signed`, `divergent`, `merges`, `forks`, `first_parent`, `first_ancestors`, `fork_point`, `merge_point`, `exactly`, `present`, `coalesce`, `remote_tags`, `ancestors(x, depth)`), working `tags()`/`remote_tags()` revsets, and new commands: `revert()`, `redo()`, `sign()`/`unsign()`, `file.search()`, `bookmark.advance()`, `tag.set()`. See [CHANGELOG.md](./CHANGELOG.md).
|
|
911
914
|
|
|
912
|
-
|
|
913
|
-
- Repository analytics and debugging tools
|
|
914
|
-
- Interactive workflows
|
|
915
|
-
- Performance optimizations
|
|
916
|
-
|
|
917
|
-
See [ROADMAP.md](./ROADMAP.md) for detailed plans through v1.0.
|
|
915
|
+
See [ROADMAP.md](./ROADMAP.md) for detailed plans, and [CHANGELOG.md](./CHANGELOG.md) for the release history.
|
|
918
916
|
|
|
919
917
|
---
|
|
920
918
|
|
|
@@ -922,7 +920,7 @@ See [ROADMAP.md](./ROADMAP.md) for detailed plans through v1.0.
|
|
|
922
920
|
|
|
923
921
|
### Requirements
|
|
924
922
|
|
|
925
|
-
- **Node.js**:
|
|
923
|
+
- **Node.js**: 20.0.0 or higher (uses the Web Crypto global, stabilized in Node 20; Node 18 is EOL)
|
|
926
924
|
- **Browsers**: Chrome 90+, Firefox 88+, Safari 14+, Edge 90+
|
|
927
925
|
- **Optional**: Git CLI (for JJ CLI interop testing)
|
|
928
926
|
|