Skip to content

Commit b5943d0

Browse files
committed
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. (cherry picked from commit f1fa96c)
1 parent f0a6f13 commit b5943d0

File tree

9 files changed

+745
-8
lines changed

9 files changed

+745
-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
@@ -14,6 +14,7 @@ import (
1414
"reflect"
1515
"runtime"
1616
"strings"
17+
"sync"
1718
"unsafe"
1819
)
1920

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

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

178237
const (
@@ -507,17 +566,42 @@ func RemoteIsValidName(name string) bool {
507566
return C.git_remote_is_valid_name(cname) == 1
508567
}
509568

510-
// Free releases the resources of the Remote.
511-
func (r *Remote) Free() {
569+
// free releases the resources of the Remote.
570+
func (r *Remote) free() {
512571
runtime.SetFinalizer(r, nil)
513572
C.git_remote_free(r.ptr)
514573
r.ptr = nil
515574
r.repo = nil
516575
}
517576

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

523607
func (c *RemoteCollection) List() ([]string, error) {
@@ -552,7 +636,7 @@ func (c *RemoteCollection) Create(name string, url string) (*Remote, error) {
552636
if ret < 0 {
553637
return nil, MakeGitError(ret)
554638
}
555-
runtime.SetFinalizer(remote, (*Remote).Free)
639+
c.trackRemote(remote)
556640
return remote, nil
557641
}
558642

@@ -568,13 +652,13 @@ func (c *RemoteCollection) CreateWithOptions(url string, option *RemoteCreateOpt
568652

569653
copts := populateRemoteCreateOptions(&C.git_remote_create_options{}, option, c.repo)
570654
defer freeRemoteCreateOptions(copts)
655+
571656
ret := C.git_remote_create_with_opts(&remote.ptr, curl, copts)
572657
runtime.KeepAlive(c.repo)
573658
if ret < 0 {
574659
return nil, MakeGitError(ret)
575660
}
576-
577-
runtime.SetFinalizer(remote, (*Remote).Free)
661+
c.trackRemote(remote)
578662
return remote, nil
579663
}
580664

@@ -610,7 +694,7 @@ func (c *RemoteCollection) CreateWithFetchspec(name string, url string, fetch st
610694
if ret < 0 {
611695
return nil, MakeGitError(ret)
612696
}
613-
runtime.SetFinalizer(remote, (*Remote).Free)
697+
c.trackRemote(remote)
614698
return remote, nil
615699
}
616700

@@ -627,7 +711,7 @@ func (c *RemoteCollection) CreateAnonymous(url string) (*Remote, error) {
627711
if ret < 0 {
628712
return nil, MakeGitError(ret)
629713
}
630-
runtime.SetFinalizer(remote, (*Remote).Free)
714+
c.trackRemote(remote)
631715
return remote, nil
632716
}
633717

@@ -644,10 +728,24 @@ func (c *RemoteCollection) Lookup(name string) (*Remote, error) {
644728
if ret < 0 {
645729
return nil, MakeGitError(ret)
646730
}
647-
runtime.SetFinalizer(remote, (*Remote).Free)
731+
c.trackRemote(remote)
648732
return remote, nil
649733
}
650734

735+
func (c *RemoteCollection) Free() {
736+
var remotes []*Remote
737+
c.Lock()
738+
for remotePtr, remote := range c.remotes {
739+
remotes = append(remotes, remote)
740+
delete(c.remotes, remotePtr)
741+
}
742+
c.Unlock()
743+
744+
for _, remote := range remotes {
745+
remotePointers.untrack(remote)
746+
}
747+
}
748+
651749
func (o *Remote) Name() string {
652750
s := C.git_remote_name(o.ptr)
653751
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)