diff --git a/CHANGELOG.md b/CHANGELOG.md index f8b4e7fa8d..bebfc8b7d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * There is a new global `--quiet` flag to silence commands' non-primary output. +* new function `working_copies()` for revsets to show the working copy commits of all workspaces. + ### Fixed bugs ## [0.15.1] - 2024-03-06 diff --git a/docs/revsets.md b/docs/revsets.md index 2f947c6321..26319bc393 100644 --- a/docs/revsets.md +++ b/docs/revsets.md @@ -171,6 +171,8 @@ given [string pattern](#string-patterns). * `present(x)`: Same as `x`, but evaluated to `none()` if any of the commits in `x` doesn't exist (e.g. is an unknown branch name.) +* `working_copies()`: The working copy commits across all the workspaces. + ## String patterns Functions that perform string matching support the following pattern syntax: diff --git a/lib/src/revset.rs b/lib/src/revset.rs index de61b438cc..b495911d52 100644 --- a/lib/src/revset.rs +++ b/lib/src/revset.rs @@ -275,6 +275,7 @@ pub const GENERATION_RANGE_EMPTY: Range = 0..0; #[derive(Clone, Debug, Eq, PartialEq)] pub enum RevsetCommitRef { WorkingCopy(WorkspaceId), + WorkingCopies, Symbol(String), RemoteSymbol { name: String, @@ -371,6 +372,10 @@ impl RevsetExpression { ))) } + pub fn working_copies() -> Rc { + Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::WorkingCopies)) + } + pub fn symbol(value: String) -> Rc { Rc::new(RevsetExpression::CommitRef(RevsetCommitRef::Symbol(value))) } @@ -1157,6 +1162,10 @@ static BUILTIN_FUNCTION_MAP: Lazy> = Lazy: expect_no_arguments(name, arguments_pair)?; Ok(RevsetExpression::all()) }); + map.insert("working_copies", |name, arguments_pair, _state| { + expect_no_arguments(name, arguments_pair)?; + Ok(RevsetExpression::working_copies()) + }); map.insert("heads", |name, arguments_pair, state| { let arg = expect_one_argument(name, arguments_pair)?; let candidates = parse_expression_rule(arg.into_inner(), state)?; @@ -2139,6 +2148,10 @@ fn resolve_commit_ref( }) } } + RevsetCommitRef::WorkingCopies => { + let wc_commits = repo.view().wc_commit_ids().values().cloned().collect_vec(); + Ok(wc_commits) + } RevsetCommitRef::VisibleHeads => Ok(repo.view().heads().iter().cloned().collect_vec()), RevsetCommitRef::Root => Ok(vec![repo.store().root_commit_id().clone()]), RevsetCommitRef::Branches(pattern) => { diff --git a/lib/tests/test_revset.rs b/lib/tests/test_revset.rs index ec52420cb1..e1a8314007 100644 --- a/lib/tests/test_revset.rs +++ b/lib/tests/test_revset.rs @@ -371,6 +371,41 @@ fn test_resolve_working_copy() { assert_eq!(resolve(ws2), vec![commit2.id().clone()]); } +#[test] +fn test_resolve_working_copies() { + let settings = testutils::user_settings(); + let test_repo = TestRepo::init(); + let repo = &test_repo.repo; + + let mut tx = repo.start_transaction(&settings); + let mut_repo = tx.mut_repo(); + + let commit1 = write_random_commit(mut_repo, &settings); + let commit2 = write_random_commit(mut_repo, &settings); + + // Add some workspaces + let ws1 = WorkspaceId::new("ws1".to_string()); + let ws2 = WorkspaceId::new("ws2".to_string()); + + // add one commit to each working copy + mut_repo + .set_wc_commit(ws1.clone(), commit1.id().clone()) + .unwrap(); + mut_repo + .set_wc_commit(ws2.clone(), commit2.id().clone()) + .unwrap(); + let resolve = || -> Vec { + RevsetExpression::working_copies() + .evaluate_programmatic(mut_repo) + .unwrap() + .iter() + .collect() + }; + + // ensure our output has those two commits + assert_eq!(resolve(), vec![commit2.id().clone(), commit1.id().clone()]); +} + #[test] fn test_resolve_symbol_branches() { let settings = testutils::user_settings();