`Store::tree_builder()` returns a `TreeBuilder`. Almost all callers
should be using the `MergedTreeBuilder` these days. This patch
therefore removes `tree_builder()` to reduce the risk of accidentally
using it.
It's surprising that a symbol expression may be resolved to multiple revisions,
and that's one of the reason we require all: modifier in some places. Let's make
a symbol resolution fail in that case so we can deprecate the all: syntax.
The new error hints are a bit less informative, but I don't want to implement
ad-hoc formatting for resolve_some_revsets_default_single(). The user will have
to review the graph anyway in order to resolve divergence/conflicts.
Closes#5632
For most callers, the special sorting of directories before paths for
directory->file transitions is not needed. This patch changes
`diff_stream()` to not do that, and instead adds a new method
specifically for that behavior. Only `local_working_copy` uses it.
This makes `path_value()` and related functions more consistent with
how the merged tree is presented to the user in the working copy and
when showing a diff.
In this implementation, we assume that predecessor commits created by old jj are
reachable from at least one of the historical views. However, there are a couple
of commands which create transitive predecessors. For example, "jj squash" into
grandparent will rebase a rewritten source, so the pre-rebase source commit
won't be visible to any views. To work around the problem, all immediate
predecessors of historically visible commits are also preserved.
Note that this change should be considered forward-incompatible change. The
stored commits may have unreachable predecessors once we run "jj op abandon &&
jj util gc".
WalkPredecessors::flush_commits() doesn't need to guard against unreachable
commits. I was wondering whether values (or old ids) of op.commit_predecessors
map should be preserved, and I decided to keep both keys and values. It's nice
that we can get rid of index.has_id() calls when we drop support for legacy
commit.predecessors.
Since we have "reverse hex" functions, it's easy to implement the same set of
functions for "forward hex". I believe our implementation is slower than
highly-optimized versions provided by e.g. faster-hex, but we don't use hex
encoding/decoding where the performance matters.
Basically, these functions work in the same way as bookmarks()/tags(). They
restrict the namespace to search the specified symbol, and unmatched symbol
isn't an error. One major difference is that ambiguous prefix triggers an error.
That's because ambiguous prefix should logically select all matching entries,
whereas the underlying functions don't provide this behavior. It's also unclear
whether we would want to get all matching commits by commit_id(prefix:'').
#5632
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.
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().
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>
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.
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.
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
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`.
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().
Resolves#6656.
Currently, evaluating an expression like `heads(::@ ~ empty())` requires
iterating over all commits in `::@` and checking the predicate against
each commit, which can be very slow. This PR implements an optimization
to convert expressions to the form `heads(x..y & filter)` when it is
possible to do so efficiently. Expressions of this form can be evaluated
using a specialized algorithm which terminates iteration early without
checking all commits.
Benchmark results on Git repo:
```
revsets/heads(author(peff)) 97.248% improvement
revsets/heads(::v2.40.0) 98.467% improvement
```
This can happen if we remove predecessors from the commit object, and if
committer timestamp wasn't updated (like in some jj-lib tests.) It's not wrong
for the store to "overwrite" existing objects, but the repo expects
new/rewritten commits have unique ids. If they didn't, cycle would be created in
mut_repo.commit_predecessors/parent_mapping.
This patch adds a `TreeValue::File::copy_id` field. The copy ids are
always empty for now. I preserved the copy id where it was easy to do
so, plus in a few non-trivial cases. In other places, however, I made
the code use a new copy id. I added a `CopyId::placeholder()`
function for creating a new copy id where we need one. We should
eventually fix all callers to either preserve an existing copy or to
generate a new one.
The original form of `create_tree()` is limited to creating (valid
UTF-8) text files but cannot create binary files, executable
files, or symlinks. Dedicated helpers like `write_executable_file()` or
`write_symlink()` partially compensated for this, but required manually
assembling the tree in the test code.
This commit introduces `TestTreeBuilder` which provides an API to
successively add entries to a tree which can represent all of the above.
`TestTreeBuilder` can then create either a single `Tree`, or a resolved
`MergedTree`.
In addition to using `TestTreeBuilder` directly, `create_tree_with()`
and `create_single_tree_with()` accept a closure which receives a
`TestTreeBuilder`. This allows test code to quickly describe the tree
without requiring the a named builder at caller scope. Riffing off
the familiar function names should help in discovering the new builder
facilities. However, it is completely possible to use `TestTreeBuilder`
directly, if preferred.
Just for completeness. Case-insensitive search can be achieved by (?i), but doc
and hint suggest that the -i suffix works for all pattern types.
Closes#6653
This is slower than traversing commit.predecessor_ids, but allows us to show
associated operations alongside the commits. Since operations are (mostly)
traversed in chronological order, the evolution graph is also emitted in that
order.
Operation ids in test_op_abandon*() are changed because reparented operations
now preserve the predecessors mapping.
Since this patch will affect whether "jj evolog" can show the associated
operations, I added a changelog entry. It's still a bit vague, but I think we
can add a more detailed explanation later.
There are two major goals:
1. garbage-collect predecessor commits referenced by immutable commits.
2. show operations alongside predecessors in "jj evolog".
The predecessors field will be removed from the Commit object. Maybe we can
also remove (writer side of) the extras table from GitBackend.
"jj evolog" will traverse the operation history to build an evolution
graph. This will be slower than the current implementation, but it seems
tolerable for mid-size repository stored in a local disk.
(when the page cache is warm)
% time jj op log -T'"."' --no-graph | wc -c
50459
jj op log -T'"."' --no-graph 0.12s user 0.31s system 103% cpu 0.418 total
Suppose we're interested in recent modifications, the traversal can often be
terminate early. I also have an idea for indexing originating operations.
https://github.com/jj-vcs/jj/pull/6405
For old operations which didn't record predecessors, "jj evolog" will fall back
to commit.predecessor_ids(). That's why commit_predecessors is Option<_>.
We don't want to derive `PartialEq` on `OpsetResolutionError` because we may need an
`Other` variant in the future that wraps a type without a `PartialEq` implementation.
Pattern-matching is used instead to assert the contents.
This serves a similar purpose to Git's patch ID mechanism, however it is
slightly different in that it only compares commits which have the same
change ID as each other (divergent changes), and it does a full
comparison of the commits to see if they would have identical trees if
rebased onto the same parents. Since most changes aren't divergent, I
believe this should have a negligible performance cost in most cases.
I think skipping these commits by default makes sense for `jj rebase`,
since usually this will be a helpful behavior for the user. With this
behavior, a safe first step when encountering divergent changes would be
to rebase one branch on top of the other, since that will abandon any
divergent changes that have identical contents to existing commits,
leaving behind any non-trivial divergent changes for the user to resolve
manually.
Apparently `gpg-agent` will fail to start if the `--homedir` path is too
long:
```
> gpg-agent --homedir=/private/tmp/nix-build-jujutsu-0.29.0-unstable-8769c5c.drv-0/jj-gpg-signing-test-4I0aI0/ --daemon
gpg-agent[6617]: directory '/private/tmp/nix-build-jujutsu-0.29.0-unstable-8769c5c.drv-0/jj-gpg-signing-test-4I0aI0/private-keys-v1.d' created
gpg-agent[6617]: socket name '/private/tmp/nix-build-jujutsu-0.29.0-unstable-8769c5c.drv-0/jj-gpg-signing-test-4I0aI0/S.gpg-agent.extra' is too long
```
I only discovered this after reading a comment on StackOverflow [0].
[0]: https://superuser.com/questions/1087554/when-adding-a-gpg-key-with-homedir-parameter-error#comment1684440_1130507
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.