- Support for fetching and pushing tags, now stabilized with tracked or untracked tag management like bookmarks
- New merge_point() revset function to find the point where multiple branches merge
- jj workspace list now shows workspace roots by default with customizable output
- New try(expr, fallback...) template function to suppress runtime errors
- jj run now processes revisions from oldest to newest by default with guaranteed start order
- jj run gained --passthrough flag to connect subprocess stdout/stderr directly to terminal
- jj run gained --ignore-changes flag to avoid editing revisions even if command modifies working copy
- jj run gained --ignore-errors flag to continue running remaining revisions on nonzero exit code
- jj file search now supports -n/--line-number to prefix matches with 1-based line numbers
- jj git push gained --allow-conflicts flag to allow pushing commits containing conflicts
- New jj tag track/untrack commands to associate local tags with remotes
- Added builtin_log() revset alias for the built-in default jj log revset
- Added config option diff.stat.max-bar-width to limit the ++-- bar width
- jj absorb now supports --interactive/-i/--tool to interactively choose which hunks to absorb
- jj git fetch now fetches tags the same way it fetches bookmarks as <name>@<remote> with automatic tracking
- jj git clone --fetch-tags=all|none|included removed in favor of --tag=PATTERN
- jj git push --all now pushes all tags in addition to bookmarks
- WorkspaceRef.root() and RepoPath.absolute() template functions now return Option<FsPath> and FsPath respectively instead of String
- jj file search now prints every matched line prefixed by file path instead of only file paths
- Passing an argument more than once is no longer an error, with last occurrence winning
- Recursive alias definitions are detected more precisely to allow repeated aliases and fallback to default commands
- Git temporary files are now cleaned up more reliably in the presence of signals
- Fixed Git HEAD mismatch after the working copy became immutable
- jj now creates a new working-copy revision as soon as the current workspace becomes immutable and during snapshotting
- Snapshotting no longer fails if working copy contains a path with non-UTF-8 name
- Fixed failure when reading configuration in copied repo with empty repo-level configuration directory
- jj diff and jj status no longer omit a rename or copy in merge commit when renamed source is present in multiple parents
About
jj is a Git-compatible version control system that is both simple and powerful. See the installation instructions to get started.
Release highlights
- Support for fetching and pushing tags has now been stabilized. Tags can be tracked or untracked just like bookmarks. Tracked tags are pushed by default.
Breaking changes
-
jj git fetchnow fetches tags the same way it fetches bookmarks. Tags are fetched as<name>@<remote>, and these remote tags are automatically tracked by local tags of the same name. Runningjj git fetchin an existing repository will re-fetch all tags to initialize the tracking state.Git's
tagOptis no longer respected. To disable tag fetching, setremotes.<name>.fetch-tags = '~*'in the jj configuration. -
jj git clone --fetch-tags=all|none|includedis removed in favor of--tag=PATTERN. -
jj git push --allnow pushes all tags in addition to bookmarks. -
The
WorkspaceRef.root()andRepoPath.absolute()template functions now returnOption<FsPath>andFsPathrespectively, instead ofString. Thepathkeyword injj config listtemplates now returnsOption<FsPath>. -
jj file searchnow prints every matched line prefixed by the file path, instead of only the file paths of files containing a match. Use--name-onlyfor the previous behavior. #9399 -
Passing an argument more than once is no longer an error. The last occurrence wins, so
jj --no-pager --no-pager versionandjj log -n 5 -n 10now work, and an argument baked into an alias or a wrapper can be given explicitly as well. Arguments that can be specified multiple times, such as--configandjj log -r, are unaffected and still collect all of their values. #8101 #9859
Deprecations
None
New features
-
New
merge_point()revset function which (similar tofork_point) finds the point where multiple branches merge. -
jj workspace listnow shows workspace roots by default. The output can be customized withtemplates.workspace_listor-T, andWorkspaceRef.root()returns an optionalFsPathvalue. #9713, #9826 -
New
try(expr, fallback...)template function to suppress runtime errors. -
jj runnow processes revisions from oldest to newest by default. The start order is guaranteed: each revision begins execution only after the previous one has started, even with--jobshigher than 1. -
jj rungained a--passthroughflag that connects the subprocess's stdout/stderr directly to the terminal instead of capturing output. -
jj rungained a--ignore-changesflag to avoid editing any revisions even if the command modifies the working copy. -
jj rungained a--ignore-errorsflag to continue running against the remaining revisions even if the command exits with a nonzero exit code. -
jj file searchnow supports-n/--line-numberto prefix each match with its 1-based line number within the file. -
jj git pushgained a--allow-conflictsflag to allow pushing commits containing conflicts. -
New
jj tag track/untrackcommands to associate local tags with remotes. Note that fetched tags are tracked by default. -
Added
builtin_log()revset alias for the built-in defaultjj logrevset.revsets.lognow defaults tobuiltin_log(), so custom log revsets can reuse the built-in default instead of copying its full expression. -
Added config option
diff.stat.max-bar-widthfor use with--statto limit the++--bar width. In the templating language,diff.stat()can optionally take a second (positional or named) argumentmax_bar_widthfor an equivalent effect. -
jj absorbnow supports--interactive/-i/--toolto let you interactively choose which hunks from the source to consider for absorption. This is useful when you only want to absorb part of a commit without first splitting it. Any hunks that are not selected or cannot be absorbed remain in the source commit.
Fixed bugs
-
Recursive alias definitions are detected more precisely. jj can now expand aliases that are simply repeated. For example, with the alias
jj = [], the commandjj jj jjwill resolve tojj. Aliases can also fall back to the default command. For example, with the aliasi = ["--ignore-working-copy"],jj iwill resolve tojj --ignore-working-copy. -
Git temporary files are now cleaned up more reliably in the presence of signals (e.g.
Ctrl-C). This should reduce the rate of "Could not acquire lock for index file" errors. (#7530) -
Fixed Git HEAD mismatch after the working copy became immutable. #9827
-
jjnow creates a new working-copy revision as soon as the one of the current workspace becomes immutable, as well as during snapshotting. This brings the behavior closer tojj0.42 and earlier. -
Snapshotting no longer fails if the working copy contains a path whose name isn't valid UTF-8. Such paths can't be tracked, so they are now skipped and reported as a warning instead. #9774
-
Fixed failure when reading configuration in copied repo with an empty repo-level configuration directory.
-
jj diffandjj statusno longer omit a rename or copy in a merge commit when the renamed source is present in more than one parent. The identical copy record reported for each parent was mistaken for a conflict and discarded. #9752 -
jj runno longer panics when its revset includes a conflicted commit. The conflict is now preserved in the rewritten commit. #9747 -
Fixed a panic when passing overly-large length parameters to ID templater functions (
commit_id.short(),commit_id.shortest(), etc.). #9833 -
jj git importandexportin colocated workspaces are now disabled by default. These commands usually do nothing, but they had a race condition.
Contributors
Thanks to the people who made this release happen!
- Aaron Bies (@slerpyyy)
- Ada Alakbarova (@ada4a)
- Alexandre Hallaine (@alexandre-hallaine)
- Amaan Qureshi (@amaanq)
- Austin Seipp (@thoughtpolice)
- Caleb White (@calebdw)
- Chawye Hsu (@chawyehsu)
- David Rieber (@drieber)
- Gaëtan Lehmann (@glehmann)
- hexbinoct (@hexbinoct)
- ivelieu (@ivelieu)
- Joseph Lou (@josephlou5)
- Josh McKinney (@joshka)
- Josh Steadmon (@steadmon)
- Kevin Liao (@kevincliao)
- Luna Schwalbe (@lunagl)
- Martin von Zweigbergk (@martinvonz)
- Matt Van Horn (@mvanhorn)
- Nika Layzell (@mystor)
- Niko Savola (@nikosavola)
- Nitzan Raz (@BackSlasher)
- Noah Lev (@camelid)
- OlshaMB (@OlshaMB)
- Philip Metzger (@PhilipMetzger)
- Remo Senekowitsch (@senekor)
- Rob Pilling (@bobrippling)
- Stephen Jennings (@jennings)
- Techcable (@Techcable)
- TogarashiPepper (@TogarashiPepper)
- Vladimir Petrzhikovskii (@0xdeafbeef)
- Waleed Khan (@arxanas)
- Wang Yuantao (@0WD0)
- xtqqczze (@xtqqczze)
- Yash Bavadiya (@xevrion)
- Yuya Nishihara (@yuja)