Merge branch 'tags-col' into next

This commit is contained in:
Carlos Martín Nieto 2015-07-31 20:24:18 +02:00
commit c0c6caed3a
4 changed files with 332 additions and 31 deletions

View File

@ -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)

167
tag.go
View File

@ -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
}

View File

@ -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: "random@hacker.com",
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
}

View File

@ -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 */