git_branchless_query/
lib.rs

1use std::fmt::Write;
2
3use git_branchless_invoke::CommandContext;
4use itertools::Itertools;
5use lib::core::dag::Dag;
6use lib::core::effects::{Effects, OperationType};
7use lib::core::eventlog::{EventLogDb, EventReplayer};
8use lib::core::repo_ext::RepoExt;
9use lib::git::{CategorizedReferenceName, GitRunInfo, Repo};
10use lib::util::{ExitCode, EyreExitOr};
11use tracing::instrument;
12
13use git_branchless_opts::{QueryArgs, ResolveRevsetOptions, Revset};
14use git_branchless_revset::resolve_commits;
15
16/// `query` command.
17#[instrument]
18pub fn command_main(ctx: CommandContext, args: QueryArgs) -> EyreExitOr<()> {
19    let CommandContext {
20        effects,
21        git_run_info,
22    } = ctx;
23    let QueryArgs {
24        revset,
25        resolve_revset_options,
26        show_branches,
27        raw,
28    } = args;
29    query(
30        &effects,
31        &git_run_info,
32        revset,
33        &resolve_revset_options,
34        show_branches,
35        raw,
36    )
37}
38
39#[instrument]
40fn query(
41    effects: &Effects,
42    git_run_info: &GitRunInfo,
43    query: Revset,
44    resolve_revset_options: &ResolveRevsetOptions,
45    show_branches: bool,
46    raw: bool,
47) -> EyreExitOr<()> {
48    let repo = Repo::from_current_dir()?;
49    let conn = repo.get_db_conn()?;
50    let event_log_db = EventLogDb::new(&conn)?;
51    let event_replayer = EventReplayer::from_event_log_db(effects, &repo, &event_log_db)?;
52    let event_cursor = event_replayer.make_default_cursor();
53    let references_snapshot = repo.get_references_snapshot()?;
54    let mut dag = Dag::open_and_sync(
55        effects,
56        &repo,
57        &event_replayer,
58        event_cursor,
59        &references_snapshot,
60    )?;
61
62    let commit_set =
63        match resolve_commits(effects, &repo, &mut dag, &[query], resolve_revset_options) {
64            Ok(commit_sets) => commit_sets[0].clone(),
65            Err(err) => {
66                err.describe(effects)?;
67                return Ok(Err(ExitCode(1)));
68            }
69        };
70
71    if show_branches {
72        let commit_oids = {
73            let (effects, _progress) = effects.start_operation(OperationType::SortCommits);
74            let _effects = effects;
75
76            let commit_set = commit_set.intersection(&dag.branch_commits);
77            dag.sort(&commit_set)?
78        };
79        let ref_names = commit_oids
80            .into_iter()
81            .flat_map(
82                |oid| match references_snapshot.branch_oid_to_names.get(&oid) {
83                    Some(branch_names) => branch_names.iter().sorted().collect_vec(),
84                    None => Vec::new(),
85                },
86            )
87            .collect_vec();
88        for ref_name in ref_names {
89            let ref_name = CategorizedReferenceName::new(ref_name);
90            writeln!(effects.get_output_stream(), "{}", ref_name.render_suffix())?;
91        }
92    } else {
93        let commit_oids = {
94            let (effects, _progress) = effects.start_operation(OperationType::SortCommits);
95            let _effects = effects;
96            dag.sort(&commit_set)?
97        };
98        for commit_oid in commit_oids {
99            if raw {
100                writeln!(effects.get_output_stream(), "{commit_oid}")?;
101            } else {
102                let commit = repo.find_commit_or_fail(commit_oid)?;
103                writeln!(
104                    effects.get_output_stream(),
105                    "{}",
106                    effects
107                        .get_glyphs()
108                        .render(commit.friendly_describe(effects.get_glyphs())?)?,
109                )?;
110            }
111        }
112    }
113
114    Ok(Ok(()))
115}