isomorphic-jj 1.4.3 → 1.8.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 +428 -0
- package/README.md +138 -29
- package/bin/isojj.js +273 -102
- package/dist/index.cjs +3401 -1683
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +3401 -1683
- package/dist/index.mjs.map +1 -1
- package/package.json +21 -7
- package/src/api/repository.js +1301 -745
- package/src/backend/isomorphic-git-backend.js +241 -138
- package/src/backend/lazy-git-backend.js +12 -19
- package/src/browser/helpers.js +8 -4
- 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 +21 -14
- package/src/core/jj-tree-state.js +16 -11
- package/src/core/jj-view-store.js +14 -7
- package/src/core/merge-driver-registry.js +92 -46
- package/src/core/operation-log.js +30 -33
- package/src/core/revset-engine.js +1114 -485
- package/src/core/storage-manager.js +73 -30
- package/src/core/tag-store.js +42 -12
- 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/atomic-write.js +44 -0
- package/src/utils/errors.js +3 -3
- package/src/utils/id-generation.js +4 -4
- package/src/utils/mutex.js +63 -0
- package/src/utils/validation.js +6 -10
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0 — 2026-09-02
|
|
4
|
+
|
|
5
|
+
The first `@johnhenry/isomorphic-jj` release since 0.1.0, and the first
|
|
6
|
+
scoped release to carry the audit fixes from PR #19 below — **the published
|
|
7
|
+
0.1.0 tarball on npm predates #19** and does not include them.
|
|
8
|
+
|
|
9
|
+
### Fixed (PR #19 — audit findings)
|
|
10
|
+
|
|
11
|
+
Fixes all 8 findings from a bug-hunt audit of the storage/operation-log,
|
|
12
|
+
undo/redo, revset, and Git-push layers:
|
|
13
|
+
|
|
14
|
+
- **Same-process storage locking.** `Storage.appendLine()`/`write()` did
|
|
15
|
+
read-then-write with no lock, so concurrent same-process calls (e.g. a
|
|
16
|
+
user command racing `BackgroundOps`'s autosnapshot) could silently drop an
|
|
17
|
+
entry. Added a path-keyed in-process async mutex (`src/utils/mutex.js`)
|
|
18
|
+
that serializes the full critical section for both methods. Scoped to
|
|
19
|
+
same-process concurrency per the audit's guidance; cross-process locking
|
|
20
|
+
is a follow-up.
|
|
21
|
+
- **`undo()` now reverts in-place content mutations.** Previously it never
|
|
22
|
+
restored `ChangeGraph`/tag mutations made in place. `describe()` now
|
|
23
|
+
snapshots the affected `ChangeGraph` node before mutating it (mirroring
|
|
24
|
+
the existing `fileSnapshot` pattern), and `undo()` restores it.
|
|
25
|
+
`operations.revert()` now also diffs/restores `tags`, not just
|
|
26
|
+
`bookmarks`.
|
|
27
|
+
- **Ambiguous change-id prefixes now throw `AMBIGUOUS_ID`.** `show()`
|
|
28
|
+
previously resolved an ambiguous prefix silently to the first match; it
|
|
29
|
+
now throws `AMBIGUOUS_ID` listing all matches.
|
|
30
|
+
- **`push()` surfaces categorized error codes.** Previously all per-ref
|
|
31
|
+
errors were swallowed into a bare ref name. Rejected refs now carry a
|
|
32
|
+
categorized `code` (`AUTH_FAILED`/`NETWORK_ERROR`/`NON_FAST_FORWARD`/
|
|
33
|
+
`OTHER`) and `reason` so callers can tell an auth failure apart from a
|
|
34
|
+
non-fast-forward rejection instead of guessing.
|
|
35
|
+
- **Conflict-marker newline fix.** Markers could fuse content onto boundary
|
|
36
|
+
markers when content lacked a trailing newline; now forces a newline
|
|
37
|
+
separator for non-empty content.
|
|
38
|
+
- **Atomic protobuf writes.** `jj-operation-store.js`, `jj-view-store.js`,
|
|
39
|
+
and `jj-tree-state.js` risked a truncated/undecodable file on a mid-write
|
|
40
|
+
crash. Now route through a shared `atomicWriteFile()` temp+rename helper
|
|
41
|
+
(`src/utils/atomic-write.js`).
|
|
42
|
+
- **Weak temp-file naming fixed alongside storage locking** by adding a
|
|
43
|
+
random suffix, so two concurrent writes can't collide on the same temp
|
|
44
|
+
filename.
|
|
45
|
+
- **Open-ended revset ranges** (`..b`, `a..`, `::`) failed with a
|
|
46
|
+
misleading "invalid IDs" error; they now delegate to the existing
|
|
47
|
+
`ancestors()`/`descendants()`/`all()` implementations.
|
|
48
|
+
|
|
49
|
+
Closes #11–#18. See [PR #19](https://github.com/johnhenry/isomorphic-jj/pull/19).
|
|
50
|
+
|
|
51
|
+
### CI (PR #20)
|
|
52
|
+
|
|
53
|
+
- Dropped macOS from the test matrix — hosted macOS runners are scarce/slow
|
|
54
|
+
to schedule and this is a pure-JS library with no native code path; Linux
|
|
55
|
+
+ Windows already cover realistic cross-platform risk. See
|
|
56
|
+
[PR #20](https://github.com/johnhenry/isomorphic-jj/pull/20).
|
|
57
|
+
|
|
58
|
+
### Documentation overhaul: examples suite
|
|
59
|
+
|
|
60
|
+
- **New `examples/` directory: 12 numbered, runnable examples** covering the
|
|
61
|
+
core loop (init/describe/new/log), stable-ID stacked changes, history
|
|
62
|
+
editing (split/squash/abandon/duplicate), branching and merging (including
|
|
63
|
+
`new({ parents })` for true merge changes), first-class conflicts, revsets,
|
|
64
|
+
bookmarks + tags (with the v0.44 `tag.track()` surface), Git interop,
|
|
65
|
+
configuration layers, undo/oplog/time travel, the `file.*` namespace, and
|
|
66
|
+
browser usage. Each example runs in a temp directory, cleans up after
|
|
67
|
+
itself, and *asserts* what it demonstrates, so the set doubles as a smoke
|
|
68
|
+
test. `npm run examples` runs the loop; `npm run example:NN` runs one.
|
|
69
|
+
- **Example 08 exercises real `git.clone()`/`git.push()`/`git.fetch()` with
|
|
70
|
+
zero network**: it builds a bare fixture repo with the git CLI and serves
|
|
71
|
+
it on 127.0.0.1 through `git http-backend` (buffering request bodies so
|
|
72
|
+
CGI gets a `CONTENT_LENGTH` — isomorphic-git sends chunked bodies). Skips
|
|
73
|
+
cleanly where the git CLI is unavailable.
|
|
74
|
+
- **CI**: the test workflow now runs the examples loop as a smoke step on
|
|
75
|
+
one matrix leg (ubuntu-latest / Node 22), deliberately a different leg
|
|
76
|
+
than the coverage gate.
|
|
77
|
+
- Companion docs for the site (opensource.johnhenry.me) were written from
|
|
78
|
+
this repo's README/API/MIGRATION/CONFIGURATION material in the same pass;
|
|
79
|
+
behavioral quirks the examples surfaced (e.g. `file()` matching snapshots
|
|
80
|
+
rather than modifications, duration revsets needing committer timestamps,
|
|
81
|
+
`init()` after `git.clone()` re-rooting `refs/heads/main`) are documented
|
|
82
|
+
there rather than papered over.
|
|
83
|
+
|
|
84
|
+
### Publishing
|
|
85
|
+
|
|
86
|
+
- `@johnhenry/isomorphic-jj@0.2.0` is published from `main`. A final
|
|
87
|
+
unscoped bridge release, `isomorphic-jj@1.8.0` (same code as this
|
|
88
|
+
release), will follow so existing unscoped installs get one more version
|
|
89
|
+
pointing at the new package before the unscoped name is deprecated.
|
|
90
|
+
|
|
91
|
+
## 0.1.0 — Track jj through v0.44
|
|
92
|
+
|
|
93
|
+
Jujutsu shipped v0.44.0 (2026-08-05) since isomorphic-jj's last parity pass
|
|
94
|
+
(v1.5.0/README, which tracked jj through v0.43). This release closes the gap
|
|
95
|
+
for the parts of v0.43–v0.44 that are in scope for a headless, browser-safe
|
|
96
|
+
reimplementation of jj's model — a library, not the `jj` CLI's terminal UX or
|
|
97
|
+
its Rust process/Git-plumbing internals.
|
|
98
|
+
|
|
99
|
+
### Added
|
|
100
|
+
|
|
101
|
+
- **`tag.track({ name, remote })` / `tag.untrack({ name })`** — jj v0.44 made
|
|
102
|
+
tags remote-trackable the same way bookmarks already were, and added `jj
|
|
103
|
+
tag track`/`untrack` to manage that per-tag. `TagStore` now persists
|
|
104
|
+
tracking state (`{ remote, remoteName }` per local tag name) the same way
|
|
105
|
+
`BookmarkStore` already did for bookmarks, and `tag.list()` includes a
|
|
106
|
+
`tracking: { remote, ref }` field once a tag is tracked. The on-disk
|
|
107
|
+
`tags.json` format gained a `{ tags, tracked }` envelope; the old flat
|
|
108
|
+
`{ name: changeId }` format is still read for backward compatibility.
|
|
109
|
+
- **`builtin_log()` revset function** — jj v0.44 added `builtin_log()` as an
|
|
110
|
+
alias for the revset the built-in `jj log` uses by default, so a custom
|
|
111
|
+
`revsets.log` config can extend it instead of duplicating it. isomorphic-jj's
|
|
112
|
+
`log()` has always defaulted to `all()` when no revset is given, so
|
|
113
|
+
`builtin_log()` is wired to the same set and can be composed like any other
|
|
114
|
+
revset function (`builtin_log() & mine()`).
|
|
115
|
+
- **`file.search({ ..., nameOnly: true })`** — jj v0.44 changed `jj file
|
|
116
|
+
search`'s default CLI output to print every matched *line* (prefixed with
|
|
117
|
+
its file path), moving the old "just the file paths" behavior behind a new
|
|
118
|
+
`--name-only` flag. isomorphic-jj's `file.search()` already returned
|
|
119
|
+
structured per-line matches (`{ path, lineNumber, line }`) — that was
|
|
120
|
+
already the new default — so the only gap was the old, path-only shape;
|
|
121
|
+
`{ nameOnly: true }` now returns a deduplicated `string[]` of matching
|
|
122
|
+
paths, matching `--name-only`.
|
|
123
|
+
|
|
124
|
+
### Changed
|
|
125
|
+
|
|
126
|
+
- **`git_refs()` / `git_head()` revsets are now documented as deprecated.**
|
|
127
|
+
Upstream jj deprecated both in favor of `bookmarks()`/`tags()`/`@`, then
|
|
128
|
+
removed them outright in jj v0.43 (calling them in real jj now errors).
|
|
129
|
+
isomorphic-jj keeps them working — removing a function outright is a
|
|
130
|
+
breaking API change this pass didn't want to force on existing callers
|
|
131
|
+
unilaterally — but they're no longer listed in the revset engine's
|
|
132
|
+
"did you mean" suggestion text, and their JSDoc now calls out the upstream
|
|
133
|
+
removal and recommends `bookmarks()`/`tags()`/`@` instead. See "Not
|
|
134
|
+
changed" below for the case for a harder break in a future major version.
|
|
135
|
+
|
|
136
|
+
### Testing
|
|
137
|
+
|
|
138
|
+
1714 tests passing (was 1701); lint (0 errors), format:check, typecheck, and
|
|
139
|
+
build all still green.
|
|
140
|
+
|
|
141
|
+
### Not changed (needs a human design call)
|
|
142
|
+
|
|
143
|
+
jj v0.43–v0.44 included several changes this pass deliberately left alone:
|
|
144
|
+
|
|
145
|
+
- **`jj run`** (v0.43 new command, gained `--passthrough`/`--ignore-changes`/
|
|
146
|
+
`--ignore-errors`/oldest-first-ordering in v0.44) — runs an arbitrary
|
|
147
|
+
subprocess against each revision's own private working copy. isomorphic-jj
|
|
148
|
+
doesn't shell out to anything and has to keep working in browsers with no
|
|
149
|
+
process/filesystem access; "run a command" doesn't have an isomorphic
|
|
150
|
+
equivalent. Adding it would mean either a Node-only stub (inconsistent with
|
|
151
|
+
the rest of the API surface) or scope-creeping into a job-runner. Flagging
|
|
152
|
+
for a maintainer decision rather than guessing.
|
|
153
|
+
- **Auto-importing remote tags/bookmarks during `git.fetch()`.** jj v0.44's
|
|
154
|
+
headline change is that `jj git fetch` now fetches tags the same way it
|
|
155
|
+
fetches bookmarks — as `<name>@<remote>`, auto-tracked by default. Wiring
|
|
156
|
+
that up properly for tags would need `git.fetch()` to actually populate
|
|
157
|
+
`TagStore`'s (and, by the same logic, `BookmarkStore`'s) remote-tracking
|
|
158
|
+
state from the fetched refs. Investigating this surfaced a **pre-existing**
|
|
159
|
+
gap: `git.fetch()` already doesn't call `bookmarks.setRemote()` for fetched
|
|
160
|
+
bookmarks today, even though `BookmarkStore.setRemote()`/`getRemote()` have
|
|
161
|
+
existed since v0.4 — remote ref sync was never wired all the way through at
|
|
162
|
+
the fetch call site. Building tag-fetch-tracking on top of that gap would
|
|
163
|
+
mean either (a) fixing the older bookmark gap too as a drive-by, our
|
|
164
|
+
papering over an inconsistency where tags "worked" and bookmarks didn't, or
|
|
165
|
+
(b) building a second, tag-only special case. Both are judgment calls
|
|
166
|
+
about a real architectural gap, not a mechanical port of one version's
|
|
167
|
+
changelog — left for a maintainer to decide. `tag.track()`/`untrack()`
|
|
168
|
+
(added this pass) at least let callers record and query tracking intent by
|
|
169
|
+
hand in the meantime.
|
|
170
|
+
- **`jj git clone --tag=PATTERN` / `--fetch-tags` removal.** Real jj replaced
|
|
171
|
+
`--fetch-tags=all|none|included` with `--tag=PATTERN` in v0.44. isojj's
|
|
172
|
+
`git.clone()`/`git.fetch()` never exposed CLI-shaped `--fetch-tags` flags in
|
|
173
|
+
the first place (just a boolean `noTags`), so there's no removed flag to
|
|
174
|
+
mirror; a `PATTERN`-based tag filter for clone/fetch would be new surface
|
|
175
|
+
area tied to the fetch-tracking gap above.
|
|
176
|
+
- **`jj git push --allow-conflicts`.** Real jj normally refuses to push
|
|
177
|
+
commits containing conflicts unless this flag is passed. isojj's
|
|
178
|
+
`git.push()` doesn't currently check for conflicts before pushing at all —
|
|
179
|
+
there's no existing guard to add a bypass flag for. Adding the guard itself
|
|
180
|
+
(and then the bypass) is more surface area than a one-version parity pass.
|
|
181
|
+
- **CLI argument parsing "last occurrence wins" (v0.44).** Checked
|
|
182
|
+
`bin/isojj.js`'s `parseArgs()`: it already stores each `--flag` into a
|
|
183
|
+
plain object keyed by flag name, so repeating a flag already naturally
|
|
184
|
+
overwrites the earlier value with no error — this was already jj v0.44's
|
|
185
|
+
new behavior by construction. No change needed.
|
|
186
|
+
- **`try(expr, fallback...)` template function, `jj workspace list` root
|
|
187
|
+
display, `diff.stat.max-bar-width`, `colors.crossed-out`, Gerrit/config-gc/
|
|
188
|
+
`/etc/jj` config-discovery changes.** These are jj's template engine and
|
|
189
|
+
terminal/config-file layers, which isomorphic-jj's CLI (a thin, generic
|
|
190
|
+
API-argument passthrough — see `bin/isojj.js`) doesn't attempt to replicate
|
|
191
|
+
1:1 for any command. Out of scope for this pass; noted here for
|
|
192
|
+
completeness of the v0.43/v0.44 diff.
|
|
193
|
+
|
|
194
|
+
## 0.0.0
|
|
195
|
+
|
|
196
|
+
- **Renamed: `isomorphic-jj` is now `@johnhenry/isomorphic-jj`, restarted at 0.0.0.** Same library, same API — a new address and era, not a maturity signal (1.7.0 lineage).
|
|
197
|
+
|
|
198
|
+
```sh
|
|
199
|
+
npm install @johnhenry/isomorphic-jj
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
All notable changes to isomorphic-jj are documented here. The project tracks the
|
|
204
|
+
[Jujutsu (jj)](https://github.com/jj-vcs/jj) CLI; each release notes the upstream
|
|
205
|
+
jj version whose semantics it targets.
|
|
206
|
+
|
|
207
|
+
## [1.7.0] — Real revset parser, testable/UX-improved CLI, background-ops fix
|
|
208
|
+
|
|
209
|
+
Closes out the remaining known-issue backlog.
|
|
210
|
+
|
|
211
|
+
### Changed — revset engine rewritten around a real tokenizer + parser + AST
|
|
212
|
+
|
|
213
|
+
`RevsetEngine.evaluate()` previously matched the whole trimmed expression
|
|
214
|
+
against one long chain of regexes, one per construct. This had two real bugs:
|
|
215
|
+
|
|
216
|
+
- **Nested function-call arguments silently mis-parsed.** A non-greedy regex
|
|
217
|
+
like `/^roots\((.+?)\)$/` truncates at the *first* `)` it sees, so
|
|
218
|
+
`roots(ancestors(x))` never worked — the previous "fix" for this was a
|
|
219
|
+
one-off hand-rolled paren-counter used only by `reachable()`.
|
|
220
|
+
- **No real operator precedence.** Mixed `&`/`|`/`~` expressions were
|
|
221
|
+
resolved by checking `.includes(' & ')` before `' | '` before `' ~ '`
|
|
222
|
+
regardless of the expression's actual structure.
|
|
223
|
+
|
|
224
|
+
The expression is now tokenized and parsed into a small AST via a real
|
|
225
|
+
recursive-descent / precedence-climbing parser (`&`/`~` at one precedence
|
|
226
|
+
tier, `|` lower, both left-associative), which is then evaluated by walking
|
|
227
|
+
the tree. This is purely a parsing-layer replacement — every per-function
|
|
228
|
+
filter/traversal method (`filterByAuthor`, `getAncestors`, `findForkPoint`,
|
|
229
|
+
etc.) is untouched, and **all 183 pre-existing revset tests pass unchanged**.
|
|
230
|
+
|
|
231
|
+
New capabilities (all strictly additive — nothing that worked before changed
|
|
232
|
+
behavior):
|
|
233
|
+
- Function-call arguments can nest arbitrarily deep: `roots(ancestors(x))`,
|
|
234
|
+
`heads(roots(ancestors(x)))`, etc.
|
|
235
|
+
- Parenthesized grouping: `(a | b) & c`.
|
|
236
|
+
- Quote-aware argument splitting: `description("a, b, and c")` no longer
|
|
237
|
+
mis-splits on the comma inside the quoted string.
|
|
238
|
+
- Whitespace around `&`/`|`/`~` is now optional (`a|b` works, not just `a | b`).
|
|
239
|
+
|
|
240
|
+
### Fixed — background-ops (td-488842)
|
|
241
|
+
|
|
242
|
+
`enableAutoSnapshot()`'s timer callback awaited only `queue()`'s immediate
|
|
243
|
+
`{ id, promise }` handle, not the operation's own settling `promise`. Since
|
|
244
|
+
`queue()` resolves as soon as the operation is *enqueued* (not once it
|
|
245
|
+
finishes), a later `describe()` rejection landed on the unobserved promise
|
|
246
|
+
and became an unhandled promise rejection instead of reaching the
|
|
247
|
+
surrounding `catch`. Now awaits the returned `promise` too.
|
|
248
|
+
|
|
249
|
+
### Changed — CLI (`bin/isojj.js`) rewritten to be testable, with real bugs fixed
|
|
250
|
+
|
|
251
|
+
The CLI had zero tests (calling `process.exit()` at import time made it
|
|
252
|
+
untestable) and several real bugs:
|
|
253
|
+
- **Made testable**: `parseArgs`/`formatOutput`/`findRepoRoot`/
|
|
254
|
+
`loadGitBackend`/`run` are now exported; `run()` returns an exit code
|
|
255
|
+
instead of calling `process.exit()` directly, guarded behind an
|
|
256
|
+
is-this-the-entrypoint check.
|
|
257
|
+
- **Fixed a parsing bug**: boolean flags (`dryRun`, `json`, `force`,
|
|
258
|
+
`interactive`, etc.) used to swallow the next token as a fake "value",
|
|
259
|
+
silently dropping a following positional — `describe --dryRun "fix typo"`
|
|
260
|
+
lost the commit message. Boolean flags never consume the next token now;
|
|
261
|
+
`--flag=true`/`--flag=false` is coerced to a real boolean.
|
|
262
|
+
- **Fixed a latent bug**: the CLI's own error message promised searching
|
|
263
|
+
"any parent up to mount point /" for a `.jj` repo but never actually did
|
|
264
|
+
so. Added `findRepoRoot()` to walk up parent directories like git/jj, so
|
|
265
|
+
the CLI now works from any subdirectory of a repo.
|
|
266
|
+
- **Wired up the real Git backend**: the CLI never passed `git`/`http` to
|
|
267
|
+
`createJJ()`, so `isojj init` silently ran in storage-only "mock" mode
|
|
268
|
+
with no real `.git` directory. It now dynamically imports isomorphic-git
|
|
269
|
+
(an optional peer dependency) when present, falling back gracefully when
|
|
270
|
+
it isn't.
|
|
271
|
+
- Added a full test suite (`tests/integration/cli.test.js`) and included
|
|
272
|
+
`bin/**/*.js` in coverage collection.
|
|
273
|
+
|
|
274
|
+
### Testing
|
|
275
|
+
|
|
276
|
+
1701 tests passing (was 1671); lint, format:check, typecheck, coverage
|
|
277
|
+
(91.2% branches), and build all green.
|
|
278
|
+
|
|
279
|
+
## [1.6.1] — Bugfix batch
|
|
280
|
+
|
|
281
|
+
Fixes nine real, pre-existing defects that the v1.5/v1.6 coverage push
|
|
282
|
+
surfaced and documented (each had a test asserting the buggy behavior with a
|
|
283
|
+
`// BUG:` note — those tests now assert the corrected behavior instead).
|
|
284
|
+
|
|
285
|
+
### Fixed
|
|
286
|
+
|
|
287
|
+
- **`bookmarks([pattern])` revset returned `[undefined]`.** `filterBookmarks()`
|
|
288
|
+
read `bookmark.target`, but `BookmarkStore.list()` returns objects keyed
|
|
289
|
+
`changeId`. It now reads `bookmark.changeId`.
|
|
290
|
+
- **`show()` never attributed bookmarks to a change**, for the same
|
|
291
|
+
`.target` vs. `.changeId` mismatch. Fixed the same way.
|
|
292
|
+
- **`bookmark.delete()`'s not-found guard was dead code.** It called
|
|
293
|
+
`bookmarks.get(name)` without `await`, so the "truthy Promise" always
|
|
294
|
+
passed the guard; the store's own `BOOKMARK_NOT_FOUND` was reached
|
|
295
|
+
instead of the friendlier method-level `NOT_FOUND`. Added the missing
|
|
296
|
+
`await`.
|
|
297
|
+
- **`operations.revert()` on a bookmark-move operation always threw
|
|
298
|
+
`BOOKMARK_EXISTS`.** Its "moved bookmark" branch called `bookmarks.set()`
|
|
299
|
+
(create-only) to move the bookmark back, but the bookmark still exists
|
|
300
|
+
during a move-revert. Changed to `bookmarks.move()`.
|
|
301
|
+
- **`ChangeGraph.getAncestors()` returned duplicate ancestors on
|
|
302
|
+
diamond-shaped history** (e.g. `a<-b`, `a<-c`, `b,c<-d`). A node was only
|
|
303
|
+
marked visited when dequeued, so a shared ancestor reachable via two
|
|
304
|
+
parents could be enqueued twice. Nodes are now marked visited the moment
|
|
305
|
+
they're enqueued.
|
|
306
|
+
- **`matchesPattern()` (merge driver registry) broke `**` glob patterns.**
|
|
307
|
+
Literal dots were escaped *after* `**` had already been converted to
|
|
308
|
+
`.*`, corrupting it into `\.*` (zero-or-more literal dots) instead of "any
|
|
309
|
+
characters". Dots are now escaped before the glob substitutions.
|
|
310
|
+
- **`IsomorphicGitBackend.getCurrentTree()` always threw
|
|
311
|
+
`TREE_READ_FAILED`.** It called `git.writeTree({ fs, dir })` without the
|
|
312
|
+
library's required `tree` argument (`git.writeTree` writes a single,
|
|
313
|
+
already-built tree — it doesn't build one from the working directory).
|
|
314
|
+
Reimplemented to walk the git index (`STAGE`) and build the nested tree
|
|
315
|
+
bottom-up, verified to produce the same oid as an equivalent real commit.
|
|
316
|
+
- **`ConflictModel._tryMergeDriver()`'s "no custom driver" guard never
|
|
317
|
+
matched.** It compared `driver === this.mergeDriverRegistry.defaultDriver`,
|
|
318
|
+
but `MergeDriverRegistry` never set a `defaultDriver` property (always
|
|
319
|
+
`undefined`), so every file was routed through the generic three-way
|
|
320
|
+
merge driver instead of falling back to the richer path-based conflict-type
|
|
321
|
+
detection. `MergeDriverRegistry` now exposes `defaultDriver`.
|
|
322
|
+
- **`IsomorphicGitBackend.stageAll()` double-wrapped per-file errors.** A
|
|
323
|
+
specific `STAGE_FILE_FAILED` thrown for one file was immediately re-wrapped
|
|
324
|
+
by the surrounding `catch` as the generic `STAGE_FAILED`, hiding the more
|
|
325
|
+
useful code. The outer catch now re-throws an already-categorized
|
|
326
|
+
`JJError` as-is.
|
|
327
|
+
|
|
328
|
+
## [1.6.0] — Automatic working-copy snapshotting
|
|
329
|
+
|
|
330
|
+
Closes a long-standing fidelity gap: the library now **walks the working
|
|
331
|
+
directory on disk** and reconciles tracked state before read/commit operations,
|
|
332
|
+
so files created, modified, or deleted **out-of-band** (an editor, the shell,
|
|
333
|
+
`git checkout`) are picked up — matching jj's "snapshot before every command".
|
|
334
|
+
Previously only files written through `jj.write()` were ever tracked, so
|
|
335
|
+
`status`/`describe`/`diff`/`file.search`/`read` couldn't see anything else.
|
|
336
|
+
|
|
337
|
+
### Added
|
|
338
|
+
|
|
339
|
+
- `WorkingCopy.walk()` — recursively lists working-directory files (excluding
|
|
340
|
+
`.git`, `.jj`, `node_modules`), robust across Node fs and in-memory fses.
|
|
341
|
+
- `WorkingCopy.snapshot()` — reconciles tracked file state with disk, returning
|
|
342
|
+
`{ added, modified, deleted }`; honors sparse patterns.
|
|
343
|
+
- `jj.snapshot()` — public method to trigger a snapshot explicitly.
|
|
344
|
+
- `createJJ({ autoSnapshot })` option (default `true`) to opt out of the
|
|
345
|
+
automatic behavior.
|
|
346
|
+
|
|
347
|
+
### Changed
|
|
348
|
+
|
|
349
|
+
- `describe()`, `status()`, `diff()`, `read()`, `file.list()`, and
|
|
350
|
+
`file.search()` now auto-snapshot the working copy first (when operating on
|
|
351
|
+
`@`), so they reflect on-disk reality. `status()` now returns real `added` and
|
|
352
|
+
`removed` lists (previously always empty).
|
|
353
|
+
- A tracked file deleted on disk is now gracefully untracked on the next
|
|
354
|
+
snapshot instead of making `describe()` throw `SNAPSHOT_FILE_FAILED` (that
|
|
355
|
+
path still applies under `{ autoSnapshot: false }`).
|
|
356
|
+
|
|
357
|
+
## [1.5.0] — Parity refresh (tracks jj through v0.43)
|
|
358
|
+
|
|
359
|
+
This release brings isomorphic-jj up to date with Jujutsu releases v0.31–v0.43,
|
|
360
|
+
adds a batch of revset functions and commands, and cleans up the toolchain.
|
|
361
|
+
|
|
362
|
+
### Added — revset functions (jj v0.31–v0.43)
|
|
363
|
+
|
|
364
|
+
- `change_id(prefix)` / `commit_id(prefix)` — resolve a change by a hex prefix (jj v0.31).
|
|
365
|
+
- `subject(pattern)` — match only the first line of a description (jj v0.26).
|
|
366
|
+
- `author_name(x)`, `author_email(x)`, `committer(x)`, `committer_name(x)`,
|
|
367
|
+
`committer_email(x)` — fine-grained signature filters (jj v0.26).
|
|
368
|
+
- `signed()` — cryptographically signed changes (jj v0.29).
|
|
369
|
+
- `divergent()` — changes flagged as divergent (jj v0.38).
|
|
370
|
+
- `merges()` — canonical alias of the existing `merge()`.
|
|
371
|
+
- `forks()` — changes with more than one child.
|
|
372
|
+
- `first_parent(x)` / `first_ancestors(x)` — first-parent navigation (jj v0.32).
|
|
373
|
+
- `fork_point(x)` — youngest common ancestor of a set (jj v0.32).
|
|
374
|
+
- `merge_point(x)` — youngest common descendant of a set (implemented ahead of
|
|
375
|
+
upstream; jj stabilized its own `merge_point()` in v0.44 with matching
|
|
376
|
+
semantics — "the point where multiple branches merge").
|
|
377
|
+
- `exactly(x, n)` — the set, but errors unless it has exactly `n` elements (jj v0.34).
|
|
378
|
+
- `present(x)` — evaluate without erroring on unknown symbols.
|
|
379
|
+
- `coalesce(a, b, …)` — the first argument that resolves to a non-empty set.
|
|
380
|
+
- `remote_tags([pattern])` — remote (slash-qualified) tag targets (jj v0.38).
|
|
381
|
+
- `ancestors(x, depth)` — generalized to accept any nested revset plus an
|
|
382
|
+
optional depth limit (previously accepted only a bare 32-hex change ID).
|
|
383
|
+
|
|
384
|
+
### Added — commands
|
|
385
|
+
|
|
386
|
+
- `revert({ revision })` — canonical replacement for `backout()` (jj renamed
|
|
387
|
+
`backout` → `revert` and removed `backout` in v0.35). `backout()` remains as a
|
|
388
|
+
deprecated alias.
|
|
389
|
+
- `redo()` — progressively re-applies operations reverted by `undo()` (jj v0.33).
|
|
390
|
+
- `sign()` / `unsign()` — record and clear signature metadata on a change (jj
|
|
391
|
+
v0.27). Previously hard `UNSUPPORTED_OPERATION` stubs; now implemented as
|
|
392
|
+
metadata operations that light up the `signed()` revset. (A pure-JS library
|
|
393
|
+
cannot verify GPG/SSH keys, so cryptographic verification is still delegated to
|
|
394
|
+
the Git layer.)
|
|
395
|
+
- `file.search({ pattern })` — search tracked file contents, regex by default
|
|
396
|
+
with `{ kind: 'substring' }` for literal matches (jj v0.37 / v0.41).
|
|
397
|
+
- `bookmark.advance({ name, to })` — move a bookmark forward only; refuses
|
|
398
|
+
non-descendant targets (jj v0.39).
|
|
399
|
+
- `tag.set({ name, changeId })` — create-or-move (upsert) a tag (jj v0.35).
|
|
400
|
+
|
|
401
|
+
### Fixed
|
|
402
|
+
|
|
403
|
+
- **`tags()` revset now works.** It was a stub returning `[]` even though a
|
|
404
|
+
`TagStore` existed; the `RevsetEngine` is now wired to the tag store and
|
|
405
|
+
`tags()` / `tags(pattern)` return real results.
|
|
406
|
+
- **Duplicate `conflicts` key in the API object.** The raw `ConflictModel` was
|
|
407
|
+
exposed under the same key as the public `conflicts` namespace and silently
|
|
408
|
+
shadowed. The raw model is now available as `jj.conflictModel`.
|
|
409
|
+
- **`npm run lint` was completely broken** — the ESLint config was authored as an
|
|
410
|
+
ES module in a `"type": "module"` package. Renamed to `.eslintrc.cjs` and
|
|
411
|
+
fixed; the source tree now lints clean (0 errors).
|
|
412
|
+
- Latent `fail()` calls in tests (removed from jest-circus) replaced with `throw`.
|
|
413
|
+
- Removed an unnecessary try/catch, an empty catch block, an unused import, and
|
|
414
|
+
several unused variables flagged by the newly-working linter.
|
|
415
|
+
|
|
416
|
+
### Changed
|
|
417
|
+
|
|
418
|
+
- `tsconfig.json` now includes the DOM/WebWorker libs and Node types so the
|
|
419
|
+
type-check reflects the library's real isomorphic runtime.
|
|
420
|
+
- Expanded the `isojj` CLI: `--version`, a far more complete `help`, positional
|
|
421
|
+
argument handling for `log`/`describe`/`file.*`, and generic pass-through of
|
|
422
|
+
all `--flags` so every API method is reachable from the CLI.
|
|
423
|
+
|
|
424
|
+
## [1.4.3] and earlier
|
|
425
|
+
|
|
426
|
+
See the git history and [ROADMAP.md](./ROADMAP.md) for the v0.1–v1.4 development
|
|
427
|
+
record (core model, history editing, Git backend, first-class conflicts, revset
|
|
428
|
+
query language, workspaces, browser support, events, and full v1.0 JJ CLI parity).
|