Commit graph

4385 commits

Author SHA1 Message Date
Yuya Nishihara
cb5b17112d revset: remove unused diagnostics argument from expect_literal() 2025-06-27 01:29:11 +00:00
Yuya Nishihara
fda95e55c2 revset: reimplement alias stack helper without using recursion
The new implementation is backported from the (unsent) json({key => value})
object template patches. This function serves the same role as
expect_expression_with(), so all callers are migrated to catch_aliases().

I think the recursive version was harder to follow, and I'll remove
expect_<construct>_with() functions for the same reason.
2025-06-27 01:29:11 +00:00
Yuya Nishihara
d62052938c revset: normalize and simplify type error messages
These messages were inconsistent, and I think "expression of" is redundant.
2025-06-27 01:29:11 +00:00
Yuya Nishihara
f2b024caa3 revset: fix alias stack of pattern/literal parse error
The root error should be attached to the alias-unwrapped span.
2025-06-27 01:29:11 +00:00
Yuya Nishihara
a864eec073 templater: make Operation object serializable 2025-06-27 01:05:59 +00:00
Yuya Nishihara
a3b0301770 op_store: pack start/end_time into TimestampRange struct
This will make JSON version of operation data look similar to the template type.
2025-06-27 01:05:59 +00:00
Yuya Nishihara
a8e5a5b328 op_store: move TimestampRange type from templater 2025-06-27 01:05:59 +00:00
Yuya Nishihara
5bfa79235d templater: make Commit object serializable 2025-06-27 01:05:47 +00:00
Yuya Nishihara
f5424facb0 merge: derive serde::Serialize for RefTarget 2025-06-27 01:05:47 +00:00
Yuya Nishihara
59fda27d6c backend: serialize Signature as struct
We usually use "kebab-case" for config objects, but I'm not sure if that's a
good choice for data structs generally. Template keywords and methods are
"snake_case", and consumers of JSON outputs would also expect it (unless the
implementation language is lisp-like.) So I didn't add rename("kebab-case").
This doesn't matter for the Signature type, though.
2025-06-27 00:48:44 +00:00
Yuya Nishihara
d5a7658122 backend: serialize Timestamp through chrono::DateTime 2025-06-27 00:48:44 +00:00
Yuya Nishihara
7748a7eab0 backend: extract Timestamp::to_datetime() function
This will be used as the default serialization format. Maybe we can add a
similar function when we switch to jiff.
2025-06-27 00:48:44 +00:00
Yuya Nishihara
1d4813abb0 repo_path: derive serde::Serialize 2025-06-27 00:48:44 +00:00
Yuya Nishihara
5131b5c0c6 object_id: implement serde::Serialize
We only need the human-readable format in templater, but this patch also adds
bytes format for completeness.
2025-06-27 00:48:44 +00:00
Yuya Nishihara
d5e3ee8819 cli: op diff: use predecessors information to pair old/new commits
This should produce better results at squash/split operations. Since "op diff"
targets can be flipped, this patch implements basic handling of reversed
predecessors graph. It should also work for sibling operations so long as there
are no multiple greatest common ancestors.

resolve_transitive_edges() takes additional "start" parameter. It might be
useful in order to omit transitive edges in evolution log. Since
walk_predecessors() usually tracks a single commit, it would be wasteful to
build a fully-resolved predecessors graph.
2025-06-27 00:48:06 +00:00
Yuya Nishihara
4783f64975 index, op_walk: leverage roots..heads operation range iterator 2025-06-27 00:48:06 +00:00
Yuya Nishihara
8f9e2b1697 op_walk: add roots..heads ancestors range iterator
There are a couple of hand-rolled implementations of roots..heads query, and I'm
going to add one more. It's wasteful to test these one by one.

