diff --git a/git_test.go b/git_test.go index 52aea1d..fff3c6c 100644 --- a/git_test.go +++ b/git_test.go @@ -1,8 +1,8 @@ package git import ( - "testing" "io/ioutil" + "testing" "time" ) @@ -14,7 +14,17 @@ func createTestRepo(t *testing.T) *Repository { checkFatal(t, err) tmpfile := "README" - err = ioutil.WriteFile(path + "/" + tmpfile, []byte("foo\n"), 0644) + err = ioutil.WriteFile(path+"/"+tmpfile, []byte("foo\n"), 0644) + checkFatal(t, err) + + return repo +} + +func createBareTestRepo(t *testing.T) *Repository { + // figure out where we can create the test repo + path, err := ioutil.TempDir("", "git2go") + checkFatal(t, err) + repo, err := InitRepository(path, true) checkFatal(t, err) return repo @@ -44,4 +54,3 @@ func seedTestRepo(t *testing.T, repo *Repository) (*Oid, *Oid) { return commitId, treeId } - diff --git a/push.go b/push.go new file mode 100644 index 0000000..0694fe3 --- /dev/null +++ b/push.go @@ -0,0 +1,181 @@ +package git + +/* +#include +#include + +int _go_git_push_status_foreach(git_push *push, void *data); +int _go_git_push_set_callbacks(git_push *push, void *packbuilder_progress_data, void *transfer_progress_data); + +*/ +import "C" +import ( + "runtime" + "unsafe" +) + +type Push struct { + ptr *C.git_push + + packbuilderProgress *PackbuilderProgressCallback + transferProgress *PushTransferProgressCallback +} + +func newPushFromC(cpush *C.git_push) *Push { + p := &Push{ptr: cpush} + runtime.SetFinalizer(p, (*Push).Free) + return p +} + +func (p *Push) Free() { + runtime.SetFinalizer(p, nil) + C.git_push_free(p.ptr) +} + +func (remote *Remote) NewPush() (*Push, error) { + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + var cpush *C.git_push + ret := C.git_push_new(&cpush, remote.ptr) + if ret < 0 { + return nil, MakeGitError(ret) + } + return newPushFromC(cpush), nil +} + +func (p *Push) Finish() error { + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_push_finish(p.ptr) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +func (p *Push) UnpackOk() bool { + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_push_unpack_ok(p.ptr) + if ret == 0 { + return false + } else { + return true + } + +} + +func (p *Push) UpdateTips(sig *Signature, msg string) error { + + var csig *C.git_signature = nil + if sig != nil { + csig = sig.toC() + defer C.free(unsafe.Pointer(csig)) + } + + cmsg := C.CString(msg) + defer C.free(unsafe.Pointer(cmsg)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_push_update_tips(p.ptr, csig, cmsg) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +func (p *Push) AddRefspec(refspec string) error { + + crefspec := C.CString(refspec) + defer C.free(unsafe.Pointer(crefspec)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_push_add_refspec(p.ptr, crefspec) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +type PushOptions struct { + Version uint + PbParallelism uint +} + +func (p *Push) SetOptions(opts PushOptions) error { + copts := C.git_push_options{version: C.uint(opts.Version), pb_parallelism: C.uint(opts.PbParallelism)} + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_push_set_options(p.ptr, &copts) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +type StatusForeachFunc func(ref string, msg string) int + +//export statusForeach +func statusForeach(_ref *C.char, _msg *C.char, _data unsafe.Pointer) C.int { + ref := C.GoString(_ref) + msg := C.GoString(_msg) + + cb := (*StatusForeachFunc)(_data) + + return C.int((*cb)(ref, msg)) +} + +func (p *Push) StatusForeach(callback StatusForeachFunc) error { + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C._go_git_push_status_foreach(p.ptr, unsafe.Pointer(&callback)) + if ret < 0 { + return MakeGitError(ret) + } + return nil + +} + +type PushCallbacks struct { + PackbuilderProgress *PackbuilderProgressCallback + TransferProgress *PushTransferProgressCallback +} + +type PackbuilderProgressCallback func(stage int, current uint, total uint) int +type PushTransferProgressCallback func(current uint, total uint, bytes uint) int + +//export packbuilderProgress +func packbuilderProgress(stage C.int, current C.uint, total C.uint, data unsafe.Pointer) C.int { + return C.int((*(*PackbuilderProgressCallback)(data))(int(stage), uint(current), uint(total))) +} + +//export pushTransferProgress +func pushTransferProgress(current C.uint, total C.uint, bytes C.size_t, data unsafe.Pointer) C.int { + return C.int((*(*PushTransferProgressCallback)(data))(uint(current), uint(total), uint(bytes))) +} + +func (p *Push) SetCallbacks(callbacks PushCallbacks) { + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + // save callbacks so they don't get GC'd + p.packbuilderProgress = callbacks.PackbuilderProgress + p.transferProgress = callbacks.TransferProgress + + C._go_git_push_set_callbacks(p.ptr, unsafe.Pointer(p.packbuilderProgress), unsafe.Pointer(p.transferProgress)) +} diff --git a/push_test.go b/push_test.go new file mode 100644 index 0000000..dfd4af7 --- /dev/null +++ b/push_test.go @@ -0,0 +1,56 @@ +package git + +import ( + "log" + "testing" + "time" +) + +func Test_Push_ToRemote(t *testing.T) { + repo := createBareTestRepo(t) + repo2 := createTestRepo(t) + + remote, err := repo2.CreateRemote("test_push", repo.Path()) + checkFatal(t, err) + + index, err := repo2.Index() + checkFatal(t, err) + + index.AddByPath("README") + + err = index.Write() + checkFatal(t, err) + + newTreeId, err := index.WriteTree() + checkFatal(t, err) + + tree, err := repo2.LookupTree(newTreeId) + checkFatal(t, err) + + sig := &Signature{Name: "Rand Om Hacker", Email: "random@hacker.com", When: time.Now()} + // this should cause master branch to be created if it does not already exist + _, err = repo2.CreateCommit("HEAD", sig, sig, "message", tree) + checkFatal(t, err) + + push, err := remote.NewPush() + checkFatal(t, err) + + err = push.AddRefspec("refs/heads/master") + checkFatal(t, err) + + err = push.Finish() + checkFatal(t, err) + + err = push.StatusForeach(func(ref string, msg string) int { + log.Printf("%s -> %s", ref, msg) + return 0 + }) + checkFatal(t, err) + + if !push.UnpackOk() { + t.Fatalf("unable to unpack") + } + + defer remote.Free() + defer repo.Free() +} diff --git a/remote.go b/remote.go index 38c1d47..ab2e174 100644 --- a/remote.go +++ b/remote.go @@ -40,27 +40,7 @@ type RemoteCallbacks struct { UpdateTipsCallback } -type Remote interface { - Save() error - Owner() Repository - Name() string - Url() string - PushUrl() string - - SetUrl(url string) error - SetPushUrl(url string) error - - AddFetch(refspec string) error - GetFetchRefspecs() ([]string, error) - SetFetchRefspecs(refspecs []string) error - AddPush(refspec string) error - GetPushRefspecs() ([]string, error) - SetPushRefspecs(refspecs []string) error - ClearRefspecs() - RefspecCount() uint -} - -type gitRemote struct { +type Remote struct { ptr *C.git_remote } @@ -161,13 +141,13 @@ func RemoteIsValidName(name string) bool { return false } -func freeRemote(o *gitRemote) { - C.git_remote_free(o.ptr) +func (r *Remote) Free() { + runtime.SetFinalizer(r, nil) + C.git_remote_free(r.ptr) } -func CreateRemote(repo *Repository, name string, url string) (Remote, error) { - remote := &gitRemote{} - runtime.SetFinalizer(remote, freeRemote) +func (repo *Repository) CreateRemote(name string, url string) (*Remote, error) { + remote := &Remote{} cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) @@ -181,12 +161,12 @@ func CreateRemote(repo *Repository, name string, url string) (Remote, error) { if ret < 0 { return nil, MakeGitError(ret) } + runtime.SetFinalizer(remote, (*Remote).Free) return remote, nil } -func CreateRemoteWithFetchspec(repo *Repository, name string, url string, fetch string) (Remote, error) { - remote := &gitRemote{} - runtime.SetFinalizer(remote, freeRemote) +func (repo *Repository) CreateRemoteWithFetchspec(name string, url string, fetch string) (*Remote, error) { + remote := &Remote{} cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) @@ -202,12 +182,12 @@ func CreateRemoteWithFetchspec(repo *Repository, name string, url string, fetch if ret < 0 { return nil, MakeGitError(ret) } + runtime.SetFinalizer(remote, (*Remote).Free) return remote, nil } -func CreateRemoteInMemory(repo *Repository, fetch string, url string) (Remote, error) { - remote := &gitRemote{} - runtime.SetFinalizer(remote, freeRemote) +func (repo *Repository) CreateRemoteInMemory(fetch string, url string) (*Remote, error) { + remote := &Remote{} curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) @@ -221,12 +201,12 @@ func CreateRemoteInMemory(repo *Repository, fetch string, url string) (Remote, e if ret < 0 { return nil, MakeGitError(ret) } + runtime.SetFinalizer(remote, (*Remote).Free) return remote, nil } -func LoadRemote(repo *Repository, name string) (Remote, error) { - remote := &gitRemote{} - runtime.SetFinalizer(remote, freeRemote) +func (repo *Repository) LoadRemote(name string) (*Remote, error) { + remote := &Remote{} cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) @@ -238,10 +218,11 @@ func LoadRemote(repo *Repository, name string) (Remote, error) { if ret < 0 { return nil, MakeGitError(ret) } + runtime.SetFinalizer(remote, (*Remote).Free) return remote, nil } -func (o *gitRemote) Save() error { +func (o *Remote) Save() error { runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -253,23 +234,23 @@ func (o *gitRemote) Save() error { return nil } -func (o *gitRemote) Owner() Repository { +func (o *Remote) Owner() Repository { return Repository{C.git_remote_owner(o.ptr)} } -func (o *gitRemote) Name() string { +func (o *Remote) Name() string { return C.GoString(C.git_remote_name(o.ptr)) } -func (o *gitRemote) Url() string { +func (o *Remote) Url() string { return C.GoString(C.git_remote_url(o.ptr)) } -func (o *gitRemote) PushUrl() string { +func (o *Remote) PushUrl() string { return C.GoString(C.git_remote_pushurl(o.ptr)) } -func (o *gitRemote) SetUrl(url string) error { +func (o *Remote) SetUrl(url string) error { curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) @@ -283,7 +264,7 @@ func (o *gitRemote) SetUrl(url string) error { return nil } -func (o *gitRemote) SetPushUrl(url string) error { +func (o *Remote) SetPushUrl(url string) error { curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) @@ -297,7 +278,7 @@ func (o *gitRemote) SetPushUrl(url string) error { return nil } -func (o *gitRemote) AddFetch(refspec string) error { +func (o *Remote) AddFetch(refspec string) error { crefspec := C.CString(refspec) defer C.free(unsafe.Pointer(crefspec)) @@ -311,7 +292,7 @@ func (o *gitRemote) AddFetch(refspec string) error { return nil } -func (o *gitRemote) GetFetchRefspecs() ([]string, error) { +func (o *Remote) GetFetchRefspecs() ([]string, error) { crefspecs := C.git_strarray{} runtime.LockOSThread() @@ -330,7 +311,7 @@ func (o *gitRemote) GetFetchRefspecs() ([]string, error) { return refspecs, nil } -func (o *gitRemote) SetFetchRefspecs(refspecs []string) error { +func (o *Remote) SetFetchRefspecs(refspecs []string) error { crefspecs := C.git_strarray{} crefspecs.count = C.size_t(len(refspecs)) crefspecs.strings = (**C.char)(C.malloc(C.size_t(unsafe.Sizeof(unsafe.Pointer(nil)) * uintptr(crefspecs.count)))) @@ -349,7 +330,7 @@ func (o *gitRemote) SetFetchRefspecs(refspecs []string) error { return nil } -func (o *gitRemote) AddPush(refspec string) error { +func (o *Remote) AddPush(refspec string) error { crefspec := C.CString(refspec) defer C.free(unsafe.Pointer(crefspec)) @@ -363,7 +344,7 @@ func (o *gitRemote) AddPush(refspec string) error { return nil } -func (o *gitRemote) GetPushRefspecs() ([]string, error) { +func (o *Remote) GetPushRefspecs() ([]string, error) { crefspecs := C.git_strarray{} runtime.LockOSThread() @@ -382,7 +363,7 @@ func (o *gitRemote) GetPushRefspecs() ([]string, error) { return refspecs, nil } -func (o *gitRemote) SetPushRefspecs(refspecs []string) error { +func (o *Remote) SetPushRefspecs(refspecs []string) error { crefspecs := C.git_strarray{} crefspecs.count = C.size_t(len(refspecs)) crefspecs.strings = (**C.char)(C.malloc(C.size_t(unsafe.Sizeof(unsafe.Pointer(nil)) * uintptr(crefspecs.count)))) @@ -401,10 +382,28 @@ func (o *gitRemote) SetPushRefspecs(refspecs []string) error { return nil } -func (o *gitRemote) ClearRefspecs() { +func (o *Remote) ClearRefspecs() { C.git_remote_clear_refspecs(o.ptr) } -func (o *gitRemote) RefspecCount() uint { +func (o *Remote) RefspecCount() uint { return uint(C.git_remote_refspec_count(o.ptr)) } + +func (o *Remote) Fetch(sig *Signature, msg string) error { + + var csig *C.git_signature = nil + if sig != nil { + csig = sig.toC() + defer C.free(unsafe.Pointer(csig)) + } + + cmsg := C.CString(msg) + defer C.free(unsafe.Pointer(cmsg)) + + ret := C.git_remote_fetch(o.ptr, csig, cmsg) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} diff --git a/wrapper.c b/wrapper.c index 7519a96..9e193ca 100644 --- a/wrapper.c +++ b/wrapper.c @@ -51,4 +51,16 @@ char *_go_git_get_strarray_n(git_strarray *array, size_t n) { return array->strings[n]; } +typedef int (*status_foreach_cb)(const char *ref, const char *msg, void *data); + +int _go_git_push_status_foreach(git_push *push, void *data) +{ + return git_push_status_foreach(push, (status_foreach_cb)statusForeach, data); +} + +int _go_git_push_set_callbacks(git_push *push, void *packbuilder_progress_data, void *transfer_progress_data) +{ + return git_push_set_callbacks(push, packbuilderProgress, packbuilder_progress_data, pushTransferProgress, transfer_progress_data); +} + /* EOF */