use crate::cli::UI;
use crate::ops::oplog;
use crate::ops::utils::short_oid;
use anyhow::{bail, Result};
use std::path::Path;
pub fn execute(path: &Path, target: &str, create: bool, detach: bool, ui: &UI) -> Result<()> {
let desc = if create {
format!("switch -c '{}'", target)
} else {
format!("switch '{}'", target)
};
oplog::with_oplog(path, "switch", &desc, || {
execute_inner(path, target, create, detach, ui)
})
}
fn execute_inner(path: &Path, target: &str, create: bool, detach: bool, ui: &UI) -> Result<()> {
let repo = crate::ops::open_repo(path)?;
if create {
let head_commit = repo.head()?.peel_to_commit()?;
let branch = repo.branch(target, &head_commit, false)?;
let obj = branch.get().peel(git2::ObjectType::Commit)?;
repo.checkout_tree(&obj, None)?;
repo.set_head(&format!("refs/heads/{}", target))?;
ui.success(format!("Switched to a new branch '{}'", target));
return Ok(());
}
if let Ok(branch) = repo.find_branch(target, git2::BranchType::Local) {
let obj = branch.get().peel(git2::ObjectType::Commit)?;
repo.checkout_tree(&obj, None)?;
repo.set_head(&format!("refs/heads/{}", target))?;
ui.success(format!("Switched to branch '{}'", target));
return Ok(());
}
for remote_name in repo.remotes()?.iter().flatten() {
let remote_branch = format!("{}/{}", remote_name, target);
if let Ok(branch) = repo.find_branch(&remote_branch, git2::BranchType::Remote) {
let commit = branch.get().peel_to_commit()?;
let local_branch = repo.branch(target, &commit, false)?;
let obj = local_branch.get().peel(git2::ObjectType::Commit)?;
repo.checkout_tree(&obj, None)?;
repo.set_head(&format!("refs/heads/{}", target))?;
ui.success(format!(
"Switched to a new branch '{}' tracking '{}'",
target, remote_branch
));
return Ok(());
}
}
if detach {
let obj = repo.revparse_single(target)?;
let commit = obj.peel_to_commit()?;
repo.checkout_tree(commit.as_object(), None)?;
repo.set_head_detached(commit.id())?;
ui.info(format!(
"HEAD is now at {} {}",
short_oid(&commit.id()),
commit.summary().unwrap_or("")
));
return Ok(());
}
bail!(
"invalid reference: '{}'\n\
hint: If you want to detach HEAD at the commit, use: securegit switch --detach {}",
target,
target
);
}