blob: 463f2fc607cd73196efed5258fa1c1c8101be5b0 [file] [log] [blame]
package git
/*
#include <git2.h>
*/
import "C"
import (
"runtime"
"unsafe"
)
type ReferenceType int
const (
ReferenceSymbolic ReferenceType = C.GIT_REF_SYMBOLIC
ReferenceOid ReferenceType = C.GIT_REF_OID
)
type Reference struct {
ptr *C.git_reference
repo *Repository
}
type ReferenceCollection struct {
repo *Repository
}
func (c *ReferenceCollection) Lookup(name string) (*Reference, error) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
var ptr *C.git_reference
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ecode := C.git_reference_lookup(&ptr, c.repo.ptr, cname)
if ecode < 0 {
return nil, MakeGitError(ecode)
}
return newReferenceFromC(ptr, c.repo), nil
}
func (c *ReferenceCollection) Create(name string, id *Oid, force bool, msg string) (*Reference, error) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
var cmsg *C.char
if msg == "" {
cmsg = nil
} else {
cmsg = C.CString(msg)
defer C.free(unsafe.Pointer(cmsg))
}
var ptr *C.git_reference
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ecode := C.git_reference_create(&ptr, c.repo.ptr, cname, id.toC(), cbool(force), cmsg)
if ecode < 0 {
return nil, MakeGitError(ecode)
}
return newReferenceFromC(ptr, c.repo), nil
}
func (c *ReferenceCollection) CreateSymbolic(name, target string, force bool, msg string) (*Reference, error) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
ctarget := C.CString(target)
defer C.free(unsafe.Pointer(ctarget))
var cmsg *C.char
if msg == "" {
cmsg = nil
} else {
cmsg = C.CString(msg)
defer C.free(unsafe.Pointer(cmsg))
}
var ptr *C.git_reference
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ecode := C.git_reference_symbolic_create(&ptr, c.repo.ptr, cname, ctarget, cbool(force), cmsg)
if ecode < 0 {
return nil, MakeGitError(ecode)
}
return newReferenceFromC(ptr, c.repo), nil
}
// EnsureLog ensures that there is a reflog for the given reference
// name and creates an empty one if necessary.
func (c *ReferenceCollection) EnsureLog(name string) error {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_reference_ensure_log(c.repo.ptr, cname)
if ret < 0 {
return MakeGitError(ret)
}
return nil
}
// HasLog returns whether there is a reflog for the given reference
// name
func (c *ReferenceCollection) HasLog(name string) (bool, error) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_reference_has_log(c.repo.ptr, cname)
if ret < 0 {
return false, MakeGitError(ret)
}
return ret == 1, nil
}
// Dwim looks up a reference by DWIMing its short name
func (c *ReferenceCollection) Dwim(name string) (*Reference, error) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var ptr *C.git_reference
ret := C.git_reference_dwim(&ptr, c.repo.ptr, cname)
if ret < 0 {
return nil, MakeGitError(ret)
}
return newReferenceFromC(ptr, c.repo), nil
}
func newReferenceFromC(ptr *C.git_reference, repo *Repository) *Reference {
ref := &Reference{ptr: ptr, repo: repo}
runtime.SetFinalizer(ref, (*Reference).Free)
return ref
}
func (v *Reference) SetSymbolicTarget(target string, msg string) (*Reference, error) {
var ptr *C.git_reference
ctarget := C.CString(target)
defer C.free(unsafe.Pointer(ctarget))
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var cmsg *C.char
if msg == "" {
cmsg = nil
} else {
cmsg = C.CString(msg)
defer C.free(unsafe.Pointer(cmsg))
}
ret := C.git_reference_symbolic_set_target(&ptr, v.ptr, ctarget, cmsg)
if ret < 0 {
return nil, MakeGitError(ret)
}
return newReferenceFromC(ptr, v.repo), nil
}
func (v *Reference) SetTarget(target *Oid, msg string) (*Reference, error) {
var ptr *C.git_reference
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var cmsg *C.char
if msg == "" {
cmsg = nil
} else {
cmsg = C.CString(msg)
defer C.free(unsafe.Pointer(cmsg))
}
ret := C.git_reference_set_target(&ptr, v.ptr, target.toC(), cmsg)
if ret < 0 {
return nil, MakeGitError(ret)
}
return newReferenceFromC(ptr, v.repo), nil
}
func (v *Reference) Resolve() (*Reference, error) {
var ptr *C.git_reference
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_reference_resolve(&ptr, v.ptr)
if ret < 0 {
return nil, MakeGitError(ret)
}
return newReferenceFromC(ptr, v.repo), nil
}
func (v *Reference) Rename(name string, force bool, msg string) (*Reference, error) {
var ptr *C.git_reference
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
var cmsg *C.char
if msg == "" {
cmsg = nil
} else {
cmsg = C.CString(msg)
defer C.free(unsafe.Pointer(cmsg))
}
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_reference_rename(&ptr, v.ptr, cname, cbool(force), cmsg)
if ret < 0 {
return nil, MakeGitError(ret)
}
return newReferenceFromC(ptr, v.repo), nil
}
func (v *Reference) Target() *Oid {
return newOidFromC(C.git_reference_target(v.ptr))
}
func (v *Reference) SymbolicTarget() string {
cstr := C.git_reference_symbolic_target(v.ptr)
if cstr == nil {
return ""
}
return C.GoString(cstr)
}
func (v *Reference) Delete() error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_reference_delete(v.ptr)
if ret < 0 {
return MakeGitError(ret)
}
return nil
}
func (v *Reference) Peel(t ObjectType) (*Object, error) {
var cobj *C.git_object
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if err := C.git_reference_peel(&cobj, v.ptr, C.git_otype(t)); err < 0 {
return nil, MakeGitError(err)
}
return allocObject(cobj, v.repo), nil
}
// Owner returns a weak reference to the repository which owns this
// reference.
func (v *Reference) Owner() *Repository {
return &Repository{
ptr: C.git_reference_owner(v.ptr),
}
}
// Cmp compares both references, retursn 0 on equality, otherwise a
// stable sorting.
func (v *Reference) Cmp(ref2 *Reference) int {
return int(C.git_reference_cmp(v.ptr, ref2.ptr))
}
// Shorthand returns a "human-readable" short reference name
func (v *Reference) Shorthand() string {
return C.GoString(C.git_reference_shorthand(v.ptr))
}
func (v *Reference) Name() string {
return C.GoString(C.git_reference_name(v.ptr))
}
func (v *Reference) Type() ReferenceType {
return ReferenceType(C.git_reference_type(v.ptr))
}
func (v *Reference) IsBranch() bool {
return C.git_reference_is_branch(v.ptr) == 1
}
func (v *Reference) IsRemote() bool {
return C.git_reference_is_remote(v.ptr) == 1
}
func (v *Reference) IsTag() bool {
return C.git_reference_is_tag(v.ptr) == 1
}
// IsNote checks if the reference is a note.
func (v *Reference) IsNote() bool {
return C.git_reference_is_note(v.ptr) == 1
}
func (v *Reference) Free() {
runtime.SetFinalizer(v, nil)
C.git_reference_free(v.ptr)
}
type ReferenceIterator struct {
ptr *C.git_reference_iterator
repo *Repository
}
type ReferenceNameIterator struct {
*ReferenceIterator
}
// NewReferenceIterator creates a new iterator over reference names
func (repo *Repository) NewReferenceIterator() (*ReferenceIterator, error) {
var ptr *C.git_reference_iterator
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_reference_iterator_new(&ptr, repo.ptr)
if ret < 0 {
return nil, MakeGitError(ret)
}
iter := &ReferenceIterator{ptr: ptr, repo: repo}
runtime.SetFinalizer(iter, (*ReferenceIterator).Free)
return iter, nil
}
// NewReferenceIterator creates a new branch iterator over reference names
func (repo *Repository) NewReferenceNameIterator() (*ReferenceNameIterator, error) {
var ptr *C.git_reference_iterator
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_reference_iterator_new(&ptr, repo.ptr)
if ret < 0 {
return nil, MakeGitError(ret)
}
iter := &ReferenceIterator{ptr: ptr, repo: repo}
runtime.SetFinalizer(iter, (*ReferenceIterator).Free)
return iter.Names(), nil
}
// NewReferenceIteratorGlob creates an iterator over reference names
// that match the speicified glob. The glob is of the usual fnmatch
// type.
func (repo *Repository) NewReferenceIteratorGlob(glob string) (*ReferenceIterator, error) {
cstr := C.CString(glob)
defer C.free(unsafe.Pointer(cstr))
var ptr *C.git_reference_iterator
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_reference_iterator_glob_new(&ptr, repo.ptr, cstr)
if ret < 0 {
return nil, MakeGitError(ret)
}
iter := &ReferenceIterator{ptr: ptr}
runtime.SetFinalizer(iter, (*ReferenceIterator).Free)
return iter, nil
}
func (i *ReferenceIterator) Names() *ReferenceNameIterator {
return &ReferenceNameIterator{i}
}
// NextName retrieves the next reference name. If the iteration is over,
// the returned error is git.ErrIterOver
func (v *ReferenceNameIterator) Next() (string, error) {
var ptr *C.char
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_reference_next_name(&ptr, v.ptr)
if ret < 0 {
return "", MakeGitError(ret)
}
return C.GoString(ptr), nil
}
// Next retrieves the next reference. If the iterationis over, the
// returned error is git.ErrIterOver
func (v *ReferenceIterator) Next() (*Reference, error) {
var ptr *C.git_reference
runtime.LockOSThread()
defer runtime.UnlockOSThread()
ret := C.git_reference_next(&ptr, v.ptr)
if ret < 0 {
return nil, MakeGitError(ret)
}
return newReferenceFromC(ptr, v.repo), nil
}
// Free the reference iterator
func (v *ReferenceIterator) Free() {
runtime.SetFinalizer(v, nil)
C.git_reference_iterator_free(v.ptr)
}
// ReferenceIsValidName ensures the reference name is well-formed.
//
// Valid reference names must follow one of two patterns:
//
// 1. Top-level names must contain only capital letters and underscores,
// and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
//
// 2. Names prefixed with "refs/" can be almost anything. You must avoid
// the characters '~', '^', ':', ' \ ', '?', '[', and '*', and the sequences
// ".." and " @ {" which have special meaning to revparse.
func ReferenceIsValidName(name string) bool {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
if C.git_reference_is_valid_name(cname) == 1 {
return true
}
return false
}