use super::helpers;
use crate::error::{GwError, Result};
use crate::git;
use crate::github::{self, PrState};
use crate::output;
use crate::state::{RepoType, WorkingDirState};
pub fn run(verbose: bool) -> Result<()> {
if !git::is_git_repo() {
return Err(GwError::NotAGitRepository);
}
let working_dir = WorkingDirState::detect();
if !working_dir.is_clean() {
output::error(&format!(
"You have uncommitted changes ({}).",
working_dir.description()
));
output::action("git stash -u -m 'WIP before sync'");
return Err(GwError::UncommittedChanges);
}
let repo_type = RepoType::detect()?;
let home_branch = repo_type.home_branch();
let current = git::current_branch()?;
if current == home_branch {
println!();
output::info(&format!("Branch: {}", output::bold(¤t)));
output::info("Fetching from origin...");
git::fetch_prune(verbose)?;
output::success("Fetched (stale remote branches pruned)");
let default_remote = git::get_default_remote_branch()?;
let default_branch = default_remote.strip_prefix("origin/").unwrap_or("main");
helpers::pull_with_output(&default_remote, default_branch, verbose)?;
output::ready("Ready", home_branch);
return Ok(());
}
println!();
output::info(&format!("Branch: {}", output::bold(¤t)));
output::info("Fetching from origin...");
git::fetch_prune(verbose)?;
if !github::is_gh_available() {
return Err(GwError::Other(
"GitHub CLI (gh) is not available. Install it from https://cli.github.com/".into(),
));
}
let pr = match github::get_pr_for_branch(¤t)? {
Some(pr) => pr,
None => {
output::warn("No PR found for this branch.");
output::hints(&["gh pr create # Create a PR first"]);
return Ok(());
}
};
output::info(&format!("PR: #{} ({})", pr.number, pr.title));
output::info(&format!("Base: {}", pr.base_branch));
let default_remote = git::get_default_remote_branch()?;
let default_branch = default_remote.strip_prefix("origin/").unwrap_or("main");
if pr.base_branch == default_branch {
output::success(&format!(
"Base is already '{}'. Nothing to sync.",
default_branch
));
output::hints(&[&format!(
"git rebase {} # If you need to update",
default_remote
)]);
return Ok(());
}
let base_pr = match github::get_pr_for_branch(&pr.base_branch)? {
Some(base_pr) => base_pr,
None => {
output::warn(&format!(
"No PR found for base branch '{}'. Cannot determine if it's merged.",
pr.base_branch
));
return Ok(());
}
};
if !base_pr.state.is_merged() {
let state_str = match &base_pr.state {
PrState::Open => "still open",
PrState::Closed => "closed (not merged)",
PrState::Merged { .. } => "merged",
};
output::warn(&format!(
"Base PR #{} ({}) is {}.",
base_pr.number, pr.base_branch, state_str
));
output::hints(&["Wait for the base PR to be merged first"]);
return Ok(());
}
output::success(&format!(
"Base PR #{} ({}) is merged ✓",
base_pr.number, pr.base_branch
));
println!();
output::info("Syncing...");
output::info(&format!(" Updating PR base to {}...", default_branch));
github::update_pr_base(pr.number, default_branch)?;
output::info(&format!(" Rebasing on {}...", default_remote));
if let Err(e) = git::rebase(&default_remote, verbose) {
output::error("Rebase failed. You may need to resolve conflicts manually.");
output::action("git rebase --continue # After resolving conflicts");
output::action("git rebase --abort # To cancel");
return Err(e);
}
output::info(" Force pushing...");
git::force_push_with_lease(¤t, verbose)?;
println!();
output::ready("Synced", ¤t);
output::hints(&[
&format!("PR #{} base is now '{}'", pr.number, default_branch),
"gw status # Check status",
]);
Ok(())
}