Display mark for remote branches with tracking branches (#861)

This commit is contained in:
jedel1043 2021-08-20 06:03:02 -05:00 committed by GitHub
parent 1faba1760c
commit 13a0f4e9e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 108 additions and 18 deletions

View file

@ -5,6 +5,8 @@ pub mod merge_ff;
pub mod merge_rebase; pub mod merge_rebase;
pub mod rename; pub mod rename;
use std::collections::HashSet;
use super::{ use super::{
remotes::get_default_remote_in_repo, utils::bytes2string, remotes::get_default_remote_in_repo, utils::bytes2string,
}; };
@ -55,13 +57,20 @@ pub struct LocalBranch {
pub remote: Option<String>, pub remote: Option<String>,
} }
///
#[derive(Debug)]
pub struct RemoteBranch {
///
pub has_tracking: bool,
}
/// ///
#[derive(Debug)] #[derive(Debug)]
pub enum BranchDetails { pub enum BranchDetails {
/// ///
Local(LocalBranch), Local(LocalBranch),
/// ///
Remote, Remote(RemoteBranch),
} }
/// ///
@ -107,13 +116,26 @@ pub fn get_branches_info(
) -> Result<Vec<BranchInfo>> { ) -> Result<Vec<BranchInfo>> {
scope_time!("get_branches_info"); scope_time!("get_branches_info");
let filter = if local { let repo = utils::repo(repo_path)?;
BranchType::Local
let (filter, remotes_with_tracking) = if local {
(BranchType::Local, HashSet::default())
} else { } else {
BranchType::Remote let remotes: HashSet<_> = repo
.branches(Some(BranchType::Local))?
.filter_map(|b| {
let branch = b.ok()?.0;
let upstream = branch.upstream();
upstream
.ok()?
.name_bytes()
.ok()
.map(ToOwned::to_owned)
})
.collect();
(BranchType::Remote, remotes)
}; };
let repo = utils::repo(repo_path)?;
let mut branches_for_display: Vec<BranchInfo> = repo let mut branches_for_display: Vec<BranchInfo> = repo
.branches(Some(filter))? .branches(Some(filter))?
.map(|b| { .map(|b| {
@ -129,6 +151,8 @@ pub fn get_branches_info(
.and_then(git2::Buf::as_str) .and_then(git2::Buf::as_str)
.map(String::from); .map(String::from);
let name_bytes = branch.name_bytes()?;
let details = if local { let details = if local {
BranchDetails::Local(LocalBranch { BranchDetails::Local(LocalBranch {
is_head: branch.is_head(), is_head: branch.is_head(),
@ -136,11 +160,14 @@ pub fn get_branches_info(
remote, remote,
}) })
} else { } else {
BranchDetails::Remote BranchDetails::Remote(RemoteBranch {
has_tracking: remotes_with_tracking
.contains(name_bytes),
})
}; };
Ok(BranchInfo { Ok(BranchInfo {
name: bytes2string(branch.name_bytes()?)?, name: bytes2string(name_bytes)?,
reference, reference,
top_commit_message: bytes2string( top_commit_message: bytes2string(
top_commit.summary_bytes().unwrap_or_default(), top_commit.summary_bytes().unwrap_or_default(),
@ -668,6 +695,17 @@ mod test_remote_branches {
repo_clone, repo_init_bare, write_commit_file, repo_clone, repo_init_bare, write_commit_file,
}; };
impl BranchInfo {
/// returns details about remote branch or None
const fn remote_details(&self) -> Option<&RemoteBranch> {
if let BranchDetails::Remote(details) = &self.details {
Some(details)
} else {
None
}
}
}
#[test] #[test]
fn test_remote_branches() { fn test_remote_branches() {
let (r1_dir, _repo) = repo_init_bare().unwrap(); let (r1_dir, _repo) = repo_init_bare().unwrap();
@ -756,4 +794,49 @@ mod test_remote_branches {
assert_eq!(&get_branch_name(clone2_dir).unwrap(), "foo"); assert_eq!(&get_branch_name(clone2_dir).unwrap(), "foo");
} }
#[test]
fn test_has_tracking() {
let (r1_dir, _repo) = repo_init_bare().unwrap();
let (clone1_dir, clone1) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone1_dir = clone1_dir.path().to_str().unwrap();
// clone1
write_commit_file(&clone1, "test.txt", "test", "commit1");
push(
clone1_dir, "origin", "master", false, false, None, None,
)
.unwrap();
create_branch(clone1_dir, "foo").unwrap();
write_commit_file(&clone1, "test.txt", "test2", "commit2");
push(clone1_dir, "origin", "foo", false, false, None, None)
.unwrap();
let branches_1 =
get_branches_info(clone1_dir, false).unwrap();
assert!(branches_1[0].remote_details().unwrap().has_tracking);
assert!(branches_1[1].remote_details().unwrap().has_tracking);
// clone2
let (clone2_dir, _clone2) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone2_dir = clone2_dir.path().to_str().unwrap();
let branches_2 =
get_branches_info(clone2_dir, false).unwrap();
assert!(
!branches_2[0].remote_details().unwrap().has_tracking
);
assert!(
!branches_2[1].remote_details().unwrap().has_tracking
);
assert!(branches_2[2].remote_details().unwrap().has_tracking);
}
} }

View file

@ -13,8 +13,12 @@ use crate::{
use anyhow::Result; use anyhow::Result;
use asyncgit::{ use asyncgit::{
sync::{ sync::{
self, branch::checkout_remote_branch, checkout_branch, self,
get_branches_info, BranchInfo, CommitId, branch::{
checkout_remote_branch, BranchDetails, LocalBranch,
RemoteBranch,
},
checkout_branch, get_branches_info, BranchInfo, CommitId,
}, },
AsyncGitNotification, CWD, AsyncGitNotification, CWD,
}; };
@ -415,6 +419,7 @@ impl BranchListComponent {
height: usize, height: usize,
) -> Text { ) -> Text {
const UPSTREAM_SYMBOL: char = '\u{2191}'; const UPSTREAM_SYMBOL: char = '\u{2191}';
const TRACKING_SYMBOL: char = '\u{2193}';
const HEAD_SYMBOL: char = '*'; const HEAD_SYMBOL: char = '*';
const EMPTY_SYMBOL: char = ' '; const EMPTY_SYMBOL: char = ' ';
const THREE_DOTS: &str = "..."; const THREE_DOTS: &str = "...";
@ -473,18 +478,20 @@ impl BranchListComponent {
.unwrap_or_default(); .unwrap_or_default();
let is_head_str = let is_head_str =
if is_head { HEAD_SYMBOL } else { EMPTY_SYMBOL }; if is_head { HEAD_SYMBOL } else { EMPTY_SYMBOL };
let has_upstream_str = if displaybranch let upstream_tracking_str = match displaybranch.details {
.local_details() BranchDetails::Local(LocalBranch {
.map(|details| details.has_upstream) has_upstream,
.unwrap_or_default() ..
{ }) if has_upstream => UPSTREAM_SYMBOL,
UPSTREAM_SYMBOL BranchDetails::Remote(RemoteBranch {
} else { has_tracking,
EMPTY_SYMBOL ..
}) if has_tracking => TRACKING_SYMBOL,
_ => EMPTY_SYMBOL,
}; };
let span_prefix = Span::styled( let span_prefix = Span::styled(
format!("{}{} ", is_head_str, has_upstream_str), format!("{}{} ", is_head_str, upstream_tracking_str),
theme.commit_author(selected), theme.commit_author(selected),
); );
let span_hash = Span::styled( let span_hash = Span::styled(