Add refspec bindings
This add support for the parse, access, and transform functions for refspec objects.
This commit is contained in:
parent
c598ea5718
commit
925a61b1e0
|
@ -0,0 +1,149 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <git2.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Refspec struct {
|
||||||
|
doNotCompare
|
||||||
|
ptr *C.git_refspec
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseRefspec parses a given refspec string
|
||||||
|
func ParseRefspec(input string, isFetch bool) (*Refspec, error) {
|
||||||
|
var ptr *C.git_refspec
|
||||||
|
|
||||||
|
cinput := C.CString(input)
|
||||||
|
defer C.free(unsafe.Pointer(cinput))
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
ret := C.git_refspec_parse(&ptr, cinput, cbool(isFetch))
|
||||||
|
if ret < 0 {
|
||||||
|
return nil, MakeGitError(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
spec := &Refspec{ptr: ptr}
|
||||||
|
runtime.SetFinalizer(spec, (*Refspec).Free)
|
||||||
|
return spec, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free releases a refspec object which has been created by ParseRefspec
|
||||||
|
func (s *Refspec) Free() {
|
||||||
|
runtime.SetFinalizer(s, nil)
|
||||||
|
C.git_refspec_free(s.ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direction returns the refspec's direction
|
||||||
|
func (s *Refspec) Direction() ConnectDirection {
|
||||||
|
direction := C.git_refspec_direction(s.ptr)
|
||||||
|
return ConnectDirection(direction)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Src returns the refspec's source specifier
|
||||||
|
func (s *Refspec) Src() string {
|
||||||
|
var ret string
|
||||||
|
cstr := C.git_refspec_src(s.ptr)
|
||||||
|
|
||||||
|
if cstr != nil {
|
||||||
|
ret = C.GoString(cstr)
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.KeepAlive(s)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dst returns the refspec's destination specifier
|
||||||
|
func (s *Refspec) Dst() string {
|
||||||
|
var ret string
|
||||||
|
cstr := C.git_refspec_dst(s.ptr)
|
||||||
|
|
||||||
|
if cstr != nil {
|
||||||
|
ret = C.GoString(cstr)
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.KeepAlive(s)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force returns the refspec's force-update setting
|
||||||
|
func (s *Refspec) Force() bool {
|
||||||
|
force := C.git_refspec_force(s.ptr)
|
||||||
|
return force != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the refspec's string representation
|
||||||
|
func (s *Refspec) String() string {
|
||||||
|
var ret string
|
||||||
|
cstr := C.git_refspec_string(s.ptr)
|
||||||
|
|
||||||
|
if cstr != nil {
|
||||||
|
ret = C.GoString(cstr)
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.KeepAlive(s)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// SrcMatches checks if a refspec's source descriptor matches a reference
|
||||||
|
func (s *Refspec) SrcMatches(refname string) bool {
|
||||||
|
cname := C.CString(refname)
|
||||||
|
defer C.free(unsafe.Pointer(cname))
|
||||||
|
|
||||||
|
matches := C.git_refspec_src_matches(s.ptr, cname)
|
||||||
|
return matches != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SrcMatches checks if a refspec's destination descriptor matches a reference
|
||||||
|
func (s *Refspec) DstMatches(refname string) bool {
|
||||||
|
cname := C.CString(refname)
|
||||||
|
defer C.free(unsafe.Pointer(cname))
|
||||||
|
|
||||||
|
matches := C.git_refspec_dst_matches(s.ptr, cname)
|
||||||
|
return matches != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform a reference to its target following the refspec's rules
|
||||||
|
func (s *Refspec) Transform(refname string) (string, error) {
|
||||||
|
buf := C.git_buf{}
|
||||||
|
|
||||||
|
cname := C.CString(refname)
|
||||||
|
defer C.free(unsafe.Pointer(cname))
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
ret := C.git_refspec_transform(&buf, s.ptr, cname)
|
||||||
|
if ret < 0 {
|
||||||
|
return "", MakeGitError(ret)
|
||||||
|
}
|
||||||
|
defer C.git_buf_dispose(&buf)
|
||||||
|
|
||||||
|
return C.GoString(buf.ptr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rtransform converts a target reference to its source reference following the
|
||||||
|
// refspec's rules
|
||||||
|
func (s *Refspec) Rtransform(refname string) (string, error) {
|
||||||
|
buf := C.git_buf{}
|
||||||
|
|
||||||
|
cname := C.CString(refname)
|
||||||
|
defer C.free(unsafe.Pointer(cname))
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
ret := C.git_refspec_rtransform(&buf, s.ptr, cname)
|
||||||
|
if ret < 0 {
|
||||||
|
return "", MakeGitError(ret)
|
||||||
|
}
|
||||||
|
defer C.git_buf_dispose(&buf)
|
||||||
|
|
||||||
|
return C.GoString(buf.ptr), nil
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRefspec(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const (
|
||||||
|
input = "+refs/heads/*:refs/remotes/origin/*"
|
||||||
|
mainLocal = "refs/heads/main"
|
||||||
|
mainRemote = "refs/remotes/origin/main"
|
||||||
|
)
|
||||||
|
|
||||||
|
refspec, err := ParseRefspec(input, true)
|
||||||
|
checkFatal(t, err)
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
|
||||||
|
s := refspec.String()
|
||||||
|
if s != input {
|
||||||
|
t.Errorf("expected string %q, got %q", input, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
if d := refspec.Direction(); d != ConnectDirectionFetch {
|
||||||
|
t.Errorf("expected fetch refspec, got direction %v", d)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pat, expected := refspec.Src(), "refs/heads/*"; pat != expected {
|
||||||
|
t.Errorf("expected refspec src %q, got %q", expected, pat)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pat, expected := refspec.Dst(), "refs/remotes/origin/*"; pat != expected {
|
||||||
|
t.Errorf("expected refspec dst %q, got %q", expected, pat)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !refspec.Force() {
|
||||||
|
t.Error("expected refspec force flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SrcMatches
|
||||||
|
|
||||||
|
if !refspec.SrcMatches(mainLocal) {
|
||||||
|
t.Errorf("refspec source did not match %q", mainLocal)
|
||||||
|
}
|
||||||
|
|
||||||
|
if refspec.SrcMatches("refs/tags/v1.0") {
|
||||||
|
t.Error("refspec source matched under refs/tags")
|
||||||
|
}
|
||||||
|
|
||||||
|
// DstMatches
|
||||||
|
|
||||||
|
if !refspec.DstMatches(mainRemote) {
|
||||||
|
t.Errorf("refspec destination did not match %q", mainRemote)
|
||||||
|
}
|
||||||
|
|
||||||
|
if refspec.DstMatches("refs/tags/v1.0") {
|
||||||
|
t.Error("refspec destination matched under refs/tags")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transforms
|
||||||
|
|
||||||
|
fromLocal, err := refspec.Transform(mainLocal)
|
||||||
|
checkFatal(t, err)
|
||||||
|
if fromLocal != mainRemote {
|
||||||
|
t.Errorf("transform by refspec returned %s; expected %s", fromLocal, mainRemote)
|
||||||
|
}
|
||||||
|
|
||||||
|
fromRemote, err := refspec.Rtransform(mainRemote)
|
||||||
|
checkFatal(t, err)
|
||||||
|
if fromRemote != mainLocal {
|
||||||
|
t.Errorf("rtransform by refspec returned %s; expected %s", fromRemote, mainLocal)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue