Commit graph

514 commits

Author SHA1 Message Date
Ilya Grigoriev
738f99ddf1 test_revset.rs: disable nightly clippy false-positive warning
I opened a bug for the clippy error:
https://github.com/frondeus/test-case/issues/122
2023-06-11 13:11:01 -07:00
Waleed Khan
23a4124d20 feat(revset): suggest similar branch names 2023-06-05 11:11:17 -05:00
Martin von Zweigbergk
29b676f24f store: do conversion to/from backend::Conflict
We now convert to/from `backend::Conflict` right before/after calling
the `Store` methods, so we can simplify by having the `Store` do the
conversion.
2023-06-04 06:48:34 -07:00
Martin von Zweigbergk
c378503991 conflicts: replace remaining uses of backend::Conflict 2023-06-04 06:48:34 -07:00
Martin von Zweigbergk
f4499aa65e conflicts: fix bug when modifying modify/delete conflicts
Currently, if the user modifies a modify/delete conflict, we always
consider the result resolved. That happens because we materialize the
missing side of the conflict as an empty string but when we parse the
conflict, we expect only the number of sides in the input
conflict. For example, if the input is a regular modify/delete
conflict with one remove and one add, the materialized markers will
have one remove and two adds (one of them empty), but when we try to
parse it, we expect one remove and only one add. When we fail to parse
it, we consider it resolved.

This commit fixes the bug by using
`conflicts::Conflict<Option<TreeValue>>` and keeping track of which
sides were supposed to be empty. We could have fixed the bug without
switching to `conflicts::Conflict`, but we want to switch anyway, and
the fix happens naturally when switching.
2023-06-04 06:48:34 -07:00
Yuya Nishihara
78a64edb22 dag_walk: rename bfs() to dfs() because it's depth-first
No callers appear to rely on traversal order, so let's just fix the function
name. Maybe it was a typo.
2023-06-04 11:47:49 +09:00
Yuya Nishihara
3ba544414c dag_walk: unbox bfs() callback, use iter::from_fn() to implement iterator
I just wanted to remove syntactic noise from callers. iter::from_fn() helps
to avoid declaring struct with lots of type parameters.
2023-06-04 11:47:49 +09:00
Yuya Nishihara
3d449c55b7 tree_builder: do not omit file entry which was previously a directory 2023-06-01 09:38:06 +09:00
Yuya Nishihara
ae9f7aba52 tests: add test for file-directory-file transition
TreeBuilder fails to handle directory-file transition right now, and leaves
the dirty file as "clean".
2023-06-01 09:38:06 +09:00
Martin von Zweigbergk
fb17e6a50e revset: use different errors for ambiguous commit/change IDs
I made a typo and got something like this:

```
Error: Commit or change id prefix "wl" is ambiguous
```

Since we can tell commit ids from change ids these days, let's make
the error message say which kind of id it is. Changing that also kind
of forced me to make a special error for empty strings. Otherwise we
would have to arbitrarily say that an empty string is a commit id or
change id. A specific error message for empty strings seems helpful,
so that's probably for the better anyway.
2023-05-31 06:28:32 -07:00
Yuya Nishihara
b7b9b8c88e index: pass only CompositeIndex to default_revset_engine::evaluate() 2023-05-29 08:15:40 +09:00
Yuya Nishihara
cf5cb380bb index: implement Index for CompositeIndex
We can't get rid of the other "impl Index"es because .as_composite() must
return a real reference type. Maybe we could turn CompositeIndex into an
owned wrapper, but I don't know if that would be worth the effort.
2023-05-29 08:15:40 +09:00
Yuya Nishihara
5989bdf781 index: move Index::as_any() to MutableIndex, obtain CompositeIndex from there
It might sound scary to add public .mutable_index() accessor, but I think
it's okay because immutable MutableIndex reference has no more power than
Index.