The added iterator will have to scan roots::heads range eagerly instead of
::roots. I think this is good for typical use cases where roots are closer to
heads than the root().
2025-06-27 00:48:06 +00:00
Yuya Nishihara
d9ff53f4c7 op_walk: remove sorting of starting ops given to walk_ancestors()
At merge point, ancestor operations are visited in order of the parents list, so
I don't think head_ops should be sorted either. walk_ancestors([op]) should be
identical to [op] + walk_ancestors(op.parents()).
2025-06-27 00:48:06 +00:00
Yuya Nishihara
937b12deae dag_walk: extract function that executes one step of topo_order_reverse_lazy()
This will be the initial step of operation range iterator. I think it can also
be a building block of various filtered operation iterators (such as operations
filtered by bookmark changes.)

topo_order_reverse_chunked() returns Result<[T], E> instead of [Result<T, E>]
because the former was easier to implement. I'm not sure which one would be
generally useful, but I think the caller would do some post processing, so
getting [T] might be handy.
2025-06-27 00:48:06 +00:00
Yuya Nishihara
ff1a4cda6c test_signing_backend: import modules from crate::
Follows up 638f1234 "sign: move TestSigningBackend to lib".
2025-06-26 12:53:39 +00:00
Yuya Nishihara
a8772e0561 git_backend: extract function that decodes "jj::trees" header value
Inlined the key lookup code instead, which seems more consistent with the other
header handling.
2025-06-25 14:52:22 +00:00
Yuya Nishihara
141d1b635f git_backend: use CHANGE_ID_COMMIT_HEADER constant consistently
Since .extra_headers().find() expects &str, I've changed these constants to
&str.
2025-06-25 14:52:22 +00:00
Yuya Nishihara
520ca5c3f0 git_backend: extract function that generates change id from Git commit
While the implementation is trivial, it's a bit noisy to have large comment
block around method chains.
2025-06-25 14:52:22 +00:00
Ilya Grigoriev
6323764f69 lib: add rustversion, turn off a clippy lint in nightly in tests
This turns off the `clippy::cloned_ref_to_slice_refs` lint in some tests
and fixes it in others, for Rust 1.89+. This seems to make `cargo clippy
--workspace --all-targets --all-features` work in stable, beta, and
nightly (1.89).

This depends on the `rustversion` crate. Other than that, it's based on
Austin's https://github.com/jj-vcs/jj/pull/6705.

Co-authored-by:  Austin Seipp <aseipp@pobox.com>
2025-06-24 01:01:25 +00:00
Scott Taylor
754ef34818 revset: optimize empty list of commits as none()
This can be helpful for expressions like `bookmarks(pattern)`, which
returns an empty list of commits when the pattern doesn't match
anything.
2025-06-23 11:42:46 +00:00
Yuya Nishihara
bc83eb0a48 id_prefix: check conflicting bookmarks/tags when calculating shortest length
Fixes #2416
2025-06-23 00:27:01 +00:00
Scott Taylor
8afd0dd8ee revset: optimize ~all() and ~none()
Now that `all()` contains all referenced commits, we can optimize
`~all()` to `none()` and `~none()` to `all()`.
2025-06-22 02:50:01 +00:00
Yuya Nishihara
d3e5f9d827 tests: add proptest that compares optimized revset output
Only the expression trees are generated, which I just needed to copy the doc
example. The commit DAG could also be generated, but doing that would mean the
commits would have to be created within a proptest loop, I think, and would make
shrinking process super slow.
2025-06-22 01:55:50 +00:00
Yuya Nishihara
68c23c3ccc revset: remove SymbolResolver abstraction, rename DefaultSymbolResolver
Since the extension point is now provided by SymbolResolverExtension, this
abstraction isn't useful anymore. This change will probably help implement
commit/change_id(pattern) functions. If the resolver were abstract type, we
would have to add resolve_commit/change_id() methods separately.
2025-06-22 00:48:10 +00:00
Yuya Nishihara
3451f468bb revset: remove FailingSymbolResolver, use DefaultSymbolResolver in tests
Since UserRevsetExpression is now a different type than the resolved one, we no
longer have to resolve internal revset by using the failing resolver.
2025-06-22 00:48:10 +00:00
Yuya Nishihara
360023bcff tests: extract helper to create DefaultSymbolResolver without extensions
I also removed resolve_symbol_with_extensions() as there are no callers who pass
in non-empty extensions.
2025-06-22 00:48:10 +00:00
Martin von Zweigbergk
3a92e7d203 id_prefix: if a prefix is ambiguous in small set, don't try full set
If a commit or change prefix is already ambiguous in the small
disambiguation set, it should be ambiguous in the full repo too, so we
should not have to attempt the lookup in the full repo.

