diff --git a/CHANGELOG.md b/CHANGELOG.md index d5b3ee7801..67ee822197 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * The storage format of branches, tags, and git refs has changed. Newly-stored repository data will no longer be loadable by older binaries. +* The `:` revset operator is deprecated. Use `::` instead. We plan to delete the + `:` form in jj 0.15+. + +* The `--allow-large-revsets` flag for `jj rebase` and `jj new` was replaced by + a `all:` before the revset. For example, use `jj rebase -d 'all:foo-'` + instead of `jj rebase --allow-large-revsets -d 'foo-'`. + +* The `--allow-large-revsets` flag for `jj rebase` and `jj new` can no longer be + used for allowing duplicate destinations. Include the potential duplicates + in a single expression instead (e.g. `jj new 'all:x|y'`). + ### New features * `jj config edit --user` and `jj config set --user` will now pick a default diff --git a/docs/config.md b/docs/config.md index 57294b2538..7efcc1a7f7 100644 --- a/docs/config.md +++ b/docs/config.md @@ -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 diff --git a/docs/github.md b/docs/github.md index 5666be5169..c09107ffa6 100644 --- a/docs/github.md +++ b/docs/github.md @@ -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 diff --git a/docs/revsets.md b/docs/revsets.md index f5a135b01f..a796f90bb2 100644 --- a/docs/revsets.md +++ b/docs/revsets.md @@ -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 diff --git a/docs/tutorial.md b/docs/tutorial.md index 6d7b5ec97a..143b9af26f 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -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()`, 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 diff --git a/examples/custom-command/main.rs b/examples/custom-command/main.rs index 1d6169de12..32e57a1c93 100644 --- a/examples/custom-command/main.rs +++ b/examples/custom-command/main.rs @@ -36,7 +36,7 @@ fn run_custom_command( match command { CustomCommands::Frobnicate(args) => { let mut workspace_command = command_helper.workspace_helper(ui)?; - let commit = workspace_command.resolve_single_rev(&args.revision)?; + let commit = workspace_command.resolve_single_rev(&args.revision, ui)?; let mut tx = workspace_command.start_transaction("Frobnicate"); let new_commit = tx .mut_repo() diff --git a/lib/src/default_index_store.rs b/lib/src/default_index_store.rs index c6ecc326ac..8594f539ec 100644 --- a/lib/src/default_index_store.rs +++ b/lib/src/default_index_store.rs @@ -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), diff --git a/lib/src/default_revset_engine.rs b/lib/src/default_revset_engine.rs index b7b1e10344..96274cd5d0 100644 --- a/lib/src/default_revset_engine.rs +++ b/lib/src/default_revset_engine.rs @@ -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, diff --git a/lib/src/revset.pest b/lib/src/revset.pest index 9d9f9a3e06..58d613a199 100644 --- a/lib/src/revset.pest +++ b/lib/src/revset.pest @@ -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 = { "|" } diff --git a/lib/src/revset.rs b/lib/src/revset.rs index 33043f67db..b854844f96 100644 --- a/lib/src/revset.rs +++ b/lib/src/revset.rs @@ -246,10 +246,12 @@ pub enum RevsetExpression { Ancestors { heads: Rc, generation: Range, + is_legacy: bool, }, Descendants { roots: Rc, generation: Range, + 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, heads: Rc, + is_legacy: bool, // TODO: maybe add generation_from_roots/heads? }, Heads(Rc), @@ -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) -> Rc { + 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) -> Rc { + 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, + heads: &Rc, + ) -> Rc { + 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( 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( 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> 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, } "###); } diff --git a/lib/src/revset_graph.rs b/lib/src/revset_graph.rs index a36c03967a..29ea2a56b1 100644 --- a/lib/src/revset_graph.rs +++ b/lib/src/revset_graph.rs @@ -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) ├─╮ diff --git a/src/cli_util.rs b/src/cli_util.rs index 8d169e1129..e128f4583f 100644 --- a/src/cli_util.rs +++ b/src/cli_util.rs @@ -957,8 +957,14 @@ impl WorkspaceCommandHelper { ) } - pub fn resolve_single_rev(&self, revision_str: &str) -> Result { - let revset_expression = self.parse_revset(revision_str)?; + /// Resolve a revset to a single revision. Return an error if the revset is + /// empty or has multiple revisions. + pub fn resolve_single_rev( + &self, + revision_str: &str, + ui: &mut Ui, + ) -> Result { + let revset_expression = self.parse_revset(revision_str, Some(ui))?; let revset = self.evaluate_revset(revset_expression)?; let mut iter = revset.iter().commits(self.repo().store()).fuse(); match (iter.next(), iter.next()) { @@ -987,21 +993,112 @@ impl WorkspaceCommandHelper { } } - pub fn resolve_revset(&self, revision_str: &str) -> Result, CommandError> { - let revset_expression = self.parse_revset(revision_str)?; + /// Resolve a revset any number of revisions (including 0). + pub fn resolve_revset( + &self, + revision_str: &str, + ui: &mut Ui, + ) -> Result, CommandError> { + let revset_expression = self.parse_revset(revision_str, Some(ui))?; let revset = self.evaluate_revset(revset_expression)?; Ok(revset.iter().commits(self.repo().store()).try_collect()?) } + /// Resolve a revset any number of revisions (including 0), but require the + /// user to indicate if they allow multiple revisions by prefixing the + /// expression with `all:`. + pub fn resolve_revset_default_single( + &self, + revision_str: &str, + ui: &mut Ui, + ) -> Result, CommandError> { + // TODO: Let pest parse the prefix too once we've dropped support for `:` + if let Some(revision_str) = revision_str.strip_prefix("all:") { + self.resolve_revset(revision_str, ui) + } else { + self.resolve_single_rev(revision_str, ui) + .map_err(|err| match err { + CommandError::UserError { message, hint } => user_error_with_hint( + message, + format!( + "{old_hint}Prefix the expression with 'all' to allow any number of \ + revisions (i.e. 'all:{}').", + revision_str, + old_hint = hint.map(|hint| format!("{hint}\n")).unwrap_or_default() + ), + ), + err => err, + }) + .map(|commit| vec![commit]) + } + } + pub fn parse_revset( &self, revision_str: &str, + ui: Option<&mut Ui>, ) -> Result, RevsetParseError> { let expression = revset::parse( revision_str, &self.revset_aliases_map, Some(&self.revset_context()), )?; + if let Some(ui) = ui { + fn has_legacy_rule(expression: &Rc) -> bool { + match expression.as_ref() { + RevsetExpression::None => false, + RevsetExpression::All => false, + RevsetExpression::Commits(_) => false, + RevsetExpression::CommitRef(_) => false, + RevsetExpression::Ancestors { + heads, + generation: _, + is_legacy, + } => *is_legacy || has_legacy_rule(heads), + RevsetExpression::Descendants { + roots, + generation: _, + is_legacy, + } => *is_legacy || has_legacy_rule(roots), + RevsetExpression::Range { + roots, + heads, + generation: _, + } => has_legacy_rule(roots) || has_legacy_rule(heads), + RevsetExpression::DagRange { + roots, + heads, + is_legacy, + } => *is_legacy || has_legacy_rule(roots) || has_legacy_rule(heads), + RevsetExpression::Heads(expression) => has_legacy_rule(expression), + RevsetExpression::Roots(expression) => has_legacy_rule(expression), + RevsetExpression::Latest { + candidates, + count: _, + } => has_legacy_rule(candidates), + RevsetExpression::Filter(_) => false, + RevsetExpression::AsFilter(expression) => has_legacy_rule(expression), + RevsetExpression::Present(expression) => has_legacy_rule(expression), + RevsetExpression::NotIn(expression) => has_legacy_rule(expression), + RevsetExpression::Union(expression1, expression2) => { + has_legacy_rule(expression1) || has_legacy_rule(expression2) + } + RevsetExpression::Intersection(expression1, expression2) => { + has_legacy_rule(expression1) || has_legacy_rule(expression2) + } + RevsetExpression::Difference(expression1, expression2) => { + has_legacy_rule(expression1) || has_legacy_rule(expression2) + } + } + } + if has_legacy_rule(&expression) { + writeln!( + ui.warning(), + "The `:` revset operator is deprecated. Please switch to `::`." + ) + .ok(); + } + } Ok(revset::optimize(expression)) } @@ -1046,7 +1143,7 @@ impl WorkspaceCommandHelper { .get_string("revsets.short-prefixes") .unwrap_or_else(|_| self.settings.default_revset()); if !revset_string.is_empty() { - let disambiguation_revset = self.parse_revset(&revset_string).unwrap(); + let disambiguation_revset = self.parse_revset(&revset_string, None).unwrap(); context = context .disambiguate_within(disambiguation_revset, Some(self.workspace_id().clone())); } @@ -1763,56 +1860,36 @@ fn load_revset_aliases( pub fn resolve_multiple_nonempty_revsets( revision_args: &[RevisionArg], workspace_command: &WorkspaceCommandHelper, + ui: &mut Ui, ) -> Result, CommandError> { let mut acc = IndexSet::new(); for revset in revision_args { - let revisions = workspace_command.resolve_revset(revset)?; + let revisions = workspace_command.resolve_revset(revset, ui)?; workspace_command.check_non_empty(&revisions)?; acc.extend(revisions); } Ok(acc) } -pub fn resolve_multiple_nonempty_revsets_flag_guarded( +pub fn resolve_multiple_nonempty_revsets_default_single( workspace_command: &WorkspaceCommandHelper, + ui: &mut Ui, revisions: &[RevisionArg], - allow_plural_revsets: bool, ) -> Result, CommandError> { - if allow_plural_revsets { - resolve_multiple_nonempty_revsets(revisions, workspace_command) - } else { - let mut commits = IndexSet::new(); - for revision_str in revisions { - let commit = workspace_command - .resolve_single_rev(revision_str) - .map_err(append_large_revsets_hint_if_multiple_revisions)?; + let mut all_commits = IndexSet::new(); + for revision_str in revisions { + let commits = workspace_command.resolve_revset_default_single(revision_str, ui)?; + workspace_command.check_non_empty(&commits)?; + for commit in commits { let commit_hash = short_commit_hash(commit.id()); - if !commits.insert(commit) { + if !all_commits.insert(commit) { return Err(user_error(format!( r#"More than one revset resolved to revision {commit_hash}"#, ))); } } - Ok(commits) - } -} - -fn append_large_revsets_hint_if_multiple_revisions(err: CommandError) -> CommandError { - match err { - CommandError::UserError { message, hint } if message.contains("more than one revision") => { - CommandError::UserError { - message, - hint: { - Some(format!( - "{old_hint}If this was intentional, specify the `--allow-large-revsets` \ - argument", - old_hint = hint.map(|h| format!("{h}\n")).unwrap_or_default() - )) - }, - } - } - _ => err, } + Ok(all_commits) } pub fn update_working_copy( diff --git a/src/commands/bench.rs b/src/commands/bench.rs index 5b078e69b9..0407901287 100644 --- a/src/commands/bench.rs +++ b/src/commands/bench.rs @@ -131,8 +131,8 @@ pub(crate) fn cmd_bench( match subcommand { BenchCommands::CommonAncestors(command_matches) => { let workspace_command = command.workspace_helper(ui)?; - let commit1 = workspace_command.resolve_single_rev(&command_matches.revision1)?; - let commit2 = workspace_command.resolve_single_rev(&command_matches.revision2)?; + let commit1 = workspace_command.resolve_single_rev(&command_matches.revision1, ui)?; + let commit2 = workspace_command.resolve_single_rev(&command_matches.revision2, ui)?; let index = workspace_command.repo().index(); let routine = || index.common_ancestors(&[commit1.id().clone()], &[commit2.id().clone()]); @@ -149,9 +149,9 @@ pub(crate) fn cmd_bench( BenchCommands::IsAncestor(command_matches) => { let workspace_command = command.workspace_helper(ui)?; let ancestor_commit = - workspace_command.resolve_single_rev(&command_matches.ancestor)?; + workspace_command.resolve_single_rev(&command_matches.ancestor, ui)?; let descendant_commit = - workspace_command.resolve_single_rev(&command_matches.descendant)?; + workspace_command.resolve_single_rev(&command_matches.descendant, ui)?; let index = workspace_command.repo().index(); let routine = || index.is_ancestor(ancestor_commit.id(), descendant_commit.id()); run_bench( @@ -208,7 +208,7 @@ fn bench_revset( revset: &str, ) -> Result<(), CommandError> { writeln!(ui, "----------Testing revset: {revset}----------")?; - let expression = workspace_command.parse_revset(revset)?; + let expression = workspace_command.parse_revset(revset, Some(ui))?; // Time both evaluation and iteration. let routine = |workspace_command: &WorkspaceCommandHelper, expression| { workspace_command diff --git a/src/commands/branch.rs b/src/commands/branch.rs index 2b337023fa..acb35caa1b 100644 --- a/src/commands/branch.rs +++ b/src/commands/branch.rs @@ -151,7 +151,7 @@ fn cmd_branch_create( } let target_commit = - workspace_command.resolve_single_rev(args.revision.as_deref().unwrap_or("@"))?; + workspace_command.resolve_single_rev(args.revision.as_deref().unwrap_or("@"), ui)?; workspace_command.check_rewritable(&target_commit)?; let mut tx = workspace_command.start_transaction(&format!( "create {} pointing to commit {}", @@ -182,7 +182,7 @@ fn cmd_branch_set( } let target_commit = - workspace_command.resolve_single_rev(args.revision.as_deref().unwrap_or("@"))?; + workspace_command.resolve_single_rev(args.revision.as_deref().unwrap_or("@"), ui)?; workspace_command.check_rewritable(&target_commit)?; if !args.allow_backwards && !branch_names.iter().all(|branch_name| { @@ -344,7 +344,7 @@ fn cmd_branch_list( let filter_expressions: Vec<_> = args .revisions .iter() - .map(|revision_str| workspace_command.parse_revset(revision_str)) + .map(|revision_str| workspace_command.parse_revset(revision_str, Some(ui))) .try_collect()?; let filter_expression = RevsetExpression::union_all(&filter_expressions); // Intersects with the set of all branch targets to minimize the lookup space. diff --git a/src/commands/git.rs b/src/commands/git.rs index fb9ad1f4c3..600c712f88 100644 --- a/src/commands/git.rs +++ b/src/commands/git.rs @@ -612,12 +612,12 @@ fn cmd_git_push( let change_commits: Vec<_> = args .change .iter() - .map(|change_str| workspace_command.resolve_single_rev(change_str)) + .map(|change_str| workspace_command.resolve_single_rev(change_str, ui)) .try_collect()?; // TODO: Narrow search space to local target commits. // TODO: Remove redundant CommitId -> Commit -> CommitId round trip. let revision_commit_ids: HashSet<_> = - resolve_multiple_nonempty_revsets(&args.revisions, &workspace_command)? + resolve_multiple_nonempty_revsets(&args.revisions, &workspace_command, ui)? .iter() .map(|commit| commit.id().clone()) .collect(); @@ -692,7 +692,7 @@ fn cmd_git_push( let short_change_id = short_change_hash(commit.change_id()); if tx .base_workspace_helper() - .resolve_single_rev(&short_change_id) + .resolve_single_rev(&short_change_id, ui) .is_ok() { // Short change ID is not ambiguous, so update the branch name to use it. @@ -985,7 +985,7 @@ fn cmd_git_submodule_print_gitmodules( ) -> Result<(), CommandError> { let workspace_command = command.workspace_helper(ui)?; let repo = workspace_command.repo(); - let commit = workspace_command.resolve_single_rev(&args.revisions)?; + let commit = workspace_command.resolve_single_rev(&args.revisions, ui)?; let gitmodules_path = RepoPath::from_internal_string(".gitmodules"); let mut gitmodules_file = match commit.tree().path_value(&gitmodules_path) { None => { diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 2cd0dbf3ca..6d5cfbc029 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -57,7 +57,7 @@ use tracing::instrument; use crate::cli_util::{ check_stale_working_copy, get_new_config_file_path, print_checkout_stats, - resolve_multiple_nonempty_revsets, resolve_multiple_nonempty_revsets_flag_guarded, + resolve_multiple_nonempty_revsets, resolve_multiple_nonempty_revsets_default_single, run_ui_editor, serialize_config_value, short_commit_hash, user_error, user_error_with_hint, write_config_value_to_file, Args, CommandError, CommandHelper, DescriptionArg, LogContentFormat, RevisionArg, WorkspaceCommandHelper, @@ -528,8 +528,8 @@ struct NewArgs { /// The change description to use #[arg(long, short, default_value = "")] message: DescriptionArg, - /// Allow revsets expanding to multiple commits in a single argument - #[arg(long, short = 'L')] + /// Deprecated. Please prefix the revset with `all:` instead. + #[arg(long, short = 'L', hide = true)] allow_large_revsets: bool, /// Insert the new change between the target commit(s) and their children #[arg(long, short = 'A', visible_alias = "after")] @@ -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): @@ -858,9 +858,8 @@ struct RebaseArgs { /// commit) #[arg(long, short, required = true)] destination: Vec, - /// Allow revsets expanding to multiple commits in a single argument (for - /// those options that can be repeated) - #[arg(long, short = 'L')] + /// Deprecated. Please prefix the revset with `all:` instead. + #[arg(long, short = 'L', hide = true)] allow_large_revsets: bool, } @@ -1274,7 +1273,7 @@ fn cmd_checkout( args: &CheckoutArgs, ) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; - let target = workspace_command.resolve_single_rev(&args.revision)?; + let target = workspace_command.resolve_single_rev(&args.revision, ui)?; let mut tx = workspace_command.start_transaction(&format!("check out commit {}", target.id().hex())); let commit_builder = tx @@ -1365,7 +1364,7 @@ Make sure they're ignored, then try again.", #[instrument(skip_all)] fn cmd_files(ui: &mut Ui, command: &CommandHelper, args: &FilesArgs) -> Result<(), CommandError> { let workspace_command = command.workspace_helper(ui)?; - let commit = workspace_command.resolve_single_rev(&args.revision)?; + let commit = workspace_command.resolve_single_rev(&args.revision, ui)?; let matcher = workspace_command.matcher_from_values(&args.paths)?; ui.request_pager(); for (name, _value) in commit.tree().entries_matching(matcher.as_ref()) { @@ -1377,7 +1376,7 @@ fn cmd_files(ui: &mut Ui, command: &CommandHelper, args: &FilesArgs) -> Result<( #[instrument(skip_all)] fn cmd_cat(ui: &mut Ui, command: &CommandHelper, args: &CatArgs) -> Result<(), CommandError> { let workspace_command = command.workspace_helper(ui)?; - let commit = workspace_command.resolve_single_rev(&args.revision)?; + let commit = workspace_command.resolve_single_rev(&args.revision, ui)?; let path = workspace_command.parse_file_path(&args.path)?; let repo = workspace_command.repo(); match commit.tree().path_value(&path) { @@ -1411,13 +1410,13 @@ fn cmd_diff(ui: &mut Ui, command: &CommandHelper, args: &DiffArgs) -> Result<(), let from_tree; let to_tree; if args.from.is_some() || args.to.is_some() { - let from = workspace_command.resolve_single_rev(args.from.as_deref().unwrap_or("@"))?; + let from = workspace_command.resolve_single_rev(args.from.as_deref().unwrap_or("@"), ui)?; from_tree = from.tree(); - let to = workspace_command.resolve_single_rev(args.to.as_deref().unwrap_or("@"))?; + let to = workspace_command.resolve_single_rev(args.to.as_deref().unwrap_or("@"), ui)?; to_tree = to.tree(); } else { let commit = - workspace_command.resolve_single_rev(args.revision.as_deref().unwrap_or("@"))?; + workspace_command.resolve_single_rev(args.revision.as_deref().unwrap_or("@"), ui)?; let parents = commit.parents(); from_tree = merge_commit_trees(workspace_command.repo().as_ref(), &parents)?; to_tree = commit.tree() @@ -1438,7 +1437,7 @@ fn cmd_diff(ui: &mut Ui, command: &CommandHelper, args: &DiffArgs) -> Result<(), #[instrument(skip_all)] fn cmd_show(ui: &mut Ui, command: &CommandHelper, args: &ShowArgs) -> Result<(), CommandError> { let workspace_command = command.workspace_helper(ui)?; - let commit = workspace_command.resolve_single_rev(&args.revision)?; + let commit = workspace_command.resolve_single_rev(&args.revision, ui)?; let template_string = command.settings().config().get_string("templates.show")?; let template = workspace_command.parse_commit_template(&template_string)?; ui.request_pager(); @@ -1560,12 +1559,12 @@ fn cmd_log(ui: &mut Ui, command: &CommandHelper, args: &LogArgs) -> Result<(), C let revset_expression = { let mut expression = if args.revisions.is_empty() { - workspace_command.parse_revset(&command.settings().default_revset())? + workspace_command.parse_revset(&command.settings().default_revset(), Some(ui))? } else { let expressions: Vec<_> = args .revisions .iter() - .map(|revision_str| workspace_command.parse_revset(revision_str)) + .map(|revision_str| workspace_command.parse_revset(revision_str, Some(ui))) .try_collect()?; RevsetExpression::union_all(&expressions) }; @@ -1720,7 +1719,7 @@ fn cmd_log(ui: &mut Ui, command: &CommandHelper, args: &LogArgs) -> Result<(), C fn cmd_obslog(ui: &mut Ui, command: &CommandHelper, args: &ObslogArgs) -> Result<(), CommandError> { let workspace_command = command.workspace_helper(ui)?; - let start_commit = workspace_command.resolve_single_rev(&args.revision)?; + let start_commit = workspace_command.resolve_single_rev(&args.revision, ui)?; let wc_commit_id = workspace_command.get_wc_commit_id(); let diff_formats = @@ -1823,8 +1822,8 @@ fn cmd_interdiff( args: &InterdiffArgs, ) -> Result<(), CommandError> { let workspace_command = command.workspace_helper(ui)?; - let from = workspace_command.resolve_single_rev(args.from.as_deref().unwrap_or("@"))?; - let to = workspace_command.resolve_single_rev(args.to.as_deref().unwrap_or("@"))?; + let from = workspace_command.resolve_single_rev(args.from.as_deref().unwrap_or("@"), ui)?; + let to = workspace_command.resolve_single_rev(args.to.as_deref().unwrap_or("@"), ui)?; let from_tree = rebase_to_dest_parent(&workspace_command, &from, &to)?; let matcher = workspace_command.matcher_from_values(&args.paths)?; @@ -1976,7 +1975,7 @@ fn cmd_describe( args: &DescribeArgs, ) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; - let commit = workspace_command.resolve_single_rev(&args.revision)?; + let commit = workspace_command.resolve_single_rev(&args.revision, ui)?; workspace_command.check_rewritable(&commit)?; let description = if args.stdin { let mut buffer = String::new(); @@ -2059,7 +2058,7 @@ fn cmd_duplicate( ) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; let to_duplicate: IndexSet = - resolve_multiple_nonempty_revsets(&args.revisions, &workspace_command)?; + resolve_multiple_nonempty_revsets(&args.revisions, &workspace_command, ui)?; to_duplicate .iter() .map(|commit| workspace_command.check_rewritable(commit)) @@ -2117,7 +2116,7 @@ fn cmd_abandon( args: &AbandonArgs, ) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; - let to_abandon = resolve_multiple_nonempty_revsets(&args.revisions, &workspace_command)?; + let to_abandon = resolve_multiple_nonempty_revsets(&args.revisions, &workspace_command, ui)?; to_abandon .iter() .map(|commit| workspace_command.check_rewritable(commit)) @@ -2164,7 +2163,7 @@ fn cmd_abandon( #[instrument(skip_all)] fn cmd_edit(ui: &mut Ui, command: &CommandHelper, args: &EditArgs) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; - let new_commit = workspace_command.resolve_single_rev(&args.revision)?; + let new_commit = workspace_command.resolve_single_rev(&args.revision, ui)?; workspace_command.check_rewritable(&new_commit)?; if workspace_command.get_wc_commit_id() == Some(new_commit.id()) { ui.write("Already editing that commit\n")?; @@ -2181,14 +2180,11 @@ fn cmd_edit(ui: &mut Ui, command: &CommandHelper, args: &EditArgs) -> Result<(), /// to be rewriteable. fn resolve_destination_revs( workspace_command: &WorkspaceCommandHelper, + ui: &mut Ui, revisions: &[RevisionArg], - allow_plural_revsets: bool, ) -> Result, CommandError> { - let commits = resolve_multiple_nonempty_revsets_flag_guarded( - workspace_command, - revisions, - allow_plural_revsets, - )?; + let commits = + resolve_multiple_nonempty_revsets_default_single(workspace_command, ui, revisions)?; let root_commit_id = workspace_command.repo().store().root_commit_id(); if commits.len() >= 2 && commits.iter().any(|c| c.id() == root_commit_id) { Err(user_error("Cannot merge with root revision")) @@ -2199,18 +2195,20 @@ fn resolve_destination_revs( #[instrument(skip_all)] fn cmd_new(ui: &mut Ui, command: &CommandHelper, args: &NewArgs) -> Result<(), CommandError> { + if args.allow_large_revsets { + return Err(user_error( + "--allow-large-revsets has been deprecated. +Please use `jj new 'all:x|y'` instead of `jj new --allow-large-revsets x y`.", + )); + } let mut workspace_command = command.workspace_helper(ui)?; assert!( !args.revisions.is_empty(), "expected a non-empty list from clap" ); - let target_commits = resolve_destination_revs( - &workspace_command, - &args.revisions, - args.allow_large_revsets, - )? - .into_iter() - .collect_vec(); + let target_commits = resolve_destination_revs(&workspace_command, ui, &args.revisions)? + .into_iter() + .collect_vec(); let target_ids = target_commits.iter().map(|c| c.id().clone()).collect_vec(); let mut tx = workspace_command.start_transaction("new empty commit"); let mut num_rebased = 0; @@ -2352,9 +2350,9 @@ fn combine_messages( #[instrument(skip_all)] fn cmd_move(ui: &mut Ui, command: &CommandHelper, args: &MoveArgs) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; - let source = workspace_command.resolve_single_rev(args.from.as_deref().unwrap_or("@"))?; + let source = workspace_command.resolve_single_rev(args.from.as_deref().unwrap_or("@"), ui)?; let mut destination = - workspace_command.resolve_single_rev(args.to.as_deref().unwrap_or("@"))?; + workspace_command.resolve_single_rev(args.to.as_deref().unwrap_or("@"), ui)?; if source.id() == destination.id() { return Err(user_error("Source and destination cannot be the same.")); } @@ -2441,7 +2439,7 @@ from the source will be moved into the destination. #[instrument(skip_all)] fn cmd_squash(ui: &mut Ui, command: &CommandHelper, args: &SquashArgs) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; - let commit = workspace_command.resolve_single_rev(&args.revision)?; + let commit = workspace_command.resolve_single_rev(&args.revision, ui)?; workspace_command.check_rewritable(&commit)?; let parents = commit.parents(); if parents.len() != 1 { @@ -2535,7 +2533,7 @@ fn cmd_unsquash( args: &UnsquashArgs, ) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; - let commit = workspace_command.resolve_single_rev(&args.revision)?; + let commit = workspace_command.resolve_single_rev(&args.revision, ui)?; workspace_command.check_rewritable(&commit)?; let parents = commit.parents(); if parents.len() != 1 { @@ -2612,7 +2610,7 @@ fn cmd_chmod(ui: &mut Ui, command: &CommandHelper, args: &ChmodArgs) -> Result<( .iter() .map(|path| workspace_command.parse_file_path(path)) .try_collect()?; - let commit = workspace_command.resolve_single_rev(&args.revision)?; + let commit = workspace_command.resolve_single_rev(&args.revision, ui)?; workspace_command.check_rewritable(&commit)?; let mut tx = workspace_command.start_transaction(&format!( @@ -2697,7 +2695,7 @@ fn cmd_resolve( ) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; let matcher = workspace_command.matcher_from_values(&args.paths)?; - let commit = workspace_command.resolve_single_rev(&args.revision)?; + let commit = workspace_command.resolve_single_rev(&args.revision, ui)?; let tree = commit.merged_tree()?; let conflicts = tree .conflicts() @@ -2850,12 +2848,12 @@ fn cmd_restore( let mut workspace_command = command.workspace_helper(ui)?; let (from_tree, to_commit); if args.from.is_some() || args.to.is_some() { - to_commit = workspace_command.resolve_single_rev(args.to.as_deref().unwrap_or("@"))?; + to_commit = workspace_command.resolve_single_rev(args.to.as_deref().unwrap_or("@"), ui)?; from_tree = workspace_command - .resolve_single_rev(args.from.as_deref().unwrap_or("@"))? + .resolve_single_rev(args.from.as_deref().unwrap_or("@"), ui)? .tree(); } else { - to_commit = workspace_command.resolve_single_rev("@")?; + to_commit = workspace_command.resolve_single_rev("@", ui)?; from_tree = merge_commit_trees(workspace_command.repo().as_ref(), &to_commit.parents())?; } workspace_command.check_rewritable(&to_commit)?; @@ -2908,16 +2906,17 @@ fn cmd_diffedit( let (target_commit, base_commits, diff_description); if args.from.is_some() || args.to.is_some() { - target_commit = workspace_command.resolve_single_rev(args.to.as_deref().unwrap_or("@"))?; + target_commit = + workspace_command.resolve_single_rev(args.to.as_deref().unwrap_or("@"), ui)?; base_commits = - vec![workspace_command.resolve_single_rev(args.from.as_deref().unwrap_or("@"))?]; + vec![workspace_command.resolve_single_rev(args.from.as_deref().unwrap_or("@"), ui)?]; diff_description = format!( "The diff initially shows the commit's changes relative to:\n{}", workspace_command.format_commit_summary(&base_commits[0]) ); } else { target_commit = - workspace_command.resolve_single_rev(args.revision.as_deref().unwrap_or("@"))?; + workspace_command.resolve_single_rev(args.revision.as_deref().unwrap_or("@"), ui)?; base_commits = target_commit.parents(); diff_description = "The diff initially shows the commit's changes.".to_string(); }; @@ -3006,7 +3005,7 @@ fn diff_summary_to_description(bytes: &[u8]) -> String { #[instrument(skip_all)] fn cmd_split(ui: &mut Ui, command: &CommandHelper, args: &SplitArgs) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; - let commit = workspace_command.resolve_single_rev(&args.revision)?; + let commit = workspace_command.resolve_single_rev(&args.revision, ui)?; workspace_command.check_rewritable(&commit)?; let matcher = workspace_command.matcher_from_values(&args.paths)?; let mut tx = @@ -3099,7 +3098,7 @@ don't make any changes, then the operation will be aborted. #[instrument(skip_all)] fn cmd_merge(ui: &mut Ui, command: &CommandHelper, args: &NewArgs) -> Result<(), CommandError> { - if !args.allow_large_revsets && args.revisions.len() < 2 { + if args.revisions.len() < 2 { return Err(CommandError::CliError(String::from( "Merge requires at least two revisions", ))); @@ -3109,22 +3108,21 @@ fn cmd_merge(ui: &mut Ui, command: &CommandHelper, args: &NewArgs) -> Result<(), #[instrument(skip_all)] fn cmd_rebase(ui: &mut Ui, command: &CommandHelper, args: &RebaseArgs) -> Result<(), CommandError> { + if args.allow_large_revsets { + return Err(user_error( + "--allow-large-revsets has been deprecated. +Please use `jj rebase -d 'all:x|y'` instead of `jj rebase --allow-large-revsets -d x -d y`.", + )); + } let mut workspace_command = command.workspace_helper(ui)?; - let new_parents = resolve_destination_revs( - &workspace_command, - &args.destination, - args.allow_large_revsets, - )? - .into_iter() - .collect_vec(); + let new_parents = resolve_destination_revs(&workspace_command, ui, &args.destination)? + .into_iter() + .collect_vec(); if let Some(rev_str) = &args.revision { rebase_revision(ui, command, &mut workspace_command, &new_parents, rev_str)?; } else if !args.source.is_empty() { - let source_commits = resolve_multiple_nonempty_revsets_flag_guarded( - &workspace_command, - &args.source, - args.allow_large_revsets, - )?; + let source_commits = + resolve_multiple_nonempty_revsets_default_single(&workspace_command, ui, &args.source)?; rebase_descendants( ui, command, @@ -3134,13 +3132,9 @@ fn cmd_rebase(ui: &mut Ui, command: &CommandHelper, args: &RebaseArgs) -> Result )?; } else { let branch_commits = if args.branch.is_empty() { - IndexSet::from([workspace_command.resolve_single_rev("@")?]) + IndexSet::from([workspace_command.resolve_single_rev("@", ui)?]) } else { - resolve_multiple_nonempty_revsets_flag_guarded( - &workspace_command, - &args.branch, - args.allow_large_revsets, - )? + resolve_multiple_nonempty_revsets_default_single(&workspace_command, ui, &args.branch)? }; rebase_branch( ui, @@ -3220,7 +3214,7 @@ fn rebase_revision( new_parents: &[Commit], rev_str: &str, ) -> Result<(), CommandError> { - let old_commit = workspace_command.resolve_single_rev(rev_str)?; + let old_commit = workspace_command.resolve_single_rev(rev_str, ui)?; workspace_command.check_rewritable(&old_commit)?; check_rebase_destinations(workspace_command.repo(), new_parents, &old_commit)?; let children_expression = RevsetExpression::commit(old_commit.id().clone()).children(); @@ -3318,10 +3312,10 @@ fn cmd_backout( args: &BackoutArgs, ) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; - let commit_to_back_out = workspace_command.resolve_single_rev(&args.revision)?; + let commit_to_back_out = workspace_command.resolve_single_rev(&args.revision, ui)?; let mut parents = vec![]; for revision_str in &args.destination { - let destination = workspace_command.resolve_single_rev(revision_str)?; + let destination = workspace_command.resolve_single_rev(revision_str, ui)?; parents.push(destination); } let mut tx = workspace_command.start_transaction(&format!( diff --git a/testing/bench-revsets-git.txt b/testing/bench-revsets-git.txt index cfab1baf1c..602afa143e 100644 --- a/testing/bench-revsets-git.txt +++ b/testing/bench-revsets-git.txt @@ -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() diff --git a/tests/test_abandon_command.rs b/tests/test_abandon_command.rs index fcd9c40cc2..b08e52f187 100644 --- a/tests/test_abandon_command.rs +++ b/tests/test_abandon_command.rs @@ -130,7 +130,7 @@ fn test_rebase_branch_with_merge() { // Test abandoning the same commit twice indirectly test_env.jj_cmd_success(&repo_path, &["undo"]); - let stdout = test_env.jj_cmd_success(&repo_path, &["abandon", "d:", "a:"]); + let stdout = test_env.jj_cmd_success(&repo_path, &["abandon", "d::", "a::"]); insta::assert_snapshot!(stdout, @r###" Abandoned the following commits: 5557ece3e631 e diff --git a/tests/test_branch_command.rs b/tests/test_branch_command.rs index c3a36015a9..4a92a57550 100644 --- a/tests/test_branch_command.rs +++ b/tests/test_branch_command.rs @@ -473,7 +473,7 @@ fn test_branch_list_filtered_by_revset() { insta::assert_snapshot!( test_env.jj_cmd_success( &local_path, - &["log", "-r:(branches() | remote_branches())", "-T", template], + &["log", "-r::(branches() | remote_branches())", "-T", template], ), @r###" ◉ e31634b64294 remote-rewrite* diff --git a/tests/test_diff_command.rs b/tests/test_diff_command.rs index 44e1408038..9c4a4307e6 100644 --- a/tests/test_diff_command.rs +++ b/tests/test_diff_command.rs @@ -389,7 +389,13 @@ fn test_color_words_diff_missing_newline() { let stdout = test_env.jj_cmd_success( &repo_path, - &["log", "-Tdescription", "-pr:@-", "--no-graph", "--reversed"], + &[ + "log", + "-Tdescription", + "-pr::@-", + "--no-graph", + "--reversed", + ], ); insta::assert_snapshot!(stdout, @r###" === Empty diff --git a/tests/test_duplicate_command.rs b/tests/test_duplicate_command.rs index 0676d394a9..0f4a51fd0b 100644 --- a/tests/test_duplicate_command.rs +++ b/tests/test_duplicate_command.rs @@ -109,7 +109,7 @@ fn test_duplicate_many() { ◉ 000000000000 "###); - let stdout = test_env.jj_cmd_success(&repo_path, &["duplicate", "b:"]); + let stdout = test_env.jj_cmd_success(&repo_path, &["duplicate", "b::"]); insta::assert_snapshot!(stdout, @r###" Duplicated 1394f625cbbd as 3b74d9691015 b Duplicated 921dde6e55c0 as 8348ddcec733 e @@ -150,7 +150,7 @@ fn test_duplicate_many() { // Try specifying the same commit twice indirectly test_env.jj_cmd_success(&repo_path, &["undo"]); - let stdout = test_env.jj_cmd_success(&repo_path, &["duplicate", "b:", "d:"]); + let stdout = test_env.jj_cmd_success(&repo_path, &["duplicate", "b::", "d::"]); insta::assert_snapshot!(stdout, @r###" Duplicated 1394f625cbbd as fa167d18a83a b Duplicated ebd06dba20ec as 2181781b4f81 d @@ -185,7 +185,7 @@ fn test_duplicate_many() { ◉ 2443ea76b0b1 a ◉ 000000000000 "###); - let stdout = test_env.jj_cmd_success(&repo_path, &["duplicate", "d:", "a"]); + let stdout = test_env.jj_cmd_success(&repo_path, &["duplicate", "d::", "a"]); insta::assert_snapshot!(stdout, @r###" Duplicated 2443ea76b0b1 as c6f7f8c4512e a Duplicated ebd06dba20ec as d94e4c55a68b d @@ -210,7 +210,7 @@ fn test_duplicate_many() { // Check for BUG -- makes too many 'a'-s, etc. test_env.jj_cmd_success(&repo_path, &["undo"]); - let stdout = test_env.jj_cmd_success(&repo_path, &["duplicate", "a:"]); + let stdout = test_env.jj_cmd_success(&repo_path, &["duplicate", "a::"]); insta::assert_snapshot!(stdout, @r###" Duplicated 2443ea76b0b1 as 0fe67a05989e a Duplicated 1394f625cbbd as e13ac0adabdf b diff --git a/tests/test_log_command.rs b/tests/test_log_command.rs index 8bef483e74..6e2509e6a7 100644 --- a/tests/test_log_command.rs +++ b/tests/test_log_command.rs @@ -30,6 +30,55 @@ fn test_log_with_empty_revision() { "###); } +#[test] +fn test_log_legacy_range_operator() { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]); + let repo_path = test_env.env_root().join("repo"); + + let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-r=@:"]); + insta::assert_snapshot!(stdout, @r###" + @ qpvuntsmwlqt test.user@example.com 2001-02-03 04:05:07.000 +07:00 230dd059e1b0 + │ (empty) (no description set) + ~ + "###); + insta::assert_snapshot!(stderr, @r###" + The `:` revset operator is deprecated. Please switch to `::`. + "###); + let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-r=:@"]); + insta::assert_snapshot!(stdout, @r###" + @ qpvuntsmwlqt test.user@example.com 2001-02-03 04:05:07.000 +07:00 230dd059e1b0 + │ (empty) (no description set) + ◉ zzzzzzzzzzzz 1970-01-01 00:00:00.000 +00:00 000000000000 + (empty) (no description set) + "###); + insta::assert_snapshot!(stderr, @r###" + The `:` revset operator is deprecated. Please switch to `::`. + "###); + let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-r=root:@"]); + insta::assert_snapshot!(stdout, @r###" + @ qpvuntsmwlqt test.user@example.com 2001-02-03 04:05:07.000 +07:00 230dd059e1b0 + │ (empty) (no description set) + ◉ zzzzzzzzzzzz 1970-01-01 00:00:00.000 +00:00 000000000000 + (empty) (no description set) + "###); + insta::assert_snapshot!(stderr, @r###" + The `:` revset operator is deprecated. Please switch to `::`. + "###); + let (stdout, stderr) = test_env.jj_cmd_ok( + &repo_path, + &["log", "-r=x", "--config-toml", "revset-aliases.x = '@:'"], + ); + insta::assert_snapshot!(stdout, @r###" + @ qpvuntsmwlqt test.user@example.com 2001-02-03 04:05:07.000 +07:00 230dd059e1b0 + │ (empty) (no description set) + ~ + "###); + insta::assert_snapshot!(stderr, @r###" + The `:` revset operator is deprecated. Please switch to `::`. + "###); +} + #[test] fn test_log_with_or_without_diff() { let test_env = TestEnvironment::default(); @@ -307,7 +356,7 @@ fn test_log_shortest_accessors() { @"qpv[untsmwlqt] ba1[a30916d29]"); insta::assert_snapshot!( - render(":@", r#"change_id.shortest() ++ " " ++ commit_id.shortest() ++ "\n""#), + render("::@", r#"change_id.shortest() ++ " " ++ commit_id.shortest() ++ "\n""#), @r###" wq 03 km f7 @@ -323,7 +372,7 @@ fn test_log_shortest_accessors() { "###); insta::assert_snapshot!( - render(":@", r#"format_id(change_id) ++ " " ++ format_id(commit_id) ++ "\n""#), + render("::@", r#"format_id(change_id) ++ " " ++ format_id(commit_id) ++ "\n""#), @r###" wq[nwkozpkust] 03[f51310b83e] km[kuslswpqwq] f7[7fb1909080] @@ -341,7 +390,7 @@ fn test_log_shortest_accessors() { // Can get shorter prefixes in configured revset test_env.add_config(r#"revsets.short-prefixes = "(@----):""#); insta::assert_snapshot!( - render(":@", r#"format_id(change_id) ++ " " ++ format_id(commit_id) ++ "\n""#), + render("::@", r#"format_id(change_id) ++ " " ++ format_id(commit_id) ++ "\n""#), @r###" w[qnwkozpkust] 03[f51310b83e] km[kuslswpqwq] f[77fb1909080] @@ -359,7 +408,7 @@ fn test_log_shortest_accessors() { // Can disable short prefixes by setting to empty string test_env.add_config(r#"revsets.short-prefixes = """#); insta::assert_snapshot!( - render(":@", r#"format_id(change_id) ++ " " ++ format_id(commit_id) ++ "\n""#), + render("::@", r#"format_id(change_id) ++ " " ++ format_id(commit_id) ++ "\n""#), @r###" wq[nwkozpkust] 03[f51310b83e] km[kuslswpqwq] f7[7fb1909080] diff --git a/tests/test_rebase_command.rs b/tests/test_rebase_command.rs index d698f8ed1a..c6528398fa 100644 --- a/tests/test_rebase_command.rs +++ b/tests/test_rebase_command.rs @@ -142,21 +142,17 @@ fn test_rebase_branch() { ◉ "###); - // Same test but with more than one revision per argument and same revision - // repeated in more than one argument + // Same test but with more than one revision per argument test_env.jj_cmd_success(&repo_path, &["undo"]); - let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-b=e|d", "-b=d", "-d=b"]); + let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-b=e|d", "-d=b"]); insta::assert_snapshot!(stderr, @r###" Error: Revset "e|d" resolved to more than one revision Hint: The revset "e|d" resolved to these revisions: e52756c82985 e 514fa6b265d4 d - If this was intentional, specify the `--allow-large-revsets` argument + Prefix the expression with 'all' to allow any number of revisions (i.e. 'all:e|d'). "###); - let stdout = test_env.jj_cmd_success( - &repo_path, - &["rebase", "-b=e|d", "-b=d", "-d=b", "--allow-large-revsets"], - ); + let stdout = test_env.jj_cmd_success(&repo_path, &["rebase", "-b=all:e|d", "-d=b"]); insta::assert_snapshot!(stdout, @r###" Rebased 2 commits Working copy now at: 817e3fb0dc64 e @@ -378,12 +374,9 @@ fn test_rebase_multiple_destinations() { Hint: The revset "b|c" resolved to these revisions: fe2e8e8b50b3 c d370aee184ba b - If this was intentional, specify the `--allow-large-revsets` argument + Prefix the expression with 'all' to allow any number of revisions (i.e. 'all:b|c'). "###); - let stdout = test_env.jj_cmd_success( - &repo_path, - &["rebase", "--allow-large-revsets", "-r", "a", "-d", "b|c"], - ); + let stdout = test_env.jj_cmd_success(&repo_path, &["rebase", "-r", "a", "-d", "all:b|c"]); insta::assert_snapshot!(stdout, @r###""###); insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" ◉ a @@ -399,50 +392,13 @@ fn test_rebase_multiple_destinations() { Error: More than one revset resolved to revision d370aee184ba "###); - // Adding --allow-large-revsets suppresses this error in addition to the large - // revsets error. - let stdout = test_env.jj_cmd_success( - &repo_path, - &[ - "rebase", - "-r", - "a", - "-d", - "b", - "-d", - "b", - "--allow-large-revsets", - ], - ); - insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" - ◉ a - ◉ b - │ @ c - ├─╯ - ◉ - "###); - let stdout = test_env.jj_cmd_success( + // Same error with 'all:' if there is overlap. + let stderr = test_env.jj_cmd_failure( &repo_path, - &[ - "rebase", - "-r", - "a", - "-d", - "b|c", - "-d", - "b", - "--allow-large-revsets", - ], + &["rebase", "-r", "a", "-d", "all:b|c", "-d", "b"], ); - insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" - ◉ a - ├─╮ - │ ◉ b - @ │ c - ├─╯ - ◉ + insta::assert_snapshot!(stderr, @r###" + Error: More than one revset resolved to revision d370aee184ba "###); let stderr = @@ -536,21 +492,17 @@ fn test_rebase_with_descendants() { ◉ "###); - // Same test as above, but with duplicate commits and multiple commits per - // argument + // Same test as above, but with multiple commits per argument test_env.jj_cmd_success(&repo_path, &["undo"]); - let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-s=b|d", "-s=d", "-d=a"]); + let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-s=b|d", "-d=a"]); insta::assert_snapshot!(stderr, @r###" Error: Revset "b|d" resolved to more than one revision Hint: The revset "b|d" resolved to these revisions: df54a9fd85ae d d370aee184ba b - If this was intentional, specify the `--allow-large-revsets` argument + Prefix the expression with 'all' to allow any number of revisions (i.e. 'all:b|d'). "###); - let stdout = test_env.jj_cmd_success( - &repo_path, - &["rebase", "-s=b|d", "-s=d", "-d=a", "--allow-large-revsets"], - ); + let stdout = test_env.jj_cmd_success(&repo_path, &["rebase", "-s=all:b|d", "-d=a"]); insta::assert_snapshot!(stdout, @r###" Rebased 3 commits Working copy now at: d17539f7ea7c d diff --git a/tests/test_revset_output.rs b/tests/test_revset_output.rs index 1f7eac1ae6..3c5f158ad5 100644 --- a/tests/test_revset_output.rs +++ b/tests/test_revset_output.rs @@ -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()"]);