Skip to content

Commit eae0077

Browse files
authoredFeb 25, 2022
Add refspec bindings (#898)
This add support for the parse, access, and transform functions for refspec objects.
1 parent e7d1b2b commit eae0077

File tree

2 files changed

+224
-0
lines changed

2 files changed

+224
-0
lines changed
 

‎refspec.go

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package git
2+
3+
/*
4+
#include <git2.h>
5+
*/
6+
import "C"
7+
import (
8+
"runtime"
9+
"unsafe"
10+
)
11+
12+
type Refspec struct {
13+
doNotCompare
14+
ptr *C.git_refspec
15+
}
16+
17+
// ParseRefspec parses a given refspec string
18+
func ParseRefspec(input string, isFetch bool) (*Refspec, error) {
19+
var ptr *C.git_refspec
20+
21+
cinput := C.CString(input)
22+
defer C.free(unsafe.Pointer(cinput))
23+
24+
runtime.LockOSThread()
25+
defer runtime.UnlockOSThread()
26+
27+
ret := C.git_refspec_parse(&ptr, cinput, cbool(isFetch))
28+
if ret < 0 {
29+
return nil, MakeGitError(ret)
30+
}
31+
32+
spec := &Refspec{ptr: ptr}
33+
runtime.SetFinalizer(spec, (*Refspec).Free)
34+
return spec, nil
35+
}
36+
37+
// Free releases a refspec object which has been created by ParseRefspec
38+
func (s *Refspec) Free() {
39+
runtime.SetFinalizer(s, nil)
40+
C.git_refspec_free(s.ptr)
41+
}
42+
43+
// Direction returns the refspec's direction
44+
func (s *Refspec) Direction() ConnectDirection {
45+
direction := C.git_refspec_direction(s.ptr)
46+
return ConnectDirection(direction)
47+
}
48+
49+
// Src returns the refspec's source specifier
50+
func (s *Refspec) Src() string {
51+
var ret string
52+
cstr := C.git_refspec_src(s.ptr)
53+
54+
if cstr != nil {
55+
ret = C.GoString(cstr)
56+
}
57+
58+
runtime.KeepAlive(s)
59+
return ret
60+
}
61+
62+
// Dst returns the refspec's destination specifier
63+
func (s *Refspec) Dst() string {
64+
var ret string
65+
cstr := C.git_refspec_dst(s.ptr)
66+
67+
if cstr != nil {
68+
ret = C.GoString(cstr)
69+
}
70+
71+
runtime.KeepAlive(s)
72+
return ret
73+
}
74+
75+
// Force returns the refspec's force-update setting
76+
func (s *Refspec) Force() bool {
77+
force := C.git_refspec_force(s.ptr)
78+
return force != 0
79+
}
80+
81+
// String returns the refspec's string representation
82+
func (s *Refspec) String() string {
83+
var ret string
84+
cstr := C.git_refspec_string(s.ptr)
85+
86+
if cstr != nil {
87+
ret = C.GoString(cstr)
88+
}
89+
90+
runtime.KeepAlive(s)
91+
return ret
92+
}
93+
94+
// SrcMatches checks if a refspec's source descriptor matches a reference
95+
func (s *Refspec) SrcMatches(refname string) bool {
96+
cname := C.CString(refname)
97+
defer C.free(unsafe.Pointer(cname))
98+
99+
matches := C.git_refspec_src_matches(s.ptr, cname)
100+
return matches != 0
101+
}
102+
103+
// SrcMatches checks if a refspec's destination descriptor matches a reference
104+
func (s *Refspec) DstMatches(refname string) bool {
105+
cname := C.CString(refname)
106+
defer C.free(unsafe.Pointer(cname))
107+
108+
matches := C.git_refspec_dst_matches(s.ptr, cname)
109+
return matches != 0
110+
}
111+
112+
// Transform a reference to its target following the refspec's rules
113+
func (s *Refspec) Transform(refname string) (string, error) {
114+
buf := C.git_buf{}
115+
116+
cname := C.CString(refname)
117+
defer C.free(unsafe.Pointer(cname))
118+
119+
runtime.LockOSThread()
120+
defer runtime.UnlockOSThread()
121+
122+
ret := C.git_refspec_transform(&buf, s.ptr, cname)
123+
if ret < 0 {
124+
return "", MakeGitError(ret)
125+
}
126+
defer C.git_buf_dispose(&buf)
127+
128+
return C.GoString(buf.ptr), nil
129+
}
130+
131+
// Rtransform converts a target reference to its source reference following the
132+
// refspec's rules
133+
func (s *Refspec) Rtransform(refname string) (string, error) {
134+
buf := C.git_buf{}
135+
136+
cname := C.CString(refname)
137+
defer C.free(unsafe.Pointer(cname))
138+
139+
runtime.LockOSThread()
140+
defer runtime.UnlockOSThread()
141+
142+
ret := C.git_refspec_rtransform(&buf, s.ptr, cname)
143+
if ret < 0 {
144+
return "", MakeGitError(ret)
145+
}
146+
defer C.git_buf_dispose(&buf)
147+
148+
return C.GoString(buf.ptr), nil
149+
}

‎refspec_test.go

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package git
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestRefspec(t *testing.T) {
8+
t.Parallel()
9+
10+
const (
11+
input = "+refs/heads/*:refs/remotes/origin/*"
12+
mainLocal = "refs/heads/main"
13+
mainRemote = "refs/remotes/origin/main"
14+
)
15+
16+
refspec, err := ParseRefspec(input, true)
17+
checkFatal(t, err)
18+
19+
// Accessors
20+
21+
s := refspec.String()
22+
if s != input {
23+
t.Errorf("expected string %q, got %q", input, s)
24+
}
25+
26+
if d := refspec.Direction(); d != ConnectDirectionFetch {
27+
t.Errorf("expected fetch refspec, got direction %v", d)
28+
}
29+
30+
if pat, expected := refspec.Src(), "refs/heads/*"; pat != expected {
31+
t.Errorf("expected refspec src %q, got %q", expected, pat)
32+
}
33+
34+
if pat, expected := refspec.Dst(), "refs/remotes/origin/*"; pat != expected {
35+
t.Errorf("expected refspec dst %q, got %q", expected, pat)
36+
}
37+
38+
if !refspec.Force() {
39+
t.Error("expected refspec force flag")
40+
}
41+
42+
// SrcMatches
43+
44+
if !refspec.SrcMatches(mainLocal) {
45+
t.Errorf("refspec source did not match %q", mainLocal)
46+
}
47+
48+
if refspec.SrcMatches("refs/tags/v1.0") {
49+
t.Error("refspec source matched under refs/tags")
50+
}
51+
52+
// DstMatches
53+
54+
if !refspec.DstMatches(mainRemote) {
55+
t.Errorf("refspec destination did not match %q", mainRemote)
56+
}
57+
58+
if refspec.DstMatches("refs/tags/v1.0") {
59+
t.Error("refspec destination matched under refs/tags")
60+
}
61+
62+
// Transforms
63+
64+
fromLocal, err := refspec.Transform(mainLocal)
65+
checkFatal(t, err)
66+
if fromLocal != mainRemote {
67+
t.Errorf("transform by refspec returned %s; expected %s", fromLocal, mainRemote)
68+
}
69+
70+
fromRemote, err := refspec.Rtransform(mainRemote)
71+
checkFatal(t, err)
72+
if fromRemote != mainLocal {
73+
t.Errorf("rtransform by refspec returned %s; expected %s", fromRemote, mainLocal)
74+
}
75+
}

0 commit comments

Comments
 (0)
Please sign in to comment.