There's the corner case that the disambiguation set contains a hidden
commit, making the look ambiguous in the disambiguation set but
unambiguous among all visible commits. I don't think we need to worry
about this case. Users should not configure such disambiguation sets,
and even if they do, it will just result in a graceful failure.
2025-06-19 11:27:29 +00:00
Yuya Nishihara
24493e8e16 revset: enable substitution rule 'x | all() -> all()'
We no longer have the 'hidden_id | all()' problem.
2025-06-19 11:21:49 +00:00
Yuya Nishihara
123088b8e4 revset: expand all() set to include all referenced commits
The semantics is similar to experimental.directaccess=true in Mercurial. Hidden
revisions and their ancestors become temporarily available. This means all() is
not exactly the same as ::visible_heads(). The latter never includes hidden
revisions.

We could instead transform all() to (all() | referenced_commits). However, this
wouldn't work if expressions like ::hidden are intersected/united with filters,
all(), etc.

Fixes #5871
2025-06-19 11:21:49 +00:00
Yuya Nishihara
ade2aea015 revset: add stub expression node for implicit visible_heads() 2025-06-19 11:21:49 +00:00
Yuya Nishihara
31264f2bda revset: add flag to stop tree substitution without rewriting, add infallible fn 2025-06-19 11:21:49 +00:00
Jonas Greitemann
94ba95bb4c merge-tools builtin: add property-based testing
This adds the proptest crate for property-based testing as well as the
proptest-state-machine crate as direct dev dependencies of jj-cli and as
dependencies of the internal testutils crate. 

Within testutils, a `proptest` module provides a reference state
machine which models the working copy as a map from path to `DirEntry`.
Directories are not represented explicitly, but are implicit in the
ancestors of entries.

The possible transitions of this state machine are for now limited to
the creation of new files (including replacements of existing files
or directories) and a `Commit` operation which the SUT can use to
snapshot a reference state. Additional transitions (moving files,
modifying file contents incrementally, ...) and states (symlinks,
submodules, conflicts, ...) may be added in the future.

This reference state machine is then applied to the builtin merge-tool's
test suite:
- The initial state is always an empty root directory.
- The `Commit` operation creates `MergedTree` from the current state.
- Each step of the way, the same test logic as in the manual
  `test_edit_diff_builtin*` tests is run to check that splitting off
  none or all of the changes results in the left or right tree,
  respectively. The "right" tree corresponds to the current state,
  whereas the "left" tree refers to the last "committed" tree.

Co-authored-by: Waleed Khan <me@waleedkhan.name>
2025-06-18 20:45:56 +00:00
Jonas Greitemann
da2835b86c merge-tools builtin: introduce assert_tree_eq!
This macro in the style of `assert_eq!` compares two trees based
on their `MergedTreeId`s. In case they do not compare equal, the
corresponding trees are dumped in the panic message.

