Skip to content

Commit 03831db

Browse files
committed
Show symbol if tag is not yet pushed
This partly addresses gitui-org#742.
1 parent ab5b29e commit 03831db

File tree

6 files changed

+130
-2
lines changed

6 files changed

+130
-2
lines changed

asyncgit/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ mod progress;
3333
mod push;
3434
mod push_tags;
3535
pub mod remote_progress;
36+
///
37+
pub mod remotes;
3638
mod revlog;
3739
mod status;
3840
pub mod sync;
@@ -85,6 +87,9 @@ pub enum AsyncNotification {
8587
///
8688
//TODO: this does not belong here
8789
SyntaxHighlighting,
90+
///
91+
//TODO: this does not belong here
92+
RemoteTags,
8893
}
8994

9095
/// current working directory `./`

asyncgit/src/remotes.rs

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use crate::{
2+
asyncjob::AsyncJob,
3+
error::Result,
4+
sync::remotes::{get_default_remote, tags_missing_remote},
5+
CWD,
6+
};
7+
8+
use std::sync::{Arc, Mutex};
9+
10+
enum JobState {
11+
Request,
12+
Response(Result<Vec<String>>),
13+
}
14+
15+
///
16+
#[derive(Clone, Default)]
17+
pub struct AsyncRemoteTagsJob {
18+
state: Arc<Mutex<Option<JobState>>>,
19+
}
20+
21+
///
22+
impl AsyncRemoteTagsJob {
23+
///
24+
pub fn new() -> Self {
25+
Self {
26+
state: Arc::new(Mutex::new(Some(JobState::Request))),
27+
}
28+
}
29+
30+
///
31+
pub fn result(&self) -> Option<Result<Vec<String>>> {
32+
if let Ok(mut state) = self.state.lock() {
33+
if let Some(state) = state.take() {
34+
return match state {
35+
JobState::Request => None,
36+
JobState::Response(result) => Some(result),
37+
};
38+
}
39+
}
40+
41+
None
42+
}
43+
}
44+
45+
impl AsyncJob for AsyncRemoteTagsJob {
46+
fn run(&mut self) {
47+
if let Ok(mut state) = self.state.lock() {
48+
*state = state.take().map(|state| match state {
49+
JobState::Request => {
50+
let result =
51+
get_default_remote(CWD).and_then(|remote| {
52+
tags_missing_remote(CWD, &remote, None)
53+
});
54+
55+
JobState::Response(result)
56+
}
57+
JobState::Response(result) => {
58+
JobState::Response(result)
59+
}
60+
});
61+
}
62+
}
63+
}

asyncgit/src/sync/remotes/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use push::remote_callbacks;
1616
use scopetime::scope_time;
1717
use utils::bytes2string;
1818

19+
pub use tags::tags_missing_remote;
20+
1921
/// origin
2022
pub const DEFAULT_REMOTE_NAME: &str = "origin";
2123

asyncgit/src/sync/remotes/tags.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fn remote_tag_refs(
7373
}
7474

