Skip to content

Commit f1fa96c

Browse files
authored
Add support for custom smart transports (#806)
This change adds support for git smart transports. This will be then used to implement http, https, and ssh transports that don't rely on the libgit2 library.
1 parent dbe032c commit f1fa96c

File tree

9 files changed

+746
-8
lines changed

9 files changed

+746
-8
lines changed

clone.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ func remoteCreateCallback(
8585
// clear finalizer as the calling C function will
8686
// free the remote itself
8787
runtime.SetFinalizer(remote, nil)
88+
remote.repo.Remotes.untrackRemote(remote)
8889

8990
return C.int(ErrorCodeOK)
9091
}

git.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,15 @@ var (
128128
type doNotCompare [0]func()
129129

130130
var pointerHandles *HandleList
131+
var remotePointers *remotePointerList
131132

132133
func init() {
133134
initLibGit2()
134135
}
135136

136137
func initLibGit2() {
137138
pointerHandles = NewHandleList()
139+
remotePointers = newRemotePointerList()
138140

139141
C.git_libgit2_init()
140142

@@ -160,7 +162,11 @@ func initLibGit2() {
160162
// After this is called, invoking any function from this library will result in
161163
// undefined behavior, so make sure this is called carefully.
162164
func Shutdown() {
165+
if err := unregisterManagedTransports(); err != nil {
166+
panic(err)
167+
}
163168
pointerHandles.Clear()
169+
remotePointers.clear()
164170

165171
C.git_libgit2_shutdown()
166172
}

git_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ import (
1313
func TestMain(m *testing.M) {
1414
ret := m.Run()
1515

16+
if err := unregisterManagedTransports(); err != nil {
17+
panic(err)
18+
}
19+
1620
// Ensure that we are not leaking any pointer handles.
1721
pointerHandles.Lock()
1822
if len(pointerHandles.handles) > 0 {
@@ -23,6 +27,16 @@ func TestMain(m *testing.M) {
2327
}
2428
pointerHandles.Unlock()
2529

30+
// Or remote pointers.
31+
remotePointers.Lock()
32+
if len(remotePointers.pointers) > 0 {
33+
for ptr, remote := range remotePointers.pointers {
34+
fmt.Printf("%016p: %+v\n", ptr, remote)
35+
}
36+
panic("remote pointer list not empty")
37+
}
38+
remotePointers.Unlock()
39+
2640
Shutdown()
2741

2842
os.Exit(ret)

remote.go

Lines changed: 106 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"reflect"
1616
"runtime"
1717
"strings"
18+
"sync"
1819
"unsafe"
1920
)
2021

@@ -174,6 +175,64 @@ type Remote struct {
174175
repo *Repository
175176
}
176177

178+
type remotePointerList struct {
179+
sync.RWMutex
180+
// stores the Go pointers
181+
pointers map[*C.git_remote]*Remote
182+
}
183+
184+
func newRemotePointerList() *remotePointerList {
185+
return &remotePointerList{
186+
pointers: make(map[*C.git_remote]*Remote),
187+
}
188+
}
189+
190+
// track adds the given pointer to the list of pointers to track and
191+
// returns a pointer value which can be passed to C as an opaque
192+
// pointer.
193+
func (v *remotePointerList) track(remote *Remote) {
194+
v.Lock()
195+
v.pointers[remote.ptr] = remote
196+
v.Unlock()
197+
198+
runtime.SetFinalizer(remote, (*Remote).Free)
199+
}
200+
201+
// untrack stops tracking the git_remote pointer.
202+
func (v *remotePointerList) untrack(remote *Remote) {
203+
v.Lock()
204+
delete(v.pointers, remote.ptr)
205+
v.Unlock()
206+
}
207+
208+
// clear stops tracking all the git_remote pointers.
209+
func (v *remotePointerList) clear() {
210+
v.Lock()
211+
var remotes []*Remote
212+
for remotePtr, remote := range v.pointers {
213+
remotes = append(remotes, remote)
214+
delete(v.pointers, remotePtr)
215+
}
216+
v.Unlock()
217+
218+
for _, remote := range remotes {
219+
remote.free()
220+
}
221+
}
222+
223+
// get retrieves the pointer from the given *git_remote.
224+
func (v *remotePointerList) get(ptr *C.git_remote) (*Remote, bool) {
225+
v.RLock()
226+
defer v.RUnlock()
227+
228+
r, ok := v.pointers[ptr]
229+
if !ok {
230+
return nil, false
231+
}
232+
233+
return r, true
234+
}
235+
177236
type CertificateKind uint
178237

179238
const (
@@ -509,17 +568,42 @@ func RemoteIsValidName(name string) bool {
509568
return C.git_remote_is_valid_name(cname) == 1
510569
}
511570

512-
// Free releases the resources of the Remote.
513-
func (r *Remote) Free() {
571+
// free releases the resources of the Remote.
572+
func (r *Remote) free() {
514573
runtime.SetFinalizer(r, nil)
515574
C.git_remote_free(r.ptr)
516575
r.ptr = nil
517576
r.repo = nil
518577
}
519578

579+
// Free releases the resources of the Remote.
580+
func (r *Remote) Free() {
581+
r.repo.Remotes.untrackRemote(r)
582+
r.free()
583+
}
584+
520585
type RemoteCollection struct {
521586
doNotCompare
522587
repo *Repository
588+
589+
sync.RWMutex
590+
remotes map[*C.git_remote]*Remote
591+
}
592+
593+
func (c *RemoteCollection) trackRemote(r *Remote) {
594+
c.Lock()
595+
c.remotes[r.ptr] = r
596+
c.Unlock()
597+
598+
remotePointers.track(r)
599+
}
600+
601+
func (c *RemoteCollection) untrackRemote(r *Remote) {
602+
c.Lock()
603+
delete(c.remotes, r.ptr)
604+
c.Unlock()
605+
606+
remotePointers.untrack(r)
523607
}
524608

525609
func (c *RemoteCollection) List() ([]string, error) {
@@ -554,7 +638,7 @@ func (c *RemoteCollection) Create(name string, url string) (*Remote, error) {
554638
if ret < 0 {
555639
return nil, MakeGitError(ret)
556640
}
557-
runtime.SetFinalizer(remote, (*Remote).Free)
641+
c.trackRemote(remote)
558642
return remote, nil
559643
}
560644

@@ -570,13 +654,13 @@ func (c *RemoteCollection) CreateWithOptions(url string, option *RemoteCreateOpt
570654

571655
copts := populateRemoteCreateOptions(&C.git_remote_create_options{}, option, c.repo)
572656
defer freeRemoteCreateOptions(copts)
657+
573658
ret := C.git_remote_create_with_opts(&remote.ptr, curl, copts)
574659
runtime.KeepAlive(c.repo)
575660
if ret < 0 {
576661
return nil, MakeGitError(ret)
577662
}
578-
579-
runtime.SetFinalizer(remote, (*Remote).Free)
663+
c.trackRemote(remote)
580664
return remote, nil
581665
}
582666

@@ -612,7 +696,7 @@ func (c *RemoteCollection) CreateWithFetchspec(name string, url string, fetch st
612696
if ret < 0 {
613697
return nil, MakeGitError(ret)
614698
}
615-
runtime.SetFinalizer(remote, (*Remote).Free)
699+
c.trackRemote(remote)
616700
return remote, nil
617701
}
618702

@@ -629,7 +713,7 @@ func (c *RemoteCollection) CreateAnonymous(url string) (*Remote, error) {
629713
if ret < 0 {
630714
return nil, MakeGitError(ret)
631715
}
632-
runtime.SetFinalizer(remote, (*Remote).Free)
716+
c.trackRemote(remote)
633717
return remote, nil
634718
}
635719

@@ -646,10 +730,24 @@ func (c *RemoteCollection) Lookup(name string) (*Remote, error) {
646730
if ret < 0 {
647731
return nil, MakeGitError(ret)
648732
}
649-
runtime.SetFinalizer(remote, (*Remote).Free)
733+
c.trackRemote(remote)
650734
return remote, nil
651735
}
652736

737+
func (c *RemoteCollection) Free() {
738+
var remotes []*Remote
739+
c.Lock()
740+
for remotePtr, remote := range c.remotes {
741+
remotes = append(remotes, remote)
742+
delete(c.remotes, remotePtr)
743+
}
744+
c.Unlock()
745+
746+
for _, remote := range remotes {
747+
remotePointers.untrack(remote)
748+
}
749+
}
750+
653751
func (o *Remote) Name() string {
654752
s := C.git_remote_name(o.ptr)
655753
runtime.KeepAlive(o)

remote_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ func TestRemoteConnectOption(t *testing.T) {
9797

9898
remote, err := repo.Remotes.CreateWithOptions("https://github.com/libgit2/TestGitRepository", option)
9999
checkFatal(t, err)
100+
defer remote.Free()
100101

101102
err = remote.ConnectFetch(nil, nil, nil)
102103
checkFatal(t, err)

repository.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func newRepositoryFromC(ptr *C.git_repository) *Repository {
4646
repo := &Repository{ptr: ptr}
4747

4848
repo.Remotes.repo = repo
49+
repo.Remotes.remotes = make(map[*C.git_remote]*Remote)
4950
repo.Submodules.repo = repo
5051
repo.References.repo = repo
5152
repo.Notes.repo = repo
@@ -144,6 +145,7 @@ func (v *Repository) Free() {
144145
ptr := v.ptr
145146
v.ptr = nil
146147
runtime.SetFinalizer(v, nil)
148+
v.Remotes.Free()
147149
if v.weak {
148150
return
149151
}

0 commit comments

Comments
 (0)