Skip to content

Show symbol if tag is not yet pushed #776

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
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
5 changes: 5 additions & 0 deletions asyncgit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ mod progress;
mod push;
mod push_tags;
pub mod remote_progress;
///
pub mod remotes;
mod revlog;
mod status;
pub mod sync;
Expand Down Expand Up @@ -85,6 +87,9 @@ pub enum AsyncGitNotification {
///
//TODO: this does not belong here
SyntaxHighlighting,
///
//TODO: this does not belong here
RemoteTags,
}

/// current working directory `./`
Expand Down
72 changes: 72 additions & 0 deletions asyncgit/src/remotes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use crate::{
asyncjob::AsyncJob,
error::Result,
sync::cred::BasicAuthCredential,
sync::remotes::{get_default_remote, tags_missing_remote},
CWD,
};

use std::sync::{Arc, Mutex};

enum JobState {
Request(Option<BasicAuthCredential>),
Response(Result<Vec<String>>),
}

///
#[derive(Clone, Default)]
pub struct AsyncRemoteTagsJob {
state: Arc<Mutex<Option<JobState>>>,
}

///
impl AsyncRemoteTagsJob {
///
pub fn new(
basic_credential: Option<BasicAuthCredential>,
) -> Self {
Self {
state: Arc::new(Mutex::new(Some(JobState::Request(
basic_credential,
)))),
}
}

///
pub fn result(&self) -> Option<Result<Vec<String>>> {
if let Ok(mut state) = self.state.lock() {
if let Some(state) = state.take() {
return match state {
JobState::Request(_) => None,
JobState::Response(result) => Some(result),
};
}
}

None
}
}

impl AsyncJob for AsyncRemoteTagsJob {
fn run(&mut self) {
if let Ok(mut state) = self.state.lock() {
*state = state.take().map(|state| match state {
JobState::Request(basic_credential) => {
let result =
get_default_remote(CWD).and_then(|remote| {
tags_missing_remote(
CWD,
&remote,
basic_credential,
)
});

JobState::Response(result)
}
JobState::Response(result) => {
JobState::Response(result)
}
});
}
}
}
2 changes: 2 additions & 0 deletions asyncgit/src/sync/remotes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use push::remote_callbacks;
use scopetime::scope_time;
use utils::bytes2string;

pub use tags::tags_missing_remote;

/// origin
pub const DEFAULT_REMOTE_NAME: &str = "origin";

Expand Down
2 changes: 1 addition & 1 deletion asyncgit/src/sync/remotes/tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ fn remote_tag_refs(
}

/// lists the remotes tags missing
fn tags_missing_remote(
pub fn tags_missing_remote(
repo_path: &str,
remote: &str,
basic_credential: Option<BasicAuthCredential>,
Expand Down
3 changes: 3 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ impl App {
),
tags_popup: TagListComponent::new(
&queue,
sender,
theme.clone(),
key_config.clone(),
),
Expand Down Expand Up @@ -357,6 +358,7 @@ impl App {
self.push_tags_popup.update_git(ev)?;
self.pull_popup.update_git(ev)?;
self.revision_files_popup.update(ev);
self.tags_popup.update(ev);

//TODO: better system for this
// can we simply process the queue here and everyone just uses the queue to schedule a cmd update?
Expand All @@ -383,6 +385,7 @@ impl App {
|| self.push_tags_popup.any_work_pending()
|| self.pull_popup.any_work_pending()
|| self.revision_files_popup.any_work_pending()
|| self.tags_popup.any_work_pending()
}

///
Expand Down
69 changes: 68 additions & 1 deletion src/components/taglist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ use crate::{
};
use anyhow::Result;
use asyncgit::{
asyncjob::AsyncSingleJob,
remotes::AsyncRemoteTagsJob,
sync::cred::{extract_username_password, need_username_password},
sync::{get_tags_with_metadata, TagWithMetadata},
CWD,
AsyncGitNotification, CWD,
};
use crossbeam_channel::Sender;
use crossterm::event::Event;
use std::convert::TryInto;
use tui::{
Expand All @@ -36,6 +40,9 @@ pub struct TagListComponent {
visible: bool,
table_state: std::cell::Cell<TableState>,
current_height: std::cell::Cell<usize>,
missing_remote_tags: Option<Vec<String>>,
async_remote_tags:
AsyncSingleJob<AsyncRemoteTagsJob, AsyncGitNotification>,
key_config: SharedKeyConfig,
}

Expand Down Expand Up @@ -65,6 +72,8 @@ impl DrawableComponent for TagListComponent {
});

let constraints = [
// symbol if tag is not yet on remote and can be pushed
Constraint::Length(1),
// tag name
Constraint::Length(tag_name_width.try_into()?),
// commit date
Expand Down Expand Up @@ -230,6 +239,7 @@ impl Component for TagListComponent {
impl TagListComponent {
pub fn new(
queue: &Queue,
sender: &Sender<AsyncGitNotification>,
theme: SharedTheme,
key_config: SharedKeyConfig,
) -> Self {
Expand All @@ -240,6 +250,11 @@ impl TagListComponent {
visible: false,
table_state: std::cell::Cell::new(TableState::default()),
current_height: std::cell::Cell::new(0),
missing_remote_tags: None,
async_remote_tags: AsyncSingleJob::new(
sender.clone(),
AsyncGitNotification::RemoteTags,
),
key_config,
}
}
Expand All @@ -251,9 +266,41 @@ impl TagListComponent {

self.update_tags()?;

let basic_credential = if need_username_password()? {
let credential = extract_username_password()?;

if credential.is_complete() {
Some(credential)
} else {
None
}
} else {
None
};

self.async_remote_tags
.spawn(AsyncRemoteTagsJob::new(basic_credential));

Ok(())
}

///
pub fn update(&mut self, event: AsyncGitNotification) {
if event == AsyncGitNotification::RemoteTags {
if let Some(job) = self.async_remote_tags.take_last() {
if let Some(Ok(missing_remote_tags)) = job.result() {
self.missing_remote_tags =
Some(missing_remote_tags);
}
}
}
}

///
pub fn any_work_pending(&self) -> bool {
self.async_remote_tags.is_pending()
}

/// fetch list of tags
pub fn update_tags(&mut self) -> Result<()> {
let tags = get_tags_with_metadata(CWD)?;
Expand Down Expand Up @@ -307,7 +354,27 @@ impl TagListComponent {

///
fn get_row(&self, tag: &TagWithMetadata) -> Row {
const UPSTREAM_SYMBOL: &str = "\u{2191}";
const EMPTY_SYMBOL: &str = " ";

let is_tag_missing_on_remote = self
.missing_remote_tags
.as_ref()
.map_or(false, |missing_remote_tags| {
let remote_tag = format!("refs/tags/{}", tag.name);

missing_remote_tags.contains(&remote_tag)
});

let has_remote_str = if is_tag_missing_on_remote {
UPSTREAM_SYMBOL
} else {
EMPTY_SYMBOL
};

let cells: Vec<Cell> = vec![
Cell::from(has_remote_str)
.style(self.theme.commit_author(false)),
Cell::from(tag.name.clone())
.style(self.theme.text(true, false)),
Cell::from(utils::time_to_string(tag.time, true))
Expand Down