Like `assert_eq!`, the macro accepts a custom format string which will
be included in the panic message.
2025-06-18 20:45:56 +00:00
Martin von Zweigbergk
c43ca3c07b address new mismatched_lifetime_syntaxes Clippy lint 2025-06-17 07:40:05 +00:00
Cyril Plisko
6a6d74710c Use Path::display().to_string() instead of Path::to_str()
Convert Path to text using infallible `display().to_string()` instead of
unwrapping `to_str()`
2025-06-16 08:41:39 +00:00
Yuya Nishihara
5edbccd8ff revset: move filter intersection right as much as possible
The previous patch works, but it seemed weird that the result depends on the
order. For example, `f1 & f2 & s` is rewritten to `s & filter(f1 & f2)`, whereas
`s & f1 & f2` was to `(s & f1) & f2`. This patch normalizes them.

This partially reverts the change in 9a7ca8edb5 "revset: add optimization pass
to flatten intersections." Thanks to the flatten_intersections() step, we no
longer need to process compound right-hand-side expressions.
2025-06-15 14:57:50 +00:00
Yuya Nishihara
03ae0296dd revset: mark f1&f2 as filter to fix check for nested filter intersection
This follows up 0a9ab49dc5 "revset: do not reinterpret set&filter intersection
as filter." As Scott discovered, 0a9ab49dc5 doesn't handle nested intersection
of filters.

With this patch, `f1&f2` is evaluated as `filter_within(all(), f1&f2)` instead
of `filter_within(filter_within(all(), f1), f2)`. The evaluation cost should be
the same as we now have ResolvedPredicateExpression::Intersection node.
2025-06-15 14:57:50 +00:00
Yuya Nishihara
40f25d99c0 revset: clean up redundant AsFilter nodes
Inner AsFilter nodes are harmless, but they are noisy in test and debug outputs.
Let's make them bubble up as we rewrite the child expression trees.
2025-06-15 14:57:50 +00:00
Yuya Nishihara
43668934df revset: add intersection node to backend filter predicates
This was originally suggested by Scott Taylor in #6679. I thought filter
intersections would have been externalized to the set intersections, but that
was wrong. Since union of filter intersections (e.g. `(f1 | f2 & f3) & s4`) is
evaluated as `filter_within(s4, f1 | f2 & f3)`, it doesn't make sense to convert
`f2 & f3` back to set intersection `filter_within(all(), f2) & f3`.
2025-06-15 14:45:21 +00:00
Yuya Nishihara
b0f3c98175 cli: evolog: group graph nodes topologically
The output looks better if the graph had long parallel history. "--limit=N" is
applied after sorting for consistency with "jj log". The doc also mentions that.

Since TopoGroupedGraphIterator emits predecessors in reverse order at squash
point, we no longer need to tweak the visiting order by walk_predecessors().
2025-06-15 01:59:33 +00:00
Yuya Nishihara
559df2cf5c graph: extend grouping iterator to support generic GraphNode<N, ID> types
This will allow us to sort evolution graph.
2025-06-15 01:59:33 +00:00
Yuya Nishihara
9efaa12111 graph: store original node item in grouping iterator without destructuring
This may be redundant if ID == N, but the extra .clone() cost shouldn't
matter. ID should be cheap to clone.
2025-06-15 01:59:33 +00:00
Yuya Nishihara
aa0284c53d evolution: include CommitId instead of OperationId in cycle detected error
This will help propagate error from accumulated predecessors graph. "jj op diff"
will collect predecessors from the operation range, and resolve transitive
predecessors within them. To translate CommitId back to OperationId, we would
have to inspect each operation again. Since this error should be caused only by
data corruption or implementation bug, the error content isn't so important.
2025-06-14 23:54:12 +00:00
Yuya Nishihara
5d5962b67e evolution: fix cycle detection and graph sorting of transitive predecessors
This shouldn't usually happen, but we can craft unsimplified evolution history
by rewriting hidden commits, for example.
2025-06-14 23:54:12 +00:00
Yuya Nishihara
f558fbc7cf evolution: split visit_op() loop to collect transitive nodes first
This helps handle non-trivial transitive graphs.
2025-06-14 23:54:12 +00:00