Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 167 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::rc::Rc;
use gh_stack::api::PullRequest;
use gh_stack::graph::FlatDep;
use gh_stack::land::{self, LandError, LandOptions};
use gh_stack::status::{self, StatusConfig};
use gh_stack::util::loop_until_confirm;
use gh_stack::Credentials;
use gh_stack::{api, git, graph, markdown, persist, tree};
Expand Down Expand Up @@ -100,6 +101,51 @@ fn clap<'a, 'b>() -> App<'a, 'b> {
Arg::with_name("no-color")
.long("no-color")
.help("Disable colors and Unicode characters"),
)
.arg(
Arg::with_name("status")
.long("status")
.takes_value(false)
.help("Show status bits (CI, approval, conflicts, stack health)"),
);

let status_cmd = SubCommand::with_name("status")
.about("Show stack status with CI, approval, and merge readiness indicators")
.setting(AppSettings::ArgRequiredElseHelp)
.arg(identifier.clone())
.arg(exclude.clone())
.arg(repository.clone())
.arg(origin.clone())
.arg(
Arg::with_name("project")
.long("project")
.short("C")
.value_name("PATH")
.help("Path to local repository (auto-detected if omitted)"),
)
.arg(
Arg::with_name("no-checks")
.long("no-checks")
.takes_value(false)
.help("Skip fetching CI/approval/conflict status from GitHub"),
)
.arg(
Arg::with_name("no-color")
.long("no-color")
.takes_value(false)
.help("Disable colors and Unicode characters"),
)
.arg(
Arg::with_name("help-legend")
.long("help-legend")
.takes_value(false)
.help("Show status bits legend"),
)
.arg(
Arg::with_name("json")
.long("json")
.takes_value(false)
.help("Output in JSON format"),
);

let autorebase = SubCommand::with_name("autorebase")
Expand Down Expand Up @@ -169,7 +215,8 @@ fn clap<'a, 'b>() -> App<'a, 'b> {
.subcommand(log)
.subcommand(rebase)
.subcommand(autorebase)
.subcommand(land);
.subcommand(land)
.subcommand(status_cmd);

app
}
Expand Down Expand Up @@ -343,8 +390,41 @@ async fn main() -> Result<(), Box<dyn Error>> {
return Ok(());
}

// Check if "short" mode
if m.is_present("short") {
// Check if --status flag is set (delegate to status handler)
if m.is_present("status") {
let no_color = m.is_present("no-color");
let show_legend = status::should_show_legend();

let config = StatusConfig {
use_color: !no_color,
use_unicode: !no_color,
show_legend,
include_checks: true,
json_output: false,
};

let repo = m
.value_of("project")
.and_then(|p| Repository::open(p).ok())
.or_else(tree::detect_repo);

let entries = status::build_status_entries(
&stack,
repo.as_ref(),
&repository,
&credentials,
&config,
)
.await;

let output = status::render_status(&entries, &config, repo.is_some());
print!("{}", output);

// Mark legend as seen after first display
if show_legend {
status::mark_legend_seen();
}
} else if m.is_present("short") {
// Original flat output
for (pr, maybe_parent) in stack {
match maybe_parent {
Expand Down Expand Up @@ -532,6 +612,90 @@ async fn main() -> Result<(), Box<dyn Error>> {
}
}

("status", Some(m)) => {
let identifier = m.value_of("identifier").unwrap();

// resolve repository with fallback chain
let remote_name = m.value_of("origin").unwrap_or("origin");
let repository = resolve_repository(m.value_of("repository"), &repository, remote_name)
.unwrap_or_else(|e| panic!("{}", e));

let json_output = m.is_present("json");

if !json_output {
println!(
"Searching for {} identifier in {} repo",
style(identifier).bold(),
style(&repository).bold()
);
}

let stack =
build_pr_stack_for_repo(identifier, &repository, &credentials, get_excluded(m))
.await?;

// Check for empty stack
if stack.is_empty() {
if json_output {
println!(r#"{{"stack": [], "trunk": "main"}}"#);
} else {
println!("No PRs found matching '{}'", identifier);
}
return Ok(());
}

let no_color = m.is_present("no-color");
let no_checks = m.is_present("no-checks");
let help_legend = m.is_present("help-legend");

// Show legend on first run or if explicitly requested
let show_legend = help_legend || (!json_output && status::should_show_legend());

let config = StatusConfig {
use_color: !no_color && !json_output,
use_unicode: !no_color && !json_output,
show_legend,
include_checks: !no_checks,
json_output,
};

let repo = m
.value_of("project")
.and_then(|p| Repository::open(p).ok())
.or_else(tree::detect_repo);

let entries = status::build_status_entries(
&stack,
repo.as_ref(),
&repository,
&credentials,
&config,
)
.await;

if json_output {
match status::render_status_json(&entries) {
Ok(json) => println!("{}", json),
Err(e) => {
eprintln!(
"{} Failed to serialize JSON: {}",
style("Error:").red().bold(),
e
);
std::process::exit(1);
}
}
} else {
let output = status::render_status(&entries, &config, repo.is_some());
print!("{}", output);

// Mark legend as seen after first display
if show_legend && !help_legend {
status::mark_legend_seen();
}
}
}

(_, _) => panic!("Invalid subcommand."),
}

Expand Down