summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md18
-rw-r--r--config.go52
-rw-r--r--diff.go46
-rw-r--r--diff_test.go33
-rw-r--r--git.go1
-rw-r--r--note.go99
-rw-r--r--note_test.go113
-rw-r--r--push.go181
-rw-r--r--push_test.go46
-rw-r--r--remote.go95
-rw-r--r--remote_test.go83
-rw-r--r--repository.go104
-rw-r--r--submodule.go52
-rw-r--r--submodule_test.go24
-rw-r--r--tree.go2
m---------vendor/libgit20
-rw-r--r--wrapper.c25
17 files changed, 718 insertions, 256 deletions
diff --git a/README.md b/README.md
index 9995707..faeb5f5 100644
--- a/README.md
+++ b/README.md
@@ -3,12 +3,24 @@ git2go
[![GoDoc](https://godoc.org/github.com/libgit2/git2go?status.svg)](http://godoc.org/github.com/libgit2/git2go) [![Build Status](https://travis-ci.org/libgit2/git2go.svg?branch=master)](https://travis-ci.org/libgit2/git2go)
-Go bindings for [libgit2](http://libgit2.github.com/). The master branch follows the latest libgit2 release.
+Go bindings for [libgit2](http://libgit2.github.com/). The master branch follows the latest libgit2 release. The versioned branches indicate which libgit2 version they work against.
Installing
----------
-This project needs libgit2, which is written in C so we need to build that as well. In order to build libgit2, you need `cmake`, `pkg-config` and a C compiler. You will also need the development packages for OpenSSL and LibSSH2 if you want to use HTTPS and SSH respectively.
+This project needs libgit2, which is written in C so we need to build that as well. In order to build libgit2, you need `cmake`, `pkg-config` and a C compiler. You will also need the development packages for OpenSSL and LibSSH2 installed if you want libgit2 to support HTTPS and SSH respectively.
+
+### Stable version
+
+git2go has versioned branches which indicate which version of libgit2 they work against. Install the development package it on your system via your favourite package manager or from source and you can use a service like gopkg.in to use the appropriate version. For the libgit2 v0.22 case, you can use
+
+ import "gopkg.in/libgit2/git2go.v22"
+
+to use a version of git2go which will work against libgit2 v0.22 and dynamically link to the library.
+
+### From master
+
+The master branch follows libgit2's master branch, which means there is no stable API or ABI to link against. git2go can statically link against a vendored version of libgit2.
Run `go get -d github.com/libgit2/git2go` to download the code and go to your `$GOPATH/src/github.com/libgit2/git2go` dir. From there, we need to build the C code and put it into the resulting go binary.
@@ -25,7 +37,7 @@ libgit2 uses OpenSSL and LibSSH2 for performing encrypted network connections. F
Running the tests
-----------------
-Similarly to installing, running the tests requires linking against the local libgit2 library, so the Makefile provides a wrapper
+For the stable version, `go test` will work as usual. For the master branch, similarly to installing, running the tests requires linking against the local libgit2 library, so the Makefile provides a wrapper
make test
diff --git a/config.go b/config.go
index 2965a69..9d25e35 100644
--- a/config.go
+++ b/config.go
@@ -35,14 +35,14 @@ const (
)
type ConfigEntry struct {
- Name string
+ Name string
Value string
Level ConfigLevel
}
func newConfigEntryFromC(centry *C.git_config_entry) *ConfigEntry {
return &ConfigEntry{
- Name: C.GoString(centry.name),
+ Name: C.GoString(centry.name),
Value: C.GoString(centry.value),
Level: ConfigLevel(centry.level),
}
@@ -74,7 +74,6 @@ func (c *Config) AddFile(path string, level ConfigLevel, force bool) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
-
ret := C.git_config_add_file_ondisk(c.ptr, cpath, C.git_config_level_t(level), cbool(force))
if ret < 0 {
return MakeGitError(ret)
@@ -130,7 +129,6 @@ func (c *Config) LookupString(name string) (string, error) {
return C.GoString(ptr), nil
}
-
func (c *Config) LookupBool(name string) (bool, error) {
var out C.int
cname := C.CString(name)
@@ -234,7 +232,6 @@ func (c *Config) SetInt32(name string, value int32) (err error) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
-
runtime.LockOSThread()
defer runtime.UnlockOSThread()
@@ -368,3 +365,48 @@ func (iter *ConfigIterator) Free() {
runtime.SetFinalizer(iter, nil)
C.free(unsafe.Pointer(iter.ptr))
}
+
+func ConfigFindGlobal() (string, error) {
+ var buf C.git_buf
+ defer C.git_buf_free(&buf)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_config_find_global(&buf)
+ if ret < 0 {
+ return "", MakeGitError(ret)
+ }
+
+ return C.GoString(buf.ptr), nil
+}
+
+func ConfigFindSystem() (string, error) {
+ var buf C.git_buf
+ defer C.git_buf_free(&buf)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_config_find_system(&buf)
+ if ret < 0 {
+ return "", MakeGitError(ret)
+ }
+
+ return C.GoString(buf.ptr), nil
+}
+
+func ConfigFindXDG() (string, error) {
+ var buf C.git_buf
+ defer C.git_buf_free(&buf)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_config_find_xdg(&buf)
+ if ret < 0 {
+ return "", MakeGitError(ret)
+ }
+
+ return C.GoString(buf.ptr), nil
+}
diff --git a/diff.go b/diff.go
index 999f067..63fa867 100644
--- a/diff.go
+++ b/diff.go
@@ -190,6 +190,46 @@ func (diff *Diff) FindSimilar(opts *DiffFindOptions) error {
return nil
}
+type DiffStats struct {
+ ptr *C.git_diff_stats
+}
+
+func (stats *DiffStats) Free() error {
+ if stats.ptr == nil {
+ return ErrInvalid
+ }
+ runtime.SetFinalizer(stats, nil)
+ C.git_diff_stats_free(stats.ptr)
+ stats.ptr = nil
+ return nil
+}
+
+func (stats *DiffStats) Insertions() int {
+ return int(C.git_diff_stats_insertions(stats.ptr))
+}
+
+func (stats *DiffStats) Deletions() int {
+ return int(C.git_diff_stats_deletions(stats.ptr))
+}
+
+func (stats *DiffStats) FilesChanged() int {
+ return int(C.git_diff_stats_files_changed(stats.ptr))
+}
+
+func (diff *Diff) Stats() (*DiffStats, error) {
+ stats := new(DiffStats)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if ecode := C.git_diff_get_stats(&stats.ptr, diff.ptr); ecode < 0 {
+ return nil, MakeGitError(ecode)
+ }
+ runtime.SetFinalizer(stats, (*DiffStats).Free)
+
+ return stats, nil
+}
+
type diffForEachData struct {
FileCallback DiffForEachFileCallback
HunkCallback DiffForEachHunkCallback
@@ -371,6 +411,8 @@ func DefaultDiffOptions() (DiffOptions, error) {
InterhunkLines: uint32(opts.interhunk_lines),
IdAbbrev: uint16(opts.id_abbrev),
MaxSize: int(opts.max_size),
+ OldPrefix: "a",
+ NewPrefix: "b",
}, nil
}
@@ -479,6 +521,8 @@ func diffOptionsToC(opts *DiffOptions) (copts *C.git_diff_options, notifyData *d
interhunk_lines: C.uint32_t(opts.InterhunkLines),
id_abbrev: C.uint16_t(opts.IdAbbrev),
max_size: C.git_off_t(opts.MaxSize),
+ old_prefix: C.CString(opts.OldPrefix),
+ new_prefix: C.CString(opts.NewPrefix),
}
if opts.NotifyCallback != nil {
@@ -493,6 +537,8 @@ func freeDiffOptions(copts *C.git_diff_options) {
if copts != nil {
cpathspec := copts.pathspec
freeStrarray(&cpathspec)
+ C.free(unsafe.Pointer(copts.old_prefix))
+ C.free(unsafe.Pointer(copts.new_prefix))
}
}
diff --git a/diff_test.go b/diff_test.go
index 84d72db..fc6fed9 100644
--- a/diff_test.go
+++ b/diff_test.go
@@ -3,6 +3,7 @@ package git
import (
"errors"
"os"
+ "strings"
"testing"
)
@@ -75,6 +76,8 @@ func TestDiffTreeToTree(t *testing.T) {
callbackInvoked = true
return nil
},
+ OldPrefix: "x1/",
+ NewPrefix: "y1/",
}
diff, err := repo.DiffTreeToTree(originalTree, newTree, &opts)
@@ -90,7 +93,19 @@ func TestDiffTreeToTree(t *testing.T) {
files := make([]string, 0)
hunks := make([]DiffHunk, 0)
lines := make([]DiffLine, 0)
+ patches := make([]string, 0)
err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) {
+ patch, err := diff.Patch(len(patches))
+ if err != nil {
+ return nil, err
+ }
+ defer patch.Free()
+ patchStr, err := patch.String()
+ if err != nil {
+ return nil, err
+ }
+ patches = append(patches, patchStr)
+
files = append(files, file.OldFile.Path)
return func(hunk DiffHunk) (DiffForEachLineCallback, error) {
hunks = append(hunks, hunk)
@@ -131,6 +146,24 @@ func TestDiffTreeToTree(t *testing.T) {
t.Fatal("Incorrect lines in diff")
}
+ if want1, want2 := "x1/README", "y1/README"; !strings.Contains(patches[0], want1) || !strings.Contains(patches[0], want2) {
+ t.Errorf("Diff patch doesn't contain %q or %q\n\n%s", want1, want2, patches[0])
+
+ }
+
+ stats, err := diff.Stats()
+ checkFatal(t, err)
+
+ if stats.Insertions() != 1 {
+ t.Fatal("Incorrect number of insertions in diff")
+ }
+ if stats.Deletions() != 1 {
+ t.Fatal("Incorrect number of deletions in diff")
+ }
+ if stats.FilesChanged() != 1 {
+ t.Fatal("Incorrect number of changed files in diff")
+ }
+
errTest := errors.New("test error")
err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) {
diff --git a/git.go b/git.go
index 89bd561..8e78710 100644
--- a/git.go
+++ b/git.go
@@ -2,6 +2,7 @@ package git
/*
#include <git2.h>
+#include <git2/sys/openssl.h>
*/
import "C"
import (
diff --git a/note.go b/note.go
new file mode 100644
index 0000000..3cdd340
--- /dev/null
+++ b/note.go
@@ -0,0 +1,99 @@
+package git
+
+/*
+#include <git2.h>
+*/
+import "C"
+
+import (
+ "runtime"
+ "unsafe"
+)
+
+// Note
+type Note struct {
+ ptr *C.git_note
+}
+
+// Free frees a git_note object
+func (n *Note) Free() error {
+ if n.ptr == nil {
+ return ErrInvalid
+ }
+ runtime.SetFinalizer(n, nil)
+ C.git_note_free(n.ptr)
+ n.ptr = nil
+ return nil
+}
+
+// Author returns the signature of the note author
+func (n *Note) Author() *Signature {
+ ptr := C.git_note_author(n.ptr)
+ return newSignatureFromC(ptr)
+}
+
+// Id returns the note object's id
+func (n *Note) Id() *Oid {
+ ptr := C.git_note_id(n.ptr)
+ return newOidFromC(ptr)
+}
+
+// Committer returns the signature of the note committer
+func (n *Note) Committer() *Signature {
+ ptr := C.git_note_committer(n.ptr)
+ return newSignatureFromC(ptr)
+}
+
+// Message returns the note message
+func (n *Note) Message() string {
+ return C.GoString(C.git_note_message(n.ptr))
+}
+
+// NoteIterator
+type NoteIterator struct {
+ ptr *C.git_note_iterator
+}
+
+// NewNoteIterator creates a new iterator for notes
+func (repo *Repository) NewNoteIterator(ref string) (*NoteIterator, error) {
+ var cref *C.char
+ if ref == "" {
+ cref = nil
+ } else {
+ cref = C.CString(ref)
+ defer C.free(unsafe.Pointer(cref))
+ }
+
+ var ptr *C.git_note_iterator
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if ret := C.git_note_iterator_new(&ptr, repo.ptr, cref); ret < 0 {
+ return nil, MakeGitError(ret)
+ }
+
+ iter := &NoteIterator{ptr: ptr}
+ runtime.SetFinalizer(iter, (*NoteIterator).Free)
+ return iter, nil
+}
+
+// Free frees the note interator
+func (v *NoteIterator) Free() {
+ runtime.SetFinalizer(v, nil)
+ C.git_note_iterator_free(v.ptr)
+}
+
+// Next returns the current item (note id & annotated id) and advances the
+// iterator internally to the next item
+func (it *NoteIterator) Next() (noteId, annotatedId *Oid, err error) {
+ noteId, annotatedId = new(Oid), new(Oid)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if ret := C.git_note_next(noteId.toC(), annotatedId.toC(), it.ptr); ret < 0 {
+ err = MakeGitError(ret)
+ }
+ return
+}
diff --git a/note_test.go b/note_test.go
new file mode 100644
index 0000000..f5e9c01
--- /dev/null
+++ b/note_test.go
@@ -0,0 +1,113 @@
+package git
+
+import (
+ "fmt"
+ "os"
+ "reflect"
+ "testing"
+ "time"
+)
+
+func TestCreateNote(t *testing.T) {
+ repo := createTestRepo(t)
+ defer os.RemoveAll(repo.Workdir())
+
+ commitId, _ := seedTestRepo(t, repo)
+
+ commit, err := repo.LookupCommit(commitId)
+ checkFatal(t, err)
+
+ note, noteId := createTestNote(t, repo, commit)
+
+ compareStrings(t, "I am a note\n", note.Message())
+ compareStrings(t, noteId.String(), note.Id().String())
+ compareStrings(t, "alice", note.Author().Name)
+ compareStrings(t, "[email protected]", note.Author().Email)
+ compareStrings(t, "alice", note.Committer().Name)
+ compareStrings(t, "[email protected]", note.Committer().Email)
+}
+
+func TestNoteIterator(t *testing.T) {
+ repo := createTestRepo(t)
+ defer os.RemoveAll(repo.Workdir())
+ seedTestRepo(t, repo)
+
+ notes := make([]*Note, 5)
+ for i := range notes {
+ commitId, _ := updateReadme(t, repo, fmt.Sprintf("README v%d\n", i+1))
+ commit, err := repo.LookupCommit(commitId)
+ checkFatal(t, err)
+
+ note, _ := createTestNote(t, repo, commit)
+ notes[i] = note
+ }
+
+ iter, err := repo.NewNoteIterator("")
+ checkFatal(t, err)
+ for {
+ noteId, commitId, err := iter.Next()
+ if err != nil {
+ if !IsErrorCode(err, ErrIterOver) {
+ checkFatal(t, err)
+ }
+ break
+ }
+
+ note, err := repo.ReadNote("", commitId)
+ checkFatal(t, err)
+
+ if !reflect.DeepEqual(note.Id(), noteId) {
+ t.Errorf("expected note oid '%v', actual '%v'", note.Id(), noteId)
+ }
+ }
+}
+
+func TestRemoveNote(t *testing.T) {
+ repo := createTestRepo(t)
+ defer os.RemoveAll(repo.Workdir())
+
+ commitId, _ := seedTestRepo(t, repo)
+
+ commit, err := repo.LookupCommit(commitId)
+ checkFatal(t, err)
+
+ note, _ := createTestNote(t, repo, commit)
+
+ _, err = repo.ReadNote("", commit.Id())
+ checkFatal(t, err)
+
+ err = repo.RemoveNote("", note.Author(), note.Committer(), commitId)
+ checkFatal(t, err)
+
+ _, err = repo.ReadNote("", commit.Id())
+ if err == nil {
+ t.Fatal("note remove failed")
+ }
+}
+
+func TestDefaultNoteRef(t *testing.T) {
+ repo := createTestRepo(t)
+ defer os.RemoveAll(repo.Workdir())
+
+ ref, err := repo.DefaultNoteRef()
+ checkFatal(t, err)
+
+ compareStrings(t, "refs/notes/commits", ref)
+}
+
+func createTestNote(t *testing.T, repo *Repository, commit *Commit) (*Note, *Oid) {
+ loc, err := time.LoadLocation("Europe/Berlin")
+ sig := &Signature{
+ Name: "alice",
+ Email: "[email protected]",
+ When: time.Date(2015, 01, 05, 13, 0, 0, 0, loc),
+ }
+
+ noteId, err := repo.CreateNote("", sig, sig, commit.Id(), "I am a note\n", false)
+ checkFatal(t, err)
+
+ note, err := repo.ReadNote("", commit.Id())
+ checkFatal(t, err)
+
+ return note, noteId
+}
diff --git a/push.go b/push.go
deleted file mode 100644
index e6fe51f..0000000
--- a/push.go
+++ /dev/null
@@ -1,181 +0,0 @@
-package git
-
-/*
-#include <git2.h>
-
-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 {
-
- ret := C.git_push_unpack_ok(p.ptr)
- if ret == 0 {
- return false
- }
- 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))
- }
-
- var cmsg *C.char
- if msg == "" {
- cmsg = nil
- } else {
- 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
index 65f4dd2..cd708c6 100644
--- a/push_test.go
+++ b/push_test.go
@@ -3,55 +3,25 @@ package git
import (
"os"
"testing"
- "time"
)
-func Test_Push_ToRemote(t *testing.T) {
+func TestRemotePush(t *testing.T) {
repo := createBareTestRepo(t)
defer os.RemoveAll(repo.Path())
- repo2 := createTestRepo(t)
- defer os.RemoveAll(repo2.Workdir())
+ localRepo := createTestRepo(t)
+ defer os.RemoveAll(localRepo.Workdir())
- remote, err := repo2.CreateRemote("test_push", repo.Path())
+ remote, err := localRepo.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: "[email protected]", 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)
+ seedTestRepo(t, localRepo)
- push, err := remote.NewPush()
+ err = remote.Push([]string{"refs/heads/master"}, nil, nil, "")
checkFatal(t, err)
- err = push.AddRefspec("refs/heads/master")
+ _, err = localRepo.LookupReference("refs/remotes/test_push/master")
checkFatal(t, err)
- err = push.Finish()
+ _, err = repo.LookupReference("refs/heads/master")
checkFatal(t, err)
-
- err = push.StatusForeach(func(ref string, msg string) int {
- 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 604ef40..2684c20 100644
--- a/remote.go
+++ b/remote.go
@@ -53,6 +53,9 @@ type CredentialsCallback func(url string, username_from_url string, allowed_type
type TransferProgressCallback func(stats TransferProgress) ErrorCode
type UpdateTipsCallback func(refname string, a *Oid, b *Oid) ErrorCode
type CertificateCheckCallback func(cert *Certificate, valid bool, hostname string) ErrorCode
+type PackbuilderProgressCallback func(stage int32, current, total uint32) ErrorCode
+type PushTransferProgressCallback func(current, total uint32, bytes uint) ErrorCode
+type PushUpdateReferenceCallback func(refname, status string) ErrorCode
type RemoteCallbacks struct {
SidebandProgressCallback TransportMessageCallback
@@ -61,6 +64,9 @@ type RemoteCallbacks struct {
TransferProgressCallback
UpdateTipsCallback
CertificateCheckCallback
+ PackProgressCallback PackbuilderProgressCallback
+ PushTransferProgressCallback
+ PushUpdateReferenceCallback
}
type Remote struct {
@@ -101,6 +107,10 @@ type HostkeyCertificate struct {
HashSHA1 [20]byte
}
+type PushOptions struct {
+ PbParallelism uint
+}
+
type RemoteHead struct {
Id *Oid
Name string
@@ -216,6 +226,38 @@ func certificateCheckCallback(_cert *C.git_cert, _valid C.int, _host *C.char, da
return int(callbacks.CertificateCheckCallback(&cert, valid, host))
}
+//export packProgressCallback
+func packProgressCallback(stage C.int, current, total C.uint, data unsafe.Pointer) int {
+ callbacks := (*RemoteCallbacks)(data)
+
+ if callbacks.PackProgressCallback == nil {
+ return 0
+ }
+
+ return int(callbacks.PackProgressCallback(int32(stage), uint32(current), uint32(total)))
+}
+
+//export pushTransferProgressCallback
+func pushTransferProgressCallback(current, total C.uint, bytes C.size_t, data unsafe.Pointer) int {
+ callbacks := (*RemoteCallbacks)(data)
+ if callbacks.PushTransferProgressCallback == nil {
+ return 0
+ }
+
+ return int(callbacks.PushTransferProgressCallback(uint32(current), uint32(total), uint(bytes)))
+}
+
+//export pushUpdateReferenceCallback
+func pushUpdateReferenceCallback(refname, status *C.char, data unsafe.Pointer) int {
+ callbacks := (*RemoteCallbacks)(data)
+
+ if callbacks.PushUpdateReferenceCallback == nil {
+ return 0
+ }
+
+ return int(callbacks.PushUpdateReferenceCallback(C.GoString(refname), C.GoString(status)))
+}
+
func RemoteIsValidName(name string) bool {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
@@ -285,7 +327,7 @@ func (repo *Repository) CreateRemote(name string, url string) (*Remote, error) {
func (repo *Repository) DeleteRemote(name string) error {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
-
+
runtime.LockOSThread()
defer runtime.UnlockOSThread()
@@ -650,3 +692,54 @@ func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) {
return heads, nil
}
+
+func (o *Remote) Push(refspecs []string, opts *PushOptions, sig *Signature, msg string) error {
+ var csig *C.git_signature = nil
+ if sig != nil {
+ csig = sig.toC()
+ defer C.free(unsafe.Pointer(csig))
+ }
+
+ var cmsg *C.char
+ if msg == "" {
+ cmsg = nil
+ } else {
+ cmsg = C.CString(msg)
+ defer C.free(unsafe.Pointer(cmsg))
+ }
+
+ var copts C.git_push_options
+ C.git_push_init_options(&copts, C.GIT_PUSH_OPTIONS_VERSION)
+ if opts != nil {
+ copts.pb_parallelism = C.uint(opts.PbParallelism)
+ }
+
+ crefspecs := C.git_strarray{}
+ crefspecs.count = C.size_t(len(refspecs))
+ crefspecs.strings = makeCStringsFromStrings(refspecs)
+ defer freeStrarray(&crefspecs)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_remote_push(o.ptr, &crefspecs, &copts, csig, cmsg)
+ if ret < 0 {
+ return MakeGitError(ret)
+ }
+ return nil
+}
+
+func (o *Remote) PruneRefs() bool {
+ return C.git_remote_prune_refs(o.ptr) > 0
+}
+
+func (o *Remote) Prune() error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_remote_prune(o.ptr)
+ if ret < 0 {
+ return MakeGitError(ret)
+ }
+ return nil
+}
diff --git a/remote_test.go b/remote_test.go
index 631a6cd..54a66ed 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -1,8 +1,10 @@
package git
import (
+ "fmt"
"os"
"testing"
+ "time"
)
func TestRefspecs(t *testing.T) {
@@ -132,3 +134,84 @@ func TestRemoteLsFiltering(t *testing.T) {
t.Fatalf("Expected head to have a name, but it's empty")
}
}
+
+func TestRemotePruneRefs(t *testing.T) {
+ repo := createTestRepo(t)
+ defer os.RemoveAll(repo.Workdir())
+ defer repo.Free()
+
+ config, err := repo.Config()
+ checkFatal(t, err)
+ defer config.Free()
+
+ err = config.SetBool("remote.origin.prune", true)
+ checkFatal(t, err)
+
+ _, err = repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository")
+ checkFatal(t, err)
+
+ remote, err := repo.LookupRemote("origin")
+ checkFatal(t, err)
+
+ if !remote.PruneRefs() {
+ t.Fatal("Expected remote to be configured to prune references")
+ }
+}
+
+func TestRemotePrune(t *testing.T) {
+ remoteRepo := createTestRepo(t)
+ defer os.RemoveAll(remoteRepo.Workdir())
+ defer remoteRepo.Free()
+
+ head, _ := seedTestRepo(t, remoteRepo)
+ commit, err := remoteRepo.LookupCommit(head)
+ checkFatal(t, err)
+ defer commit.Free()
+
+ sig := &Signature{
+ Name: "Rand Om Hacker",
+ Email: "[email protected]",
+ When: time.Now(),
+ }
+
+ remoteRef, err := remoteRepo.CreateBranch("test-prune", commit, true, sig, "branch test-prune")
+ checkFatal(t, err)
+
+ repo := createTestRepo(t)
+ defer os.RemoveAll(repo.Workdir())
+ defer repo.Free()
+
+ config, err := repo.Config()
+ checkFatal(t, err)
+ defer config.Free()
+
+ remoteUrl := fmt.Sprintf("file://%s", remoteRepo.Workdir())
+ remote, err := repo.CreateRemote("origin", remoteUrl)
+ checkFatal(t, err)
+
+ err = remote.Fetch([]string{"test-prune"}, sig, "")
+ checkFatal(t, err)
+
+ _, err = repo.CreateReference("refs/remotes/origin/test-prune", head, true, sig, "remote reference")
+ checkFatal(t, err)
+
+ err = remoteRef.Delete()
+ checkFatal(t, err)
+
+ err = config.SetBool("remote.origin.prune", true)
+ checkFatal(t, err)
+
+ rr, err := repo.LookupRemote("origin")
+ checkFatal(t, err)
+
+ err = rr.ConnectFetch()
+ checkFatal(t, err)
+
+ err = rr.Prune()
+ checkFatal(t, err)
+
+ _, err = repo.LookupReference("refs/remotes/origin/test-prune")
+ if err == nil {
+ t.Fatal("Expected error getting a pruned reference")
+ }
+}
diff --git a/repository.go b/repository.go
index 2baeb4a..7760c3a 100644
--- a/repository.go
+++ b/repository.go
@@ -456,7 +456,7 @@ func (v *Repository) TreeBuilder() (*TreeBuilder, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- if ret := C.git_treebuilder_create(&bld.ptr, nil); ret < 0 {
+ if ret := C.git_treebuilder_new(&bld.ptr, v.ptr, nil); ret < 0 {
return nil, MakeGitError(ret)
}
runtime.SetFinalizer(bld, (*TreeBuilder).Free)
@@ -471,7 +471,7 @@ func (v *Repository) TreeBuilderFromTree(tree *Tree) (*TreeBuilder, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- if ret := C.git_treebuilder_create(&bld.ptr, tree.cast_ptr); ret < 0 {
+ if ret := C.git_treebuilder_new(&bld.ptr, v.ptr, tree.cast_ptr); ret < 0 {
return nil, MakeGitError(ret)
}
runtime.SetFinalizer(bld, (*TreeBuilder).Free)
@@ -530,3 +530,103 @@ func (v *Repository) DwimReference(name string) (*Reference, error) {
return newReferenceFromC(ptr, v), nil
}
+
+// CreateNote adds a note for an object
+func (v *Repository) CreateNote(
+ ref string, author, committer *Signature, id *Oid,
+ note string, force bool) (*Oid, error) {
+
+ oid := new(Oid)
+
+ var cref *C.char
+ if ref == "" {
+ cref = nil
+ } else {
+ cref = C.CString(ref)
+ defer C.free(unsafe.Pointer(cref))
+ }
+
+ authorSig := author.toC()
+ defer C.git_signature_free(authorSig)
+
+ committerSig := committer.toC()
+ defer C.git_signature_free(committerSig)
+
+ cnote := C.CString(note)
+ defer C.free(unsafe.Pointer(cnote))
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_note_create(
+ oid.toC(), v.ptr, cref, authorSig,
+ committerSig, id.toC(), cnote, cbool(force))
+
+ if ret < 0 {
+ return nil, MakeGitError(ret)
+ }
+ return oid, nil
+}
+
+// ReadNote reads the note for an object
+func (v *Repository) ReadNote(ref string, id *Oid) (*Note, error) {
+ var cref *C.char
+ if ref == "" {
+ cref = nil
+ } else {
+ cref = C.CString(ref)
+ defer C.free(unsafe.Pointer(cref))
+ }
+
+ note := new(Note)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if ret := C.git_note_read(&note.ptr, v.ptr, cref, id.toC()); ret < 0 {
+ return nil, MakeGitError(ret)
+ }
+
+ runtime.SetFinalizer(note, (*Note).Free)
+ return note, nil
+}
+
+// RemoveNote removes the note for an object
+func (v *Repository) RemoveNote(ref string, author, committer *Signature, id *Oid) error {
+ var cref *C.char
+ if ref == "" {
+ cref = nil
+ } else {
+ cref = C.CString(ref)
+ defer C.free(unsafe.Pointer(cref))
+ }
+
+ authorSig := author.toC()
+ defer C.git_signature_free(authorSig)
+
+ committerSig := committer.toC()
+ defer C.git_signature_free(committerSig)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_note_remove(v.ptr, cref, authorSig, committerSig, id.toC())
+ if ret < 0 {
+ return MakeGitError(ret)
+ }
+ return nil
+}
+
+// DefaultNoteRef returns the default notes reference for a repository
+func (v *Repository) DefaultNoteRef() (string, error) {
+ var ptr *C.char
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if ret := C.git_note_default_ref(&ptr, v.ptr); ret < 0 {
+ return "", MakeGitError(ret)
+ }
+
+ return C.GoString(ptr), nil
+}
diff --git a/submodule.go b/submodule.go
index d5ab69f..6923c61 100644
--- a/submodule.go
+++ b/submodule.go
@@ -11,6 +11,14 @@ import (
"unsafe"
)
+// SubmoduleUpdateOptions
+type SubmoduleUpdateOptions struct {
+ *CheckoutOpts
+ *RemoteCallbacks
+ CloneCheckoutStrategy CheckoutStrategy
+ Signature *Signature
+}
+
// Submodule
type Submodule struct {
ptr *C.git_submodule
@@ -20,10 +28,10 @@ type SubmoduleUpdate int
const (
SubmoduleUpdateReset SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_RESET
- SubmoduleUpdateCheckout SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_CHECKOUT
- SubmoduleUpdateRebase SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_REBASE
- SubmoduleUpdateMerge SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_MERGE
- SubmoduleUpdateNone SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_NONE
+ SubmoduleUpdateCheckout SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_CHECKOUT
+ SubmoduleUpdateRebase SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_REBASE
+ SubmoduleUpdateMerge SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_MERGE
+ SubmoduleUpdateNone SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_NONE
)
type SubmoduleIgnore int
@@ -89,10 +97,10 @@ func (repo *Repository) LookupSubmodule(name string) (*Submodule, error) {
type SubmoduleCbk func(sub *Submodule, name string) int
//export SubmoduleVisitor
-func SubmoduleVisitor(csub unsafe.Pointer, name string, cfct unsafe.Pointer) int {
+func SubmoduleVisitor(csub unsafe.Pointer, name *C.char, cfct unsafe.Pointer) C.int {
sub := &Submodule{(*C.git_submodule)(csub)}
fct := *(*SubmoduleCbk)(cfct)
- return fct(sub, name)
+ return (C.int)(fct(sub, C.GoString(name)))
}
func (repo *Repository) ForeachSubmodule(cbk SubmoduleCbk) error {
@@ -226,8 +234,8 @@ func (sub *Submodule) SetIgnore(ignore SubmoduleIgnore) SubmoduleIgnore {
return SubmoduleIgnore(o)
}
-func (sub *Submodule) Update() SubmoduleUpdate {
- o := C.git_submodule_update(sub.ptr)
+func (sub *Submodule) UpdateStrategy() SubmoduleUpdate {
+ o := C.git_submodule_update_strategy(sub.ptr)
return SubmoduleUpdate(o)
}
@@ -307,3 +315,31 @@ func (repo *Repository) ReloadAllSubmodules(force bool) error {
}
return nil
}
+
+func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error {
+ var copts C.git_submodule_update_options
+ populateSubmoduleUpdateOptions(&copts, opts)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_submodule_update(sub.ptr, cbool(init), &copts)
+ if ret < 0 {
+ return MakeGitError(ret)
+ }
+
+ return nil
+}
+
+func populateSubmoduleUpdateOptions(ptr *C.git_submodule_update_options, opts *SubmoduleUpdateOptions) {
+ C.git_submodule_update_init_options(ptr, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION)
+
+ if opts == nil {
+ return
+ }
+
+ populateCheckoutOpts(&ptr.checkout_opts, opts.CheckoutOpts)
+ populateRemoteCallbacks(&ptr.remote_callbacks, opts.RemoteCallbacks)
+ ptr.clone_checkout_strategy = C.uint(opts.CloneCheckoutStrategy)
+ ptr.signature = opts.Signature.toC()
+}
diff --git a/submodule_test.go b/submodule_test.go
new file mode 100644
index 0000000..1c8f471
--- /dev/null
+++ b/submodule_test.go
@@ -0,0 +1,24 @@
+package git
+
+import (
+ "testing"
+)
+
+func TestSubmoduleForeach(t *testing.T) {
+ repo := createTestRepo(t)
+ seedTestRepo(t, repo)
+
+ _, err := repo.AddSubmodule("http://example.org/submodule", "submodule", true)
+ checkFatal(t, err)
+
+ i := 0
+ err = repo.ForeachSubmodule(func(sub *Submodule, name string) int {
+ i++
+ return 0
+ })
+ checkFatal(t, err)
+
+ if i != 1 {
+ t.Fatalf("expected one submodule found but got %i", i)
+ }
+}
diff --git a/tree.go b/tree.go
index efabce5..c18d02a 100644
--- a/tree.go
+++ b/tree.go
@@ -161,7 +161,7 @@ func (v *TreeBuilder) Write() (*Oid, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- err := C.git_treebuilder_write(oid.toC(), v.repo.ptr, v.ptr)
+ err := C.git_treebuilder_write(oid.toC(), v.ptr)
if err < 0 {
return nil, MakeGitError(err)
diff --git a/vendor/libgit2 b/vendor/libgit2
-Subproject 169497d1e7c238d2925577d1af3dc03e9a507cd
+Subproject 04bdd97f2b63793a8720fd19007911e946ba3c5
diff --git a/wrapper.c b/wrapper.c
index 6e33fa2..938fd17 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -1,9 +1,7 @@
#include "_cgo_export.h"
-#include "git2.h"
-#include "git2/sys/odb_backend.h"
-#include "git2/sys/refdb_backend.h"
-#include "git2/submodule.h"
-#include "git2/pack.h"
+#include <git2.h>
+#include <git2/sys/odb_backend.h>
+#include <git2/sys/refdb_backend.h>
typedef int (*gogit_submodule_cbk)(git_submodule *sm, const char *name, void *payload);
@@ -71,24 +69,17 @@ void _go_git_setup_diff_notify_callbacks(git_diff_options *opts) {
void _go_git_setup_callbacks(git_remote_callbacks *callbacks) {
typedef int (*completion_cb)(git_remote_completion_type type, void *data);
typedef int (*update_tips_cb)(const char *refname, const git_oid *a, const git_oid *b, void *data);
+ typedef int (*push_update_reference_cb)(const char *refname, const char *status, void *data);
+
callbacks->sideband_progress = (git_transport_message_cb)sidebandProgressCallback;
callbacks->completion = (completion_cb)completionCallback;
callbacks->credentials = (git_cred_acquire_cb)credentialsCallback;
callbacks->transfer_progress = (git_transfer_progress_cb)transferProgressCallback;
callbacks->update_tips = (update_tips_cb)updateTipsCallback;
callbacks->certificate_check = (git_transport_certificate_check_cb) certificateCheckCallback;
-}
-
-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);
+ callbacks->pack_progress = (git_packbuilder_progress) packProgressCallback;
+ callbacks->push_transfer_progress = (git_push_transfer_progress) pushTransferProgressCallback;
+ callbacks->push_update_reference = (push_update_reference_cb) pushUpdateReferenceCallback;
}
int _go_blob_chunk_cb(char *buffer, size_t maxLen, void *payload)