use crate::error::{GwError, Result};
use crate::git;
use crate::github::{self, PrInfo, PrState};
use crate::output;
use crate::state::{NextAction, RepoType, SyncState, WorkingDirState};
pub fn run() -> Result<()> {
if !git::is_git_repo() {
return Err(GwError::NotAGitRepository);
}
let repo_type = RepoType::detect()?;
let home_branch = repo_type.home_branch();
let current = git::current_branch()?;
let working_dir = WorkingDirState::detect();
let sync_state = SyncState::detect(¤t).unwrap_or(SyncState::NoUpstream);
let has_remote = git::remote_branch_exists(¤t);
println!();
match &repo_type {
RepoType::MainRepo => {
output::info("Repository: main repo");
}
RepoType::Worktree { home_branch } => {
output::info(&format!(
"Repository: worktree (home: {})",
output::bold(home_branch)
));
}
}
if current == home_branch {
output::success(&format!("Branch: {} (home)", output::bold(¤t)));
} else {
output::info(&format!(
"Branch: {} (home: {})",
output::bold(¤t),
home_branch
));
}
match working_dir {
WorkingDirState::Clean => {
output::success("Working directory: clean");
}
_ => {
output::warn(&format!("Working directory: {}", working_dir.description()));
}
}
match &sync_state {
SyncState::NoUpstream => {
output::info("Upstream: no tracking branch");
}
SyncState::Synced => {
output::success("Upstream: synced");
}
SyncState::HasUnpushedCommits { count } => {
output::warn(&format!("Upstream: {} unpushed commit(s)", count));
}
SyncState::Behind { count } => {
output::warn(&format!("Upstream: {} commit(s) behind", count));
}
SyncState::Diverged { ahead, behind } => {
output::warn(&format!(
"Upstream: diverged ({} ahead, {} behind)",
ahead, behind
));
}
}
let (pr_info, base_pr_merged) = if current != home_branch {
get_and_show_pr_info(¤t)
} else {
(None, None)
};
if current != home_branch {
if has_remote {
output::info(&format!("Remote: origin/{} exists", current));
} else {
output::info("Remote: not pushed");
}
}
let stash_count = git::stash_count();
if stash_count > 0 {
output::info(&format!("Stashes: {}", stash_count));
}
let next_action = NextAction::detect(
¤t,
home_branch,
&working_dir,
&sync_state,
pr_info.as_ref(),
has_remote,
base_pr_merged.as_deref(),
);
next_action.display(¤t);
Ok(())
}
fn get_and_show_pr_info(branch: &str) -> (Option<PrInfo>, Option<String>) {
if !github::is_gh_available() {
return (None, None);
}
match github::get_pr_for_branch(branch) {
Ok(Some(pr)) => {
let state_str = match &pr.state {
PrState::Open => "OPEN",
PrState::Merged { .. } => "MERGED",
PrState::Closed => "CLOSED",
};
let method_str = match &pr.state {
PrState::Merged { method, .. } => format!(" ({})", method),
_ => String::new(),
};
match &pr.state {
PrState::Open => {
output::info(&format!("PR: #{} {} [{}]", pr.number, pr.title, state_str));
}
PrState::Merged { .. } => {
output::success(&format!(
"PR: #{} {} [{}{}]",
pr.number, pr.title, state_str, method_str
));
}
PrState::Closed => {
output::warn(&format!("PR: #{} {} [{}]", pr.number, pr.title, state_str));
}
}
if pr.base_branch != "main" {
output::info(&format!("Base: {} (not main)", pr.base_branch));
}
let base_pr_merged = if pr.base_branch != "main" && pr.state.is_open() {
check_base_pr_merged(&pr.base_branch)
} else {
None
};
(Some(pr), base_pr_merged)
}
Ok(None) => {
output::info("PR: none");
(None, None)
}
Err(e) => {
output::warn(&format!("Could not fetch PR info: {}", e));
(None, None)
}
}
}
fn check_base_pr_merged(base_branch: &str) -> Option<String> {
match github::get_pr_for_branch(base_branch) {
Ok(Some(base_pr)) => {
if base_pr.state.is_merged() {
output::success(&format!("Base PR: #{} [MERGED] ✓", base_pr.number));
Some(base_branch.to_string())
} else {
let state_str = if base_pr.state.is_open() {
"OPEN"
} else {
"CLOSED"
};
output::info(&format!("Base PR: #{} [{}]", base_pr.number, state_str));
None
}
}
Ok(None) => {
output::info(&format!("Base PR: none (for {})", base_branch));
None
}
Err(_) => None,
}
}