Skip to content

Commit

Permalink
feat(revset): allow branches() to accept a text pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
claytonrcarter committed Dec 31, 2023
1 parent f5484e5 commit 8ca6b55
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- BREAKING (#1128) Arguments/revsets passed to `git sync` are now resolved to their respective stacks.
- This allows `git sync my-branch` to work as expected, instead of needing to use `git sync 'stack(my-branch)'`. The behavior of `git sync` when called without arguments is not affected by this change. If you rely on the previous behavior, please use `git move -x <commit(s)/revset> -d 'main()'` instead.
- (#1169) `git record` now accepts multible `--message` arguments.
- (#1130) `branches()` revset function now accepts an optional text pattern argument to limit which branches are matched.

### Fixed

Expand Down
67 changes: 60 additions & 7 deletions git-branchless-revset/src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -179,8 +180,48 @@ 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 pattern = match eval0_or_1_pattern(ctx, name, args)? {
Some(pattern) => pattern,
None => return Ok(ctx.dag.branch_commits.clone()),
};

let branch_oid_to_names = ctx
.repo
.get_references_snapshot()
.wrap_err("Could not get references snapshot for repo")
.map_err(EvalError::OtherError)?
.branch_oid_to_names;

let branch_commits = make_pattern_matcher_for_set(
ctx,
name,
args,
Box::new(move |_repo, commit| {
let branches_at_commit = match branch_oid_to_names.get(&commit.get_oid()) {
Some(branches) => branches,
None => return Ok(false),
};

let result = branches_at_commit
.iter()
.filter_map(
|branch_name| match CategorizedReferenceName::new(branch_name) {
name @ CategorizedReferenceName::LocalBranch { .. } => {
Some(name.render_suffix())
}
// we only care about local branches
CategorizedReferenceName::RemoteBranch { .. }
| CategorizedReferenceName::OtherRef { .. } => None,
},
)
.any(|branch_name| pattern.matches_text(branch_name.as_str()));

Ok(result)
}),
Some(ctx.dag.branch_commits.clone()),
)?;

Ok(branch_commits)
}

#[instrument]
Expand Down Expand Up @@ -275,12 +316,24 @@ fn fn_stack(ctx: &mut Context, name: &str, args: &[Expr]) -> EvalResult {

type MatcherFn = dyn Fn(&Repo, &Commit) -> Result<bool, PatternError> + Sync + Send;

#[instrument(skip(f))]
/// Make a pattern matcher that operates on all visible commits.
fn make_pattern_matcher(
ctx: &mut Context,
name: &str,
args: &[Expr],
f: Box<MatcherFn>,
) -> Result<CommitSet, EvalError> {
make_pattern_matcher_for_set(ctx, name, args, f, None)
}

/// Make a pattern matcher that operates only on the given set of commits.
#[instrument(skip(f))]
fn make_pattern_matcher_for_set(
ctx: &mut Context,
name: &str,
args: &[Expr],
f: Box<MatcherFn>,
commits_to_match: Option<CommitSet>,
) -> Result<CommitSet, EvalError> {
struct Matcher {
expr: String,
Expand All @@ -301,7 +354,7 @@ fn make_pattern_matcher(
expr: Expr::FunctionCall(Cow::Borrowed(name), args.to_vec()).to_string(),
f,
};
let matcher = make_pattern_matcher_set(ctx, ctx.repo, Box::new(matcher))?;
let matcher = make_pattern_matcher_set(ctx, ctx.repo, Box::new(matcher), commits_to_match)?;
Ok(matcher)
}

Expand Down
79 changes: 79 additions & 0 deletions git-branchless-revset/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1268,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()?;
Expand Down
26 changes: 15 additions & 11 deletions git-branchless-revset/src/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,13 @@ pub(super) fn make_pattern_matcher_set(
ctx: &mut Context,
repo: &Repo,
matcher: Box<dyn PatternMatcher>,
commits_to_match: Option<CommitSet>,
) -> Result<CommitSet, PatternError> {
struct MatcherNameSetQuery {
effects: Effects,
matcher: Box<dyn PatternMatcher>,
repo: Arc<Mutex<Repo>>,
visible_commits: CommitSet,
commits_to_match: CommitSet,
}

impl MatcherNameSetQuery {
Expand All @@ -152,10 +153,10 @@ pub(super) fn make_pattern_matcher_set(
)));
let _effects = effects;

let len = self.visible_commits.count().await?;
let len = self.commits_to_match.count().await?;
progress.notify_progress(0, len);

let stream = self.visible_commits.iter().await?;
let stream = self.commits_to_match.iter().await?;
let commit_oids = stream.collect::<Vec<_>>().await;
let repo = self.repo.lock().unwrap();
let repo_pool = RepoResource::new_pool(&repo).map_err(make_dag_backend_error)?;
Expand Down Expand Up @@ -191,7 +192,7 @@ pub(super) fn make_pattern_matcher_set(
}

async fn contains(&self, name: &CommitVertex) -> eden_dag::Result<bool> {
if !self.visible_commits.contains(name).await? {
if !self.commits_to_match.contains(name).await? {
return Ok(false);
}

Expand All @@ -209,18 +210,21 @@ pub(super) fn make_pattern_matcher_set(
}

let repo = repo.try_clone().map_err(PatternError::Repo)?;
let visible_commits = ctx
.dag
.query_visible_commits_slow()
.map_err(EvalError::OtherError)
.map_err(Box::new)?
.clone();
let commits_to_match = match commits_to_match {
Some(commits) => commits,
None => ctx
.dag
.query_visible_commits_slow()
.map_err(EvalError::OtherError)
.map_err(Box::new)?
.clone(),
};

let matcher = Arc::new(MatcherNameSetQuery {
effects: ctx.effects.clone(),
matcher,
repo: Arc::new(Mutex::new(repo)),
visible_commits,
commits_to_match,
});
Ok(CommitSet::from_async_evaluate_contains(
{
Expand Down

0 comments on commit 8ca6b55

Please sign in to comment.