diff --git a/git-branchless-revset/src/builtins.rs b/git-branchless-revset/src/builtins.rs index 48b20ab4a..c1034bf48 100644 --- a/git-branchless-revset/src/builtins.rs +++ b/git-branchless-revset/src/builtins.rs @@ -3,11 +3,12 @@ use eden_dag::nameset::hints::Hints; use lib::core::dag::CommitSet; use lib::core::eventlog::{EventLogDb, EventReplayer}; +use lib::core::repo_ext::RepoExt; use lib::core::rewrite::find_rewrite_target; use lib::git::{ - get_latest_test_command_path, get_test_tree_dir, Commit, MaybeZeroOid, Repo, - SerializedNonZeroOid, SerializedTestResult, TEST_ABORT_EXIT_CODE, TEST_INDETERMINATE_EXIT_CODE, - TEST_SUCCESS_EXIT_CODE, + get_latest_test_command_path, get_test_tree_dir, CategorizedReferenceName, Commit, + MaybeZeroOid, Repo, SerializedNonZeroOid, SerializedTestResult, TEST_ABORT_EXIT_CODE, + TEST_INDETERMINATE_EXIT_CODE, TEST_SUCCESS_EXIT_CODE, }; use std::borrow::Cow; use std::collections::HashMap; @@ -179,8 +180,46 @@ fn fn_heads(ctx: &mut Context, name: &str, args: &[Expr]) -> EvalResult { #[instrument] fn fn_branches(ctx: &mut Context, name: &str, args: &[Expr]) -> EvalResult { - eval0(ctx, name, args)?; - Ok(ctx.dag.branch_commits.clone()) + let all_branch_commits = ctx.dag.branch_commits.clone(); + let branch_commits = match eval0_or_1_pattern(ctx, name, args)? { + None => all_branch_commits, + Some(pattern) => make_pattern_matcher( + ctx, + name, + args, + Box::new(move |repo, commit| { + let references_snapshot = match repo.get_references_snapshot() { + Ok(snapshot) => snapshot, + Err(err) => { + warn!(?repo, ?err, "Could not get references snapshot for repo"); + return Ok(false); + } + }; + let mut local_branches_at_commit = match references_snapshot + .branch_oid_to_names + .get(&commit.get_oid()) + { + None => return Ok(false), + Some(branches) => branches.iter().filter_map(|branch_name| { + let ref_name = CategorizedReferenceName::new(branch_name); + match ref_name { + CategorizedReferenceName::LocalBranch { .. } => { + Some(ref_name.render_suffix()) + } + CategorizedReferenceName::RemoteBranch { .. } + | CategorizedReferenceName::OtherRef { .. } => None, + } + }), + }; + let result = local_branches_at_commit + .any(|branch_name| pattern.matches_text(branch_name.as_str())); + + Ok(result) + }), + )?, + }; + + Ok(branch_commits) } #[instrument] diff --git a/git-branchless-revset/src/eval.rs b/git-branchless-revset/src/eval.rs index 68ccacb1d..dce037baf 100644 --- a/git-branchless-revset/src/eval.rs +++ b/git-branchless-revset/src/eval.rs @@ -1216,10 +1216,7 @@ mod tests { )?; { - let expr = Expr::FunctionCall( - Cow::Borrowed("merges"), - vec![] - ); + let expr = Expr::FunctionCall(Cow::Borrowed("merges"), vec![]); insta::assert_debug_snapshot!(eval_and_sort(&effects, &repo, &mut dag, &expr), @r###" Ok( [ @@ -1241,10 +1238,7 @@ mod tests { let expr = Expr::FunctionCall( Cow::Borrowed("not"), - vec![Expr::FunctionCall( - Cow::Borrowed("merges"), - vec![] - )], + vec![Expr::FunctionCall(Cow::Borrowed("merges"), vec![])], ); insta::assert_debug_snapshot!(eval_and_sort(&effects, &repo, &mut dag, &expr), @r###" Ok( @@ -1274,6 +1268,85 @@ mod tests { Ok(()) } + #[test] + fn test_eval_branches_with_pattern() -> eyre::Result<()> { + let git = make_git()?; + git.init_repo()?; + + git.detach_head()?; + git.commit_file("test1", 1)?; + git.run(&["branch", "test-1"])?; + git.commit_file("test2", 2)?; + git.run(&["branch", "test-2"])?; + + let effects = Effects::new_suppress_for_test(Glyphs::text()); + let repo = git.get_repo()?; + let conn = repo.get_db_conn()?; + let event_log_db = EventLogDb::new(&conn)?; + let event_replayer = EventReplayer::from_event_log_db(&effects, &repo, &event_log_db)?; + let event_cursor = event_replayer.make_default_cursor(); + let references_snapshot = repo.get_references_snapshot()?; + let mut dag = Dag::open_and_sync( + &effects, + &repo, + &event_replayer, + event_cursor, + &references_snapshot, + )?; + + { + let expr = Expr::FunctionCall(Cow::Borrowed("branches"), vec![]); + insta::assert_debug_snapshot!(eval_and_sort(&effects, &repo, &mut dag, &expr), @r###" + Ok( + [ + Commit { + inner: Commit { + id: f777ecc9b0db5ed372b2615695191a8a17f79f24, + summary: "create initial.txt", + }, + }, + Commit { + inner: Commit { + id: 62fc20d2a290daea0d52bdc2ed2ad4be6491010e, + summary: "create test1.txt", + }, + }, + Commit { + inner: Commit { + id: 96d1c37a3d4363611c49f7e52186e189a04c531f, + summary: "create test2.txt", + }, + }, + ], + ) + "###); + + let expr = Expr::FunctionCall( + Cow::Borrowed("branches"), + vec![Expr::Name(Cow::Borrowed("glob:test*"))], + ); + insta::assert_debug_snapshot!(eval_and_sort(&effects, &repo, &mut dag, &expr), @r###" + Ok( + [ + Commit { + inner: Commit { + id: 62fc20d2a290daea0d52bdc2ed2ad4be6491010e, + summary: "create test1.txt", + }, + }, + Commit { + inner: Commit { + id: 96d1c37a3d4363611c49f7e52186e189a04c531f, + summary: "create test2.txt", + }, + }, + ], + ) + "###); + } + Ok(()) + } + #[test] fn test_eval_aliases() -> eyre::Result<()> { let git = make_git()?;