Updated the CLI to support line range based file fixing.
* We added `line_range_arg` and `run_tool_if_zero_line_ranges` to `ToolConfig` so users can configure how changed line ranges are passed to tools and explicitly specify whether a tool should run when there are zero modified lines.
* We introduced the `--all-lines` argument to allow users to fall back to the previous behavior of formatting the entire file when `line_range_arg` is specified.
* We use a new `compute_regions_to_format` function to calculate the changed line ranges dynamically before applying each individual formatter tool in the sequence.
* Because a formatter tool might add or remove lines, shifting the line numbers for subsequent tools, this determines the exact target ranges by diffing the static base file content against the intermediate output of the previous tool.
* To validate this logic, we extended the `fake-formatter` test fixture with a `--line-ranges` argument to restrict execution to specific lines. We also introduced a `--split-even-length-lines` flag to mimic realistic formatters that expand line counts, proving our dynamic range recalculation properly shifts lines in complete end-to-end tests.
Mostly done with OpenCode and Claude Sonnet 4.6 with the following prompt,
and reviewed manually:
Replace the Result.unwrap with the ? operator in test functions.
While doing that, make sure to:
* Add TestResult as return type in the test functions where you replace
unwrap by the ? operator, and Ok(()) at the end.
* Do the replacement only in method with the `#[test]` decorator.
* Don't replace unwraps in closures.
* Don't replace Option.unwrap.
* Be careful to not change the insta snapshot references.
* Run the tests each time you're done with a file.
* Keep the unwraps that you can't replace with the ? operator.
Note that:
* You can identify all the Result.unwrap locations with this command:
cargo clippy --tests --message-format=short -- -W clippy::unwrap_used 2>&1 | grep / | grep Result | grep <filename>
* Begin with the file with the most Result.unwrap in them as shown by
this command:
cargo clippy --tests --message-format=short -- -W clippy::unwrap_used 2>&1 | grep Result | cut -d: -f1 | sort | uniq -c | sort -nr
* Some non test files have inline tests.
Before this PR:
❯ cargo clippy --tests --message-format=short -- -W clippy::unwrap_used 2>&1 | grep Result | wc -l
3973
After this PR:
❯ cargo clippy --tests --message-format=short -- -W clippy::unwrap_used 2>&1 | grep Result | wc -l
1064
Currently, checking out a commit with conflicts in a colocated workspace
causes many editors to show all existing files as added since they
aren't present in the Git HEAD commit. Using the tree of the first side
of the commit as a base tree and then adding the '.jjconflict-*' trees
on top of it would cause any files identical to the first tree to show
as unchanged, matching Git's conflict behavior.
One potential downside to this change is that it becomes harder for a
user to notice if they incorrectly check out a conflicted commit using
`git switch`, since previously all of their files would've disappeared
so it would be more difficult to ignore. However, I think this could be
solved a different way, such as by adding a warning when snapshotting if
'.jjconflict' files are detected. Keeping the files from the first
tree also has the benefit that '.gitignore' files will still be present,
meaning that when a user does `jj abandon` to recover, it won't delete
their ignored files.
Using 'JJ-CONFLICT-README' as the name for the readme file instead of
'README' makes it more clear that this file is related to the
'.jjconflict-*' trees, since now the repo's actual README file might be
present as well in the tree.
An example with parents `rtsqusxu` and `ysrnknol`:
```
<<<<<<< conflict 1 of 1
%%%%%%% diff from: vpxusssl 38d49363 "description of base"
\\\\\\\ to: rtsqusxu 2768b0b9 "description of left"
-base
+left
+++++++ ysrnknol 7a20f389 "description of right"
right
>>>>>>> conflict 1 of 1 ends
```
Conflict labels will generally start with lowercase change IDs, so
making all of the text lowercase makes it more consistent. I also
removed the "contents of" text, since conflict labels will already be
long enough, and this text doesn't add anything. Similarly, I removed
the "(conflict 1 of 1)" note from the Git conflict markers since Git
doesn't include this information, and including it would result in extra
long lines once we add conflict labels.
Running "cargo insta test --workspace", results in numerous warnings:
Snapshot test passes but the existing value is in a legacy format.
Please run `cargo insta test --force-update-snapshots` to update to
a newer format.
This commit is the result of running the suggested command.
As a follow-up to #8115, this moves all references in the codebase to use the new website.
I didn't update the older CHANGELOG entries because I figured they're intended
to be immutable.
* `Some(x).unwrap()` is not necessary.
* Add a new test to ensure that we handle the fix child process IO
without deadlock. The new test introduces byte mode to fake-formatter
which pipe the stdin contents to stdout byte by byte in a streaming
fashion. The test contents are 512KB, which is larger than page size,
can trigger the blocking read behavior, and doesn't make the test the
slowest.
On Windows, spawning a child process finds the command relative to the
parent's working directory. If a command is specified as
`["path/inside/repo/tool.exe"]`, then tool won't be found if `jj fix` is
run from a subdirectory of the workspace.
There doesn't seem to be a good way to change this behavior, nor does it
seem easy to convert `program` into an absolute path because
`["tool.exe"]` could refer either to a file on the PATH or a file
committed at the root of the repository.
So, we add a new variable `$root` that can be used in this situation.
Workspace-relative tools on Windows should be defined using this
variable:
```toml
# Tools on the PATH
command = ["tool.exe"]
# Workspace-relative tools
command = ["$root/tool.exe"]
command = ["$root/nested/dir/tool.exe"]
```
On Unix, the command is found relative to the working directory of the
child process, so this isn't an issue.
Fixes#7144
Fix tools often emit information on stderr for the user to see. Since
fix tools are run in parallel and stderr is unbuffered, this information
can be jumbled together. This seems to happen especially frequently on
Windows.
This change locks stderr before writing to it so the stderr output from
a tool is written all at once.
We haven't had any reports of problems from people who opted in. Since
it's early in the release cycle now, let's now test it on everyone who
builds from head, so we get almost a month of testing from those
people before it's enabled by default in a released version.
This impacts lots of test cases because the change-id header is added
to the Git commit. Most are uninteresting. `test_git_fetch` now sees
some divergent changes where it used to see only divergent bookmarks,
which makes sense.
There have been a number of users confused about why
their commits are immutable, or what to do about it, ex.
[https://github.com/jj-vcs/jj/discussions/5659].
Separately, I feel that the cli is too quick to suggest
`--ignore-immutable`, without context of the consequences. A new user
could see that the command is failing, see a helpful hint to make it not
fail, apply it and move on. This has wildly different consequences, from
`jj squash --into someone_elses_branch@origin` rewriting a single commit,
to `jj edit 'root()+'` rewriting your entire history.
This commit changes the immutable hint by doing the following:
* Adds a short description of what immutable commits are used for, and a
link to the relevant docs, to the hint message.
* Shows the number of immutable commits that would be rewritten if
the operation had succeeded.
* Removes the suggestion to use `--ignore-immutable`.
Previously, tools invoked by `jj fix` did not have their
`.current_dir()` set, and would just run from whatever directory the
user was in.
Now, the tools will always be invoked from the same directory that
`jj root` gives.
As a motivating example, consider a configuration path that's always at
the workspace root:
```toml
[fix.tools.something]
# previous:
command = ["cmd", "--config", "$root/tool.toml"]
# ^^^^^^ not possible
# now:
command = ["cmd", "--config", "./tool.toml"]
# ^^ now, just use a relative path
```
I'm going to add "[EOF]" marker to test that command output is terminated by
newline char. This patch ensures that callers who expect a raw output string
would never be affected by any normalization passes.
Some common normalization functions are extracted as CommandOutputString
methods.
With this change a warning is shown if the user does not explicitly specify the target revision, but the behavior is unchanged (it still defaults to the working copy).
In the future the warning will be turned into an error. In other words, it will be required to specify target revision.
The bulk of the changes here are to prepare tests for the upcoming change, to make the transition easier.
For additional details please see:
* https://github.com/jj-vcs/jj/issues/5374
* https://github.com/jj-vcs/jj/discussions/5363
Adds an optional `fix.tools.TOOL.enabled` config that disables use of a fix
tool (if omitted, the tool is enabled). This is useful for defining tools in
the user's configuration without enabling them for all repositories:
```toml
# ~/.jjconfig.toml
[fix.tools.rustfmt]
enabled = false
command = ["rustfmt", "--emit", "stdout"]
patterns = ["glob:'**/*.rs'"]
```
Then to enable it in a repository:
```shell
$ jj config set --repo fix.tools.rustfmt.enabled true
```