Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rebase -s: add support for --insert-after and --insert-before #3607

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

* New template function `raw_escape_sequence(...)` preserves escape sequences.

* New `coalesce(revsets...)` revset which returns commits in the first revset
in the `revsets` list that does not evaluate to `none()`.

### Fixed bugs

* Error on `trunk()` revset resolution is now handled gracefully.
Expand Down
4 changes: 4 additions & 0 deletions docs/revsets.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,10 @@ given [string pattern](#string-patterns).
example, `at_operation(@-, visible_heads())` will return all heads which were
visible at the previous operation.

* `coalesce(revsets...)`: Commits in the first revset in the list of `revsets`
which does not evaluate to `none()`. If all revsets evaluate to `none()`, then
the result of `coalesce` will also be `none()`.

[operation]: glossary.md#operation

??? examples
Expand Down
8 changes: 8 additions & 0 deletions lib/src/default_index/revset_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,14 @@ impl<'index> EvaluationContext<'index> {
self.take_latest_revset(candidate_set.as_ref(), *count),
))
}
ResolvedExpression::Coalesce(expression1, expression2) => {
let set1 = self.evaluate(expression1)?;
if set1.positions().attach(index).next().is_some() {
Ok(set1)
} else {
self.evaluate(expression2)
}
}
ResolvedExpression::Union(expression1, expression2) => {
let set1 = self.evaluate(expression1)?;
let set2 = self.evaluate(expression2)?;
Expand Down
26 changes: 26 additions & 0 deletions lib/src/revset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ pub enum RevsetExpression {
/// Copy of `repo.view().heads()`, should be set by `resolve_symbols()`.
visible_heads: Option<Vec<CommitId>>,
},
Coalesce(Rc<Self>, Rc<Self>),
Present(Rc<Self>),
NotIn(Rc<Self>),
Union(Rc<Self>, Rc<Self>),
Expand Down Expand Up @@ -528,6 +529,7 @@ pub enum ResolvedExpression {
candidates: Box<Self>,
count: usize,
},
Coalesce(Box<Self>, Box<Self>),
Union(Box<Self>, Box<Self>),
/// Intersects `candidates` with `predicate` by filtering.
FilterWithin {
Expand Down Expand Up @@ -853,6 +855,17 @@ static BUILTIN_FUNCTION_MAP: Lazy<HashMap<&'static str, RevsetFunction>> = Lazy:
visible_heads: None,
}))
});
map.insert("coalesce", |diagnostics, function, context| {
let ([], args) = function.expect_some_arguments()?;
let mut revset = RevsetExpression::none();
for arg in args.iter().rev() {
revset = Rc::new(RevsetExpression::Coalesce(
lower_expression(diagnostics, arg, context)?,
revset,
));
}
Ok(revset)
});
map
});

Expand Down Expand Up @@ -1171,6 +1184,12 @@ fn try_transform_expression<E>(
visible_heads: visible_heads.clone(),
}
}),
RevsetExpression::Coalesce(expression1, expression2) => transform_rec_pair(
(expression1, expression2),
pre,
post,
)?
.map(|(expression1, expression2)| RevsetExpression::Coalesce(expression1, expression2)),
RevsetExpression::Present(candidates) => {
transform_rec(candidates, pre, post)?.map(RevsetExpression::Present)
}
Expand Down Expand Up @@ -2050,6 +2069,10 @@ impl VisibilityResolutionContext<'_> {
let context = VisibilityResolutionContext { visible_heads };
context.resolve(candidates)
}
RevsetExpression::Coalesce(expression1, expression2) => ResolvedExpression::Coalesce(
self.resolve(expression1).into(),
self.resolve(expression2).into(),
),
RevsetExpression::Present(_) => {
panic!("Expression '{expression:?}' should have been resolved by caller");
}
Expand Down Expand Up @@ -2130,6 +2153,9 @@ impl VisibilityResolutionContext<'_> {
RevsetExpression::AtOperation { .. } => {
ResolvedPredicateExpression::Set(self.resolve(expression).into())
}
RevsetExpression::Coalesce(_, _) => {
ResolvedPredicateExpression::Set(self.resolve(expression).into())
}
RevsetExpression::Present(_) => {
panic!("Expression '{expression:?}' should have been resolved by caller")
}
Expand Down
78 changes: 78 additions & 0 deletions lib/tests/test_revset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3029,6 +3029,84 @@ fn test_evaluate_expression_at_operation() {
);
}

#[test]
fn test_evaluate_expression_coalesce() {
let settings = testutils::user_settings();
let test_repo = TestRepo::init();
let repo = &test_repo.repo;
let root_commit_id = repo.store().root_commit_id().clone();

let mut tx = repo.start_transaction(&settings);
let mut_repo = tx.repo_mut();
let mut graph_builder = CommitGraphBuilder::new(&settings, mut_repo);
let commit1 = graph_builder.initial_commit();
let commit2 = graph_builder.commit_with_parents(&[&commit1]);
mut_repo.set_local_bookmark_target("commit1", RefTarget::normal(commit1.id().clone()));
mut_repo.set_local_bookmark_target("commit2", RefTarget::normal(commit2.id().clone()));

assert_eq!(resolve_commit_ids(mut_repo, "coalesce()"), vec![]);
assert_eq!(resolve_commit_ids(mut_repo, "coalesce(none())"), vec![]);
assert_eq!(
resolve_commit_ids(mut_repo, "coalesce(all())"),
vec![
commit2.id().clone(),
commit1.id().clone(),
root_commit_id.clone(),
]
);
assert_eq!(
resolve_commit_ids(mut_repo, "coalesce(all(), commit1)"),
vec![
commit2.id().clone(),
commit1.id().clone(),
root_commit_id.clone(),
]
);
assert_eq!(
resolve_commit_ids(mut_repo, "coalesce(none(), commit1)"),
vec![commit1.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, "coalesce(commit1, commit2)"),
vec![commit1.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, "coalesce(none(), none(), commit2)"),
vec![commit2.id().clone()]
);
assert_eq!(
resolve_commit_ids(mut_repo, "coalesce(none(), commit1, commit2)"),
vec![commit1.id().clone()]
);
// Should resolve invalid symbols regardless of whether a specific revset is
// evaluated.
assert_matches!(
try_resolve_commit_ids(mut_repo, "coalesce(all(), commit1_invalid)"),
Err(RevsetResolutionError::NoSuchRevision { name, .. })
if name == "commit1_invalid"
);
assert_matches!(
try_resolve_commit_ids(mut_repo, "coalesce(none(), commit1_invalid)"),
Err(RevsetResolutionError::NoSuchRevision { name, .. })
if name == "commit1_invalid"
);
assert_matches!(
try_resolve_commit_ids(mut_repo, "coalesce(all(), commit1, commit2_invalid)"),
Err(RevsetResolutionError::NoSuchRevision { name, .. })
if name == "commit2_invalid"
);
assert_matches!(
try_resolve_commit_ids(mut_repo, "coalesce(none(), commit1, commit2_invalid)"),
Err(RevsetResolutionError::NoSuchRevision { name, .. })
if name == "commit2_invalid"
);
assert_matches!(
try_resolve_commit_ids(mut_repo, "coalesce(none(), commit1, commit2, commit2_invalid)"),
Err(RevsetResolutionError::NoSuchRevision { name, .. })
if name == "commit2_invalid"
);
}

#[test]
fn test_evaluate_expression_union() {
let settings = testutils::user_settings();
Expand Down