Improve default branch guessing
This commit is contained in:
90
src/repo.rs
90
src/repo.rs
@@ -1042,12 +1042,80 @@ impl RepoHandle {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_branch(&self) -> Result<Branch, String> {
|
pub fn get_remote_default_branch(&self, remote_name: &str) -> Result<Option<Branch>, String> {
|
||||||
let branch_names = vec!["main", "master"];
|
// libgit2's `git_remote_default_branch()` and `Remote::default_branch()`
|
||||||
|
// need an actual connection to the remote, so they may fail.
|
||||||
|
if let Some(mut remote) = self.find_remote(remote_name)? {
|
||||||
|
if remote.connected() {
|
||||||
|
let remote = remote; // unmut
|
||||||
|
if let Ok(remote_default_branch) = remote.default_branch() {
|
||||||
|
return Ok(Some(self.find_local_branch(&remote_default_branch)?));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for branch_name in &branch_names {
|
// Note that <remote>/HEAD only exists after a normal clone, there is no way to get the
|
||||||
|
// remote HEAD afterwards. So this is a "best effort" approach.
|
||||||
|
if let Ok(remote_head) = self.find_remote_branch(&remote_name, "HEAD") {
|
||||||
|
if let Some(pointer_name) = remote_head.as_reference().symbolic_target() {
|
||||||
|
if let Some(local_branch_name) =
|
||||||
|
pointer_name.strip_prefix(&format!("refs/remotes/{}/", remote_name))
|
||||||
|
{
|
||||||
|
return Ok(Some(self.find_local_branch(&local_branch_name)?));
|
||||||
|
} else {
|
||||||
|
eprintln!("Remote HEAD ({}) pointer is invalid", pointer_name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eprintln!("Remote HEAD does not point to a symbolic target");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_branch(&self) -> Result<Branch, String> {
|
||||||
|
// This is a bit of a guessing game.
|
||||||
|
//
|
||||||
|
// In the best case, there is only one remote. Then, we can check <remote>/HEAD to get the
|
||||||
|
// default remote branch.
|
||||||
|
//
|
||||||
|
// If there are multiple remotes, we first check whether they all have the same
|
||||||
|
// <remote>/HEAD branch. If yes, good! If not, we use whatever "origin" uses, if that
|
||||||
|
// exists. If it does not, there is no way to reliably get a remote default branch.
|
||||||
|
//
|
||||||
|
// In this case, we just try to guess a local branch from a list. If even that does not
|
||||||
|
// work, well, bad luck.
|
||||||
|
let remotes = self.remotes()?;
|
||||||
|
|
||||||
|
if remotes.len() == 1 {
|
||||||
|
let remote_name = &remotes[0];
|
||||||
|
if let Some(default_branch) = self.get_remote_default_branch(&remote_name)? {
|
||||||
|
return Ok(default_branch);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut default_branches: Vec<Branch> = vec![];
|
||||||
|
for remote_name in remotes {
|
||||||
|
if let Some(default_branch) = self.get_remote_default_branch(&remote_name)? {
|
||||||
|
default_branches.push(default_branch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !default_branches.is_empty() {
|
||||||
|
if default_branches.len() == 1 {
|
||||||
|
return Ok(default_branches.remove(0));
|
||||||
|
} else {
|
||||||
|
if default_branches
|
||||||
|
.windows(2)
|
||||||
|
.all(|w| w[0].name() == w[1].name())
|
||||||
|
{
|
||||||
|
return Ok(default_branches.remove(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for branch_name in &vec!["main", "master"] {
|
||||||
if let Ok(branch) = self.0.find_branch(branch_name, git2::BranchType::Local) {
|
if let Ok(branch) = self.0.find_branch(branch_name, git2::BranchType::Local) {
|
||||||
return Ok(Branch(branch))
|
return Ok(Branch(branch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1458,6 +1526,20 @@ impl RemoteHandle<'_> {
|
|||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn connected(&mut self) -> bool {
|
||||||
|
self.0.connected()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_branch(&self) -> Result<String, String> {
|
||||||
|
Ok(self
|
||||||
|
.0
|
||||||
|
.default_branch()
|
||||||
|
.map_err(convert_libgit2_error)?
|
||||||
|
.as_str()
|
||||||
|
.expect("Remote branch name is not valid utf-8")
|
||||||
|
.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_pushable(&self) -> Result<bool, String> {
|
pub fn is_pushable(&self) -> Result<bool, String> {
|
||||||
let remote_type = detect_remote_type(self.0.url().expect("Remote name is not valid utf-8"))
|
let remote_type = detect_remote_type(self.0.url().expect("Remote name is not valid utf-8"))
|
||||||
.ok_or_else(|| String::from("Could not detect remote type"))?;
|
.ok_or_else(|| String::from("Could not detect remote type"))?;
|
||||||
|
|||||||
Reference in New Issue
Block a user