Skip to content

Commit 76eaaba

Browse files
authored
Merge pull request #5 from Termina/tags
feat(tags): add 'tags' command to display git tags
2 parents 17b95ce + 9650f6f commit 76eaaba

6 files changed

Lines changed: 142 additions & 0 deletions

File tree

.DS_Store

-6 KB
Binary file not shown.

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
/target
2+
3+
.DS_Store

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,36 @@ then jump to `demo` with:
8585
gg demo
8686
```
8787

88+
### Show Git Tags
89+
90+
List git tags in chronological order. By default, it shows the latest 10 tags.
91+
92+
```bash
93+
in tags
94+
```
95+
96+
Example output:
97+
```
98+
Showing the last 10 of 12 tags (from 2025-07-31 to 2025-07-31). Use --all to see all.
99+
100+
2025-07-31 v0.1.03 Test tag 3
101+
2025-07-31 v0.1.04 Test tag 4
102+
2025-07-31 v0.1.05 Test tag 5
103+
2025-07-31 v0.1.06 Test tag 6
104+
2025-07-31 v0.1.07 Test tag 7
105+
2025-07-31 v0.1.08 Test tag 8
106+
2025-07-31 v0.1.09 Test tag 9
107+
2025-07-31 v0.1.10 Test tag 10
108+
2025-07-31 v0.1.11 Test tag 11
109+
2025-07-31 v0.1.12 Test tag 12
110+
```
111+
112+
To view all tags, use the `--all` flag.
113+
114+
```bash
115+
in tags --all
116+
```
117+
88118
### License
89119

90120
MIT

src/args.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub enum InspectionCommand {
1818
ListFileSize(InspectForFileSize),
1919
DirMark(InspectForDirMark),
2020
FinishBranch(InspectForFinishBranch),
21+
ShowTags(InspectForTags),
2122
}
2223

2324
/// command for inspecting IP addresses.
@@ -29,6 +30,15 @@ pub struct InspectForIp {
2930
pub detailed: bool,
3031
}
3132

33+
/// command for showing tags.
34+
#[derive(FromArgs, PartialEq, Debug)]
35+
#[argh(subcommand, name = "tags")]
36+
pub struct InspectForTags {
37+
/// show all tags
38+
#[argh(switch, long = "all")]
39+
pub all: bool,
40+
}
41+
3242
/// command for copying files.
3343
#[derive(FromArgs, PartialEq, Debug)]
3444
#[argh(subcommand, name = "cpfile")]

src/main.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod args;
22
mod dir_marks;
33
mod git;
44
mod show_file_size;
5+
mod tags;
56

67
use sysinfo::System;
78

@@ -123,6 +124,12 @@ fn main() -> Result<(), String> {
123124
std::process::exit(1);
124125
}
125126
}
127+
ShowTags(options) => {
128+
if let Err(e) = tags::show_tags(&options) {
129+
eprintln!("Error showing tags: {}", e);
130+
std::process::exit(1);
131+
}
132+
}
126133
}
127134

128135
Ok(())

src/tags.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use crate::args::InspectForTags;
2+
use std::process::Command;
3+
4+
#[derive(Clone)]
5+
struct TagInfo {
6+
date: String,
7+
name: String,
8+
subject: String,
9+
}
10+
11+
pub fn show_tags(options: &InspectForTags) -> Result<(), String> {
12+
let output = Command::new("git")
13+
.args([
14+
"for-each-ref",
15+
"--sort=creatordate", // Sort by date ascending
16+
"--format=%(creatordate:short)\t%(refname:short)\t%(subject)",
17+
"refs/tags",
18+
])
19+
.output()
20+
.map_err(|e| e.to_string())?;
21+
22+
if !output.status.success() {
23+
return Err(String::from_utf8_lossy(&output.stderr).to_string());
24+
}
25+
26+
let output_str = String::from_utf8_lossy(&output.stdout);
27+
if output_str.is_empty() {
28+
println!("No tags found.");
29+
return Ok(());
30+
}
31+
32+
let all_tags: Vec<TagInfo> = output_str
33+
.lines()
34+
.filter_map(|line| {
35+
let parts: Vec<&str> = line.splitn(3, '\t').collect();
36+
if parts.len() == 3 {
37+
Some(TagInfo {
38+
date: parts[0].to_string(),
39+
name: parts[1].to_string(),
40+
subject: parts[2].to_string(),
41+
})
42+
} else {
43+
None
44+
}
45+
})
46+
.collect();
47+
48+
let total_tags = all_tags.len();
49+
let tags_to_display = if options.all {
50+
all_tags.clone()
51+
} else {
52+
let tags: Vec<TagInfo> = all_tags.iter().rev().take(10).cloned().collect();
53+
tags.into_iter().rev().collect()
54+
};
55+
56+
if tags_to_display.is_empty() {
57+
println!("No tags to display.");
58+
return Ok(());
59+
}
60+
61+
if !options.all && total_tags > tags_to_display.len() {
62+
if let (Some(first_tag), Some(last_tag)) = (all_tags.first(), all_tags.last()) {
63+
println!(
64+
"Showing the last {} of {} tags (from {} to {}). Use --all to see all.",
65+
tags_to_display.len(),
66+
total_tags,
67+
first_tag.date,
68+
last_tag.date
69+
);
70+
println!(); // Add a blank line for separation
71+
}
72+
}
73+
74+
75+
let mut max_tag_width = 0;
76+
for tag in &tags_to_display {
77+
if tag.name.len() > max_tag_width {
78+
max_tag_width = tag.name.len();
79+
}
80+
}
81+
82+
for tag in tags_to_display {
83+
println!(
84+
"{} {:<width$} {}",
85+
tag.date,
86+
tag.name,
87+
tag.subject,
88+
width = max_tag_width
89+
);
90+
}
91+
92+
Ok(())
93+
}

0 commit comments

Comments
 (0)