Skip to main content

changeset_git/repository/
tag.rs

1use crate::{Result, TagInfo};
2
3use super::Repository;
4
5impl Repository {
6    /// Returns `Ok(true)` if the tag was deleted, `Ok(false)` if the tag was not found.
7    ///
8    /// # Errors
9    ///
10    /// Returns an error if the delete operation fails for reasons other than "not found".
11    pub fn delete_tag(&self, name: &str) -> Result<bool> {
12        let refname = format!("refs/tags/{name}");
13        match self.inner.find_reference(&refname) {
14            Ok(mut reference) => {
15                reference.delete()?;
16                Ok(true)
17            }
18            Err(e) if e.code() == git2::ErrorCode::NotFound => Ok(false),
19            Err(e) => Err(e.into()),
20        }
21    }
22
23    /// # Errors
24    ///
25    /// Returns an error if the tag cannot be created or already exists.
26    pub fn create_tag(&self, name: &str, message: &str) -> Result<TagInfo> {
27        let head = self.inner.head()?.peel_to_commit()?;
28        let sig = self.inner.signature()?;
29
30        self.inner
31            .tag(name, head.as_object(), &sig, message, false)?;
32
33        Ok(TagInfo::new(name.to_string(), head.id().to_string()))
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::super::tests::setup_test_repo;
40
41    #[test]
42    fn create_annotated_tag() -> anyhow::Result<()> {
43        let (_dir, repo) = setup_test_repo()?;
44
45        let tag_info = repo.create_tag("v1.0.0", "Release version 1.0.0")?;
46
47        assert_eq!(tag_info.name(), "v1.0.0");
48
49        let head = repo.inner.head()?.peel_to_commit()?;
50        assert_eq!(tag_info.target_sha(), &head.id().to_string());
51
52        let tag = repo.inner.find_reference("refs/tags/v1.0.0")?;
53        assert!(tag.peel_to_tag().is_ok());
54
55        Ok(())
56    }
57
58    #[test]
59    fn create_tag_with_crate_prefix() -> anyhow::Result<()> {
60        let (_dir, repo) = setup_test_repo()?;
61
62        let tag_info = repo.create_tag("my-crate@v0.1.0", "Release my-crate version 0.1.0")?;
63
64        assert_eq!(tag_info.name(), "my-crate@v0.1.0");
65
66        let tag = repo.inner.find_reference("refs/tags/my-crate@v0.1.0")?;
67        assert!(tag.peel_to_tag().is_ok());
68
69        Ok(())
70    }
71
72    #[test]
73    fn duplicate_tag_fails() -> anyhow::Result<()> {
74        let (_dir, repo) = setup_test_repo()?;
75
76        repo.create_tag("v1.0.0", "First tag")?;
77        let result = repo.create_tag("v1.0.0", "Duplicate tag");
78
79        assert!(result.is_err());
80
81        Ok(())
82    }
83
84    #[test]
85    fn delete_existing_tag_returns_true() -> anyhow::Result<()> {
86        let (_dir, repo) = setup_test_repo()?;
87
88        repo.create_tag("v1.0.0", "Tag to delete")?;
89        assert!(repo.inner.find_reference("refs/tags/v1.0.0").is_ok());
90
91        let deleted = repo.delete_tag("v1.0.0")?;
92
93        assert!(deleted);
94        assert!(repo.inner.find_reference("refs/tags/v1.0.0").is_err());
95
96        Ok(())
97    }
98
99    #[test]
100    fn delete_nonexistent_tag_returns_false() -> anyhow::Result<()> {
101        let (_dir, repo) = setup_test_repo()?;
102
103        let deleted = repo.delete_tag("nonexistent-tag")?;
104
105        assert!(!deleted);
106
107        Ok(())
108    }
109}