@@ -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+
177236type CertificateKind uint
178237
179238const (
@@ -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+
520585type 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
525609func (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+
653751func (o * Remote ) Name () string {
654752 s := C .git_remote_name (o .ptr )
655753 runtime .KeepAlive (o )
0 commit comments