mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-26 14:00:51 +00:00
revsets: allow ::
as synonym for :
The `--allow-large-revsets` flag we have on `jj rebase` and `jj new` allows the user to do e.g. `jj rebase --allow-large-revsets -b main.. -d main` to rebase all commits that are not in main onto main. The reason we don't allow these revsets to resolve to multiple commits by default is that we think users might specify multiple commits by mistake. That's probably not much of a problem with `jj rebase -b` (maybe we should always allow that to resolve to multiple commits), but the user might want to know if `jj rebase -d @-` resolves to multiple commits. One problem with having a flag to allow multiple commits is that it needs to be added to every command where we want to allow multiple commits but default to one. Also, it should probably apply to each revset argument those commands take. For example, even if the user meant `-b main..` to resolve to multiple commits, they might not have meant `-d main` to resolve to multiple commits (which it will in case of a conflicted branch), so we might want separate `--allow-large-revsets-in-destination` and `--allow-large-revsets-in-source`, which gets quite cumbersome. It seems better to have some syntax in the individual revsets for saying that multiple commits are allowed. One proposal I had was to use a `multiple()` revset function which would have no effect in general but would be used as a marker if used at the top level (e.g. `jj rebase -d 'multiple(@-)'`). After some discussion on the PR adding that function (#1911), it seems that the consensus is to instead use a prefix like `many:` or `all:`. That avoids the problem with having a function that has no effect unless it's used at the top level (`jj rebase -d 'multiple(x)|y'` would have no effect). Since we already have the `:` operator for DAG ranges, we need to change it to make room for `many:`/`all:` syntax. This commit starts that by allowing both `:` and `::`. I have tried to update the documentation in this commit to either mention both forms, or just the new and preferred `::` form. However, it's useless to search for `:` in Rust code, so I'm sure I've missed many instances. We'll have to address those as we notice them. I'll let most tests use `:` until we deprecate it or delete it.
This commit is contained in:
parent
a22255bd51
commit
48580ed8b1
13 changed files with 195 additions and 90 deletions
|
@ -22,6 +22,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
* `jj log` output is now topologically grouped.
|
||||
[#242](https://github.com/martinvonz/jj/issues/242)
|
||||
|
||||
* The `:` revset operator can now be written as `::` instead. We plan to
|
||||
delete the `:` form in jj 0.15+.
|
||||
|
||||
### Fixed bugs
|
||||
|
||||
## [0.8.0] - 2023-07-09
|
||||
|
|
|
@ -196,7 +196,7 @@ To get shorter prefixes for certain revisions, set `revsets.short-prefixes`:
|
|||
|
||||
```toml
|
||||
# Prioritize the current branch
|
||||
revsets.short-prefixes = "(main..@):"
|
||||
revsets.short-prefixes = "(main..@)::"
|
||||
```
|
||||
|
||||
### Relative timestamps
|
||||
|
@ -259,7 +259,7 @@ You can define aliases for commands, including their arguments. For example:
|
|||
```toml
|
||||
# `jj l` shows commits on the working-copy commit's (anonymous) branch
|
||||
# compared to the `main` branch
|
||||
aliases.l = ["log", "-r", "(main..@): | (main..@)-"]
|
||||
aliases.l = ["log", "-r", "(main..@):: | (main..@)-"]
|
||||
```
|
||||
|
||||
## Editor
|
||||
|
|
|
@ -148,7 +148,7 @@ remote
|
|||
Log all remote branches, which you authored or committed to
|
||||
`jj log -r 'remote_branches() & (committer(your@email.com) | author(your@email.com))'`
|
||||
Log all descendants of the current working copy, which aren't on a remote
|
||||
`jj log -r ':@ & ~remote_branches()'`
|
||||
`jj log -r '::@ & ~remote_branches()'`
|
||||
|
||||
## Merge conflicts
|
||||
|
||||
|
|
|
@ -58,14 +58,16 @@ only symbols.
|
|||
* `~x`: Revisions that are not in `x`.
|
||||
* `x-`: Parents of `x`.
|
||||
* `x+`: Children of `x`.
|
||||
* `:x`: Ancestors of `x`, including the commits in `x` itself.
|
||||
* `x:`: Descendants of `x`, including the commits in `x` itself.
|
||||
* `x:y`: Descendants of `x` that are also ancestors of `y`. Equivalent to
|
||||
`x: & :y`. This is what `git log` calls `--ancestry-path x..y`.
|
||||
* `::x`: Ancestors of `x`, including the commits in `x` itself.
|
||||
* `x::`: Descendants of `x`, including the commits in `x` itself.
|
||||
* `x::y`: Descendants of `x` that are also ancestors of `y`. Equivalent
|
||||
to `x:: & ::y`. This is what `git log` calls `--ancestry-path x..y`.
|
||||
* `:x`, `x:`, and `x:y`: Deprecated synonyms for `::x`, `x::`, and `x::y`. We
|
||||
plan to delete them in jj 0.15+.
|
||||
* `x..y`: Ancestors of `y` that are not also ancestors of `x`. Equivalent to
|
||||
`:y ~ :x`. This is what `git log` calls `x..y` (i.e. the same as we call it).
|
||||
`::y ~ ::x`. This is what `git log` calls `x..y` (i.e. the same as we call it).
|
||||
* `..x`: Ancestors of `x`, including the commits in `x` itself. Equivalent to
|
||||
`:x` and provided for consistency.
|
||||
`::x` and provided for consistency.
|
||||
* `x..`: Revisions that are not ancestors of `x`.
|
||||
|
||||
You can use parentheses to control evaluation order, such as `(x & y) | z` or
|
||||
|
@ -78,9 +80,9 @@ revsets (expressions) as arguments.
|
|||
|
||||
* `parents(x)`: Same as `x-`.
|
||||
* `children(x)`: Same as `x+`.
|
||||
* `ancestors(x)`: Same as `:x`.
|
||||
* `descendants(x)`: Same as `x:`.
|
||||
* `connected(x)`: Same as `x:x`. Useful when `x` includes several commits.
|
||||
* `ancestors(x)`: Same as `::x`.
|
||||
* `descendants(x)`: Same as `x::`.
|
||||
* `connected(x)`: Same as `x::x`. Useful when `x` includes several commits.
|
||||
* `all()`: All visible commits in the repo.
|
||||
* `none()`: No commits. This function is rarely useful; it is provided for
|
||||
completeness.
|
||||
|
@ -169,7 +171,7 @@ jj log -r 'remote_branches(remote=origin)..'
|
|||
Show all ancestors of the working copy (almost like plain `git log`)
|
||||
|
||||
```
|
||||
jj log -r :@
|
||||
jj log -r ::@
|
||||
```
|
||||
|
||||
Show the initial commits in the repo (the ones Git calls "root commits"):
|
||||
|
@ -187,8 +189,9 @@ jj log -r 'tags() | branches()'
|
|||
Show local commits leading up to the working copy, as well as descendants of
|
||||
those commits:
|
||||
|
||||
|
||||
```
|
||||
jj log -r '(remote_branches()..@):'
|
||||
jj log -r '(remote_branches()..@)::'
|
||||
```
|
||||
|
||||
Show commits authored by "martinvonz" and containing the word "reset" in the
|
||||
|
|
|
@ -159,7 +159,7 @@ called the "root commit". It's the root commit of every repo. The `root` symbol
|
|||
in the revset matches it.
|
||||
|
||||
There are also operators for getting the parents (`foo-`), children (`foo+`),
|
||||
ancestors (`:foo`), descendants (`foo:`), DAG range (`foo:bar`, like
|
||||
ancestors (`::foo`), descendants (`foo::`), DAG range (`foo::bar`, like
|
||||
`git log --ancestry-path`), range (`foo..bar`, same as Git's). There are also a
|
||||
few more functions, such as `heads(<set>)`, which filters out revisions in the
|
||||
input set if they're ancestors of other revisions in the set.
|
||||
|
@ -345,7 +345,7 @@ $ jj new -m ABC; printf 'A\nB\nc\n' > file
|
|||
Working copy now at: 6f30cd1fb351 ABC
|
||||
$ jj new -m ABCD; printf 'A\nB\nC\nD\n' > file
|
||||
Working copy now at: a67491542e10 ABCD
|
||||
$ jj log -r master:@
|
||||
$ jj log -r master::@
|
||||
@ mrxqplykzpkw martinvonz@google.com 2023-02-12 19:38:21.000 -08:00 b98c607bf87f
|
||||
│ ABCD
|
||||
◉ kwtuwqnmqyqp martinvonz@google.com 2023-02-12 19:38:12.000 -08:00 30aecc0871ea
|
||||
|
|
|
@ -2951,7 +2951,7 @@ mod tests {
|
|||
);
|
||||
|
||||
// Merge range with sub-range (1..4 + 2..3 should be 1..4, not 1..3):
|
||||
// 8,7,6->5:1..4, B5_1->5:2..3
|
||||
// 8,7,6->5::1..4, B5_1->5::2..3
|
||||
assert_eq!(
|
||||
walk_commit_ids(
|
||||
&[&ids[8], &ids[7], &ids[6], &id_branch5_1].map(Clone::clone),
|
||||
|
|
|
@ -712,7 +712,7 @@ impl<'index> EvaluationContext<'index> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Calculates `root_set:head_set`.
|
||||
/// Calculates `root_set::head_set`.
|
||||
fn collect_dag_range<'a, 'b, S, T>(
|
||||
&self,
|
||||
root_set: &S,
|
||||
|
|
|
@ -28,15 +28,19 @@ parents_op = { "-" }
|
|||
children_op = { "+" }
|
||||
compat_parents_op = { "^" }
|
||||
|
||||
dag_range_op = { ":" }
|
||||
dag_range_pre_op = { ":" }
|
||||
dag_range_post_op = { ":" }
|
||||
dag_range_op = { "::" }
|
||||
dag_range_pre_op = { "::" }
|
||||
dag_range_post_op = { "::" }
|
||||
// TODO: Drop support for these in 0.15+
|
||||
legacy_dag_range_op = { ":" }
|
||||
legacy_dag_range_pre_op = { ":" }
|
||||
legacy_dag_range_post_op = { ":" }
|
||||
range_op = { ".." }
|
||||
range_pre_op = { ".." }
|
||||
range_post_op = { ".." }
|
||||
range_ops = _{ dag_range_op | range_op }
|
||||
range_pre_ops = _{ dag_range_pre_op | range_pre_op }
|
||||
range_post_ops = _{ dag_range_post_op | range_post_op }
|
||||
range_ops = _{ dag_range_op | legacy_dag_range_op | range_op }
|
||||
range_pre_ops = _{ dag_range_pre_op | legacy_dag_range_pre_op | range_pre_op }
|
||||
range_post_ops = _{ dag_range_post_op | legacy_dag_range_post_op | range_post_op }
|
||||
|
||||
negate_op = { "~" }
|
||||
union_op = { "|" }
|
||||
|
|
|
@ -246,10 +246,12 @@ pub enum RevsetExpression {
|
|||
Ancestors {
|
||||
heads: Rc<RevsetExpression>,
|
||||
generation: Range<u64>,
|
||||
is_legacy: bool,
|
||||
},
|
||||
Descendants {
|
||||
roots: Rc<RevsetExpression>,
|
||||
generation: Range<u64>,
|
||||
is_legacy: bool,
|
||||
},
|
||||
// Commits that are ancestors of "heads" but not ancestors of "roots"
|
||||
Range {
|
||||
|
@ -261,6 +263,7 @@ pub enum RevsetExpression {
|
|||
DagRange {
|
||||
roots: Rc<RevsetExpression>,
|
||||
heads: Rc<RevsetExpression>,
|
||||
is_legacy: bool,
|
||||
// TODO: maybe add generation_from_roots/heads?
|
||||
},
|
||||
Heads(Rc<RevsetExpression>),
|
||||
|
@ -357,6 +360,7 @@ impl RevsetExpression {
|
|||
Rc::new(RevsetExpression::Ancestors {
|
||||
heads: self.clone(),
|
||||
generation: 1..2,
|
||||
is_legacy: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -365,6 +369,14 @@ impl RevsetExpression {
|
|||
Rc::new(RevsetExpression::Ancestors {
|
||||
heads: self.clone(),
|
||||
generation: GENERATION_RANGE_FULL,
|
||||
is_legacy: false,
|
||||
})
|
||||
}
|
||||
fn legacy_ancestors(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {
|
||||
Rc::new(RevsetExpression::Ancestors {
|
||||
heads: self.clone(),
|
||||
generation: GENERATION_RANGE_FULL,
|
||||
is_legacy: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -373,6 +385,7 @@ impl RevsetExpression {
|
|||
Rc::new(RevsetExpression::Descendants {
|
||||
roots: self.clone(),
|
||||
generation: 1..2,
|
||||
is_legacy: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -381,6 +394,14 @@ impl RevsetExpression {
|
|||
Rc::new(RevsetExpression::Descendants {
|
||||
roots: self.clone(),
|
||||
generation: GENERATION_RANGE_FULL,
|
||||
is_legacy: false,
|
||||
})
|
||||
}
|
||||
fn legacy_descendants(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {
|
||||
Rc::new(RevsetExpression::Descendants {
|
||||
roots: self.clone(),
|
||||
generation: GENERATION_RANGE_FULL,
|
||||
is_legacy: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -393,6 +414,17 @@ impl RevsetExpression {
|
|||
Rc::new(RevsetExpression::DagRange {
|
||||
roots: self.clone(),
|
||||
heads: heads.clone(),
|
||||
is_legacy: false,
|
||||
})
|
||||
}
|
||||
pub fn legacy_dag_range_to(
|
||||
self: &Rc<RevsetExpression>,
|
||||
heads: &Rc<RevsetExpression>,
|
||||
) -> Rc<RevsetExpression> {
|
||||
Rc::new(RevsetExpression::DagRange {
|
||||
roots: self.clone(),
|
||||
heads: heads.clone(),
|
||||
is_legacy: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -737,9 +769,15 @@ fn parse_expression_rule(
|
|||
| Op::infix(Rule::compat_sub_op, Assoc::Left))
|
||||
.op(Op::prefix(Rule::negate_op))
|
||||
// Ranges can't be nested without parentheses. Associativity doesn't matter.
|
||||
.op(Op::infix(Rule::dag_range_op, Assoc::Left) | Op::infix(Rule::range_op, Assoc::Left))
|
||||
.op(Op::prefix(Rule::dag_range_pre_op) | Op::prefix(Rule::range_pre_op))
|
||||
.op(Op::postfix(Rule::dag_range_post_op) | Op::postfix(Rule::range_post_op))
|
||||
.op(Op::infix(Rule::dag_range_op, Assoc::Left)
|
||||
| Op::infix(Rule::legacy_dag_range_op, Assoc::Left)
|
||||
| Op::infix(Rule::range_op, Assoc::Left))
|
||||
.op(Op::prefix(Rule::dag_range_pre_op)
|
||||
| Op::prefix(Rule::legacy_dag_range_pre_op)
|
||||
| Op::prefix(Rule::range_pre_op))
|
||||
.op(Op::postfix(Rule::dag_range_post_op)
|
||||
| Op::postfix(Rule::legacy_dag_range_post_op)
|
||||
| Op::postfix(Rule::range_post_op))
|
||||
// Neighbors
|
||||
.op(Op::postfix(Rule::parents_op)
|
||||
| Op::postfix(Rule::children_op)
|
||||
|
@ -750,10 +788,12 @@ fn parse_expression_rule(
|
|||
.map_prefix(|op, rhs| match op.as_rule() {
|
||||
Rule::negate_op => Ok(rhs?.negated()),
|
||||
Rule::dag_range_pre_op | Rule::range_pre_op => Ok(rhs?.ancestors()),
|
||||
Rule::legacy_dag_range_pre_op => Ok(rhs?.legacy_ancestors()),
|
||||
r => panic!("unexpected prefix operator rule {r:?}"),
|
||||
})
|
||||
.map_postfix(|lhs, op| match op.as_rule() {
|
||||
Rule::dag_range_post_op => Ok(lhs?.descendants()),
|
||||
Rule::legacy_dag_range_post_op => Ok(lhs?.legacy_descendants()),
|
||||
Rule::range_post_op => Ok(lhs?.range(&RevsetExpression::visible_heads())),
|
||||
Rule::parents_op => Ok(lhs?.parents()),
|
||||
Rule::children_op => Ok(lhs?.children()),
|
||||
|
@ -767,6 +807,7 @@ fn parse_expression_rule(
|
|||
Rule::difference_op => Ok(lhs?.minus(&rhs?)),
|
||||
Rule::compat_sub_op => Err(not_infix_op(&op, "~", "difference")),
|
||||
Rule::dag_range_op => Ok(lhs?.dag_range_to(&rhs?)),
|
||||
Rule::legacy_dag_range_op => Ok(lhs?.legacy_dag_range_to(&rhs?)),
|
||||
Rule::range_op => Ok(lhs?.range(&rhs?)),
|
||||
r => panic!("unexpected infix operator rule {r:?}"),
|
||||
})
|
||||
|
@ -1276,16 +1317,20 @@ fn try_transform_expression<E>(
|
|||
RevsetExpression::All => None,
|
||||
RevsetExpression::Commits(_) => None,
|
||||
RevsetExpression::CommitRef(_) => None,
|
||||
RevsetExpression::Ancestors { heads, generation } => transform_rec(heads, pre, post)?
|
||||
.map(|heads| RevsetExpression::Ancestors {
|
||||
heads,
|
||||
generation: generation.clone(),
|
||||
}),
|
||||
RevsetExpression::Descendants { roots, generation } => transform_rec(roots, pre, post)?
|
||||
.map(|roots| RevsetExpression::Descendants {
|
||||
roots,
|
||||
generation: generation.clone(),
|
||||
}),
|
||||
RevsetExpression::Ancestors {
|
||||
heads, generation, ..
|
||||
} => transform_rec(heads, pre, post)?.map(|heads| RevsetExpression::Ancestors {
|
||||
heads,
|
||||
generation: generation.clone(),
|
||||
is_legacy: false,
|
||||
}),
|
||||
RevsetExpression::Descendants {
|
||||
roots, generation, ..
|
||||
} => transform_rec(roots, pre, post)?.map(|roots| RevsetExpression::Descendants {
|
||||
roots,
|
||||
generation: generation.clone(),
|
||||
is_legacy: false,
|
||||
}),
|
||||
RevsetExpression::Range {
|
||||
roots,
|
||||
heads,
|
||||
|
@ -1297,9 +1342,14 @@ fn try_transform_expression<E>(
|
|||
generation: generation.clone(),
|
||||
}
|
||||
}),
|
||||
RevsetExpression::DagRange { roots, heads } => {
|
||||
transform_rec_pair((roots, heads), pre, post)?
|
||||
.map(|(roots, heads)| RevsetExpression::DagRange { roots, heads })
|
||||
RevsetExpression::DagRange { roots, heads, .. } => {
|
||||
transform_rec_pair((roots, heads), pre, post)?.map(|(roots, heads)| {
|
||||
RevsetExpression::DagRange {
|
||||
roots,
|
||||
heads,
|
||||
is_legacy: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
RevsetExpression::Heads(candidates) => {
|
||||
transform_rec(candidates, pre, post)?.map(RevsetExpression::Heads)
|
||||
|
@ -1493,10 +1543,13 @@ fn fold_difference(expression: &Rc<RevsetExpression>) -> TransformedExpression {
|
|||
match (expression.as_ref(), complement.as_ref()) {
|
||||
// :heads & ~(:roots) -> roots..heads
|
||||
(
|
||||
RevsetExpression::Ancestors { heads, generation },
|
||||
RevsetExpression::Ancestors {
|
||||
heads, generation, ..
|
||||
},
|
||||
RevsetExpression::Ancestors {
|
||||
heads: roots,
|
||||
generation: GENERATION_RANGE_FULL,
|
||||
..
|
||||
},
|
||||
) => Rc::new(RevsetExpression::Range {
|
||||
roots: roots.clone(),
|
||||
|
@ -1540,6 +1593,7 @@ fn unfold_difference(expression: &Rc<RevsetExpression>) -> TransformedExpression
|
|||
let heads_ancestors = Rc::new(RevsetExpression::Ancestors {
|
||||
heads: heads.clone(),
|
||||
generation: generation.clone(),
|
||||
is_legacy: false,
|
||||
});
|
||||
Some(heads_ancestors.intersection(&roots.ancestors().negated()))
|
||||
}
|
||||
|
@ -1568,6 +1622,7 @@ fn fold_generation(expression: &Rc<RevsetExpression>) -> TransformedExpression {
|
|||
RevsetExpression::Ancestors {
|
||||
heads,
|
||||
generation: generation1,
|
||||
..
|
||||
} => {
|
||||
match heads.as_ref() {
|
||||
// (h-)- -> ancestors(ancestors(h, 1), 1) -> ancestors(h, 2)
|
||||
|
@ -1576,9 +1631,11 @@ fn fold_generation(expression: &Rc<RevsetExpression>) -> TransformedExpression {
|
|||
RevsetExpression::Ancestors {
|
||||
heads,
|
||||
generation: generation2,
|
||||
..
|
||||
} => Some(Rc::new(RevsetExpression::Ancestors {
|
||||
heads: heads.clone(),
|
||||
generation: add_generation(generation1, generation2),
|
||||
is_legacy: false,
|
||||
})),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -1586,6 +1643,7 @@ fn fold_generation(expression: &Rc<RevsetExpression>) -> TransformedExpression {
|
|||
RevsetExpression::Descendants {
|
||||
roots,
|
||||
generation: generation1,
|
||||
..
|
||||
} => {
|
||||
match roots.as_ref() {
|
||||
// (r+)+ -> descendants(descendants(r, 1), 1) -> descendants(r, 2)
|
||||
|
@ -1594,9 +1652,11 @@ fn fold_generation(expression: &Rc<RevsetExpression>) -> TransformedExpression {
|
|||
RevsetExpression::Descendants {
|
||||
roots,
|
||||
generation: generation2,
|
||||
..
|
||||
} => Some(Rc::new(RevsetExpression::Descendants {
|
||||
roots: roots.clone(),
|
||||
generation: add_generation(generation1, generation2),
|
||||
is_legacy: false,
|
||||
})),
|
||||
_ => None,
|
||||
}
|
||||
|
@ -1986,11 +2046,15 @@ impl VisibilityResolutionContext<'_> {
|
|||
RevsetExpression::CommitRef(_) => {
|
||||
panic!("Expression '{expression:?}' should have been resolved by caller");
|
||||
}
|
||||
RevsetExpression::Ancestors { heads, generation } => ResolvedExpression::Ancestors {
|
||||
RevsetExpression::Ancestors {
|
||||
heads, generation, ..
|
||||
} => ResolvedExpression::Ancestors {
|
||||
heads: self.resolve(heads).into(),
|
||||
generation: generation.clone(),
|
||||
},
|
||||
RevsetExpression::Descendants { roots, generation } => ResolvedExpression::DagRange {
|
||||
RevsetExpression::Descendants {
|
||||
roots, generation, ..
|
||||
} => ResolvedExpression::DagRange {
|
||||
roots: self.resolve(roots).into(),
|
||||
heads: self.resolve_visible_heads().into(),
|
||||
generation_from_roots: generation.clone(),
|
||||
|
@ -2004,7 +2068,7 @@ impl VisibilityResolutionContext<'_> {
|
|||
heads: self.resolve(heads).into(),
|
||||
generation: generation.clone(),
|
||||
},
|
||||
RevsetExpression::DagRange { roots, heads } => ResolvedExpression::DagRange {
|
||||
RevsetExpression::DagRange { roots, heads, .. } => ResolvedExpression::DagRange {
|
||||
roots: self.resolve(roots).into(),
|
||||
heads: self.resolve(heads).into(),
|
||||
generation_from_roots: GENERATION_RANGE_FULL,
|
||||
|
@ -2266,6 +2330,7 @@ mod tests {
|
|||
Rc::new(RevsetExpression::Ancestors {
|
||||
heads: wc_symbol.clone(),
|
||||
generation: 1..2,
|
||||
is_legacy: false,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -2273,13 +2338,15 @@ mod tests {
|
|||
Rc::new(RevsetExpression::Ancestors {
|
||||
heads: wc_symbol.clone(),
|
||||
generation: GENERATION_RANGE_FULL,
|
||||
is_legacy: false,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
foo_symbol.children(),
|
||||
Rc::new(RevsetExpression::Descendants {
|
||||
roots: foo_symbol.clone(),
|
||||
generation: 1..2
|
||||
generation: 1..2,
|
||||
is_legacy: false,
|
||||
}),
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -2287,6 +2354,7 @@ mod tests {
|
|||
Rc::new(RevsetExpression::Descendants {
|
||||
roots: foo_symbol.clone(),
|
||||
generation: GENERATION_RANGE_FULL,
|
||||
is_legacy: false,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -2294,6 +2362,7 @@ mod tests {
|
|||
Rc::new(RevsetExpression::DagRange {
|
||||
roots: foo_symbol.clone(),
|
||||
heads: wc_symbol.clone(),
|
||||
is_legacy: false,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -2301,6 +2370,7 @@ mod tests {
|
|||
Rc::new(RevsetExpression::DagRange {
|
||||
roots: foo_symbol.clone(),
|
||||
heads: foo_symbol.clone(),
|
||||
is_legacy: false,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -2420,11 +2490,11 @@ mod tests {
|
|||
// Parse the "children" operator
|
||||
assert_eq!(parse("@+"), Ok(wc_symbol.children()));
|
||||
// Parse the "ancestors" operator
|
||||
assert_eq!(parse(":@"), Ok(wc_symbol.ancestors()));
|
||||
assert_eq!(parse("::@"), Ok(wc_symbol.ancestors()));
|
||||
// Parse the "descendants" operator
|
||||
assert_eq!(parse("@:"), Ok(wc_symbol.descendants()));
|
||||
assert_eq!(parse("@::"), Ok(wc_symbol.descendants()));
|
||||
// Parse the "dag range" operator
|
||||
assert_eq!(parse("foo:bar"), Ok(foo_symbol.dag_range_to(&bar_symbol)));
|
||||
assert_eq!(parse("foo::bar"), Ok(foo_symbol.dag_range_to(&bar_symbol)));
|
||||
// Parse the "range" prefix operator
|
||||
assert_eq!(parse("..@"), Ok(wc_symbol.ancestors()));
|
||||
assert_eq!(
|
||||
|
@ -2447,10 +2517,10 @@ mod tests {
|
|||
// Parentheses are allowed before suffix operators
|
||||
assert_eq!(parse("(@)-"), Ok(wc_symbol.parents()));
|
||||
// Space is allowed around expressions
|
||||
assert_eq!(parse(" :@ "), Ok(wc_symbol.ancestors()));
|
||||
assert_eq!(parse("( :@ )"), Ok(wc_symbol.ancestors()));
|
||||
assert_eq!(parse(" ::@ "), Ok(wc_symbol.ancestors()));
|
||||
assert_eq!(parse("( ::@ )"), Ok(wc_symbol.ancestors()));
|
||||
// Space is not allowed around prefix operators
|
||||
assert_eq!(parse(" : @ "), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse(" :: @ "), Err(RevsetParseErrorKind::SyntaxError));
|
||||
// Incomplete parse
|
||||
assert_eq!(parse("foo | -"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
// Space is allowed around infix operators and function arguments
|
||||
|
@ -2565,12 +2635,15 @@ mod tests {
|
|||
assert_eq!(parse("x|y&z").unwrap(), parse("x|(y&z)").unwrap());
|
||||
assert_eq!(parse("x|y~z").unwrap(), parse("x|(y~z)").unwrap());
|
||||
// Parse repeated "ancestors"/"descendants"/"dag range"/"range" operators
|
||||
assert_eq!(parse(":foo:"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("::foo"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("foo::"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("foo::bar"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse(":foo:bar"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("foo:bar:"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("::foo::"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse(":::foo"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("::::foo"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("foo:::"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("foo::::"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("foo:::bar"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("foo::::bar"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("::foo::bar"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("foo::bar::"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("....foo"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("foo...."), Err(RevsetParseErrorKind::SyntaxError));
|
||||
assert_eq!(parse("foo.....bar"), Err(RevsetParseErrorKind::SyntaxError));
|
||||
|
@ -2579,8 +2652,8 @@ mod tests {
|
|||
// Parse combinations of "parents"/"children" operators and the range operators.
|
||||
// The former bind more strongly.
|
||||
assert_eq!(parse("foo-+"), Ok(foo_symbol.parents().children()));
|
||||
assert_eq!(parse("foo-:"), Ok(foo_symbol.parents().descendants()));
|
||||
assert_eq!(parse(":foo+"), Ok(foo_symbol.children().ancestors()));
|
||||
assert_eq!(parse("foo-::"), Ok(foo_symbol.parents().descendants()));
|
||||
assert_eq!(parse("::foo+"), Ok(foo_symbol.children().ancestors()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -3531,6 +3604,7 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 1..2,
|
||||
is_legacy: false,
|
||||
},
|
||||
),
|
||||
CommitRef(
|
||||
|
@ -3569,6 +3643,7 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 1..2,
|
||||
is_legacy: false,
|
||||
},
|
||||
),
|
||||
Filter(
|
||||
|
@ -3886,6 +3961,7 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 2..3,
|
||||
is_legacy: false,
|
||||
}
|
||||
"###);
|
||||
insta::assert_debug_snapshot!(optimize(parse(":(foo---)").unwrap()), @r###"
|
||||
|
@ -3896,6 +3972,7 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 3..18446744073709551615,
|
||||
is_legacy: false,
|
||||
}
|
||||
"###);
|
||||
insta::assert_debug_snapshot!(optimize(parse("(:foo)---").unwrap()), @r###"
|
||||
|
@ -3906,6 +3983,7 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 3..18446744073709551615,
|
||||
is_legacy: false,
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -3919,8 +3997,10 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 3..4,
|
||||
is_legacy: false,
|
||||
},
|
||||
generation: 1..2,
|
||||
is_legacy: false,
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -3951,6 +4031,7 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 3..18446744073709551615,
|
||||
is_legacy: false,
|
||||
},
|
||||
Ancestors {
|
||||
heads: CommitRef(
|
||||
|
@ -3959,6 +4040,7 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 2..18446744073709551615,
|
||||
is_legacy: false,
|
||||
},
|
||||
)
|
||||
"###);
|
||||
|
@ -3981,6 +4063,7 @@ mod tests {
|
|||
generation: 0..18446744073709551615,
|
||||
},
|
||||
generation: 2..3,
|
||||
is_legacy: false,
|
||||
}
|
||||
"###);
|
||||
insta::assert_debug_snapshot!(optimize(parse("foo..(bar..baz)").unwrap()), @r###"
|
||||
|
@ -4014,6 +4097,7 @@ mod tests {
|
|||
Rc::new(RevsetExpression::Ancestors {
|
||||
heads,
|
||||
generation: GENERATION_RANGE_EMPTY,
|
||||
is_legacy: false,
|
||||
})
|
||||
};
|
||||
insta::assert_debug_snapshot!(
|
||||
|
@ -4028,6 +4112,7 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 0..0,
|
||||
is_legacy: false,
|
||||
}
|
||||
"###
|
||||
);
|
||||
|
@ -4043,6 +4128,7 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 0..0,
|
||||
is_legacy: false,
|
||||
}
|
||||
"###
|
||||
);
|
||||
|
@ -4059,6 +4145,7 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 2..3,
|
||||
is_legacy: false,
|
||||
}
|
||||
"###);
|
||||
insta::assert_debug_snapshot!(optimize(parse("(foo+++):").unwrap()), @r###"
|
||||
|
@ -4069,6 +4156,7 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 3..18446744073709551615,
|
||||
is_legacy: false,
|
||||
}
|
||||
"###);
|
||||
insta::assert_debug_snapshot!(optimize(parse("(foo:)+++").unwrap()), @r###"
|
||||
|
@ -4079,6 +4167,7 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 3..18446744073709551615,
|
||||
is_legacy: false,
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -4092,8 +4181,10 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 3..4,
|
||||
is_legacy: false,
|
||||
},
|
||||
generation: 1..2,
|
||||
is_legacy: false,
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -4109,12 +4200,14 @@ mod tests {
|
|||
),
|
||||
),
|
||||
generation: 2..3,
|
||||
is_legacy: false,
|
||||
},
|
||||
heads: CommitRef(
|
||||
Symbol(
|
||||
"bar",
|
||||
),
|
||||
),
|
||||
is_legacy: false,
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
|
|
@ -678,7 +678,7 @@ mod tests {
|
|||
A
|
||||
|
||||
"###);
|
||||
// A:F is picked at A, and A will be unblocked. Then, C:D at C, ...
|
||||
// A::F is picked at A, and A will be unblocked. Then, C::D at C, ...
|
||||
insta::assert_snapshot!(format_graph(topo_grouped(graph.iter().cloned())), @r###"
|
||||
G direct(A)
|
||||
│
|
||||
|
@ -730,7 +730,7 @@ mod tests {
|
|||
A
|
||||
|
||||
"###);
|
||||
// A:K is picked at A, and A will be unblocked. Then, H:I at H, ...
|
||||
// A::K is picked at A, and A will be unblocked. Then, H::I at H, ...
|
||||
insta::assert_snapshot!(format_graph(topo_grouped(graph.iter().cloned())), @r###"
|
||||
L direct(A)
|
||||
│
|
||||
|
@ -793,7 +793,7 @@ mod tests {
|
|||
A
|
||||
|
||||
"###);
|
||||
// A:K is picked at A, and A will be unblocked. Then, E:G at E, ...
|
||||
// A::K is picked at A, and A will be unblocked. Then, E::G at E, ...
|
||||
insta::assert_snapshot!(format_graph(topo_grouped(graph.iter().cloned())), @r###"
|
||||
L direct(A)
|
||||
│
|
||||
|
@ -855,7 +855,8 @@ mod tests {
|
|||
│
|
||||
~
|
||||
"###);
|
||||
// K-E,J is resolved without queuing new heads. Then, G:H, F:I, B:C, and A:D.
|
||||
// K-E,J is resolved without queuing new heads. Then, G::H, F::I, B::C, and
|
||||
// A::D.
|
||||
insta::assert_snapshot!(format_graph(topo_grouped(graph.iter().cloned())), @r###"
|
||||
K direct(E), direct(J)
|
||||
├─╮
|
||||
|
@ -919,7 +920,8 @@ mod tests {
|
|||
│
|
||||
~
|
||||
"###);
|
||||
// K-I,J is resolved without queuing new heads. Then, D:F, B:H, C:E, and A:G.
|
||||
// K-I,J is resolved without queuing new heads. Then, D::F, B::H, C::E, and
|
||||
// A::G.
|
||||
insta::assert_snapshot!(format_graph(topo_grouped(graph.iter().cloned())), @r###"
|
||||
K direct(I), direct(J)
|
||||
├─╮
|
||||
|
|
|
@ -782,7 +782,7 @@ struct SplitArgs {
|
|||
/// * all descendants of those commits
|
||||
///
|
||||
/// In other words, `jj rebase -b X -d Y` rebases commits in the revset
|
||||
/// `(Y..X):` (which is equivalent to `jj rebase -s 'roots(Y..X)' -d Y` for a
|
||||
/// `(Y..X)::` (which is equivalent to `jj rebase -s 'roots(Y..X)' -d Y` for a
|
||||
/// single root). For example, either `jj rebase -b L -d O` or `jj rebase -b M
|
||||
/// -d O` would transform your history like this (because `L` and `M` are on the
|
||||
/// same "branch", relative to the destination):
|
||||
|
|
|
@ -4,21 +4,21 @@
|
|||
v1.0.0
|
||||
v2.40.0
|
||||
# Old history
|
||||
:v1.0.0
|
||||
::v1.0.0
|
||||
..v1.0.0
|
||||
# More history
|
||||
:v2.40.0
|
||||
::v2.40.0
|
||||
..v2.40.0
|
||||
# Only recent history
|
||||
v2.39.0..v2.40.0
|
||||
:v2.40.0 ~ :v2.39.0
|
||||
v2.39.0:v2.40.0
|
||||
::v2.40.0 ~ ::v2.39.0
|
||||
v2.39.0::v2.40.0
|
||||
# Tags and branches
|
||||
tags()
|
||||
branches()
|
||||
# Intersection of range with a small subset
|
||||
tags() & :v2.40.0
|
||||
v2.39.0 & :v2.40.0
|
||||
tags() & ::v2.40.0
|
||||
v2.39.0 & ::v2.40.0
|
||||
# Author and committer
|
||||
author(peff)
|
||||
committer(gitster)
|
||||
|
@ -26,8 +26,8 @@ committer(gitster)
|
|||
author(peff) & committer(gitster)
|
||||
author(peff) | committer(gitster)
|
||||
# Intersection of filter with a small subset
|
||||
:v1.0.0 & (author(peff) & committer(gitster))
|
||||
:v1.0.0 & (author(peff) | committer(gitster))
|
||||
::v1.0.0 & (author(peff) & committer(gitster))
|
||||
::v1.0.0 & (author(peff) | committer(gitster))
|
||||
# Roots and heads of small subsets
|
||||
roots(tags())
|
||||
heads(tags())
|
||||
|
@ -35,32 +35,32 @@ heads(tags())
|
|||
roots(author(peff))
|
||||
heads(author(peff))
|
||||
# Roots and heads of range
|
||||
roots(:v2.40.0)
|
||||
heads(:v2.40.0)
|
||||
roots(::v2.40.0)
|
||||
heads(::v2.40.0)
|
||||
# Parents and ancestors of old commit
|
||||
v1.0.0-
|
||||
v1.0.0---
|
||||
:v1.0.0---
|
||||
::v1.0.0---
|
||||
# Children and descendants of old commit
|
||||
v1.0.0+
|
||||
v1.0.0+++
|
||||
v1.0.0+++:
|
||||
v1.0.0+++::
|
||||
# Parents and ancestors of recent commit
|
||||
v2.40.0-
|
||||
v2.40.0---
|
||||
:v2.40.0---
|
||||
::v2.40.0---
|
||||
# Children and descendants of recent commit
|
||||
v2.40.0+
|
||||
v2.40.0+++
|
||||
v2.40.0+++:
|
||||
v2.40.0+++::
|
||||
# Parents and ancestors of small subset
|
||||
tags()-
|
||||
tags()---
|
||||
:tags()---
|
||||
::tags()---
|
||||
# Children and descendants of small subset
|
||||
tags()+
|
||||
tags()+++
|
||||
tags()+++:
|
||||
tags()+++::
|
||||
# Filter that doesn't read commit object
|
||||
merges()
|
||||
~merges()
|
||||
|
|
|
@ -29,7 +29,7 @@ fn test_syntax_error() {
|
|||
1 | x &
|
||||
| ^---
|
||||
|
|
||||
= expected dag_range_pre_op, range_pre_op, negate_op, or primary
|
||||
= expected dag_range_pre_op, legacy_dag_range_pre_op, range_pre_op, negate_op, or primary
|
||||
"###);
|
||||
|
||||
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "x - y"]);
|
||||
|
@ -61,12 +61,12 @@ fn test_bad_function_call() {
|
|||
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
|
||||
let repo_path = test_env.env_root().join("repo");
|
||||
|
||||
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "all(or:nothing)"]);
|
||||
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "all(or::nothing)"]);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Error: Failed to parse revset: --> 1:5
|
||||
|
|
||||
1 | all(or:nothing)
|
||||
| ^--------^
|
||||
1 | all(or::nothing)
|
||||
| ^---------^
|
||||
|
|
||||
= Invalid arguments to revset function "all": Expected 0 arguments
|
||||
"###);
|
||||
|
@ -141,12 +141,12 @@ fn test_bad_function_call() {
|
|||
= Invalid file pattern: Path "../out" is not in the repo
|
||||
"###);
|
||||
|
||||
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "root:whatever()"]);
|
||||
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "root::whatever()"]);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Error: Failed to parse revset: --> 1:6
|
||||
Error: Failed to parse revset: --> 1:7
|
||||
|
|
||||
1 | root:whatever()
|
||||
| ^------^
|
||||
1 | root::whatever()
|
||||
| ^------^
|
||||
|
|
||||
= Revset function "whatever" doesn't exist
|
||||
"###);
|
||||
|
@ -279,7 +279,7 @@ fn test_alias() {
|
|||
1 | whatever &
|
||||
| ^---
|
||||
|
|
||||
= expected dag_range_pre_op, range_pre_op, negate_op, or primary
|
||||
= expected dag_range_pre_op, legacy_dag_range_pre_op, range_pre_op, negate_op, or primary
|
||||
"###);
|
||||
|
||||
let stderr = test_env.jj_cmd_failure(&repo_path, &["log", "-r", "identity()"]);
|
||||
|
|
Loading…
Reference in a new issue