Skip to content

Commit 132559e

Browse files
author
Stephan Dilly
authored
support annotated tags (#1073)
1 parent d6ace56 commit 132559e

File tree

12 files changed

+206
-47
lines changed

12 files changed

+206
-47
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ These are the high level goals before calling out `1.0`:
7979
* notify-based change detection ([#1](https://github.com/extrawurst/gitui/issues/1))
8080
* interactive rebase ([#32](https://github.com/extrawurst/gitui/issues/32))
8181
* popup history and back button ([#846](https://github.com/extrawurst/gitui/issues/846))
82+
* delete tag on remote ([#1074](https://github.com/extrawurst/gitui/issues/1074))
8283

8384
## 5. <a name="limitations"></a> Known Limitations <small><sup>[Top ▲](#table-of-contents)</sup></small>
8485

asyncgit/src/sync/commit.rs

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,27 +95,35 @@ pub fn commit(repo_path: &RepoPath, msg: &str) -> Result<CommitId> {
9595
///
9696
/// This function will return an `Err(…)` variant if the tag’s name is refused
9797
/// by git or if the tag already exists.
98-
pub fn tag(
98+
pub fn tag_commit(
9999
repo_path: &RepoPath,
100100
commit_id: &CommitId,
101101
tag: &str,
102+
message: Option<&str>,
102103
) -> Result<CommitId> {
103-
scope_time!("tag");
104+
scope_time!("tag_commit");
104105

105106
let repo = repo(repo_path)?;
106107

107-
let signature = signature_allow_undefined_name(&repo)?;
108108
let object_id = commit_id.get_oid();
109109
let target =
110110
repo.find_object(object_id, Some(ObjectType::Commit))?;
111111

112-
Ok(repo.tag(tag, &target, &signature, "", false)?.into())
112+
let c = if let Some(message) = message {
113+
let signature = signature_allow_undefined_name(&repo)?;
114+
repo.tag(tag, &target, &signature, message, false)?.into()
115+
} else {
116+
repo.tag_lightweight(tag, &target, false)?.into()
117+
};
118+
119+
Ok(c)
113120
}
114121

115122
#[cfg(test)]
116123
mod tests {
117124

118125
use crate::error::Result;
126+
use crate::sync::tags::Tag;
119127
use crate::sync::RepoPath;
120128
use crate::sync::{
121129
commit, get_commit_details, get_commit_files, stage_add_file,
@@ -124,7 +132,7 @@ mod tests {
124132
utils::get_head,
125133
LogWalker,
126134
};
127-
use commit::{amend, tag};
135+
use commit::{amend, tag_commit};
128136
use git2::Repository;
129137
use std::{fs::File, io::Write, path::Path};
130138

@@ -238,25 +246,56 @@ mod tests {
238246

239247
let new_id = commit(repo_path, "commit msg")?;
240248

241-
tag(repo_path, &new_id, "tag")?;
249+
tag_commit(repo_path, &new_id, "tag", None)?;
242250

243251
assert_eq!(
244252
get_tags(repo_path).unwrap()[&new_id],
245-
vec!["tag"]
253+
vec![Tag::new("tag")]
246254
);
247255

248-
assert!(matches!(tag(repo_path, &new_id, "tag"), Err(_)));
256+
assert!(matches!(
257+
tag_commit(repo_path, &new_id, "tag", None),
258+
Err(_)
259+
));
249260

250261
assert_eq!(
251262
get_tags(repo_path).unwrap()[&new_id],
252-
vec!["tag"]
263+
vec![Tag::new("tag")]
253264
);
254265

255-
tag(repo_path, &new_id, "second-tag")?;
266+
tag_commit(repo_path, &new_id, "second-tag", None)?;
256267

257268
assert_eq!(
258269
get_tags(repo_path).unwrap()[&new_id],
259-
vec!["second-tag", "tag"]
270+
vec![Tag::new("second-tag"), Tag::new("tag")]
271+
);
272+
273+
Ok(())
274+
}
275+
276+
#[test]
277+
fn test_tag_with_message() -> Result<()> {
278+
let file_path = Path::new("foo");
279+
let (_td, repo) = repo_init_empty().unwrap();
280+
let root = repo.path().parent().unwrap();
281+
let repo_path: &RepoPath =
282+
&root.as_os_str().to_str().unwrap().into();
283+
284+
File::create(&root.join(file_path))?
285+
.write_all(b"test\nfoo")?;
286+
287+
stage_add_file(repo_path, file_path)?;
288+
289+
let new_id = commit(repo_path, "commit msg")?;
290+
291+
tag_commit(repo_path, &new_id, "tag", Some("tag-message"))?;
292+
293+
assert_eq!(
294+
get_tags(repo_path).unwrap()[&new_id][0]
295+
.annotation
296+
.as_ref()
297+
.unwrap(),
298+
"tag-message"
260299
);
261300

262301
Ok(())

asyncgit/src/sync/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub use branch::{
4040
merge_rebase::merge_upstream_rebase, rename::rename_branch,
4141
validate_branch_name, BranchCompare, BranchInfo,
4242
};
43-
pub use commit::{amend, commit, tag};
43+
pub use commit::{amend, commit, tag_commit};
4444
pub use commit_details::{
4545
get_commit_details, CommitDetails, CommitMessage, CommitSignature,
4646
};
@@ -80,7 +80,7 @@ pub use stash::{
8080
};
8181
pub use state::{repo_state, RepoState};
8282
pub use tags::{
83-
delete_tag, get_tags, get_tags_with_metadata, CommitTags,
83+
delete_tag, get_tags, get_tags_with_metadata, CommitTags, Tag,
8484
TagWithMetadata, Tags,
8585
};
8686
pub use tree::{tree_file_content, tree_files, TreeFile};

asyncgit/src/sync/remotes/tags.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ mod tests {
181181
let commit1 =
182182
write_commit_file(&clone1, "test.txt", "test", "commit1");
183183

184-
sync::tag(clone1_dir, &commit1, "tag1").unwrap();
184+
sync::tag_commit(clone1_dir, &commit1, "tag1", None).unwrap();
185185

186186
push(
187187
clone1_dir, "origin", "master", false, false, None, None,
@@ -229,7 +229,7 @@ mod tests {
229229
let commit1 =
230230
write_commit_file(&clone1, "test.txt", "test", "commit1");
231231

232-
sync::tag(clone1_dir, &commit1, "tag1").unwrap();
232+
sync::tag_commit(clone1_dir, &commit1, "tag1", None).unwrap();
233233

234234
push(
235235
clone1_dir, "origin", "master", false, false, None, None,
@@ -263,7 +263,7 @@ mod tests {
263263
let commit1 =
264264
write_commit_file(&clone1, "test.txt", "test", "commit1");
265265

266-
sync::tag(clone1_dir, &commit1, "tag1").unwrap();
266+
sync::tag_commit(clone1_dir, &commit1, "tag1", None).unwrap();
267267

268268
push(
269269
clone1_dir, "origin", "master", false, false, None, None,
@@ -305,7 +305,7 @@ mod tests {
305305

306306
// clone1 - creates tag
307307

308-
sync::tag(clone1_dir, &commit1, "tag1").unwrap();
308+
sync::tag_commit(clone1_dir, &commit1, "tag1", None).unwrap();
309309

310310
let tags1 = sync::get_tags(clone1_dir).unwrap();
311311

@@ -345,7 +345,7 @@ mod tests {
345345

346346
// clone1 - creates tag
347347

348-
sync::tag(clone1_dir, &commit1, "tag1").unwrap();
348+
sync::tag_commit(clone1_dir, &commit1, "tag1", None).unwrap();
349349

350350
let tags1 = sync::get_tags(clone1_dir).unwrap();
351351

asyncgit/src/sync/tags.rs

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
11
use super::{get_commits_info, CommitId, RepoPath};
2-
use crate::{error::Result, sync::repository::repo};
2+
use crate::{
3+
error::Result,
4+
sync::{repository::repo, utils::bytes2string},
5+
};
36
use scopetime::scope_time;
47
use std::collections::{BTreeMap, HashMap, HashSet};
58

9+
///
10+
#[derive(Clone, Hash, PartialEq, Debug)]
11+
pub struct Tag {
12+
/// tag name
13+
pub name: String,
14+
/// tag annotation
15+
pub annotation: Option<String>,
16+
}
17+
18+
impl Tag {
19+
///
20+
pub fn new(name: &str) -> Self {
21+
Self {
22+
name: name.into(),
23+
annotation: None,
24+
}
25+
}
26+
}
27+
628
/// all tags pointing to a single commit
7-
pub type CommitTags = Vec<String>;
29+
pub type CommitTags = Vec<Tag>;
830
/// hashmap of tag target commit hash to tag names
931
pub type Tags = BTreeMap<CommitId, CommitTags>;
1032

@@ -29,7 +51,7 @@ pub fn get_tags(repo_path: &RepoPath) -> Result<Tags> {
2951
scope_time!("get_tags");
3052

3153
let mut res = Tags::new();
32-
let mut adder = |key, value: String| {
54+
let mut adder = |key, value: Tag| {
3355
if let Some(key) = res.get_mut(&key) {
3456
key.push(value);
3557
} else {
@@ -44,17 +66,31 @@ pub fn get_tags(repo_path: &RepoPath) -> Result<Tags> {
4466
// skip the `refs/tags/` part
4567
String::from_utf8(name[10..name.len()].into())
4668
{
47-
//NOTE: find_tag (git_tag_lookup) only works on annotated tags
48-
// lightweight tags `id` already points to the target commit
69+
//NOTE: find_tag (using underlying git_tag_lookup) only
70+
// works on annotated tags lightweight tags `id` already
71+
// points to the target commit
4972
// see https://github.com/libgit2/libgit2/issues/5586
50-
if let Ok(commit) = repo
73+
let commit = if let Ok(commit) = repo
5174
.find_tag(id)
5275
.and_then(|tag| tag.target())
5376
.and_then(|target| target.peel_to_commit())
5477
{
55-
adder(CommitId::new(commit.id()), name);
78+
Some(CommitId::new(commit.id()))
5679
} else if repo.find_commit(id).is_ok() {
57-
adder(CommitId::new(id), name);
80+
Some(CommitId::new(id))
81+
} else {
82+
None
83+
};
84+
85+
let annotation = repo
86+
.find_tag(id)
87+
.ok()
88+
.as_ref()
89+
.and_then(git2::Tag::message_bytes)
90+
.and_then(|msg| bytes2string(msg).ok());
91+
92+
if let Some(commit) = commit {
93+
adder(commit, Tag { name, annotation });
5894
}
5995

6096
return true;
@@ -78,7 +114,7 @@ pub fn get_tags_with_metadata(
78114
.iter()
79115
.flat_map(|(commit_id, tags)| {
80116
tags.iter()
81-
.map(|tag| (tag.as_ref(), commit_id))
117+
.map(|tag| (tag.name.as_ref(), commit_id))
82118
.collect::<Vec<(&str, &CommitId)>>()
83119
})
84120
.collect();
@@ -167,7 +203,10 @@ mod tests {
167203
repo.tag("b", &target, &sig, "", false).unwrap();
168204

169205
assert_eq!(
170-
get_tags(repo_path).unwrap()[&CommitId::new(head_id)],
206+
get_tags(repo_path).unwrap()[&CommitId::new(head_id)]
207+
.iter()
208+
.map(|t| &t.name)
209+
.collect::<Vec<_>>(),
171210
vec!["a", "b"]
172211
);
173212

src/components/commit_details/details.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::{
1212
};
1313
use anyhow::Result;
1414
use asyncgit::sync::{
15-
self, CommitDetails, CommitId, CommitMessage, RepoPathRef,
15+
self, CommitDetails, CommitId, CommitMessage, RepoPathRef, Tag,
1616
};
1717
use crossterm::event::Event;
1818
use std::clone::Clone;
@@ -31,7 +31,7 @@ use super::style::Detail;
3131
pub struct DetailsComponent {
3232
repo: RepoPathRef,
3333
data: Option<CommitDetails>,
34-
tags: Vec<String>,
34+
tags: Vec<Tag>,
3535
theme: SharedTheme,
3636
focused: bool,
3737
current_width: Cell<u16>,
@@ -224,7 +224,7 @@ impl DetailsComponent {
224224
itertools::Itertools::intersperse(
225225
self.tags.iter().map(|tag| {
226226
Span::styled(
227-
Cow::from(tag),
227+
Cow::from(&tag.name),
228228
self.theme.text(true, false),
229229
)
230230
}),

src/components/commitlist.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use anyhow::Result;
1313
use asyncgit::sync::{CommitId, Tags};
1414
use chrono::{DateTime, Local};
1515
use crossterm::event::Event;
16+
use itertools::Itertools;
1617
use std::{
1718
borrow::Cow, cell::Cell, cmp, convert::TryFrom, time::Instant,
1819
};
@@ -320,11 +321,10 @@ impl CommitList {
320321
.take(height)
321322
.enumerate()
322323
{
323-
let tags = self
324-
.tags
325-
.as_ref()
326-
.and_then(|t| t.get(&e.id))
327-
.map(|tags| tags.join(" "));
324+
let tags =
325+
self.tags.as_ref().and_then(|t| t.get(&e.id)).map(
326+
|tags| tags.iter().map(|t| &t.name).join(" "),
327+
);
328328

329329
let marked = if any_marked {
330330
self.is_marked(&e.id)

0 commit comments

Comments
 (0)