7575
/// lists the remotes tags missing
76-
fn tags_missing_remote(
76+
pub fn tags_missing_remote(
7777
repo_path: &str,
7878
remote: &str,
7979
basic_credential: Option<BasicAuthCredential>,

src/app.rs

+3
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ impl App {
167167
),
168168
tags_popup: TagListComponent::new(
169169
&queue,
170+
sender,
170171
theme.clone(),
171172
key_config.clone(),
172173
),
@@ -357,6 +358,7 @@ impl App {
357358
self.push_tags_popup.update_git(ev)?;
358359
self.pull_popup.update_git(ev)?;
359360
self.revision_files_popup.update(ev);
361+
self.tags_popup.update(ev);
360362

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

388391
///

src/components/taglist.rs

+56-1
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ use crate::{
1111
};
1212
use anyhow::Result;
1313
use asyncgit::{
14+
asyncjob::AsyncSingleJob,
15+
remotes::AsyncRemoteTagsJob,
1416
sync::{get_tags_with_metadata, TagWithMetadata},
15-
CWD,
17+
AsyncNotification, CWD,
1618
};
19+
use crossbeam_channel::Sender;
1720
use crossterm::event::Event;
1821
use std::convert::TryInto;
1922
use tui::{
@@ -36,6 +39,9 @@ pub struct TagListComponent {
3639
visible: bool,
3740
table_state: std::cell::Cell<TableState>,
3841
current_height: std::cell::Cell<usize>,
42+
missing_remote_tags: Option<Vec<String>>,
43+
async_remote_tags:
44+
AsyncSingleJob<AsyncRemoteTagsJob, AsyncNotification>,
3945
key_config: SharedKeyConfig,
4046
}
4147

@@ -65,6 +71,8 @@ impl DrawableComponent for TagListComponent {
6571
});
6672

6773
let constraints = [
74+
// symbol if tag is not yet on remote and can be pushed
75+
Constraint::Length(1),
6876
// tag name
6977
Constraint::Length(tag_name_width.try_into()?),
7078
// commit date
@@ -228,6 +236,7 @@ impl Component for TagListComponent {
228236
impl TagListComponent {
229237
pub fn new(
230238
queue: &Queue,
239+
sender: &Sender<AsyncNotification>,
231240
theme: SharedTheme,
232241
key_config: SharedKeyConfig,
233242
) -> Self {
@@ -238,6 +247,11 @@ impl TagListComponent {
238247
visible: false,
239248
table_state: std::cell::Cell::new(TableState::default()),
240249
current_height: std::cell::Cell::new(0),
250+
missing_remote_tags: None,
251+
async_remote_tags: AsyncSingleJob::new(
252+
sender.clone(),
253+
AsyncNotification::RemoteTags,
254+
),
241255
key_config,
242256
}
243257
}
@@ -252,12 +266,33 @@ impl TagListComponent {
252266
Ok(())
253267
}
254268

269+
///
270+
pub fn update(&mut self, event: AsyncNotification) {
271+
if event == AsyncNotification::RemoteTags {
272+
if let Some(job) = self.async_remote_tags.take_last() {
273+
if let Some(Ok(missing_remote_tags)) = job.result() {
274+
self.missing_remote_tags =
275+
Some(missing_remote_tags);
276+
}
277+
}
278+
}
279+
}
280+
281+
///
282+
pub fn any_work_pending(&self) -> bool {
283+
self.async_remote_tags.is_pending()
284+
}
285+
255286
/// fetch list of tags
256287
pub fn update_tags(&mut self) -> Result<()> {
257288
let tags = get_tags_with_metadata(CWD)?;
258289

259290
self.tags = Some(tags);
260291

292+
if self.missing_remote_tags.is_none() {
293+
self.async_remote_tags.spawn(AsyncRemoteTagsJob::new());
294+
}
295+
261296
Ok(())
262297
}
263298

@@ -305,7 +340,27 @@ impl TagListComponent {
305340

306341
///
307342
fn get_row(&self, tag: &TagWithMetadata) -> Row {
343+
const UPSTREAM_SYMBOL: &str = "\u{2191}";
344+
const EMPTY_SYMBOL: &str = " ";
345+
346+
let is_tag_missing_on_remote = self
347+
.missing_remote_tags
348+
.as_ref()
349+
.map_or(false, |missing_remote_tags| {
350+
let remote_tag = format!("refs/tags/{}", tag.name);
351+
352+
missing_remote_tags.contains(&remote_tag)
353+
});
354+
355+
let has_remote_str = if is_tag_missing_on_remote {
356+
UPSTREAM_SYMBOL
357+
} else {
358+
EMPTY_SYMBOL
359+
};
360+
308361
let cells: Vec<Cell> = vec![
362+
Cell::from(has_remote_str)
363+
.style(self.theme.commit_author(false)),
309364
Cell::from(tag.name.clone())
310365
.style(self.theme.text(true, false)),
311366
Cell::from(utils::time_to_string(tag.time, true))

0 commit comments

Comments
 (0)