This allows us to implement Index for lifetime-bound type such as
CompositeIndex<'_>.
2023-05-29 08:15:40 +09:00
Yuya Nishihara
93284a153f index: obtain CompositeIndex from ReadonlyIndexWrapper
I'll remove Index::as_any() so that Index can be implemented for reference
wrapper.
2023-05-29 08:15:40 +09:00
Yuya Nishihara
fb77c55268 index: use as_composite() to access to index stats
The idea is that .as_composite() is equivalent to .as_index(), but for the
implementation type. I'm going to add "impl Index for CompositeIndex" to
clean up index references passed to revset engine.
2023-05-29 08:15:40 +09:00
Martin von Zweigbergk
f838d083d3 tree: add test of merge of executable bit
We didn't seem to have any tests of this, so let's add one before I
change the implementation.
2023-05-24 22:00:38 -07:00
Yuya Nishihara
e24fe817c9 tests: invoke .walk_revs() through CompositeIndex
Prepares for removal of the index trait method.
2023-05-24 01:02:37 +09:00
Yuya Nishihara
b01614bbdd cleanup: leverage scoped thread in tests 2023-05-21 21:02:58 +09:00
Yuya Nishihara
38a7e7fd62 git_backend: on read_commit(), bulk-update extra metadata table of ancestors
Otherwise, "jj init --git-repo ." would create extra table files per commit,
and merge them.

I considered adding an explicit GitBackend method to be called from
git::import_refs(), but the call order matters. The method should be invoked
before calling store.get_commit(..) or mut_repo.add_head(..). Since commits
are likely to be loaded from the head, we can instead make read_commit()
import ancestor metadata at all.

