Skip to content

Commit ef0bc68

Browse files
cruesslerStephan Dilly
authored and
Stephan Dilly
committed
Add popup for tags
This closes #483.
1 parent 38d976c commit ef0bc68

File tree

13 files changed

+597
-7
lines changed

13 files changed

+597
-7
lines changed

asyncgit/src/revlog.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ impl AsyncLog {
6868
Ok(list[min..max].to_vec())
6969
}
7070

71+
///
72+
pub fn position(&self, id: CommitId) -> Result<Option<usize>> {
73+
let list = self.current.lock()?;
74+
let position = list.iter().position(|&x| x == id);
75+
76+
Ok(position)
77+
}
78+
7179
///
7280
pub fn is_pending(&self) -> bool {
7381
self.pending.load(Ordering::Relaxed)

asyncgit/src/sync/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ pub use stash::{
6464
get_stashes, stash_apply, stash_drop, stash_pop, stash_save,
6565
};
6666
pub use state::{repo_state, RepoState};
67-
pub use tags::{get_tags, CommitTags, Tags};
67+
pub use tags::{
68+
delete_tag, get_tags, get_tags_with_metadata, CommitTags,
69+
TagWithMetadata, Tags,
70+
};
6871
pub use tree::{tree_file_content, tree_files, TreeFile};
6972
pub use utils::{
7073
get_head, get_head_tuple, is_bare_repo, is_repo, repo_dir,

asyncgit/src/sync/tags.rs

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
1-
use super::{utils::repo, CommitId};
1+
use super::{get_commits_info, utils::repo, CommitId};
22
use crate::error::Result;
33
use scopetime::scope_time;
4-
use std::collections::BTreeMap;
4+
use std::collections::{BTreeMap, HashMap, HashSet};
55

66
/// all tags pointing to a single commit
77
pub type CommitTags = Vec<String>;
88
/// hashmap of tag target commit hash to tag names
99
pub type Tags = BTreeMap<CommitId, CommitTags>;
1010

11+
///
12+
pub struct TagWithMetadata {
13+
///
14+
pub name: String,
15+
///
16+
pub author: String,
17+
///
18+
pub time: i64,
19+
///
20+
pub message: String,
21+
///
22+
pub commit_id: CommitId,
23+
}
24+
25+
static MAX_MESSAGE_WIDTH: usize = 100;
26+
1127
/// returns `Tags` type filled with all tags found in repo
1228
pub fn get_tags(repo_path: &str) -> Result<Tags> {
1329
scope_time!("get_tags");
@@ -31,8 +47,12 @@ pub fn get_tags(repo_path: &str) -> Result<Tags> {
3147
//NOTE: find_tag (git_tag_lookup) only works on annotated tags
3248
// lightweight tags `id` already points to the target commit
3349
// see https://github.com/libgit2/libgit2/issues/5586
34-
if let Ok(tag) = repo.find_tag(id) {
35-
adder(CommitId::new(tag.target_id()), name);
50+
if let Ok(commit) = repo
51+
.find_tag(id)
52+
.and_then(|tag| tag.target())
53+
.and_then(|target| target.peel_to_commit())
54+
{
55+
adder(CommitId::new(commit.id()), name);
3656
} else if repo.find_commit(id).is_ok() {
3757
adder(CommitId::new(id), name);
3858
}
@@ -45,6 +65,69 @@ pub fn get_tags(repo_path: &str) -> Result<Tags> {
4565
Ok(res)
4666
}
4767

68+
///
69+
pub fn get_tags_with_metadata(
70+
repo_path: &str,
71+
) -> Result<Vec<TagWithMetadata>> {
72+
scope_time!("get_tags_with_metadata");
73+
74+
let tags_grouped_by_commit_id = get_tags(repo_path)?;
75+
76+
let tags_with_commit_id: Vec<(&str, &CommitId)> =
77+
tags_grouped_by_commit_id
78+
.iter()
79+
.flat_map(|(commit_id, tags)| {
80+
tags.iter()
81+
.map(|tag| (tag.as_ref(), commit_id))
82+
.collect::<Vec<(&str, &CommitId)>>()
83+
})
84+
.collect();
85+
86+
let unique_commit_ids: HashSet<_> = tags_with_commit_id
87+
.iter()
88+
.copied()
89+
.map(|(_, &commit_id)| commit_id)
90+
.collect();
91+
let mut commit_ids = Vec::with_capacity(unique_commit_ids.len());
92+
commit_ids.extend(unique_commit_ids);
93+
94+
let commit_infos =
95+
get_commits_info(repo_path, &commit_ids, MAX_MESSAGE_WIDTH)?;
96+
let unique_commit_infos: HashMap<_, _> = commit_infos
97+
.iter()
98+
.map(|commit_info| (commit_info.id, commit_info))
99+
.collect();
100+
101+
let mut tags: Vec<TagWithMetadata> = tags_with_commit_id
102+
.into_iter()
103+
.filter_map(|(tag, commit_id)| {
104+
unique_commit_infos.get(commit_id).map(|commit_info| {
105+
TagWithMetadata {
106+
name: String::from(tag),
107+
author: commit_info.author.clone(),
108+
time: commit_info.time,
109+
message: commit_info.message.clone(),
110+
commit_id: *commit_id,
111+
}
112+
})
113+
})
114+
.collect();
115+
116+
tags.sort_unstable_by(|a, b| b.time.cmp(&a.time));
117+
118+
Ok(tags)
119+
}
120+
121+
///
122+
pub fn delete_tag(repo_path: &str, tag_name: &str) -> Result<()> {
123+
scope_time!("delete_tag");
124+
125+
let repo = repo(repo_path)?;
126+
repo.tag_delete(tag_name)?;
127+
128+
Ok(())
129+
}
130+
48131
#[cfg(test)]
49132
mod tests {
50133
use super::*;
@@ -82,5 +165,26 @@ mod tests {
82165
get_tags(repo_path).unwrap()[&CommitId::new(head_id)],
83166
vec!["a", "b"]
84167
);
168+
169+
let tags = get_tags_with_metadata(repo_path).unwrap();
170+
171+
assert_eq!(tags.len(), 2);
172+
assert_eq!(tags[0].name, "a");
173+
assert_eq!(tags[0].message, "initial");
174+
assert_eq!(tags[1].name, "b");
175+
assert_eq!(tags[1].message, "initial");
176+
assert_eq!(tags[0].commit_id, tags[1].commit_id);
177+
178+
delete_tag(repo_path, "a").unwrap();
179+
180+
let tags = get_tags(repo_path).unwrap();
181+
182+
assert_eq!(tags.len(), 1);
183+
184+
delete_tag(repo_path, "b").unwrap();
185+
186+
let tags = get_tags(repo_path).unwrap();
187+
188+
assert_eq!(tags.len(), 0);
85189
}
86190
}

src/app.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
InspectCommitComponent, MsgComponent, PullComponent,
1010
PushComponent, PushTagsComponent, RenameBranchComponent,
1111
ResetComponent, RevisionFilesComponent, StashMsgComponent,
12-
TagCommitComponent,
12+
TagCommitComponent, TagListComponent,
1313
},
1414
draw_popups,
1515
input::{Input, InputEvent, InputState},
@@ -56,6 +56,7 @@ pub struct App {
5656
create_branch_popup: CreateBranchComponent,
5757
rename_branch_popup: RenameBranchComponent,
5858
select_branch_popup: BranchListComponent,
59+
tags_popup: TagListComponent,
5960
cmdbar: RefCell<CommandBar>,
6061
tab: usize,
6162
revlog: Revlog,
@@ -164,6 +165,11 @@ impl App {
164165
theme.clone(),
165166
key_config.clone(),
166167
),
168+
tags_popup: TagListComponent::new(
169+
&queue,
170+
theme.clone(),
171+
key_config.clone(),
172+
),
167173
do_quit: false,
168174
cmdbar: RefCell::new(CommandBar::new(
169175
theme.clone(),
@@ -399,6 +405,7 @@ impl App {
399405
rename_branch_popup,
400406
select_branch_popup,
401407
revision_files_popup,
408+
tags_popup,
402409
help,
403410
revlog,
404411
status_tab,
@@ -569,11 +576,26 @@ impl App {
569576
InternalEvent::SelectBranch => {
570577
self.select_branch_popup.open()?;
571578
}
579+
InternalEvent::Tags => {
580+
self.tags_popup.open()?;
581+
}
572582
InternalEvent::TabSwitch => self.set_tab(0)?,
573583
InternalEvent::InspectCommit(id, tags) => {
574584
self.inspect_commit_popup.open(id, tags)?;
575585
flags.insert(NeedsUpdate::ALL | NeedsUpdate::COMMANDS)
576586
}
587+
InternalEvent::SelectCommitInRevlog(id) => {
588+
if let Err(error) = self.revlog.select_commit(id) {
589+
self.queue.borrow_mut().push_back(
590+
InternalEvent::ShowErrorMsg(
591+
error.to_string(),
592+
),
593+
)
594+
} else {
595+
self.tags_popup.hide();
596+
flags.insert(NeedsUpdate::ALL)
597+
}
598+
}
577599
InternalEvent::OpenExternalEditor(path) => {
578600
self.input.set_polling(false);
579601
self.external_editor_popup.show()?;
@@ -639,6 +661,18 @@ impl App {
639661
self.select_branch_popup.update_branches()?;
640662
}
641663
}
664+
Action::DeleteTag(tag_name) => {
665+
if let Err(error) = sync::delete_tag(CWD, &tag_name) {
666+
self.queue.borrow_mut().push_back(
667+
InternalEvent::ShowErrorMsg(
668+
error.to_string(),
669+
),
670+
)
671+
} else {
672+
flags.insert(NeedsUpdate::ALL);
673+
self.tags_popup.update_tags()?;
674+
}
675+
}
642676
Action::ForcePush(branch, force) => self
643677
.queue
644678
.borrow_mut()

src/components/commitlist.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,10 @@ impl CommitList {
287287
fn relative_selection(&self) -> usize {
288288
self.selection.saturating_sub(self.items.index_offset())
289289
}
290+
291+
pub fn select_entry(&mut self, position: usize) {
292+
self.selection = position;
293+
}
290294
}
291295

292296
impl DrawableComponent for CommitList {

src/components/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod revision_files;
2222
mod stashmsg;
2323
mod syntax_text;
2424
mod tag_commit;
25+
mod taglist;
2526
mod textinput;
2627
mod utils;
2728

@@ -48,6 +49,7 @@ pub use revision_files::RevisionFilesComponent;
4849
pub use stashmsg::StashMsgComponent;
4950
pub use syntax_text::SyntaxTextComponent;
5051
pub use tag_commit::TagCommitComponent;
52+
pub use taglist::TagListComponent;
5153
pub use textinput::{InputType, TextInputComponent};
5254
pub use utils::filetree::FileTreeItemKind;
5355

src/components/reset.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,15 @@ impl ResetComponent {
168168
branch_ref,
169169
),
170170
),
171+
Action::DeleteTag(tag_name) => (
172+
strings::confirm_title_delete_tag(
173+
&self.key_config,
174+
),
175+
strings::confirm_msg_delete_tag(
176+
&self.key_config,
177+
tag_name,
178+
),
179+
),
171180
Action::ForcePush(branch, _force) => (
172181
strings::confirm_title_force_push(
173182
&self.key_config,

0 commit comments

Comments
 (0)