summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--blob.go13
-rw-r--r--checkout.go40
-rw-r--r--commit.go55
-rw-r--r--config.go322
-rw-r--r--git.go60
-rw-r--r--git_test.go47
-rw-r--r--index.go20
-rw-r--r--index_test.go15
-rw-r--r--object.go83
-rw-r--r--object_test.go77
-rw-r--r--odb.go155
-rw-r--r--odb_test.go62
-rw-r--r--packbuilder.go65
-rw-r--r--reference.go188
-rw-r--r--reference_test.go138
-rw-r--r--repository.go138
-rw-r--r--submodule.go67
-rw-r--r--tree.go70
-rw-r--r--walk.go19
-rw-r--r--wrapper.c4
20 files changed, 1437 insertions, 201 deletions
diff --git a/blob.go b/blob.go
index 73e3ab3..b638c4f 100644
--- a/blob.go
+++ b/blob.go
@@ -1,30 +1,23 @@
package git
/*
-#cgo pkg-config: libgit2
#include <git2.h>
#include <git2/errors.h>
*/
import "C"
import (
- "runtime"
"unsafe"
)
type Blob struct {
- ptr *C.git_object
+ gitObject
}
-func (v *Blob) Free() {
- runtime.SetFinalizer(v, nil)
- C.git_object_free(v.ptr)
-}
-
-func (v *Blob) Size() int64 {
+func (v Blob) Size() int64 {
return int64(C.git_blob_rawsize(v.ptr))
}
-func (v *Blob) Contents() []byte {
+func (v Blob) Contents() []byte {
size := C.int(C.git_blob_rawsize(v.ptr))
buffer := unsafe.Pointer(C.git_blob_rawcontent(v.ptr))
return C.GoBytes(buffer, size)
diff --git a/checkout.go b/checkout.go
index bbeb240..f4c1d4e 100644
--- a/checkout.go
+++ b/checkout.go
@@ -1,7 +1,6 @@
package git
/*
-#cgo pkg-config: libgit2
#include <git2.h>
git_checkout_opts git_checkout_opts_init() {
git_checkout_opts ret = GIT_CHECKOUT_OPTS_INIT;
@@ -10,28 +9,29 @@ git_checkout_opts git_checkout_opts_init() {
*/
import "C"
import (
+ "runtime"
"os"
)
type CheckoutStrategy uint
const (
- CHECKOUT_NONE CheckoutStrategy = C.GIT_CHECKOUT_NONE // Dry run, no actual updates
- CHECKOUT_SAFE = C.GIT_CHECKOUT_SAFE // Allow safe updates that cannot overwrite uncommitted data
- CHECKOUT_SAFE_CREATE = C.GIT_CHECKOUT_SAFE_CREATE // Allow safe updates plus creation of missing files
- CHECKOUT_FORCE = C.GIT_CHECKOUT_FORCE // Allow all updates to force working directory to look like index
- CHECKOUT_ALLOW_CONFLICTS = C.GIT_CHECKOUT_ALLOW_CONFLICTS // Allow checkout to make safe updates even if conflicts are found
- CHECKOUT_REMOVE_UNTRACKED = C.GIT_CHECKOUT_REMOVE_UNTRACKED // Remove untracked files not in index (that are not ignored)
- CHECKOUT_REMOVE_IGNORED = C.GIT_CHECKOUT_REMOVE_IGNORED // Remove ignored files not in index
- CHECKOUT_UPDATE_ONLY = C.GIT_CHECKOUT_UPDATE_ONLY // Only update existing files, don't create new ones
- CHECKOUT_DONT_UPDATE_INDEX = C.GIT_CHECKOUT_DONT_UPDATE_INDEX // Normally checkout updates index entries as it goes; this stops that
- CHECKOUT_NO_REFRESH = C.GIT_CHECKOUT_NO_REFRESH // Don't refresh index/config/etc before doing checkout
- CHECKOUT_DISABLE_PATHSPEC_MATCH = C.GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH // Treat pathspec as simple list of exact match file paths
- CHECKOUT_SKIP_UNMERGED = C.GIT_CHECKOUT_SKIP_UNMERGED // Allow checkout to skip unmerged files (NOT IMPLEMENTED)
- CHECKOUT_USE_OURS = C.GIT_CHECKOUT_USE_OURS // For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED)
- CHECKOUT_USE_THEIRS = C.GIT_CHECKOUT_USE_THEIRS // For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED)
- CHECKOUT_UPDATE_SUBMODULES = C.GIT_CHECKOUT_UPDATE_SUBMODULES // Recursively checkout submodules with same options (NOT IMPLEMENTED)
- CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = C.GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED // Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED)
+ CheckoutNone CheckoutStrategy = C.GIT_CHECKOUT_NONE // Dry run, no actual updates
+ CheckoutSafe = C.GIT_CHECKOUT_SAFE // Allow safe updates that cannot overwrite uncommitted data
+ CheckoutSafeCreate = C.GIT_CHECKOUT_SAFE_CREATE // Allow safe updates plus creation of missing files
+ CheckoutForce = C.GIT_CHECKOUT_FORCE // Allow all updates to force working directory to look like index
+ CheckoutAllowConflicts = C.GIT_CHECKOUT_ALLOW_CONFLICTS // Allow checkout to make safe updates even if conflicts are found
+ CheckoutRemoveUntracked = C.GIT_CHECKOUT_REMOVE_UNTRACKED // Remove untracked files not in index (that are not ignored)
+ CheckoutRemoveIgnored = C.GIT_CHECKOUT_REMOVE_IGNORED // Remove ignored files not in index
+ CheckotUpdateOnly = C.GIT_CHECKOUT_UPDATE_ONLY // Only update existing files, don't create new ones
+ CheckoutDontUpdateIndex = C.GIT_CHECKOUT_DONT_UPDATE_INDEX // Normally checkout updates index entries as it goes; this stops that
+ CheckoutNoRefresh = C.GIT_CHECKOUT_NO_REFRESH // Don't refresh index/config/etc before doing checkout
+ CheckooutDisablePathspecMatch = C.GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH // Treat pathspec as simple list of exact match file paths
+ CheckoutSkipUnmerged = C.GIT_CHECKOUT_SKIP_UNMERGED // Allow checkout to skip unmerged files (NOT IMPLEMENTED)
+ CheckoutUserOurs = C.GIT_CHECKOUT_USE_OURS // For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED)
+ CheckoutUseTheirs = C.GIT_CHECKOUT_USE_THEIRS // For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED)
+ CheckoutUpdateSubmodules = C.GIT_CHECKOUT_UPDATE_SUBMODULES // Recursively checkout submodules with same options (NOT IMPLEMENTED)
+ CheckoutUpdateSubmodulesIfChanged = C.GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED // Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED)
)
type CheckoutOpts struct {
@@ -60,6 +60,9 @@ func (v *Repository) Checkout(opts *CheckoutOpts) error {
var copts C.git_checkout_opts
populateCheckoutOpts(&copts, opts)
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_checkout_head(v.ptr, &copts)
if ret < 0 {
return LastError()
@@ -73,6 +76,9 @@ func (v *Repository) CheckoutIndex(index *Index, opts *CheckoutOpts) error {
var copts C.git_checkout_opts
populateCheckoutOpts(&copts, opts)
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_checkout_index(v.ptr, index.ptr, &copts)
if ret < 0 {
return LastError()
diff --git a/commit.go b/commit.go
index d31f684..0c64c76 100644
--- a/commit.go
+++ b/commit.go
@@ -10,49 +10,65 @@ import "C"
import (
"runtime"
- "unsafe"
"time"
+ "unsafe"
)
// Commit
type Commit struct {
- ptr *C.git_commit
+ gitObject
}
-func (c *Commit) Id() *Oid {
- return newOidFromC(C.git_commit_id(c.ptr))
-}
-
-func (c *Commit) Message() string {
+func (c Commit) Message() string {
return C.GoString(C.git_commit_message(c.ptr))
}
-func (c *Commit) Tree() (*Tree, error) {
- tree := new(Tree)
+func (c Commit) Tree() (*Tree, error) {
+ var ptr *C.git_object
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
- err := C.git_commit_tree(&tree.ptr, c.ptr)
+ err := C.git_commit_tree(&ptr, c.ptr)
if err < 0 {
return nil, LastError()
}
- runtime.SetFinalizer(tree, (*Tree).Free)
- return tree, nil
+ return allocObject(ptr).(*Tree), nil
}
-func (c *Commit) TreeId() *Oid {
+func (c Commit) TreeId() *Oid {
return newOidFromC(C.git_commit_tree_id(c.ptr))
}
-func (c *Commit) Author() *Signature {
+func (c Commit) Author() *Signature {
ptr := C.git_commit_author(c.ptr)
return newSignatureFromC(ptr)
}
-func (c *Commit) Committer() *Signature {
+func (c Commit) Committer() *Signature {
ptr := C.git_commit_committer(c.ptr)
return newSignatureFromC(ptr)
}
+func (c *Commit) Parent(n uint) *Commit {
+ var cobj *C.git_object
+ ret := C.git_commit_parent(&cobj, c.ptr, C.uint(n))
+ if ret != 0 {
+ return nil
+ }
+
+ return allocObject(cobj).(*Commit)
+}
+
+func (c *Commit) ParentId(n uint) *Oid {
+ return newOidFromC(C.git_commit_parent_id(c.ptr, C.uint(n)))
+}
+
+func (c *Commit) ParentCount() uint {
+ return uint(C.git_commit_parentcount(c.ptr))
+}
+
// Signature
type Signature struct {
@@ -74,10 +90,15 @@ func newSignatureFromC(sig *C.git_signature) *Signature {
// the offset in mintes, which is what git wants
func (v *Signature) Offset() int {
_, offset := v.When.Zone()
- return offset/60
+ return offset / 60
}
-func (sig *Signature) toC() (*C.git_signature) {
+func (sig *Signature) toC() *C.git_signature {
+
+ if sig == nil {
+ return nil
+ }
+
var out *C.git_signature
name := C.CString(sig.Name)
diff --git a/config.go b/config.go
index ec8b8d0..7665b20 100644
--- a/config.go
+++ b/config.go
@@ -1,24 +1,97 @@
package git
/*
-#cgo pkg-config: libgit2
#include <git2.h>
#include <git2/errors.h>
*/
import "C"
import (
+ "runtime"
"unsafe"
)
+type ConfigLevel int
+
+const (
+ // System-wide configuration file; /etc/gitconfig on Linux systems
+ ConfigLevelSystem ConfigLevel = C.GIT_CONFIG_LEVEL_SYSTEM
+
+ // XDG compatible configuration file; typically ~/.config/git/config
+ ConfigLevelXDG ConfigLevel = C.GIT_CONFIG_LEVEL_XDG
+
+ // User-specific configuration file (also called Global configuration
+ // file); typically ~/.gitconfig
+ ConfigLevelGlobal ConfigLevel = C.GIT_CONFIG_LEVEL_GLOBAL
+
+ // Repository specific configuration file; $WORK_DIR/.git/config on
+ // non-bare repos
+ ConfigLevelLocal ConfigLevel = C.GIT_CONFIG_LEVEL_LOCAL
+
+ // Application specific configuration file; freely defined by applications
+ ConfigLevelApp ConfigLevel = C.GIT_CONFIG_LEVEL_APP
+
+ // Represents the highest level available config file (i.e. the most
+ // specific config file available that actually is loaded)
+ ConfigLevelHighest ConfigLevel = C.GIT_CONFIG_HIGHEST_LEVEL
+)
+
+type ConfigEntry struct {
+ Name string
+ Value string
+ Level ConfigLevel
+}
+
+func newConfigEntryFromC(centry *C.git_config_entry) *ConfigEntry {
+ return &ConfigEntry{
+ Name: C.GoString(centry.name),
+ Value: C.GoString(centry.value),
+ Level: ConfigLevel(centry.level),
+ }
+}
+
type Config struct {
ptr *C.git_config
}
-func (c *Config) LookupInt32(name string) (v int32, err error) {
+// NewConfig creates a new empty configuration object
+func NewConfig() (*Config, error) {
+ config := new(Config)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if ret := C.git_config_new(&config.ptr); ret < 0 {
+ return nil, LastError()
+ }
+
+ return config, nil
+}
+
+// AddFile adds a file-backed backend to the config object at the specified level.
+func (c *Config) AddFile(path string, level ConfigLevel, force bool) error {
+ cpath := C.CString(path)
+ defer C.free(unsafe.Pointer(cpath))
+
+ 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 LastError()
+ }
+
+ return nil
+}
+
+func (c *Config) LookupInt32(name string) (int32, error) {
var out C.int32_t
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_config_get_int32(&out, c.ptr, cname)
if ret < 0 {
return 0, LastError()
@@ -27,11 +100,14 @@ func (c *Config) LookupInt32(name string) (v int32, err error) {
return int32(out), nil
}
-func (c *Config) LookupInt64(name string) (v int64, err error) {
+func (c *Config) LookupInt64(name string) (int64, error) {
var out C.int64_t
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_config_get_int64(&out, c.ptr, cname)
if ret < 0 {
return 0, LastError()
@@ -40,26 +116,108 @@ func (c *Config) LookupInt64(name string) (v int64, err error) {
return int64(out), nil
}
-func (c *Config) LookupString(name string) (v string, err error) {
+func (c *Config) LookupString(name string) (string, error) {
var ptr *C.char
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
- ret := C.git_config_get_string(&ptr, c.ptr, cname)
- if ret < 0 {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if ret := C.git_config_get_string(&ptr, c.ptr, cname); ret < 0 {
return "", LastError()
}
return C.GoString(ptr), nil
}
-func (c *Config) Set(name, value string) (err error) {
+
+func (c *Config) LookupBool(name string) (bool, error) {
+ var out C.int
+ cname := C.CString(name)
+ defer C.free(unsafe.Pointer(cname))
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_config_get_bool(&out, c.ptr, cname)
+ if ret < 0 {
+ return false, LastError()
+ }
+
+ return out != 0, nil
+}
+
+func (c *Config) NewMultivarIterator(name, regexp string) (*ConfigIterator, error) {
+ cname := C.CString(name)
+ defer C.free(unsafe.Pointer(cname))
+
+ var cregexp *C.char
+ if regexp == "" {
+ cregexp = nil
+ } else {
+ cregexp = C.CString(regexp)
+ defer C.free(unsafe.Pointer(cregexp))
+ }
+
+ iter := new(ConfigIterator)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_config_multivar_iterator_new(&iter.ptr, c.ptr, cname, cregexp)
+ if ret < 0 {
+ return nil, LastError()
+ }
+
+ runtime.SetFinalizer(iter, (*ConfigIterator).Free)
+ return iter, nil
+}
+
+// NewIterator creates an iterator over each entry in the
+// configuration
+func (c *Config) NewIterator() (*ConfigIterator, error) {
+ iter := new(ConfigIterator)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_config_iterator_new(&iter.ptr, c.ptr)
+ if ret < 0 {
+ return nil, LastError()
+ }
+
+ return iter, nil
+}
+
+// NewIteratorGlob creates an iterator over each entry in the
+// configuration whose name matches the given regular expression
+func (c *Config) NewIteratorGlob(regexp string) (*ConfigIterator, error) {
+ iter := new(ConfigIterator)
+ cregexp := C.CString(regexp)
+ defer C.free(unsafe.Pointer(cregexp))
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_config_iterator_glob_new(&iter.ptr, c.ptr, cregexp)
+ if ret < 0 {
+ return nil, LastError()
+ }
+
+ return iter, nil
+}
+
+func (c *Config) SetString(name, value string) (err error) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
cvalue := C.CString(value)
defer C.free(unsafe.Pointer(cvalue))
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_config_set_string(c.ptr, cname, cvalue)
if ret < 0 {
return LastError()
@@ -67,3 +225,153 @@ func (c *Config) Set(name, value string) (err error) {
return nil
}
+
+func (c *Config) Free() {
+ runtime.SetFinalizer(c, nil)
+ C.git_config_free(c.ptr)
+}
+
+func (c *Config) SetInt32(name string, value int32) (err error) {
+ cname := C.CString(name)
+ defer C.free(unsafe.Pointer(cname))
+
+ ret := C.git_config_set_int32(c.ptr, cname, C.int32_t(value))
+ if ret < 0 {
+ return LastError()
+ }
+
+ return nil
+}
+
+func (c *Config) SetInt64(name string, value int64) (err error) {
+ cname := C.CString(name)
+ defer C.free(unsafe.Pointer(cname))
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_config_set_int64(c.ptr, cname, C.int64_t(value))
+ if ret < 0 {
+ return LastError()
+ }
+
+ return nil
+}
+
+func (c *Config) SetBool(name string, value bool) (err error) {
+ cname := C.CString(name)
+ defer C.free(unsafe.Pointer(cname))
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_config_set_bool(c.ptr, cname, cbool(value))
+ if ret < 0 {
+ return LastError()
+ }
+
+ return nil
+}
+
+func (c *Config) SetMultivar(name, regexp, value string) (err error) {
+ cname := C.CString(name)
+ defer C.free(unsafe.Pointer(cname))
+
+ cregexp := C.CString(regexp)
+ defer C.free(unsafe.Pointer(cregexp))
+
+ cvalue := C.CString(value)
+ defer C.free(unsafe.Pointer(cvalue))
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_config_set_multivar(c.ptr, cname, cregexp, cvalue)
+ if ret < 0 {
+ return LastError()
+ }
+
+ return nil
+}
+
+func (c *Config) Delete(name string) error {
+ cname := C.CString(name)
+ defer C.free(unsafe.Pointer(cname))
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_config_delete_entry(c.ptr, cname)
+
+ if ret < 0 {
+ return LastError()
+ }
+
+ return nil
+}
+
+// OpenLevel creates a single-level focused config object from a multi-level one
+func (c *Config) OpenLevel(parent *Config, level ConfigLevel) (*Config, error) {
+ config := new(Config)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_config_open_level(&config.ptr, parent.ptr, C.git_config_level_t(level))
+ if ret < 0 {
+ return nil, LastError()
+ }
+
+ return config, nil
+}
+
+// OpenOndisk creates a new config instance containing a single on-disk file
+func OpenOndisk(parent *Config, path string) (*Config, error) {
+ cpath := C.CString(path)
+ defer C.free(unsafe.Pointer(cpath))
+
+ config := new(Config)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if ret := C.git_config_open_ondisk(&config.ptr, cpath); ret < 0 {
+ return nil, LastError()
+ }
+
+ return config, nil
+}
+
+// Refresh refreshes the configuration to reflect any changes made externally e.g. on disk
+func (c *Config) Refresh() error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if ret := C.git_config_refresh(c.ptr); ret < 0 {
+ return LastError()
+ }
+
+ return nil
+}
+
+type ConfigIterator struct {
+ ptr *C.git_config_iterator
+}
+
+// Next returns the next entry for this iterator
+func (iter *ConfigIterator) Next() (*ConfigEntry, error) {
+ var centry *C.git_config_entry
+
+ ret := C.git_config_next(&centry, iter.ptr)
+ if ret < 0 {
+ return nil, LastError()
+ }
+
+ return newConfigEntryFromC(centry), nil
+}
+
+func (iter *ConfigIterator) Free() {
+ runtime.SetFinalizer(iter, nil)
+ C.free(unsafe.Pointer(iter.ptr))
+}
+
diff --git a/git.go b/git.go
index fdc640a..72ea33e 100644
--- a/git.go
+++ b/git.go
@@ -8,6 +8,8 @@ package git
import "C"
import (
"bytes"
+ "errors"
+ "runtime"
"unsafe"
"strings"
)
@@ -18,6 +20,10 @@ const (
ENOTFOUND = C.GIT_ENOTFOUND
)
+var (
+ ErrIterOver = errors.New("Iteration is over")
+)
+
func init() {
C.git_threads_init()
}
@@ -52,6 +58,9 @@ func NewOidFromString(s string) (*Oid, error) {
cs := C.CString(s)
defer C.free(unsafe.Pointer(cs))
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
if C.git_oid_fromstr(o.toC(), cs) < 0 {
return nil, LastError()
}
@@ -84,7 +93,7 @@ func (oid *Oid) Equal(oid2 *Oid) bool {
}
func (oid *Oid) IsZero() bool {
- for _, a := range(oid.bytes) {
+ for _, a := range oid.bytes {
if a != '0' {
return false
}
@@ -96,29 +105,56 @@ func (oid *Oid) NCmp(oid2 *Oid, n uint) int {
return bytes.Compare(oid.bytes[:n], oid2.bytes[:n])
}
+func ShortenOids(ids []*Oid, minlen int) (int, error) {
+ shorten := C.git_oid_shorten_new(C.size_t(minlen))
+ if shorten == nil {
+ panic("Out of memory")
+ }
+ defer C.git_oid_shorten_free(shorten)
+
+ var ret C.int
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ for _, id := range ids {
+ buf := make([]byte, 41)
+ C.git_oid_fmt((*C.char)(unsafe.Pointer(&buf[0])), id.toC())
+ buf[40] = 0
+ ret = C.git_oid_shorten_add(shorten, (*C.char)(unsafe.Pointer(&buf[0])))
+ if ret < 0 {
+ return int(ret), LastError()
+ }
+ }
+ return int(ret), nil
+}
+
type GitError struct {
Message string
- Code int
+ Code int
}
-func (e GitError) Error() string{
+func (e GitError) Error() string {
return e.Message
}
func LastError() error {
err := C.giterr_last()
+ if err == nil {
+ return &GitError{"No message", 0}
+ }
return &GitError{C.GoString(err.message), int(err.klass)}
}
func cbool(b bool) C.int {
- if (b) {
+ if b {
return C.int(1)
}
return C.int(0)
}
func ucbool(b bool) C.uint {
- if (b) {
+ if b {
return C.uint(1)
}
return C.uint(0)
@@ -131,14 +167,16 @@ func Discover(start string, across_fs bool, ceiling_dirs []string) (string, erro
cstart := C.CString(start)
defer C.free(unsafe.Pointer(cstart))
- retpath := (*C.char)(C.malloc(C.GIT_PATH_MAX))
- defer C.free(unsafe.Pointer(retpath))
+ var buf C.git_buf
+ defer C.git_buf_free(&buf)
- r := C.git_repository_discover(retpath, C.GIT_PATH_MAX, cstart, cbool(across_fs), ceildirs)
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
- if r == 0 {
- return C.GoString(retpath), nil
+ ret := C.git_repository_discover(&buf, cstart, cbool(across_fs), ceildirs)
+ if ret < 0 {
+ return "", LastError()
}
- return "", LastError()
+ return C.GoString(buf.ptr), nil
}
diff --git a/git_test.go b/git_test.go
new file mode 100644
index 0000000..52aea1d
--- /dev/null
+++ b/git_test.go
@@ -0,0 +1,47 @@
+package git
+
+import (
+ "testing"
+ "io/ioutil"
+ "time"
+)
+
+func createTestRepo(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, false)
+ checkFatal(t, err)
+
+ tmpfile := "README"
+ err = ioutil.WriteFile(path + "/" + tmpfile, []byte("foo\n"), 0644)
+ checkFatal(t, err)
+
+ return repo
+}
+
+func seedTestRepo(t *testing.T, repo *Repository) (*Oid, *Oid) {
+ loc, err := time.LoadLocation("Europe/Berlin")
+ checkFatal(t, err)
+ sig := &Signature{
+ Name: "Rand Om Hacker",
+ Email: "[email protected]",
+ When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc),
+ }
+
+ idx, err := repo.Index()
+ checkFatal(t, err)
+ err = idx.AddByPath("README")
+ checkFatal(t, err)
+ treeId, err := idx.WriteTree()
+ checkFatal(t, err)
+
+ message := "This is a commit\n"
+ tree, err := repo.LookupTree(treeId)
+ checkFatal(t, err)
+ commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree)
+ checkFatal(t, err)
+
+ return commitId, treeId
+}
+
diff --git a/index.go b/index.go
index 3ebb605..bc11025 100644
--- a/index.go
+++ b/index.go
@@ -1,7 +1,6 @@
package git
/*
-#cgo pkg-config: libgit2
#include <git2.h>
#include <git2/errors.h>
*/
@@ -38,6 +37,9 @@ func (v *Index) AddByPath(path string) error {
cstr := C.CString(path)
defer C.free(unsafe.Pointer(cstr))
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_index_add_bypath(v.ptr, cstr)
if ret < 0 {
return LastError()
@@ -48,6 +50,10 @@ func (v *Index) AddByPath(path string) error {
func (v *Index) WriteTree() (*Oid, error) {
oid := new(Oid)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_index_write_tree(oid.toC(), v.ptr)
if ret < 0 {
return nil, LastError()
@@ -56,6 +62,18 @@ func (v *Index) WriteTree() (*Oid, error) {
return oid, nil
}
+func (v *Index) Write() (error) {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_index_write(v.ptr)
+ if ret < 0 {
+ return LastError()
+ }
+
+ return nil
+}
+
func (v *Index) Free() {
runtime.SetFinalizer(v, nil)
C.git_index_free(v.ptr)
diff --git a/index_test.go b/index_test.go
index fe6fb87..9828d0f 100644
--- a/index_test.go
+++ b/index_test.go
@@ -4,23 +4,8 @@ import (
"os"
"runtime"
"testing"
- "io/ioutil"
)
-func createTestRepo(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, false)
- checkFatal(t, err)
-
- tmpfile := "README"
- err = ioutil.WriteFile(path + "/" + tmpfile, []byte("foo\n"), 0644)
- checkFatal(t, err)
-
- return repo
-}
-
func TestCreateRepoAndStage(t *testing.T) {
repo := createTestRepo(t)
defer os.RemoveAll(repo.Workdir())
diff --git a/object.go b/object.go
new file mode 100644
index 0000000..090be1f
--- /dev/null
+++ b/object.go
@@ -0,0 +1,83 @@
+package git
+
+/*
+#include <git2.h>
+#include <git2/errors.h>
+*/
+import "C"
+import "runtime"
+
+type ObjectType int
+
+const (
+ ObjectAny ObjectType = C.GIT_OBJ_ANY
+ ObjectBad = C.GIT_OBJ_BAD
+ ObjectCommit = C.GIT_OBJ_COMMIT
+ ObjectTree = C.GIT_OBJ_TREE
+ ObjectBlob = C.GIT_OBJ_BLOB
+ ObjectTag = C.GIT_OBJ_TAG
+)
+
+type Object interface {
+ Free()
+ Id() *Oid
+ Type() ObjectType
+}
+
+type gitObject struct {
+ ptr *C.git_object
+}
+
+func (t ObjectType) String() (string) {
+ switch (t) {
+ case ObjectAny:
+ return "Any"
+ case ObjectBad:
+ return "Bad"
+ case ObjectCommit:
+ return "Commit"
+ case ObjectTree:
+ return "Tree"
+ case ObjectBlob:
+ return "Blob"
+ case ObjectTag:
+ return "Tag"
+ }
+ // Never reached
+ return ""
+}
+
+func (o gitObject) Id() *Oid {
+ return newOidFromC(C.git_commit_id(o.ptr))
+}
+
+func (o gitObject) Type() ObjectType {
+ return ObjectType(C.git_object_type(o.ptr))
+}
+
+func (o *gitObject) Free() {
+ runtime.SetFinalizer(o, nil)
+ C.git_commit_free(o.ptr)
+}
+
+func allocObject(cobj *C.git_object) Object {
+
+ switch ObjectType(C.git_object_type(cobj)) {
+ case ObjectCommit:
+ commit := &Commit{gitObject{cobj}}
+ runtime.SetFinalizer(commit, (*Commit).Free)
+ return commit
+
+ case ObjectTree:
+ tree := &Tree{gitObject{cobj}}
+ runtime.SetFinalizer(tree, (*Tree).Free)
+ return tree
+
+ case ObjectBlob:
+ blob := &Blob{gitObject{cobj}}
+ runtime.SetFinalizer(blob, (*Blob).Free)
+ return blob
+ }
+
+ return nil
+}
diff --git a/object_test.go b/object_test.go
new file mode 100644
index 0000000..85daf78
--- /dev/null
+++ b/object_test.go
@@ -0,0 +1,77 @@
+package git
+
+import (
+ "os"
+ "testing"
+)
+
+func TestObjectPoymorphism(t *testing.T) {
+ repo := createTestRepo(t)
+ defer os.RemoveAll(repo.Workdir())
+ commitId, treeId := seedTestRepo(t, repo)
+
+ var obj Object
+
+ commit, err := repo.LookupCommit(commitId)
+ checkFatal(t, err)
+
+ obj = commit
+ if obj.Type() != ObjectCommit {
+ t.Fatalf("Wrong object type, expected commit, have %v", obj.Type())
+ }
+
+ commitTree, err := commit.Tree()
+ checkFatal(t, err)
+ commitTree.EntryCount()
+
+ tree, err := repo.LookupTree(treeId)
+ checkFatal(t, err)
+
+ obj = tree
+ if obj.Type() != ObjectTree {
+ t.Fatalf("Wrong object type, expected tree, have %v", obj.Type())
+ }
+
+ tree2, ok := obj.(*Tree)
+ if !ok {
+ t.Fatalf("Converting back to *Tree is not ok")
+ }
+
+ entry := tree2.EntryByName("README")
+ if entry == nil {
+ t.Fatalf("Tree did not have expected \"README\" entry")
+ }
+
+ if entry.Filemode != FilemodeBlob {
+ t.Fatal("Wrong filemode for \"README\"")
+ }
+
+ _, ok = obj.(*Commit)
+ if ok {
+ t.Fatalf("*Tree is somehow the same as *Commit")
+ }
+
+ obj, err = repo.Lookup(tree.Id())
+ checkFatal(t, err)
+
+ _, ok = obj.(*Tree)
+ if !ok {
+ t.Fatalf("Lookup creates the wrong type")
+ }
+
+ if obj.Type() != ObjectTree {
+ t.Fatalf("Type() doesn't agree with dynamic type")
+ }
+
+ obj, err = repo.RevparseSingle("HEAD")
+ checkFatal(t, err)
+ if obj.Type() != ObjectCommit || obj.Id().String() != commit.Id().String() {
+ t.Fatalf("Failed to parse the right revision")
+ }
+
+ obj, err = repo.RevparseSingle("HEAD^{tree}")
+ checkFatal(t, err)
+ if obj.Type() != ObjectTree || obj.Id().String() != tree.Id().String() {
+ t.Fatalf("Failed to parse the right revision")
+ }
+}
diff --git a/odb.go b/odb.go
index bf17171..953791c 100644
--- a/odb.go
+++ b/odb.go
@@ -1,9 +1,10 @@
package git
/*
-#cgo pkg-config: libgit2
#include <git2.h>
#include <git2/errors.h>
+
+extern int _go_git_odb_foreach(git_odb *db, void *payload);
*/
import "C"
import (
@@ -12,15 +13,6 @@ import (
"runtime"
)
-var (
- OBJ_ANY = C.GIT_OBJ_ANY
- OBJ_BAD = C.GIT_OBJ_BAD
- OBJ_COMMIT = C.GIT_OBJ_COMMIT
- OBJ_TREE = C.GIT_OBJ_TREE
- OBJ_BLOB = C.GIT_OBJ_BLOB
- OBJ_TAG = C.GIT_OBJ_TAG
-)
-
type Odb struct {
ptr *C.git_odb
}
@@ -30,9 +22,13 @@ func (v *Odb) Exists(oid *Oid) bool {
return ret != 0
}
-func (v *Odb) Write(data []byte, otype int) (oid *Oid, err error) {
+func (v *Odb) Write(data []byte, otype ObjectType) (oid *Oid, err error) {
oid = new(Oid)
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&data))
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_odb_write(oid.toC(), v.ptr, unsafe.Pointer(hdr.Data), C.size_t(hdr.Len), C.git_otype(otype))
if ret < 0 {
@@ -44,6 +40,10 @@ func (v *Odb) Write(data []byte, otype int) (oid *Oid, err error) {
func (v *Odb) Read(oid *Oid) (obj *OdbObject, err error) {
obj = new(OdbObject)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_odb_read(&obj.ptr, v.ptr, oid.toC())
if ret < 0 {
return nil, LastError()
@@ -53,6 +53,75 @@ func (v *Odb) Read(oid *Oid) (obj *OdbObject, err error) {
return
}
+//export odbForEachCb
+func odbForEachCb(id *C.git_oid, payload unsafe.Pointer) int {
+ ch := *(*chan *Oid)(payload)
+ oid := newOidFromC(id)
+ // Because the channel is unbuffered, we never read our own data. If ch is
+ // readable, the user has sent something on it, which means we should
+ // abort.
+ select {
+ case ch <- oid:
+ case <-ch:
+ return -1
+ }
+ return 0;
+}
+
+func (v *Odb) forEachWrap(ch chan *Oid) {
+ C._go_git_odb_foreach(v.ptr, unsafe.Pointer(&ch))
+ close(ch)
+}
+
+func (v *Odb) ForEach() chan *Oid {
+ ch := make(chan *Oid, 0)
+ go v.forEachWrap(ch)
+ return ch
+}
+
+// Hash determines the object-ID (sha1) of a data buffer.
+func (v *Odb) Hash(data []byte, otype ObjectType) (oid *Oid, err error) {
+ oid = new(Oid)
+ header := (*reflect.SliceHeader)(unsafe.Pointer(&data))
+ ptr := unsafe.Pointer(header.Data)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_odb_hash(oid.toC(), ptr, C.size_t(header.Len), C.git_otype(otype));
+ if ret < 0 {
+ err = LastError()
+ }
+ return
+}
+
+// NewReadStream opens a read stream from the ODB. Reading from it will give you the
+// contents of the object.
+func (v *Odb) NewReadStream(id *Oid) (*OdbReadStream, error) {
+ stream := new(OdbReadStream)
+ ret := C.git_odb_open_rstream(&stream.ptr, v.ptr, id.toC())
+ if ret < 0 {
+ return nil, LastError()
+ }
+
+ runtime.SetFinalizer(stream, (*OdbReadStream).Free)
+ return stream, nil
+}
+
+// NewWriteStream opens a write stream to the ODB, which allows you to
+// create a new object in the database. The size and type must be
+// known in advance
+func (v *Odb) NewWriteStream(size int, otype ObjectType) (*OdbWriteStream, error) {
+ stream := new(OdbWriteStream)
+ ret := C.git_odb_open_wstream(&stream.ptr, v.ptr, C.size_t(size), C.git_otype(otype))
+ if ret < 0 {
+ return nil, LastError()
+ }
+
+ runtime.SetFinalizer(stream, (*OdbWriteStream).Free)
+ return stream, nil
+}
+
type OdbObject struct {
ptr *C.git_odb_object
}
@@ -84,3 +153,67 @@ func (object *OdbObject) Data() (data []byte) {
return blob
}
+type OdbReadStream struct {
+ ptr *C.git_odb_stream
+}
+
+// Read reads from the stream
+func (stream *OdbReadStream) Read(data []byte) (int, error) {
+ header := (*reflect.SliceHeader)(unsafe.Pointer(&data))
+ ptr := (*C.char)(unsafe.Pointer(header.Data))
+ size := C.size_t(header.Cap)
+ ret := C.git_odb_stream_read(stream.ptr, ptr, size)
+ if ret < 0 {
+ return 0, LastError()
+ }
+
+ header.Len = int(ret)
+
+ return len(data), nil
+}
+
+// Close is a dummy function in order to implement the Closer and
+// ReadCloser interfaces
+func (stream *OdbReadStream) Close() error {
+ return nil
+}
+
+func (stream *OdbReadStream) Free() {
+ runtime.SetFinalizer(stream, nil)
+ C.git_odb_stream_free(stream.ptr)
+}
+
+type OdbWriteStream struct {
+ ptr *C.git_odb_stream
+ Id Oid
+}
+
+// Write writes to the stream
+func (stream *OdbWriteStream) Write(data []byte) (int, error) {
+ header := (*reflect.SliceHeader)(unsafe.Pointer(&data))
+ ptr := (*C.char)(unsafe.Pointer(header.Data))
+ size := C.size_t(header.Len)
+
+ ret := C.git_odb_stream_write(stream.ptr, ptr, size)
+ if ret < 0 {
+ return 0, LastError()
+ }
+
+ return len(data), nil
+}
+
+// Close signals that all the data has been written and stores the
+// resulting object id in the stream's Id field.
+func (stream *OdbWriteStream) Close() error {
+ ret := C.git_odb_stream_finalize_write(stream.Id.toC(), stream.ptr)
+ if ret < 0 {
+ return LastError()
+ }
+
+ return nil
+}
+
+func (stream *OdbWriteStream) Free() {
+ runtime.SetFinalizer(stream, nil)
+ C.git_odb_stream_free(stream.ptr)
+}
diff --git a/odb_test.go b/odb_test.go
new file mode 100644
index 0000000..a4f8943
--- /dev/null
+++ b/odb_test.go
@@ -0,0 +1,62 @@
+package git
+
+import (
+ "io"
+ "os"
+ "testing"
+)
+
+func TestOdbStream(t *testing.T) {
+ repo := createTestRepo(t)
+ defer os.RemoveAll(repo.Workdir())
+ _, _ = seedTestRepo(t, repo)
+
+ odb, error := repo.Odb()
+ checkFatal(t, error)
+
+ str := "hello, world!"
+
+ stream, error := odb.NewWriteStream(len(str), ObjectBlob)
+ checkFatal(t, error)
+ n, error := io.WriteString(stream, str)
+ checkFatal(t, error)
+ if n != len(str) {
+ t.Fatalf("Bad write length %v != %v", n, len(str))
+ }
+
+ error = stream.Close()
+ checkFatal(t, error)
+
+ expectedId, error := NewOidFromString("30f51a3fba5274d53522d0f19748456974647b4f")
+ checkFatal(t, error)
+ if stream.Id.Cmp(expectedId) != 0 {
+ t.Fatal("Wrong data written")
+ }
+}
+
+func TestOdbHash(t *testing.T) {
+
+ repo := createTestRepo(t)
+ defer os.RemoveAll(repo.Workdir())
+ _, _ = seedTestRepo(t, repo)
+
+ odb, error := repo.Odb()
+ checkFatal(t, error)
+
+ str := `tree 115fcae49287c82eb55bb275cbbd4556fbed72b7
+parent 66e1c476199ebcd3e304659992233132c5a52c6c
+author John Doe <[email protected]> 1390682018 +0000
+committer John Doe <[email protected]> 1390682018 +0000
+
+Initial commit.`;
+
+ oid, error := odb.Hash([]byte(str), ObjectCommit)
+ checkFatal(t, error)
+
+ coid, error := odb.Write([]byte(str), ObjectCommit)
+ checkFatal(t, error)
+
+ if oid.Cmp(coid) != 0 {
+ t.Fatal("Hash and write Oids are different")
+ }
+} \ No newline at end of file
diff --git a/packbuilder.go b/packbuilder.go
index 8cc03bd..333f183 100644
--- a/packbuilder.go
+++ b/packbuilder.go
@@ -1,7 +1,6 @@
package git
/*
-#cgo pkg-config: libgit2
#include <git2.h>
#include <git2/errors.h>
#include <git2/pack.h>
@@ -11,9 +10,10 @@ extern int _go_git_packbuilder_foreach(git_packbuilder *pb, void *payload);
*/
import "C"
import (
+ "io"
+ "os"
"runtime"
"unsafe"
- "io"
)
type Packbuilder struct {
@@ -22,6 +22,10 @@ type Packbuilder struct {
func (repo *Repository) NewPackbuilder() (*Packbuilder, error) {
builder := &Packbuilder{}
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_packbuilder_new(&builder.ptr, repo.ptr)
if ret != 0 {
return nil, LastError()
@@ -38,6 +42,10 @@ func (pb *Packbuilder) Free() {
func (pb *Packbuilder) Insert(id *Oid, name string) error {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_packbuilder_insert(pb.ptr, id.toC(), cname)
if ret != 0 {
return LastError()
@@ -46,6 +54,9 @@ func (pb *Packbuilder) Insert(id *Oid, name string) error {
}
func (pb *Packbuilder) InsertCommit(id *Oid) error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_packbuilder_insert_commit(pb.ptr, id.toC())
if ret != 0 {
return LastError()
@@ -54,6 +65,9 @@ func (pb *Packbuilder) InsertCommit(id *Oid) error {
}
func (pb *Packbuilder) InsertTree(id *Oid) error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_packbuilder_insert_tree(pb.ptr, id.toC())
if ret != 0 {
return LastError()
@@ -65,10 +79,14 @@ func (pb *Packbuilder) ObjectCount() uint32 {
return uint32(C.git_packbuilder_object_count(pb.ptr))
}
-func (pb *Packbuilder) WriteToFile(name string) error {
+func (pb *Packbuilder) WriteToFile(name string, mode os.FileMode) error {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
- ret := C.git_packbuilder_write(pb.ptr, cname)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_packbuilder_write(pb.ptr, cname, C.uint(mode.Perm()), nil, nil)
if ret != 0 {
return LastError()
}
@@ -76,10 +94,11 @@ func (pb *Packbuilder) WriteToFile(name string) error {
}
func (pb *Packbuilder) Write(w io.Writer) error {
- ch := pb.ForEach()
+ ch, stop := pb.ForEach()
for slice := range ch {
_, err := w.Write(slice)
if err != nil {
+ close(stop)
return err
}
}
@@ -90,22 +109,40 @@ func (pb *Packbuilder) Written() uint32 {
return uint32(C.git_packbuilder_written(pb.ptr))
}
+type packbuilderCbData struct {
+ ch chan<- []byte
+ stop <-chan bool
+}
+
//export packbuilderForEachCb
func packbuilderForEachCb(buf unsafe.Pointer, size C.size_t, payload unsafe.Pointer) int {
- ch := *(*chan []byte)(payload)
+ data := (*packbuilderCbData)(payload)
+ ch := data.ch
+ stop := data.stop
slice := C.GoBytes(buf, C.int(size))
- ch <- slice
+ select {
+ case <- stop:
+ return -1
+ case ch <- slice:
+ }
+
return 0
}
-func (pb *Packbuilder) forEachWrap(ch chan []byte) {
- C._go_git_packbuilder_foreach(pb.ptr, unsafe.Pointer(&ch))
- close(ch)
+func (pb *Packbuilder) forEachWrap(data *packbuilderCbData) {
+ C._go_git_packbuilder_foreach(pb.ptr, unsafe.Pointer(data))
+ close(data.ch)
}
-func (pb *Packbuilder) ForEach() chan []byte {
- ch := make(chan []byte, 0)
- go pb.forEachWrap(ch)
- return ch
+// Foreach sends the packfile as slices through the "data" channel. If
+// you want to stop the pack-building process (e.g. there's an error
+// writing to the output), close or write a value into the "stop"
+// channel.
+func (pb *Packbuilder) ForEach() (<-chan []byte, chan<- bool) {
+ ch := make(chan []byte)
+ stop := make(chan bool)
+ data := packbuilderCbData{ch, stop}
+ go pb.forEachWrap(&data)
+ return ch, stop
}
diff --git a/reference.go b/reference.go
index 820d166..a2f1636 100644
--- a/reference.go
+++ b/reference.go
@@ -1,7 +1,6 @@
package git
/*
-#cgo pkg-config: libgit2
#include <git2.h>
#include <git2/errors.h>
*/
@@ -11,9 +10,11 @@ import (
"unsafe"
)
-var (
- SYMBOLIC = C.GIT_REF_SYMBOLIC
- OID = C.GIT_REF_OID
+type ReferenceType int
+
+const (
+ ReferenceSymbolic ReferenceType = C.GIT_REF_SYMBOLIC
+ ReferenceOid = C.GIT_REF_OID
)
type Reference struct {
@@ -27,12 +28,22 @@ func newReferenceFromC(ptr *C.git_reference) *Reference {
return ref
}
-func (v *Reference) SetSymbolicTarget(target string) (*Reference, error) {
+func (v *Reference) SetSymbolicTarget(target string, sig *Signature, msg string) (*Reference, error) {
var ptr *C.git_reference
+
ctarget := C.CString(target)
defer C.free(unsafe.Pointer(ctarget))
- ret := C.git_reference_symbolic_set_target(&ptr, v.ptr, ctarget)
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ csig := sig.toC()
+ defer C.free(unsafe.Pointer(csig))
+
+ cmsg := C.CString(msg)
+ defer C.free(unsafe.Pointer(cmsg))
+
+ ret := C.git_reference_symbolic_set_target(&ptr, v.ptr, ctarget, csig, cmsg)
if ret < 0 {
return nil, LastError()
}
@@ -40,10 +51,19 @@ func (v *Reference) SetSymbolicTarget(target string) (*Reference, error) {
return newReferenceFromC(ptr), nil
}
-func (v *Reference) SetTarget(target *Oid) (*Reference, error) {
+func (v *Reference) SetTarget(target *Oid, sig *Signature, msg string) (*Reference, error) {
var ptr *C.git_reference
- ret := C.git_reference_set_target(&ptr, v.ptr, target.toC())
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ csig := sig.toC()
+ defer C.free(unsafe.Pointer(csig))
+
+ cmsg := C.CString(msg)
+ defer C.free(unsafe.Pointer(cmsg))
+
+ ret := C.git_reference_set_target(&ptr, v.ptr, target.toC(), csig, cmsg)
if ret < 0 {
return nil, LastError()
}
@@ -54,6 +74,9 @@ func (v *Reference) SetTarget(target *Oid) (*Reference, error) {
func (v *Reference) Resolve() (*Reference, error) {
var ptr *C.git_reference
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_reference_resolve(&ptr, v.ptr)
if ret < 0 {
return nil, LastError()
@@ -62,12 +85,21 @@ func (v *Reference) Resolve() (*Reference, error) {
return newReferenceFromC(ptr), nil
}
-func (v *Reference) Rename(name string, force bool) (*Reference, error) {
+func (v *Reference) Rename(name string, force bool, sig *Signature, msg string) (*Reference, error) {
var ptr *C.git_reference
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
- ret := C.git_reference_rename(&ptr, v.ptr, cname, cbool(force))
+ 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_reference_rename(&ptr, v.ptr, cname, cbool(force), csig, cmsg)
if ret < 0 {
return nil, LastError()
@@ -90,6 +122,9 @@ func (v *Reference) SymbolicTarget() string {
}
func (v *Reference) Delete() error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_reference_delete(v.ptr)
if ret < 0 {
@@ -103,11 +138,140 @@ func (v *Reference) Name() string {
return C.GoString(C.git_reference_name(v.ptr))
}
-func (v *Reference) Type() int {
- return int(C.git_reference_type(v.ptr))
+func (v *Reference) Type() ReferenceType {
+ return ReferenceType(C.git_reference_type(v.ptr))
+}
+
+func (v *Reference) IsBranch() bool {
+ return C.git_reference_is_branch(v.ptr) == 1
+}
+
+func (v *Reference) IsRemote() bool {
+ return C.git_reference_is_remote(v.ptr) == 1
+}
+
+func (v *Reference) IsTag() bool {
+ return C.git_reference_is_tag(v.ptr) == 1
}
func (v *Reference) Free() {
runtime.SetFinalizer(v, nil)
C.git_reference_free(v.ptr)
}
+
+type ReferenceIterator struct {
+ ptr *C.git_reference_iterator
+ repo *Repository
+}
+
+// NewReferenceIterator creates a new iterator over reference names
+func (repo *Repository) NewReferenceIterator() (*ReferenceIterator, error) {
+ var ptr *C.git_reference_iterator
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_reference_iterator_new(&ptr, repo.ptr)
+ if ret < 0 {
+ return nil, LastError()
+ }
+
+ iter := &ReferenceIterator{repo: repo, ptr: ptr}
+ runtime.SetFinalizer(iter, (*ReferenceIterator).Free)
+ return iter, nil
+}
+
+// NewReferenceIteratorGlob creates an iterator over reference names
+// that match the speicified glob. The glob is of the usual fnmatch
+// type.
+func (repo *Repository) NewReferenceIteratorGlob(glob string) (*ReferenceIterator, error) {
+ cstr := C.CString(glob)
+ defer C.free(unsafe.Pointer(cstr))
+ var ptr *C.git_reference_iterator
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_reference_iterator_glob_new(&ptr, repo.ptr, cstr)
+ if ret < 0 {
+ return nil, LastError()
+ }
+
+ iter := &ReferenceIterator{repo: repo, ptr: ptr}
+ runtime.SetFinalizer(iter, (*ReferenceIterator).Free)
+ return iter, nil
+}
+
+// NextName retrieves the next reference name. If the iteration is over,
+// the returned error is git.ErrIterOver
+func (v *ReferenceIterator) NextName() (string, error) {
+ var ptr *C.char
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_reference_next_name(&ptr, v.ptr)
+ if ret == ITEROVER {
+ return "", ErrIterOver
+ }
+ if ret < 0 {
+ return "", LastError()
+ }
+
+ return C.GoString(ptr), nil
+}
+
+// Create a channel from the iterator. You can use range on the
+// returned channel to iterate over all the references names. The channel
+// will be closed in case any error is found.
+func (v *ReferenceIterator) NameIter() <-chan string {
+ ch := make(chan string)
+ go func() {
+ defer close(ch)
+ name, err := v.NextName()
+ for err == nil {
+ ch <- name
+ name, err = v.NextName()
+ }
+ }()
+
+ return ch
+}
+
+// Next retrieves the next reference. If the iterationis over, the
+// returned error is git.ErrIterOver
+func (v *ReferenceIterator) Next() (*Reference, error) {
+ var ptr *C.git_reference
+ ret := C.git_reference_next(&ptr, v.ptr)
+ if ret == ITEROVER {
+ return nil, ErrIterOver
+ }
+ if ret < 0 {
+ return nil, LastError()
+ }
+
+ return newReferenceFromC(ptr), nil
+}
+
+// Create a channel from the iterator. You can use range on the
+// returned channel to iterate over all the references names. The channel
+// will be closed in case any error is found.
+func (v *ReferenceIterator) Iter() <-chan *Reference {
+ ch := make(chan *Reference)
+ go func() {
+ defer close(ch)
+ name, err := v.Next()
+ for err == nil {
+ ch <- name
+ name, err = v.Next()
+ }
+ }()
+
+ return ch
+}
+
+// Free the reference iterator
+func (v *ReferenceIterator) Free() {
+ runtime.SetFinalizer(v, nil)
+ C.git_reference_iterator_free(v.ptr)
+}
diff --git a/reference_test.go b/reference_test.go
index 8043833..156960a 100644
--- a/reference_test.go
+++ b/reference_test.go
@@ -3,6 +3,7 @@ package git
import (
"os"
"runtime"
+ "sort"
"testing"
"time"
)
@@ -11,6 +12,8 @@ func TestRefModification(t *testing.T) {
repo := createTestRepo(t)
defer os.RemoveAll(repo.Workdir())
+ commitId, treeId := seedTestRepo(t, repo)
+
loc, err := time.LoadLocation("Europe/Berlin")
checkFatal(t, err)
sig := &Signature{
@@ -18,30 +21,16 @@ func TestRefModification(t *testing.T) {
When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc),
}
-
- idx, err := repo.Index()
- checkFatal(t, err)
- err = idx.AddByPath("README")
- checkFatal(t, err)
- treeId, err := idx.WriteTree()
- checkFatal(t, err)
-
- message := "This is a commit\n"
- tree, err := repo.LookupTree(treeId)
- checkFatal(t, err)
- commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree)
- checkFatal(t, err)
-
- _, err = repo.CreateReference("refs/tags/tree", treeId, true)
+ _, err = repo.CreateReference("refs/tags/tree", treeId, true, sig, "testTreeTag")
checkFatal(t, err)
tag, err := repo.LookupReference("refs/tags/tree")
checkFatal(t, err)
- checkRefType(t, tag, OID)
+ checkRefType(t, tag, ReferenceOid)
ref, err := repo.LookupReference("HEAD")
checkFatal(t, err)
- checkRefType(t, ref, SYMBOLIC)
+ checkRefType(t, ref, ReferenceSymbolic)
if target := ref.Target(); target != nil {
t.Fatalf("Expected nil *Oid, got %v", target)
@@ -49,7 +38,7 @@ func TestRefModification(t *testing.T) {
ref, err = ref.Resolve()
checkFatal(t, err)
- checkRefType(t, ref, OID)
+ checkRefType(t, ref, ReferenceOid)
if target := ref.Target(); target == nil {
t.Fatalf("Expected valid target got nil")
@@ -63,15 +52,122 @@ func TestRefModification(t *testing.T) {
t.Fatalf("Wrong ref target")
}
- _, err = tag.Rename("refs/tags/renamed", false)
+ _, err = tag.Rename("refs/tags/renamed", false, nil, "")
checkFatal(t, err)
tag, err = repo.LookupReference("refs/tags/renamed")
checkFatal(t, err)
- checkRefType(t, ref, OID)
+ checkRefType(t, ref, ReferenceOid)
+
+}
+
+func TestIterator(t *testing.T) {
+ repo := createTestRepo(t)
+ defer os.RemoveAll(repo.Workdir())
+
+ loc, err := time.LoadLocation("Europe/Berlin")
+ checkFatal(t, err)
+ sig := &Signature{
+ Name: "Rand Om Hacker",
+ Email: "[email protected]",
+ When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc),
+ }
+
+ idx, err := repo.Index()
+ checkFatal(t, err)
+ err = idx.AddByPath("README")
+ checkFatal(t, err)
+ treeId, err := idx.WriteTree()
+ checkFatal(t, err)
+ message := "This is a commit\n"
+ tree, err := repo.LookupTree(treeId)
+ checkFatal(t, err)
+ commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree)
+ checkFatal(t, err)
+
+ _, err = repo.CreateReference("refs/heads/one", commitId, true, sig, "headOne")
+ checkFatal(t, err)
+
+ _, err = repo.CreateReference("refs/heads/two", commitId, true, sig, "headTwo")
+ checkFatal(t, err)
+
+ _, err = repo.CreateReference("refs/heads/three", commitId, true, sig, "headThree")
+ checkFatal(t, err)
+
+ iter, err := repo.NewReferenceIterator()
+ checkFatal(t, err)
+
+ var list []string
+ expected := []string{
+ "refs/heads/master",
+ "refs/heads/one",
+ "refs/heads/three",
+ "refs/heads/two",
+ }
+
+ // test some manual iteration
+ name, err := iter.NextName()
+ for err == nil {
+ list = append(list, name)
+ name, err = iter.NextName()
+ }
+ if err != ErrIterOver {
+ t.Fatal("Iteration not over")
+ }
+
+ sort.Strings(list)
+ compareStringList(t, expected, list)
+
+ // test the iterator for full refs, rather than just names
+ iter, err = repo.NewReferenceIterator()
+ checkFatal(t, err)
+ count := 0
+ _, err = iter.Next()
+ for err == nil {
+ count++
+ _, err = iter.Next()
+ }
+ if err != ErrIterOver {
+ t.Fatal("Iteration not over")
+ }
+
+ if count != 4 {
+ t.Fatalf("Wrong number of references returned %v", count)
+ }
+
+ // test the channel iteration
+ list = []string{}
+ iter, err = repo.NewReferenceIterator()
+ for name := range iter.NameIter() {
+ list = append(list, name)
+ }
+
+ sort.Strings(list)
+ compareStringList(t, expected, list)
+
+ iter, err = repo.NewReferenceIteratorGlob("refs/heads/t*")
+ expected = []string{
+ "refs/heads/three",
+ "refs/heads/two",
+ }
+
+ list = []string{}
+ for name := range iter.NameIter() {
+ list = append(list, name)
+ }
+
+ compareStringList(t, expected, list)
+}
+
+func compareStringList(t *testing.T, expected, actual []string) {
+ for i, v := range expected {
+ if actual[i] != v {
+ t.Fatalf("Bad list")
+ }
+ }
}
-func checkRefType(t *testing.T, ref *Reference, kind int) {
+func checkRefType(t *testing.T, ref *Reference, kind ReferenceType) {
if ref.Type() == kind {
return
}
diff --git a/repository.go b/repository.go
index b9a10ad..48c2b46 100644
--- a/repository.go
+++ b/repository.go
@@ -1,14 +1,13 @@
package git
/*
-#cgo pkg-config: libgit2
#include <git2.h>
#include <git2/errors.h>
*/
import "C"
import (
- "unsafe"
"runtime"
+ "unsafe"
)
// Repository
@@ -22,6 +21,9 @@ func OpenRepository(path string) (*Repository, error) {
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_repository_open(&repo.ptr, cpath)
if ret < 0 {
return nil, LastError()
@@ -37,6 +39,9 @@ func InitRepository(path string, isbare bool) (*Repository, error) {
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_repository_init(&repo.ptr, cpath, ucbool(isbare))
if ret < 0 {
return nil, LastError()
@@ -54,16 +59,24 @@ func (v *Repository) Free() {
func (v *Repository) Config() (*Config, error) {
config := new(Config)
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_repository_config(&config.ptr, v.ptr)
if ret < 0 {
return nil, LastError()
}
+ runtime.SetFinalizer(config, (*Config).Free)
return config, nil
}
func (v *Repository) Index() (*Index, error) {
var ptr *C.git_index
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_repository_index(&ptr, v.ptr)
if ret < 0 {
return nil, LastError()
@@ -72,35 +85,49 @@ func (v *Repository) Index() (*Index, error) {
return newIndexFromC(ptr), nil
}
-func (v *Repository) LookupTree(oid *Oid) (*Tree, error) {
- tree := new(Tree)
- ret := C.git_tree_lookup(&tree.ptr, v.ptr, oid.toC())
+func (v *Repository) lookupType(oid *Oid, t ObjectType) (Object, error) {
+ var ptr *C.git_object
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_object_lookup(&ptr, v.ptr, oid.toC(), C.git_otype(t))
if ret < 0 {
return nil, LastError()
}
- return tree, nil
+ return allocObject(ptr), nil
}
-func (v *Repository) LookupCommit(o *Oid) (*Commit, error) {
- commit := new(Commit)
- ecode := C.git_commit_lookup(&commit.ptr, v.ptr, o.toC())
- if ecode < 0 {
- return nil, LastError()
+func (v *Repository) Lookup(oid *Oid) (Object, error) {
+ return v.lookupType(oid, ObjectAny)
+}
+
+func (v *Repository) LookupTree(oid *Oid) (*Tree, error) {
+ obj, err := v.lookupType(oid, ObjectTree)
+ if err != nil {
+ return nil, err
}
- return commit, nil
+ return obj.(*Tree), nil
}
-func (v *Repository) LookupBlob(o *Oid) (*Blob, error) {
- blob := new(Blob)
- ecode := C.git_blob_lookup(&blob.ptr, v.ptr, o.toC())
- if ecode < 0 {
- return nil, LastError()
+func (v *Repository) LookupCommit(oid *Oid) (*Commit, error) {
+ obj, err := v.lookupType(oid, ObjectCommit)
+ if err != nil {
+ return nil, err
+ }
+
+ return obj.(*Commit), nil
+}
+
+func (v *Repository) LookupBlob(oid *Oid) (*Blob, error) {
+ obj, err := v.lookupType(oid, ObjectBlob)
+ if err != nil {
+ return nil, err
}
- runtime.SetFinalizer(blob, (*Blob).Free)
- return blob, nil
+ return obj.(*Blob), nil
}
func (v *Repository) LookupReference(name string) (*Reference, error) {
@@ -108,6 +135,9 @@ func (v *Repository) LookupReference(name string) (*Reference, error) {
defer C.free(unsafe.Pointer(cname))
var ptr *C.git_reference
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ecode := C.git_reference_lookup(&ptr, v.ptr, cname)
if ecode < 0 {
return nil, LastError()
@@ -116,12 +146,22 @@ func (v *Repository) LookupReference(name string) (*Reference, error) {
return newReferenceFromC(ptr), nil
}
-func (v *Repository) CreateReference(name string, oid *Oid, force bool) (*Reference, error) {
+func (v *Repository) CreateReference(name string, oid *Oid, force bool, sig *Signature, msg string) (*Reference, error) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
+
+ csig := sig.toC()
+ defer C.free(unsafe.Pointer(csig))
+
+ cmsg := C.CString(msg)
+ defer C.free(unsafe.Pointer(cmsg))
+
var ptr *C.git_reference
- ecode := C.git_reference_create(&ptr, v.ptr, cname, oid.toC(), cbool(force))
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ecode := C.git_reference_create(&ptr, v.ptr, cname, oid.toC(), cbool(force), csig, cmsg)
if ecode < 0 {
return nil, LastError()
}
@@ -129,14 +169,25 @@ func (v *Repository) CreateReference(name string, oid *Oid, force bool) (*Refere
return newReferenceFromC(ptr), nil
}
-func (v *Repository) CreateSymbolicReference(name, target string, force bool) (*Reference, error) {
+func (v *Repository) CreateSymbolicReference(name, target string, force bool, sig *Signature, msg string) (*Reference, error) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
+
ctarget := C.CString(target)
defer C.free(unsafe.Pointer(ctarget))
+
+ csig := sig.toC()
+ defer C.free(unsafe.Pointer(csig))
+
+ cmsg := C.CString(msg)
+ defer C.free(unsafe.Pointer(cmsg))
+
var ptr *C.git_reference
- ecode := C.git_reference_symbolic_create(&ptr, v.ptr, cname, ctarget, cbool(force))
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ecode := C.git_reference_symbolic_create(&ptr, v.ptr, cname, ctarget, cbool(force), csig, cmsg)
if ecode < 0 {
return nil, LastError()
}
@@ -146,6 +197,10 @@ func (v *Repository) CreateSymbolicReference(name, target string, force bool) (*
func (v *Repository) Walk() (*RevWalk, error) {
walk := new(RevWalk)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ecode := C.git_revwalk_new(&walk.ptr, v.ptr)
if ecode < 0 {
return nil, LastError()
@@ -171,7 +226,7 @@ func (v *Repository) CreateCommit(
var cparents []*C.git_commit = nil
var parentsarg **C.git_commit = nil
- nparents:= len(parents)
+ nparents := len(parents)
if nparents > 0 {
cparents = make([]*C.git_commit, nparents)
for i, v := range parents {
@@ -186,10 +241,13 @@ func (v *Repository) CreateCommit(
committerSig := committer.toC()
defer C.git_signature_free(committerSig)
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_commit_create(
oid.toC(), v.ptr, cref,
authorSig, committerSig,
- nil, cmsg, tree.ptr, C.int(nparents), parentsarg)
+ nil, cmsg, tree.ptr, C.size_t(nparents), parentsarg)
if ret < 0 {
return nil, LastError()
@@ -205,6 +263,10 @@ func (v *Odb) Free() {
func (v *Repository) Odb() (odb *Odb, err error) {
odb = new(Odb)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
if ret := C.git_repository_odb(&odb.ptr, v.ptr); ret < 0 {
return nil, LastError()
}
@@ -217,7 +279,7 @@ func (repo *Repository) Path() string {
return C.GoString(C.git_repository_path(repo.ptr))
}
-func (repo *Repository) IsBare() (bool) {
+func (repo *Repository) IsBare() bool {
return C.git_repository_is_bare(repo.ptr) != 0
}
@@ -229,6 +291,9 @@ func (repo *Repository) SetWorkdir(workdir string, updateGitlink bool) error {
cstr := C.CString(workdir)
defer C.free(unsafe.Pointer(cstr))
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
if C.git_repository_set_workdir(repo.ptr, cstr, cbool(updateGitlink)) < 0 {
return LastError()
}
@@ -237,11 +302,32 @@ func (repo *Repository) SetWorkdir(workdir string, updateGitlink bool) error {
func (v *Repository) TreeBuilder() (*TreeBuilder, error) {
bld := new(TreeBuilder)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
if ret := C.git_treebuilder_create(&bld.ptr, nil); ret < 0 {
return nil, LastError()
}
+ runtime.SetFinalizer(bld, (*TreeBuilder).Free)
bld.repo = v
return bld, nil
}
+func (v *Repository) RevparseSingle(spec string) (Object, error) {
+ cspec := C.CString(spec)
+ defer C.free(unsafe.Pointer(cspec))
+
+ var ptr *C.git_object
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ecode := C.git_revparse_single(&ptr, v.ptr, cspec)
+ if ecode < 0 {
+ return nil, LastError()
+ }
+
+ return allocObject(ptr), nil
+}
diff --git a/submodule.go b/submodule.go
index e1e031b..9abf333 100644
--- a/submodule.go
+++ b/submodule.go
@@ -1,7 +1,6 @@
package git
/*
-#cgo pkg-config: libgit2
#include <git2.h>
#include <git2/errors.h>
@@ -9,6 +8,7 @@ extern int _go_git_visit_submodule(git_repository *repo, void *fct);
*/
import "C"
import (
+ "runtime"
"unsafe"
)
@@ -20,7 +20,7 @@ type Submodule struct {
type SubmoduleUpdate int
const (
- SubmoduleUpdateDefault SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_DEFAULT
+ SubmoduleUpdateReset SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_RESET
SubmoduleUpdateCheckout = C.GIT_SUBMODULE_UPDATE_CHECKOUT
SubmoduleUpdateRebase = C.GIT_SUBMODULE_UPDATE_REBASE
SubmoduleUpdateMerge = C.GIT_SUBMODULE_UPDATE_MERGE
@@ -30,7 +30,7 @@ const (
type SubmoduleIgnore int
const (
- SubmoduleIgnoreDefault SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_DEFAULT
+ SubmoduleIgnoreReset SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_RESET
SubmoduleIgnoreNone = C.GIT_SUBMODULE_IGNORE_NONE
SubmoduleIgnoreUntracked = C.GIT_SUBMODULE_IGNORE_UNTRACKED
SubmoduleIgnoreDirty = C.GIT_SUBMODULE_IGNORE_DIRTY
@@ -56,6 +56,14 @@ const (
SubmoduleStatusWdUntracked = C.GIT_SUBMODULE_STATUS_WD_UNTRACKED
)
+type SubmoduleRecurse int
+
+const (
+ SubmoduleRecurseNo SubmoduleRecurse = C.GIT_SUBMODULE_RECURSE_NO
+ SubmoduleRecurseYes = C.GIT_SUBMODULE_RECURSE_YES
+ SubmoduleRecurseOndemand = C.GIT_SUBMODULE_RECURSE_ONDEMAND
+)
+
func SubmoduleStatusIsUnmodified(status int) bool {
o := SubmoduleStatus(status) & ^(SubmoduleStatusInHead | SubmoduleStatusInIndex |
SubmoduleStatusInConfig | SubmoduleStatusInWd)
@@ -67,6 +75,10 @@ func (repo *Repository) LookupSubmodule(name string) (*Submodule, error) {
defer C.free(unsafe.Pointer(cname))
sub := new(Submodule)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_submodule_lookup(&sub.ptr, repo.ptr, cname)
if ret < 0 {
return nil, LastError()
@@ -85,6 +97,9 @@ func SubmoduleVisitor(csub unsafe.Pointer, name string, cfct unsafe.Pointer) int
}
func (repo *Repository) ForeachSubmodule(cbk SubmoduleCbk) error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C._go_git_visit_submodule(repo.ptr, unsafe.Pointer(&cbk))
if ret < 0 {
return LastError()
@@ -99,6 +114,10 @@ func (repo *Repository) AddSubmodule(url, path string, use_git_link bool) (*Subm
defer C.free(unsafe.Pointer(cpath))
sub := new(Submodule)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_submodule_add_setup(&sub.ptr, repo.ptr, curl, cpath, cbool(use_git_link))
if ret < 0 {
return nil, LastError()
@@ -107,6 +126,9 @@ func (repo *Repository) AddSubmodule(url, path string, use_git_link bool) (*Subm
}
func (sub *Submodule) FinalizeAdd() error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_submodule_add_finalize(sub.ptr)
if ret < 0 {
return LastError()
@@ -115,6 +137,9 @@ func (sub *Submodule) FinalizeAdd() error {
}
func (sub *Submodule) AddToIndex(write_index bool) error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_submodule_add_to_index(sub.ptr, cbool(write_index))
if ret < 0 {
return LastError()
@@ -123,6 +148,9 @@ func (sub *Submodule) AddToIndex(write_index bool) error {
}
func (sub *Submodule) Save() error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_submodule_save(sub.ptr)
if ret < 0 {
return LastError()
@@ -155,6 +183,9 @@ func (sub *Submodule) SetUrl(url string) error {
curl := C.CString(url)
defer C.free(unsafe.Pointer(curl))
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_submodule_set_url(sub.ptr, curl)
if ret < 0 {
return LastError()
@@ -206,15 +237,15 @@ func (sub *Submodule) SetUpdate(update SubmoduleUpdate) SubmoduleUpdate {
return SubmoduleUpdate(o)
}
-func (sub *Submodule) FetchRecurseSubmodules() bool {
- if 0 == C.git_submodule_fetch_recurse_submodules(sub.ptr) {
- return false
- }
- return true
+func (sub *Submodule) FetchRecurseSubmodules() SubmoduleRecurse {
+ return SubmoduleRecurse(C.git_submodule_fetch_recurse_submodules(sub.ptr));
}
-func (sub *Submodule) SetFetchRecurseSubmodules(v bool) error {
- ret := C.git_submodule_set_fetch_recurse_submodules(sub.ptr, cbool(v))
+func (sub *Submodule) SetFetchRecurseSubmodules(recurse SubmoduleRecurse) error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_submodule_set_fetch_recurse_submodules(sub.ptr, C.git_submodule_recurse_t(recurse))
if ret < 0 {
return LastError()
}
@@ -222,6 +253,9 @@ func (sub *Submodule) SetFetchRecurseSubmodules(v bool) error {
}
func (sub *Submodule) Init(overwrite bool) error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_submodule_init(sub.ptr, cbool(overwrite))
if ret < 0 {
return LastError()
@@ -230,6 +264,9 @@ func (sub *Submodule) Init(overwrite bool) error {
}
func (sub *Submodule) Sync() error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_submodule_sync(sub.ptr)
if ret < 0 {
return LastError()
@@ -239,6 +276,10 @@ func (sub *Submodule) Sync() error {
func (sub *Submodule) Open() (*Repository, error) {
repo := new(Repository)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_submodule_open(&repo.ptr, sub.ptr)
if ret < 0 {
return nil, LastError()
@@ -247,6 +288,9 @@ func (sub *Submodule) Open() (*Repository, error) {
}
func (sub *Submodule) Reload() error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_submodule_reload(sub.ptr)
if ret < 0 {
return LastError()
@@ -255,6 +299,9 @@ func (sub *Submodule) Reload() error {
}
func (repo *Repository) ReloadAllSubmodules() error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_submodule_reload_all(repo.ptr)
if ret < 0 {
return LastError()
diff --git a/tree.go b/tree.go
index dc82929..8c74e5d 100644
--- a/tree.go
+++ b/tree.go
@@ -13,41 +13,37 @@ import (
"unsafe"
)
+type Filemode int
+const (
+ FilemodeNew Filemode = C.GIT_FILEMODE_NEW
+ FilemodeTree = C.GIT_FILEMODE_TREE
+ FilemodeBlob = C.GIT_FILEMODE_BLOB
+ FilemodeBlobExecutable = C.GIT_FILEMODE_BLOB_EXECUTABLE
+ FilemodeLink = C.GIT_FILEMODE_LINK
+ FilemodeCommit = C.GIT_FILEMODE_COMMIT
+)
+
type Tree struct {
- ptr *C.git_tree
+ gitObject
}
type TreeEntry struct {
Name string
Id *Oid
- Type int
+ Type ObjectType
+ Filemode int
}
func newTreeEntry(entry *C.git_tree_entry) *TreeEntry {
return &TreeEntry{
C.GoString(C.git_tree_entry_name(entry)),
newOidFromC(C.git_tree_entry_id(entry)),
- int(C.git_tree_entry_type(entry)),
+ ObjectType(C.git_tree_entry_type(entry)),
+ int(C.git_tree_entry_filemode(entry)),
}
}
-func (t *Tree) Free() {
- runtime.SetFinalizer(t, nil)
- C.git_tree_free(t.ptr)
-}
-
-func TreeLookup(repo *Repository, oid *Oid) (*Tree, error) {
- tree := new(Tree)
- err := C.git_tree_lookup(&tree.ptr, repo.ptr, oid.toC())
- if err < 0 {
- return nil, LastError()
- }
-
- runtime.SetFinalizer(tree, (*Tree).Free)
- return tree, nil
-}
-
-func (t *Tree) EntryByName(filename string) *TreeEntry {
+func (t Tree) EntryByName(filename string) *TreeEntry {
cname := C.CString(filename)
defer C.free(unsafe.Pointer(cname))
@@ -59,7 +55,25 @@ func (t *Tree) EntryByName(filename string) *TreeEntry {
return newTreeEntry(entry)
}
-func (t *Tree) EntryByIndex(index uint64) *TreeEntry {
+// EntryByPath looks up an entry by its full path, recursing into
+// deeper trees if necessary (i.e. if there are slashes in the path)
+func (t Tree) EntryByPath(path string) (*TreeEntry, error) {
+ cpath := C.CString(path)
+ defer C.free(unsafe.Pointer(cpath))
+ var entry *C.git_tree_entry
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ ret := C.git_tree_entry_bypath(&entry, t.ptr, cpath)
+ if ret < 0 {
+ return nil, LastError()
+ }
+
+ return newTreeEntry(entry), nil
+}
+
+func (t Tree) EntryByIndex(index uint64) *TreeEntry {
entry := C.git_tree_entry_byindex(t.ptr, C.size_t(index))
if entry == nil {
return nil
@@ -68,7 +82,7 @@ func (t *Tree) EntryByIndex(index uint64) *TreeEntry {
return newTreeEntry(entry)
}
-func (t *Tree) EntryCount() uint64 {
+func (t Tree) EntryCount() uint64 {
num := C.git_tree_entrycount(t.ptr)
return uint64(num)
}
@@ -84,7 +98,10 @@ func CallbackGitTreeWalk(_root unsafe.Pointer, _entry unsafe.Pointer, ptr unsafe
return C.int(callback(root, newTreeEntry(entry)))
}
-func (t *Tree) Walk(callback TreeWalkCallback) error {
+func (t Tree) Walk(callback TreeWalkCallback) error {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
err := C._go_git_treewalk(
t.ptr,
C.GIT_TREEWALK_PRE,
@@ -112,6 +129,9 @@ func (v *TreeBuilder) Insert(filename string, id *Oid, filemode int) (error) {
cfilename := C.CString(filename)
defer C.free(unsafe.Pointer(cfilename))
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
err := C.git_treebuilder_insert(nil, v.ptr, cfilename, id.toC(), C.git_filemode_t(filemode))
if err < 0 {
return LastError()
@@ -122,6 +142,10 @@ func (v *TreeBuilder) Insert(filename string, id *Oid, filemode int) (error) {
func (v *TreeBuilder) Write() (*Oid, error) {
oid := new(Oid)
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
err := C.git_treebuilder_write(oid.toC(), v.repo.ptr, v.ptr)
if err < 0 {
diff --git a/walk.go b/walk.go
index 216eb65..6979b6b 100644
--- a/walk.go
+++ b/walk.go
@@ -1,7 +1,6 @@
package git
/*
-#cgo pkg-config: libgit2
#include <git2.h>
#include <git2/errors.h>
*/
@@ -9,15 +8,17 @@ import "C"
import (
"io"
+ "runtime"
)
// RevWalk
+type SortType uint
const (
- SORT_NONE = C.GIT_SORT_NONE
- SORT_TOPOLOGICAL = C.GIT_SORT_TOPOLOGICAL
- SORT_TIME = C.GIT_SORT_TIME
- SORT_REVERSE = C.GIT_SORT_REVERSE
+ SortNone SortType = C.GIT_SORT_NONE
+ SortTopological = C.GIT_SORT_TOPOLOGICAL
+ SortTime = C.GIT_SORT_TIME
+ SortReverse = C.GIT_SORT_REVERSE
)
type RevWalk struct {
@@ -34,6 +35,9 @@ func (v *RevWalk) Push(id *Oid) {
}
func (v *RevWalk) PushHead() (err error) {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ecode := C.git_revwalk_push_head(v.ptr)
if ecode < 0 {
err = LastError()
@@ -43,6 +47,9 @@ func (v *RevWalk) PushHead() (err error) {
}
func (v *RevWalk) Next(oid *Oid) (err error) {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
ret := C.git_revwalk_next(oid.toC(), v.ptr)
switch {
case ret == ITEROVER:
@@ -81,7 +88,7 @@ func (v *RevWalk) Iterate(fun RevWalkIterator) (err error) {
return nil
}
-func (v *RevWalk) Sorting(sm uint) {
+func (v *RevWalk) Sorting(sm SortType) {
C.git_revwalk_sorting(v.ptr, C.uint(sm))
}
diff --git a/wrapper.c b/wrapper.c
index 67f34fd..2af3974 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -20,4 +20,8 @@ int _go_git_packbuilder_foreach(git_packbuilder *pb, void *payload)
return git_packbuilder_foreach(pb, (git_packbuilder_foreach_cb)&packbuilderForEachCb, payload);
}
+int _go_git_odb_foreach(git_odb *db, void *payload)
+{
+ return git_odb_foreach(db, (git_odb_foreach_cb)&odbForEachCb, payload);
+}
/* EOF */