Skip to content

Display mark for remote branches with tracking branches #861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 90 additions & 7 deletions asyncgit/src/sync/branch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pub mod merge_ff;
pub mod merge_rebase;
pub mod rename;

use std::collections::HashSet;

use super::{
remotes::get_default_remote_in_repo, utils::bytes2string,
};
Expand Down Expand Up @@ -55,13 +57,20 @@ pub struct LocalBranch {
pub remote: Option<String>,
}

///
#[derive(Debug)]
pub struct RemoteBranch {
///
pub has_tracking: bool,
}

///
#[derive(Debug)]
pub enum BranchDetails {
///
Local(LocalBranch),
///
Remote,
Remote(RemoteBranch),
}

///
Expand Down Expand Up @@ -107,13 +116,26 @@ pub fn get_branches_info(
) -> Result<Vec<BranchInfo>> {
scope_time!("get_branches_info");

let filter = if local {
BranchType::Local
let repo = utils::repo(repo_path)?;

let (filter, remotes_with_tracking) = if local {
(BranchType::Local, HashSet::default())
} 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
.branches(Some(filter))?
.map(|b| {
Expand All @@ -129,18 +151,23 @@ pub fn get_branches_info(
.and_then(git2::Buf::as_str)
.map(String::from);

let name_bytes = branch.name_bytes()?;

let details = if local {
BranchDetails::Local(LocalBranch {
is_head: branch.is_head(),
has_upstream: upstream.is_ok(),
remote,
})
} else {
BranchDetails::Remote
BranchDetails::Remote(RemoteBranch {
has_tracking: remotes_with_tracking
.contains(name_bytes),
})
};

Ok(BranchInfo {
name: bytes2string(branch.name_bytes()?)?,
name: bytes2string(name_bytes)?,
reference,
top_commit_message: bytes2string(
top_commit.summary_bytes().unwrap_or_default(),
Expand Down Expand Up @@ -668,6 +695,17 @@ mod test_remote_branches {
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]
fn test_remote_branches() {
let (r1_dir, _repo) = repo_init_bare().unwrap();
Expand Down Expand Up @@ -756,4 +794,49 @@ mod test_remote_branches {

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);
}
}
29 changes: 18 additions & 11 deletions src/components/branchlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ use crate::{
use anyhow::Result;
use asyncgit::{
sync::{
self, branch::checkout_remote_branch, checkout_branch,
get_branches_info, BranchInfo, CommitId,
self,
branch::{
checkout_remote_branch, BranchDetails, LocalBranch,
RemoteBranch,
},
checkout_branch, get_branches_info, BranchInfo, CommitId,
},
AsyncGitNotification, CWD,
};
Expand Down Expand Up @@ -415,6 +419,7 @@ impl BranchListComponent {
height: usize,
) -> Text {
const UPSTREAM_SYMBOL: char = '\u{2191}';
const TRACKING_SYMBOL: char = '\u{2193}';
const HEAD_SYMBOL: char = '*';
const EMPTY_SYMBOL: char = ' ';
const THREE_DOTS: &str = "...";
Expand Down Expand Up @@ -473,18 +478,20 @@ impl BranchListComponent {
.unwrap_or_default();
let is_head_str =
if is_head { HEAD_SYMBOL } else { EMPTY_SYMBOL };
let has_upstream_str = if displaybranch
.local_details()
.map(|details| details.has_upstream)
.unwrap_or_default()
{
UPSTREAM_SYMBOL
} else {
EMPTY_SYMBOL
let upstream_tracking_str = match displaybranch.details {
BranchDetails::Local(LocalBranch {
has_upstream,
..
}) if has_upstream => UPSTREAM_SYMBOL,
BranchDetails::Remote(RemoteBranch {
has_tracking,
..
}) if has_tracking => TRACKING_SYMBOL,
_ => EMPTY_SYMBOL,
};

let span_prefix = Span::styled(
format!("{}{} ", is_head_str, has_upstream_str),
format!("{}{} ", is_head_str, upstream_tracking_str),
theme.commit_author(selected),
);
let span_hash = Span::styled(
Expand Down