summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--repository.go34
-rw-r--r--tag.go167
-rw-r--r--tag_test.go157
-rw-r--r--wrapper.c5
4 files changed, 332 insertions, 31 deletions
diff --git a/repository.go b/repository.go
index 44509af..62fde6d 100644
--- a/repository.go
+++ b/repository.go
@@ -27,6 +27,9 @@ type Repository struct {
// Notes represents the collection of notes and can be used to
// read, write and delete notes from this repository.
Notes NoteCollection
+ // Tags represents the collection of tags and can be used to create,
+ // list and iterate tags in this repository.
+ Tags TagsCollection
}
func newRepositoryFromC(ptr *C.git_repository) *Repository {
@@ -36,6 +39,7 @@ func newRepositoryFromC(ptr *C.git_repository) *Repository {
repo.Submodules.repo = repo
repo.References.repo = repo
repo.Notes.repo = repo
+ repo.Tags.repo = repo
runtime.SetFinalizer(repo, (*Repository).Free)
@@ -317,36 +321,6 @@ func (v *Repository) CreateCommit(
return oid, nil
}
-func (v *Repository) CreateTag(
- name string, commit *Commit, tagger *Signature, message string) (*Oid, error) {
-
- oid := new(Oid)
-
- cname := C.CString(name)
- defer C.free(unsafe.Pointer(cname))
-
- cmessage := C.CString(message)
- defer C.free(unsafe.Pointer(cmessage))
-
- taggerSig, err := tagger.toC()
- if err != nil {
- return nil, err
- }
- defer C.git_signature_free(taggerSig)
-
- ctarget := commit.gitObject.ptr
-
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
-
- ret := C.git_tag_create(oid.toC(), v.ptr, cname, ctarget, taggerSig, cmessage, 0)
- if ret < 0 {
- return nil, MakeGitError(ret)
- }
-
- return oid, nil
-}
-
func (v *Odb) Free() {
runtime.SetFinalizer(v, nil)
C.git_odb_free(v.ptr)
diff --git a/tag.go b/tag.go
index 89ac8bd..ca85156 100644
--- a/tag.go
+++ b/tag.go
@@ -2,8 +2,14 @@ package git
/*
#include <git2.h>
+
+extern int _go_git_tag_foreach(git_repository *repo, void *payload);
*/
import "C"
+import (
+ "runtime"
+ "unsafe"
+)
// Tag
type Tag struct {
@@ -42,3 +48,164 @@ func (t Tag) TargetId() *Oid {
func (t Tag) TargetType() ObjectType {
return ObjectType(C.git_tag_target_type(t.cast_ptr))
}
+
+type TagsCollection struct {
+ repo *Repository
+}
+
+func (c *TagsCollection) Create(
+ name string, commit *Commit, tagger *Signature, message string) (*Oid, error) {
+
+ oid := new(Oid)
+
+ cname := C.CString(name)
+ defer C.free(unsafe.Pointer(cname))
+
+ cmessage := C.CString(message)
+ defer C.free(unsafe.Pointer(cmessage))
+
+ taggerSig, err := tagger.toC()
+ if err != nil {
+ return nil, err
+ }
+ defer C.git_signature_free(taggerSig)
+
+ ctarget := commit.gitObject.ptr
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_tag_create(oid.toC(), c.repo.ptr, cname, ctarget, taggerSig, cmessage, 0)
+ if ret < 0 {
+ return nil, MakeGitError(ret)
+ }
+
+ return oid, nil
+}
+
+// CreateLightweight creates a new lightweight tag pointing to a commit
+// and returns the id of the target object.
+//
+// The name of the tag is validated for consistency (see git_tag_create() for the rules
+// https://libgit2.github.com/libgit2/#HEAD/group/tag/git_tag_create) and should
+// not conflict with an already existing tag name.
+//
+// If force is true and a reference already exists with the given name, it'll be replaced.
+//
+// The created tag is a simple reference and can be queried using
+// repo.References.Lookup("refs/tags/<name>"). The name of the tag (eg "v1.0.0")
+// is queried with ref.Shorthand().
+func (c *TagsCollection) CreateLightweight(name string, commit *Commit, force bool) (*Oid, error) {
+
+ oid := new(Oid)
+
+ cname := C.CString(name)
+ defer C.free(unsafe.Pointer(cname))
+
+ ctarget := commit.gitObject.ptr
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ err := C.git_tag_create_lightweight(oid.toC(), c.repo.ptr, cname, ctarget, cbool(force))
+ if err < 0 {
+ return nil, MakeGitError(err)
+ }
+
+ return oid, nil
+}
+
+// List returns the names of all the tags in the repository,
+// eg: ["v1.0.1", "v2.0.0"].
+func (c *TagsCollection) List() ([]string, error) {
+ var strC C.git_strarray
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ecode := C.git_tag_list(&strC, c.repo.ptr)
+ if ecode < 0 {
+ return nil, MakeGitError(ecode)
+ }
+ defer C.git_strarray_free(&strC)
+
+ tags := makeStringsFromCStrings(strC.strings, int(strC.count))
+ return tags, nil
+}
+
+// ListWithMatch returns the names of all the tags in the repository
+// that match a given pattern.
+//
+// The pattern is a standard fnmatch(3) pattern http://man7.org/linux/man-pages/man3/fnmatch.3.html
+func (c *TagsCollection) ListWithMatch(pattern string) ([]string, error) {
+ var strC C.git_strarray
+
+ patternC := C.CString(pattern)
+ defer C.free(unsafe.Pointer(patternC))
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ecode := C.git_tag_list_match(&strC, patternC, c.repo.ptr)
+ if ecode < 0 {
+ return nil, MakeGitError(ecode)
+ }
+ defer C.git_strarray_free(&strC)
+
+ tags := makeStringsFromCStrings(strC.strings, int(strC.count))
+ return tags, nil
+}
+
+// TagForeachCallback is called for each tag in the repository.
+//
+// The name is the full ref name eg: "refs/tags/v1.0.0".
+//
+// Note that the callback is called for lightweight tags as well,
+// so repo.LookupTag() will return an error for these tags. Use
+// repo.References.Lookup() instead.
+type TagForeachCallback func(name string, id *Oid) error
+type tagForeachData struct {
+ callback TagForeachCallback
+ err error
+}
+
+//export gitTagForeachCb
+func gitTagForeachCb(name *C.char, id *C.git_oid, handle unsafe.Pointer) int {
+ payload := pointerHandles.Get(handle)
+ data, ok := payload.(*tagForeachData)
+ if !ok {
+ panic("could not retrieve tag foreach CB handle")
+ }
+
+ err := data.callback(C.GoString(name), newOidFromC(id))
+ if err != nil {
+ data.err = err
+ return C.GIT_EUSER
+ }
+
+ return 0
+}
+
+// Foreach calls the callback for each tag in the repository.
+func (c *TagsCollection) Foreach(callback TagForeachCallback) error {
+ data := tagForeachData{
+ callback: callback,
+ err: nil,
+ }
+
+ handle := pointerHandles.Track(&data)
+ defer pointerHandles.Untrack(handle)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ err := C._go_git_tag_foreach(c.repo.ptr, handle)
+ if err == C.GIT_EUSER {
+ return data.err
+ }
+ if err < 0 {
+ return MakeGitError(err)
+ }
+
+ return nil
+}
diff --git a/tag_test.go b/tag_test.go
index 74f9fec..2fdfe00 100644
--- a/tag_test.go
+++ b/tag_test.go
@@ -1,6 +1,7 @@
package git
import (
+ "errors"
"testing"
"time"
)
@@ -24,6 +25,146 @@ func TestCreateTag(t *testing.T) {
compareStrings(t, commitId.String(), tag.TargetId().String())
}
+func TestCreateTagLightweight(t *testing.T) {
+ repo := createTestRepo(t)
+ defer cleanupTestRepo(t, repo)
+
+ commitID, _ := seedTestRepo(t, repo)
+
+ commit, err := repo.LookupCommit(commitID)
+ checkFatal(t, err)
+
+ tagID, err := repo.Tags.CreateLightweight("v0.1.0", commit, false)
+ checkFatal(t, err)
+
+ _, err = repo.Tags.CreateLightweight("v0.1.0", commit, true)
+ checkFatal(t, err)
+
+ ref, err := repo.References.Lookup("refs/tags/v0.1.0")
+ checkFatal(t, err)
+
+ compareStrings(t, "refs/tags/v0.1.0", ref.Name())
+ compareStrings(t, "v0.1.0", ref.Shorthand())
+ compareStrings(t, tagID.String(), commitID.String())
+ compareStrings(t, commitID.String(), ref.Target().String())
+}
+
+func TestListTags(t *testing.T) {
+ repo := createTestRepo(t)
+ defer cleanupTestRepo(t, repo)
+
+ commitID, _ := seedTestRepo(t, repo)
+
+ commit, err := repo.LookupCommit(commitID)
+ checkFatal(t, err)
+
+ createTag(t, repo, commit, "v1.0.1", "Release v1.0.1")
+
+ commitID, _ = updateReadme(t, repo, "Release version 2")
+
+ commit, err = repo.LookupCommit(commitID)
+ checkFatal(t, err)
+
+ createTag(t, repo, commit, "v2.0.0", "Release v2.0.0")
+
+ expected := []string{
+ "v1.0.1",
+ "v2.0.0",
+ }
+
+ actual, err := repo.Tags.List()
+ checkFatal(t, err)
+
+ compareStringList(t, expected, actual)
+}
+
+func TestListTagsWithMatch(t *testing.T) {
+ repo := createTestRepo(t)
+ defer cleanupTestRepo(t, repo)
+
+ commitID, _ := seedTestRepo(t, repo)
+
+ commit, err := repo.LookupCommit(commitID)
+ checkFatal(t, err)
+
+ createTag(t, repo, commit, "v1.0.1", "Release v1.0.1")
+
+ commitID, _ = updateReadme(t, repo, "Release version 2")
+
+ commit, err = repo.LookupCommit(commitID)
+ checkFatal(t, err)
+
+ createTag(t, repo, commit, "v2.0.0", "Release v2.0.0")
+
+ expected := []string{
+ "v2.0.0",
+ }
+
+ actual, err := repo.Tags.ListWithMatch("v2*")
+ checkFatal(t, err)
+
+ compareStringList(t, expected, actual)
+
+ expected = []string{
+ "v1.0.1",
+ }
+
+ actual, err = repo.Tags.ListWithMatch("v1*")
+ checkFatal(t, err)
+
+ compareStringList(t, expected, actual)
+}
+
+func TestTagForeach(t *testing.T) {
+ repo := createTestRepo(t)
+ defer cleanupTestRepo(t, repo)
+
+ commitID, _ := seedTestRepo(t, repo)
+
+ commit, err := repo.LookupCommit(commitID)
+ checkFatal(t, err)
+
+ tag1 := createTag(t, repo, commit, "v1.0.1", "Release v1.0.1")
+
+ commitID, _ = updateReadme(t, repo, "Release version 2")
+
+ commit, err = repo.LookupCommit(commitID)
+ checkFatal(t, err)
+
+ tag2 := createTag(t, repo, commit, "v2.0.0", "Release v2.0.0")
+
+ expectedNames := []string{
+ "refs/tags/v1.0.1",
+ "refs/tags/v2.0.0",
+ }
+ actualNames := []string{}
+ expectedOids := []string{
+ tag1.String(),
+ tag2.String(),
+ }
+ actualOids := []string{}
+
+ err = repo.Tags.Foreach(func(name string, id *Oid) error {
+ actualNames = append(actualNames, name)
+ actualOids = append(actualOids, id.String())
+ return nil
+ })
+ checkFatal(t, err)
+
+ compareStringList(t, expectedNames, actualNames)
+ compareStringList(t, expectedOids, actualOids)
+
+ fakeErr := errors.New("fake error")
+
+ err = repo.Tags.Foreach(func(name string, id *Oid) error {
+ return fakeErr
+ })
+
+ if err != fakeErr {
+ t.Fatalf("Tags.Foreach() did not return the expected error, got %v", err)
+ }
+}
+
func compareStrings(t *testing.T, expected, value string) {
if value != expected {
t.Fatalf("expected '%v', actual '%v'", expected, value)
@@ -39,7 +180,21 @@ func createTestTag(t *testing.T, repo *Repository, commit *Commit) *Oid {
When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc),
}
- tagId, err := repo.CreateTag("v0.0.0", commit, sig, "This is a tag")
+ tagId, err := repo.Tags.Create("v0.0.0", commit, sig, "This is a tag")
+ checkFatal(t, err)
+ return tagId
+}
+
+func createTag(t *testing.T, repo *Repository, commit *Commit, name, message string) *Oid {
+ loc, err := time.LoadLocation("Europe/Bucharest")
+ checkFatal(t, err)
+ sig := &Signature{
+ Name: "Rand Om Hacker",
+ Email: "[email protected]",
+ When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc),
+ }
+
+ tagId, err := repo.Tags.Create(name, commit, sig, message)
checkFatal(t, err)
return tagId
}
diff --git a/wrapper.c b/wrapper.c
index 75cc03c..1efe5d7 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -131,4 +131,9 @@ int _go_git_index_remove_all(git_index *index, const git_strarray *pathspec, voi
return git_index_remove_all(index, pathspec, cb, callback);
}
+int _go_git_tag_foreach(git_repository *repo, void *payload)
+{
+ return git_tag_foreach(repo, (git_tag_foreach_cb)&gitTagForeachCb, payload);
+}
+
/* EOF */