If movement commands don't find a target commit, they fail. However,
it's usually not intuitive why they fail because in non-edit mode the
start commit is the parent of the working commit.
Adding the start commit change hash to the error message makes it easier
for the user to figure out what is going on.
Also, specifying 'No **other** descendant...' helps make it clear what
`jj` is really looking for.
Part of #3947
Not all callers need this information, but I assumed it's relatively cheap to
look up the source path in the target tree compared to diffing.
This could be represented as Regular(_)|Copied(_, _)|Renamed(_, _), but it's
a bit weird if Copied and Renamed were separate variants. Instead, I decided
to wrap copy metadata in Option.
This patch adds accessor methods as I'm going to change the underlying data
types. Since entry values are consumed separately, these methods are implemented
on CopiesTreeDiffEntryPath, not on *TreeDiffEntry.
Git reports a rename source as deleted if the rename target is excluded. I
think that's because Git restricts the search space to the specified paths. For
example, Git doesn't also recognize a rename if the source path is excluded
whereas jj does.
I don't think we need to copy the exact behavior of Git, so this patch just
moves matcher application to earlier stage. This change will help remove
collect_copied_sources().
The added get_copy_records() helper could be moved to jj_lib, but we'll probably
want a stream version of this function in library, and writing a stream adapter
isn't as simple as iterator.
In this patch, I use the number of adds<->removes alternation as a threshold,
which approximates the visual complexity of diff hunks. I don't think user can
choose the threshold intuitively, but we need a config knob to try out some.
I set `max-inline-alternation = 3` locally. 0 and 1 mean "disable inlining"
and "inline adds-only/removes-only lines" respectively.
I've added "diff.<format>" config namespace assuming "ui.diff" will be
reorganized as "ui.diff-formatter" or something. #3327
Some other metrics I've tried:
```
// Per-line alternation. This also works well, but can't measure complexity of
// changes across lines.
fn count_max_diff_alternation_per_line(diff_lines: &[DiffLine]) -> usize {
diff_lines
.iter()
.map(|line| {
let sides = line.hunks.iter().map(|&(side, _)| side);
sides
.filter(|&side| side != DiffLineHunkSide::Both)
.dedup() // omit e.g. left->both->left
.count()
})
.max()
.unwrap_or(0)
}
// Per-line occupancy of changes. Large diffs don't always look complex.
fn max_diff_token_ratio_per_line(diff_lines: &[DiffLine]) -> f32 {
diff_lines
.iter()
.filter_map(|line| {
let [both_len, left_len, right_len] =
line.hunks.iter().fold([0, 0, 0], |mut acc, (side, data)| {
let index = match side {
DiffLineHunkSide::Both => 0,
DiffLineHunkSide::Left => 1,
DiffLineHunkSide::Right => 2,
};
acc[index] += data.len();
acc
});
// left/right-only change is readable
(left_len != 0 && right_len != 0).then(|| {
let diff_len = left_len + right_len;
let total_len = both_len + left_len + right_len;
(diff_len as f32) / (total_len as f32)
})
})
.reduce(f32::max)
.unwrap_or(0.0)
}
// Total occupancy of changes. Large diffs don't always look complex.
fn total_change_ratio(diff_lines: &[DiffLine]) -> f32 {
let (diff_len, total_len) = diff_lines
.iter()
.flat_map(|line| &line.hunks)
.fold((0, 0), |(diff_len, total_len), (side, data)| {
let l = data.len();
match side {
DiffLineHunkSide::Both => (diff_len, total_len + l),
DiffLineHunkSide::Left => (diff_len + l, total_len + l),
DiffLineHunkSide::Right => (diff_len + l, total_len + l),
}
});
(diff_len as f32) / (total_len as f32)
}
```
Though this is needed only for the last line, checking it for each line is
cheap. As I'm going to add another rendering style, the condition to pad "\n"
would become more complicated.
* We started with a tristate flag where:
- Auto - Maintain current behaviour. This edits if
the wc parent is not a head commit. Else, it will
create a new commit on the parent of the wc in
the direction of movement.
- Always - Always edit
- Never - Never edit, prefer the new+squash workflow.
However, consensus the review thread is that `auto` mode where we try to infer when to
switch to `edit mode`, should be removed. So `ui.movement.edit` is a boolean flag now.
- true: edit mode
- false: new+squash mode
* Also add a `--no-edit` flag as the explicit inverse of `--edit` and
ensure both flags take precedence over the config.
* Update tests that assumed edit mode inference, to specify `--edit` explicitly.
NOTE: #4302 was squashed into this commit, so see that closed PR for review history.
Part of #3947
[VSCodium](https://vscodium.com/) is a free/libre distribution of
Microsoft's Visual Studio Code editor, it's functionally more or less
the same, but distributed under a FOSS license, unlike VS Code.
This adds VSCodium as a merge tool.
I plan to provide a richer version of `TreeDiffEntry` with copy info
(and to make `TreeDiffEntry` itself "poorer"). Most callers want to
know about copies/renames, but at least working copy implementations
probably don't. This patch adds separate `diff_stream()` and
`diff_stream_with_copies()` so we can provide the simpler interface
for callers that don't need copy info.
The tree-level conflicts have worked well in practice and we don't
want to allow users to use legacy trees for new commits. We don't
really support legacy trees very well since 0590f8bece anyway.
* Derive a bunch of standard and useful traits for `movement_util::Direction`
as it is a simple type. Importantly `Copy`.
* Return `&'static str` from Direction.cmd()
* Refactor out `MovementArgs` to reduce the number of arguments
to `movement_util::move_to_commit`.
* Implement `From<&NextArgs/&PrevArgs>` for MovementArgs
Part of #3947
This allows us to select rendering function hunk by hunk. For example, a hunk
with lots of small changes could be rendered without interleaving left/right
words. Another good thing is that context line handling can be simplified as
the whole context hunk is available.
I'm going to split color-words diffs to by_line() and by_word() stages.
Perhaps, Diff::default_refinement() can be removed once all non-test callers
are migrated.
The code in both cli/src/commands/{next,prev}.rs is identical except
for the direction of movement. This commit pull the parts that make
sense out into cli/src/movement_util.rs so it's easier to see the
differences.
Part of #3947
Add gratuitous `jj log` output to more points in the tests.
This makes it easier to understand the intended changes
by literally visualizing the commit tree we are after each movement.
This is at least useful for me since I find the new+squash workflow
confusing.
Test behaviour is not changed.
Part of #3947
I'm thinking of adding some heuristics to render hunks containing lots of
small word changes differently, in a similar manner to the unified diffs. This
patch might help add some pre/post-processing at consumer.
files::diff() is inlined to caller to get around 'self borrowing.
When rebasing a new child commit on top of the moved commit(s), the
order of the new child commit's parent commits is now correctly
preserved if the original parent commit is now a parent of the moved
commit(s).
Closes#3969.
I think they were adding too much noise to commit diffs. Only the tests
focused on skipping rebasing will include the commit and change IDs,
other tests will omit them.
* Add `builtin_immutable_heads()` in the `revsets.toml`.
* Redefine `immutable_heads()` in terms of `builtin_immutable_heads()`
* Warn if user redefines `builtin_immutable_heads()`, `mutable()` or
`immutable()`.
* Update module constant in revset_util.rs from BUILTIN_IMMUTABLE_HEADS
to USER_IMMUTABLE_HEADS to avoid confusion since it points at
`immutable_heads()` **and** we now have a revset-alias
literally named `builtin_immutable_heads()`.
* Add unittest
* Update CHANGELOG
* Update documentation.
Fixes: #4162
- add support for copy tracking to `diff --stat`
- switch `--summary` to match git's output more closely
- rework `show_diff_summary` signature to be more consistent
Add home directory expansion for SSH key filepaths. This allows the
`signing.key` configuration value to work more universally across both
Linux and macOS without requiring an absolute path.
This moved and renamed the previous `expand_git_path` function to a more
generic location, and the prior use was updated accordingly.
- force each diff command to explicitly enable copy tracking
- enable copy tracking in diff_summary
- post-process for diff iterator
- post-process for diff stream
- update changelog
- use a single commit instead of an array of them. This simplifies the
implementation. A higher level api can wrap this when an array of
commits is desired and those semantics are figured out.
- since this API is directly 1-1 on parents, there are no conflicts
- if we introduce a higher level API that handles lists of commits, we
may need to restore the conflict/resolved distinction, but for now
simplify
The "git" command appears to chdir() to the --work-tree directory first, then
read() the core.excludesFile file. There's no manual relative path resolution
in "git".
Fixes#4222
Since "op abandon" just rewrites DAG, it works no matter if the heads are
merged or not. This change will help crash recovery. "op abandon
--at-op=<one-of-the-heads>" can't be used because ancestor operations would be
preserved by the other head.
Suppose a squash node in obslog is analogous to a merge in revisions log, it
makes sense to show diffs from auto-merge (or auto-squash) parents. This
basically means a non-partial squash node no longer shows diffs.
This also fixes missing diffs at the root predecessors if there were.
As the doc comment states, this function only supports change of "a single
added and removed commit", "only a single added", or "single removed commit."
It doesn't make sense to show diffs from the parent commit ignoring multiple
removed commits for example.
I think it might be nice to have this in the upcoming release, but I'd
like to warn people that their changes will be lost if they aren't
careful, and to not rely on the syntax being fixed just yet.
This partially reverts 543036c753 "cli: run 'op log' without loading repo or
merging concurrent ops." User can now get around the issue by --at-op=@
--ignore-working-copy.
The idea is that --at-op specifies a certain operation, so --at-op=@ can be
interpreted as the option to select _the_ known head operation. This helps
eliminate special cases from "op log" which doesn't snapshot nor merge
concurrent ops.
Author dates and committer dates can be filtered like so:
committer_date(before:"1 hour ago") # more than 1 hour ago
committer_date(after:"1 hour ago") # 1 hour ago or less
A date range can be created by combining revsets. For example, to see any
revisions committed yesterday:
committer_date(after:"yesterday") & committer_date(before:"today")
To avoid always printing the rebase instructions to fix a conflict
even when a child commit to fix the conflict already exists, implement
the following:
* If working commit has conflicts:
* Continue printing the same message we print today.
* If working commit has no conflicts:
* If any parent has conflicts, we print: "Conflict in parent is resolved in working copy".
Also explicitly not printing the "conflicting parent" here, since a merge commit
could have conflict in multiple parents.
* If no parent has any conflicts: exit quietly.
* Update unittests for conflict hinting update.
* Update CHANGELOG
It's not so important, but this removes duplicated "diff" labels from template
output. Perhaps, this also fixes "diff access-denied" label in file-by-file
external diffs.
The inner show_*() functions no longer add "diff" labels, but that's okay
because all CLI callers (except for the templater) use DiffRenderer.
The added test shows the "diff" label is repeated because of auto-labeling of
templater. The original "--color=always" test is also kept to ensure that color
sequences are unchanged even if we remove one of the "diff" labels.
The width parameter is mandatory so it wouldn't fall back to ui.term_width() by
mistake. The API is getting messy and we might want to extract some parameters
to separate struct.
Fixes#4158
Suppose cmd_root() will gain options to print other kind of root paths, it's
probably simpler to inline path handling there instead of turning cmd_root()
into a dispatcher function. If cmd_root() remains with the current form, maybe
we can add aliases.root = ["workspace", "root"] instead.
Users may try to run `jj workspace add <name>` without specifying a
path, which results in the workspace being created in the current
directory. This can be confusing, since the workspace contents will also
be snapshotted in the original workspace if it is not sparse. Adding a
warning should reduce confusion in this case.
The high level changes include:
- Reworking `fix_file_ids()` to loop over multiple candidate tools per file,
piping file content between them. Only the final file content is written to
the store, and content is no longer read for changed files that don't match
any of the configured patterns.
- New struct `ToolsConfig` to represent the parsed/validated configuration.
- New function `get_tools_config()` to create a `ToolsConfig` from a `Config`.
- New tests; the only old behavior that has changed is that we don't require
`fix.tool-command` if `fix.tools` defines one or more tools. The general
approach to validating the config is to fail early if anything is weird.
Co-Authored-By: Josh Steadmon <steadmon@google.com>
This allows tests to easily distinguish the effects of multiple formatters that
could be applied during a single `jj fix` command, which is helpful for testing
the feature for configuring multiple formatters on potentially overlapping
filesets.
Uppercase is a counterpart to lowercase. Append exposes the number of changes
and their order of execution in the file content, which provides a terse way of
writing test expectations.
This implements a building block of "signed-off-by line" #1399 and "commit
--verbose" #1946. We'll probably need an easy way to customize the diff part,
but I'm not sure if it can be as simple as a template alias function. User
might want to embed diffs without "JJ: " prefixes?
Perhaps, we can deprecate "ui.default-description", but it's not addressed in
this patch. It could be replaced with "default_description" template alias,
but we might want to configure default per command. Suppose we add a default
"backout_description" template, it would have to be rendered against the
source commit, not the newly-created backout commit.
The template key is named as "draft_commit_description" because it is the
template to generate an editor template. "templates.commit_description_template"
sounds a bit odd.
There's one minor behavior change: the default description is now terminated
by "\n".
Closes#1354
description_template_for_*() functions will be merged soon.
We don't need to set the default description to the second commit of the split
because its description should be kept empty if it was.
This could be handled by description_template_for_describe(), but I think it's
better to do mutation by caller. I also think commands like "backout" or "new"
can do a similar thing to generate a default commit description from the source
or merge parent commits.
This will help consolidate description_template_for_*() functions and port them
to templater. This change also means the committer (and operation start)
timestamp is set earlier. I think that's good.
This is part of migrating away from legacy trees (with path-level
conflicts). I can't think of any practical impact (we already compare
the tree ids equal).
This basically reverts 20eb9ecec1 "git: don't abandon HEAD commit when it
loses a branch." I think the new behavior is more consistent because the Git
HEAD is equivalent to @- in jj, so it shouldn't be considered a named ref.
Note that we've made old HEAD branch not considered at 92cfffd843 "git: on
external HEAD move, do not abandon old branch."
#4108
It's wrong to deduce loaded_at_head from command-line arguments if the repo was
loaded at an arbitrary operation. Instead, the caller should specify whether
the working copy state is synchronized with the repo view.
I think .for_loaded_repo(ui, workspace, repo, true) would be bad for
readability, so I added separate functions. I'm not happy with the name
.for_temporary_repo(), but it seems okay for the current call sites.
--at-op should be invalid on repo initialization. "init --ignore-working-copy"
could be supported by using working_copy.reset(), but I don't think it's
worth the effort. If the working directory is empty, --ignore-working-copy
is meaningless, and if the directory is not empty, the user would probably
want to do snapshot at some point.
I'm going to fix misuse of CommandHelper::for_loaded_repo(), which expects
that the given repo respects the --at-operation option.
I don't think all of the added tests are useful, but "clone
--ignore-working-copy" might be legit as a replacement for bare repos.
The user can define the setting `git.private-commits` as they desire. For
example:
git.private-commits = 'description(glob:"wip:*")'
If any commits are in this revset, then the push is aborted.
If a commit would be private but already exists on the remote, then it does
not block pushes, nor do its descendents block pushes unless they are also
contained in `git.private-commits`.
Closes#3376
The user probably would expect the path to be relative to their current
directory rather than the workspace root. For instance, if the user is
in a child directory and runs `jj workspace add ../../name`, then they
might be surprised if we printed "../name" instead of "../../name".
These functions mirror the ones in WorkspaceCommandHelper. I'm not sure if all
of them will have to be public, but parse_commit_template() might be useful in
order to implement description template.
Prevents a warning from being printed when renaming branches in a
colocated repo, since git tracking branches were being considered as
remote tracking branches.
It's inconvenient that we have to quote glob patterns as 'glob:"*.rs"'. Suppose
filesets are usually specified in shell, it's better to allow unquoted strings
if possible. This change also means we'll probably abandon #2101 "make the
parsing of string arguments stricter."
Note that we can no longer introduce ? operator or [] subscript syntax in
filesets.
Closes#4053
Now that `jj move` does not accept `-r` (since it has `--from` and `--to`), `jj set --to` seems more useful
than `jj set -r`. `create --to` is also added for ease of switching
between `branch create` and `branch move`.