Alternatively, we could make a Git commit hidden until it's inserted into
the extra table. It's rather big change, and I wouldn't like to do that
without thinking more thoroughly.
2023-05-21 08:29:00 +09:00
Yuya Nishihara
a9422460cb git_backend: ensure change id generated from git commit id never reassigned
Fixes #924
2023-05-20 15:53:23 +09:00
Yuya Nishihara
9aa72f6f1d git_backend: add lock to prevent racy change id assignments
My first attempt was to fix up corrupted index when merging, but it turned
out to be not easy because the self side may contain corrupted data. It's
also possible that two concurrent commit operations have exactly the same
view state (because change id isn't hashed into commit id), and only the
table heads diverge.

#924
2023-05-20 15:53:23 +09:00
Yuya Nishihara
3655da4f01 tests: add tests for concurrent git commit/change id assignment
Since non-Git metadata isn't hashed, we can't rely on the consistency
provided by content-addressed storage. The problem is also described in
https://github.com/martinvonz/jj/issues/3#issuecomment-947998487

#924
2023-05-20 15:53:23 +09:00
Ilya Grigoriev
714aff63e6 git.rs: properly abandon commits from moved/deleted branches on remote (#864)
This bug concerns the way `import_refs` that gets called by `fetch` computes
the heads that should be visible after the import.

Previously, the list of such heads was computed *before* local branches were
updated based on changes to the remote branches. So, commits that should have
been abandoned based on this update of the local branches weren't properly
abandoned.

Now, `import_refs` tracks the heads that need to be visible because of some ref
in a mapping keyed by the ref. If the ref moves or is deleted, the
corresponding heads are updated.

Fixes #864
2023-05-17 17:57:58 -07:00
Ilya Grigoriev
cf4a603eb4 Tests demonstrating a similar bug with moved rather than deleted branch 2023-05-17 17:57:58 -07:00
Ilya Grigoriev
a0ee2b0dbd lib/tests/test_git.rs: New test to demonstrate #864's root cause 2023-05-17 17:57:58 -07:00
Ilya Grigoriev
bda3d3e50b test_import_refs_reimport: very minor improvement to a test 2023-05-17 17:57:58 -07:00
Martin von Zweigbergk
87a925d736 git_backend: return timestamps for what was actually written
Now that we return the written commit from `write_commit()`, let's
make the timestamps match what was actually written, accounting for
the whole-second precision and the adjustment we do to avoid
collisions.
2023-05-12 15:20:44 -07:00
Martin von Zweigbergk
e7419e76a1 backend: replace git_repo() by as_any()
This has several advantages:

 * Makes it possible to downcast to non-Git custom backends (might be
   useful at Google, but we haven't needed it yet)

 * Lets us access more specific functionality on the `GitBackend`,
   making it possible to access the `git2::Repository` without
   creating a copy of it.

 * Removes the dependency on Git from the backend
2023-05-12 08:05:09 -07:00
Yuya Nishihara
f58beca760 revset: move resolve_symbol() to tests
It's no longer used in library code.
2023-05-12 21:31:29 +09:00
Martin von Zweigbergk
916b00c33e id_prefix: remove repo field from IdPrefixContext
By passing the repo as argument to the methods instead, we can remove
the `repo` field and the associated lifetime. Thanks to Yuya for the
suggestion.
2023-05-11 23:41:24 -07:00
Martin von Zweigbergk
6a4502cb5d prefixes: allow resolving shorter ids within a revset
In large repos, the unique prefixes can get somewhat long (~6 hex
digits seems typical in the Linux repo), which makes them less useful
for manually entering on the CLI. The user typically cares most about
a small set of commits, so it would be nice to give shorter unique ids
to those. That's what Mercurial enables with its
`experimental.revisions.disambiguatewithin` config. This commit
provides an implementation of that feature in `IdPrefixContext`.

In very large repos, it can also be slow to calculate the unique
prefixes, especially if it involves a request to a server. This
feature becomes much more important in such repos.
2023-05-11 23:41:24 -07:00
Martin von Zweigbergk
efd743339c revset: don't allow symbols in RevsetExpression::resolve()
When creating `RevsetExpression` programmatically, I think we should
use commit ids instead of symbols in the expression. This commit adds
a check for that by using a `SymbolResolver` that always errors
out.
2023-05-11 23:41:24 -07:00
Martin von Zweigbergk
99e9cd70d1 cli: make WorkspaceCommandHelper create SymbolResolver
I would eventually want the `SymbolResolver` to be customizable (in
custom `jj` binaries), so we want to make sure we always use the
customized version of it.

I left `RevsetExpression::resolve()` unchanged. I consider that to be
for programmatically created expressions.
2023-05-11 23:41:24 -07:00
Yuya Nishihara
92cfffd843 git: on external HEAD move, do not abandon old branch
The current behavior was introduced by 20eb9ecec1 "git: don't abandon
HEAD commit when it loses a branch." While the change made HEAD mutation
behavior more consistent with a plain ref operation, HEAD can also move on
checkout, and checkout shouldn't be considered a history rewriting operation.

I'm not saying the new behavior is always correct, but I think it's safer
than losing old HEAD branch. I also think this change will help if we want
to extract HEAD management function from git::import_refs().

Fixes #1042.
2023-05-11 10:15:31 +09:00
Yuya Nishihara
66d405fa5f git: add tests that simulate external checkout/amend in colocated repo
I'm going to change the behavior of _without_ref() case to mitigate #1042.
2023-05-11 10:15:31 +09:00
Benjamin Saunders
ccbb34fddb working_copy: introduce snapshot progress callback 2023-05-06 11:07:46 -07:00
Yuya Nishihara
837e8aa81a revset: add substitution rule for nested descendants/children
The substitution rule and tests are copied from ancestors/parents. The backend
logic will be reimplemented later. For now, it naively repeats children().
2023-04-24 20:45:13 +09:00
Martin von Zweigbergk
c60f14899a index: remove entry_by_id() from trait
It no longer needs to be on the `Index` trait, thereby removing the
last direct use of `IndexEntry` in the trait (it's still used
indirectly in `walk_revs()`).
2023-04-18 18:32:23 -07:00
Yuya Nishihara
5351371d51 revset: resolve visible heads prior to evaluation 2023-04-10 00:39:58 +09:00
Yuya Nishihara
0fcc13a6f4 revset: make resolve() return different type describing evaluation plan
New ResolvedExpression enum ensures that the evaluation engine doesn't have
to know the symbol resolution details. In this commit, I've moved Filter
and NotIn resolution to resolve_visibility(). Implicit All/VisibleHeads
resolution will be migrated later.

It's tempting to combine resolve_symbols() and resolve_visibility() to get
rid of panic!()s, but the resolution might have to be two passes to first
resolve&collect explicit commit ids, and then substitute "all()" with
"(:visible_heads())|commit_id|..". It's also possible to apply some tree
transformation after symbol resolution.
2023-04-10 00:39:58 +09:00
Yuya Nishihara
6c2525cb93 revset: add "resolve" method to RevsetExpression, always call it
I'll make the resolution stage mandatory, and have it return a "resolved"
type. RevsetExpression::evaluate() will be moved to the "resolved" type.
2023-04-10 00:39:58 +09:00
Yuya Nishihara
adfd52445b revset: reimplement children to not scan visible ancestors twice
It's slightly faster, and removes the use of RevsetExpression::descendants()
API.
2023-04-08 12:13:30 +09:00
Yuya Nishihara
85fb1f74c3 revset: for roots:heads, terminate ancestor lookup at min(roots) 2023-04-08 12:13:30 +09:00
Martin von Zweigbergk
24a512683b revset: add a revset function for finding commits with conflicts
This adds `conflict()` revset that selects commits with conflicts. We
may want to extend it later to consider only conflicts at certain
paths.
2023-04-06 16:46:21 -07:00
Martin von Zweigbergk
e1c57338a1 revset: split out no-args head() to visible_heads()
The `heads()` revset function with one argument is the counterpart to
`roots()`. Without arguments, it returns the visible heads in the
repo, i.e. `heads(all())`. The two use cases are quite different, and
I think it would be good to clarify that the no-arg form returns the
visible heads, so let's split that out to a new `visible_heads()`
function.
2023-04-03 23:46:34 -07:00
Yuya Nishihara
982062bd75 revset: do not always evaluate filter node to InternalRevset
This basically removes hidden 'all() &' from union/negation of filters. To
achieve that, I have two options: 1. add separate evaluation path (like the
one this commit introduced), or 2. wrap "all()" revset to override predicate
as Box::new(|_| true) function. I took the former since it's less ad-hoc.

We can add an explicit RevsetExpression node to branch between evaluate()
and evaluate_predicate(), but I don't think it would simplify the
implementation at this point. We might need such node if we want to resolve
"all()" at resolve_symbols(). It might be even better to extract a subset of
RevsetExpression enum, which only contains evaluatable nodes.

The cost of 'all() &' isn't significant for most filters. '~merges()' is
the exception. For jj repo,

    revsets/:v0.3.0 & (author(martinvonz) | committer(martinvonz))
    --------------------------------------------------------------
    base     1.06      11.2±0.04m
    new      1.00      10.5±0.05m

    revsets/~merges()
    -----------------
    base     1.69     750.0±8.47µ
    new      1.00     444.1±3.50µ
2023-04-04 15:21:21 +09:00
Yuya Nishihara
c28d2d7784 revset: split RevsetError into RevsetResolution/EvaluationError
This makes it clear that RevsetExpression::Present node is noop at the
evaluation stage.

RevsetEvaluationError::StoreError is unused right now, but I'm not sure if
it should be removed. It makes some sense that evaluate() can propagate
StoreError as it has access to the store.
2023-04-03 10:55:03 +09:00
Martin von Zweigbergk
3546cc1bf6 revset: pass in store, index, and heads instead of whole Repo
The `Repo` is a higher-level type that the index shouldn't have to
know about. With this change, a custom revset implementation should be
able evaluate the revset on a server without knowing which repo it
refers to.
2023-03-30 20:15:45 -07:00
Martin von Zweigbergk
3ff1ab520b revset: remove public_heads()
The `public_heads()` revset only contains the root commit in
practice. I'm not sure what we want to do about phases, but since we
don't have any real support for them yet, let's just remove this
revset. I didn't update the changelog because we don't seem to have
documented the revset function (and it seems unlikely that users who
found out about it found it useful enough to use it when they could
just use `root`).
2023-03-30 20:15:45 -07:00
Yuya Nishihara
0532301e03 revset: add latest(candidates, count) predicate
This serves the role of limit() in Mercurial. Since revsets in JJ is
(conceptually) an unordered set, a "limit" predicate should define its
ordering criteria. That's why the added predicate is named as "latest".

Closes #1110
2023-03-25 23:48:50 +09:00