git_branchless_revset/
resolve.rs1use std::fmt::Write;
2
3use eyre::WrapErr;
4use git_branchless_opts::{ResolveRevsetOptions, Revset};
5use lib::core::config::get_smartlog_default_revset;
6use lib::core::dag::{CommitSet, Dag};
7use lib::core::effects::Effects;
8use lib::git::Repo;
9use thiserror::Error;
10use tracing::instrument;
11
12use crate::eval::EvalError;
13use crate::parser::ParseError;
14use crate::Expr;
15use crate::{eval, parse};
16
17#[allow(clippy::enum_variant_names)]
19#[derive(Debug, Error)]
20pub enum ResolveError {
21 #[error("parse error in {expr:?}: {source}")]
22 ParseError { expr: String, source: ParseError },
23
24 #[error("evaluation error in {expr:?}: {source}")]
25 EvalError { expr: String, source: EvalError },
26
27 #[error("DAG query error: {source}")]
28 DagError { source: eden_dag::Error },
29
30 #[error(transparent)]
31 OtherError { source: eyre::Error },
32}
33
34impl ResolveError {
35 pub fn describe(self, effects: &Effects) -> eyre::Result<()> {
36 match self {
37 ResolveError::ParseError { expr, source } => {
38 writeln!(
39 effects.get_error_stream(),
40 "Parse error for expression '{expr}': {source}"
41 )?;
42 Ok(())
43 }
44 ResolveError::EvalError { expr, source } => {
45 writeln!(
46 effects.get_error_stream(),
47 "Evaluation error for expression '{expr}': {source}"
48 )?;
49 Ok(())
50 }
51 ResolveError::DagError { source } => Err(source.into()),
52 ResolveError::OtherError { source } => Err(source),
53 }
54 }
55}
56
57pub fn check_revset_syntax(repo: &Repo, revsets: &[Revset]) -> Result<(), ParseError> {
59 for Revset(revset) in revsets {
60 if let Ok(Some(_)) = repo.revparse_single_commit(revset) {
61 continue;
62 }
63 let _expr: Expr = parse(revset)?;
64 }
65 Ok(())
66}
67
68#[instrument]
74pub fn resolve_commits(
75 effects: &Effects,
76 repo: &Repo,
77 dag: &mut Dag,
78 revsets: &[Revset],
79 options: &ResolveRevsetOptions,
80) -> Result<Vec<CommitSet>, ResolveError> {
81 let mut dag_with_obsolete = if options.show_hidden_commits {
82 Some(
83 dag.clear_obsolete_commits(repo)
84 .map_err(|err| ResolveError::OtherError { source: err })?,
85 )
86 } else {
87 None
88 };
89 let dag = dag_with_obsolete.as_mut().unwrap_or(dag);
90
91 let mut commit_sets = Vec::new();
92 for Revset(revset) in revsets {
93 if let Ok(Some(commit)) = repo.revparse_single_commit(revset) {
98 let commit_set = CommitSet::from(commit.get_oid());
99 dag.sync_from_oids(effects, repo, CommitSet::empty(), commit_set.clone())
100 .map_err(|err| ResolveError::OtherError { source: err })?;
101 commit_sets.push(commit_set);
102 continue;
103 }
104
105 let expr = parse(revset).map_err(|err| ResolveError::ParseError {
106 expr: revset.clone(),
107 source: err,
108 })?;
109 let commits = eval(effects, repo, dag, &expr).map_err(|err| ResolveError::EvalError {
110 expr: revset.clone(),
111 source: err,
112 })?;
113
114 commit_sets.push(commits);
115 }
116 Ok(commit_sets)
117}
118
119pub fn resolve_default_smartlog_commits(
122 effects: &Effects,
123 repo: &Repo,
124 dag: &mut Dag,
125) -> eyre::Result<CommitSet> {
126 let revset = Revset(get_smartlog_default_revset(repo)?);
127 let results = resolve_commits(
128 effects,
129 repo,
130 dag,
131 &[revset],
132 &ResolveRevsetOptions::default(),
133 )
134 .wrap_err("Resolving default smartlog commits")?;
135 let commits = results.first().unwrap();
136 Ok(commits.clone())
137}