Skip to main content

git_stk/commands/
status.rs

1use anyhow::Result;
2use clap_complete::engine::ArgValueCompleter;
3
4use crate::commands::Run;
5use crate::completions;
6use crate::providers::{ReviewState, detect_provider, review_provider};
7use crate::{git, stack};
8
9/// Print local and remote stack status for a branch.
10#[derive(Debug, clap::Args)]
11pub struct Status {
12    #[arg(add = ArgValueCompleter::new(completions::branch_candidates))]
13    branch: Option<String>,
14}
15
16impl Run for Status {
17    fn run(self) -> Result<()> {
18        print_status(self.branch.as_deref())
19    }
20}
21
22pub fn print_status(branch: Option<&str>) -> Result<()> {
23    let branch = branch
24        .map(str::to_owned)
25        .map_or_else(git::current_branch, Ok)?;
26    let parent = stack::parent_for_branch(&branch)?;
27    let children = stack::children_for_branch(&branch)?;
28
29    println!("branch: {branch}");
30    match parent.as_deref() {
31        Some(parent) => println!("parent: {parent}"),
32        None => println!("parent: none"),
33    }
34    if children.is_empty() {
35        println!("children: none");
36    } else {
37        println!("children: {}", children.join(", "));
38    }
39
40    let provider = detect_provider()?;
41    println!("provider: {} ({})", provider.kind, provider.source);
42    let review_provider = review_provider(provider.kind);
43
44    let review = review_provider.review_for_branch(&branch)?;
45    match &review {
46        Some(review) => {
47            println!(
48                "review: {} {} {} -> {}",
49                review.id, review.state, review.branch, review.base
50            );
51            println!("url: {}", review.url);
52
53            if let Some(parent) = parent.as_deref()
54                && parent != review.base
55            {
56                println!(
57                    "warning: review base is {}, local parent is {parent} - run `git stk submit`",
58                    review.base
59                );
60            }
61        }
62        None => println!("review: none"),
63    }
64
65    // Teach the loop: the next command, derived from review states and
66    // local drift. A sync covers the restack, so the nudges don't stack.
67    let mut hints = Vec::new();
68    if let Some(review) = &review
69        && review.state == ReviewState::Merged
70    {
71        hints.push(format!(
72            "review {} is merged - run `git stk sync`",
73            review.id
74        ));
75    }
76    if let Some(parent) = parent.as_deref() {
77        if let Ok(Some(parent_review)) = review_provider.review_for_branch(parent)
78            && parent_review.branch == parent
79            && parent_review.state == ReviewState::Merged
80        {
81            hints.push(format!(
82                "parent review {} is merged - run `git stk sync`",
83                parent_review.id
84            ));
85        }
86
87        if hints.is_empty()
88            && let Some(hint) = stack::behind_parent_hint(&branch, parent)
89        {
90            hints.push(hint);
91        }
92    }
93    for hint in hints {
94        println!("hint: {hint}");
95    }
96
97    Ok(())
98}