[next] Add stash support #257
|
@ -8,7 +8,7 @@ Go bindings for [libgit2](http://libgit2.github.com/). The `master` branch follo
|
||||||
Installing
|
Installing
|
||||||
----------
|
----------
|
||||||
|
|
||||||
This project wraps the functionality provided by libgit2. If you're using a stable version, install it to your system via your system's package manaager and then install git2go as usual.
|
This project wraps the functionality provided by libgit2. If you're using a stable version, install it to your system via your system's package manager and then install git2go as usual.
|
||||||
|
|
||||||
Otherwise (`next` which tracks an unstable version), we need to build libgit2 as well. In order to build it, 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.
|
Otherwise (`next` which tracks an unstable version), we need to build libgit2 as well. In order to build it, 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.
|
||||||
|
|
||||||
|
|
6
blame.go
6
blame.go
|
@ -58,8 +58,8 @@ func (v *Repository) BlameFile(path string, opts *BlameOptions) (*Blame, error)
|
||||||
version: C.GIT_BLAME_OPTIONS_VERSION,
|
version: C.GIT_BLAME_OPTIONS_VERSION,
|
||||||
flags: C.uint32_t(opts.Flags),
|
flags: C.uint32_t(opts.Flags),
|
||||||
min_match_characters: C.uint16_t(opts.MinMatchCharacters),
|
min_match_characters: C.uint16_t(opts.MinMatchCharacters),
|
||||||
min_line: C.uint32_t(opts.MinLine),
|
min_line: C.size_t(opts.MinLine),
|
||||||
max_line: C.uint32_t(opts.MaxLine),
|
max_line: C.size_t(opts.MaxLine),
|
||||||
}
|
}
|
||||||
if opts.NewestCommit != nil {
|
if opts.NewestCommit != nil {
|
||||||
copts.newest_commit = *opts.NewestCommit.toC()
|
copts.newest_commit = *opts.NewestCommit.toC()
|
||||||
|
@ -100,7 +100,7 @@ func (blame *Blame) HunkByIndex(index int) (BlameHunk, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (blame *Blame) HunkByLine(lineno int) (BlameHunk, error) {
|
func (blame *Blame) HunkByLine(lineno int) (BlameHunk, error) {
|
||||||
ptr := C.git_blame_get_hunk_byline(blame.ptr, C.uint32_t(lineno))
|
ptr := C.git_blame_get_hunk_byline(blame.ptr, C.size_t(lineno))
|
||||||
if ptr == nil {
|
if ptr == nil {
|
||||||
return BlameHunk{}, ErrInvalid
|
return BlameHunk{}, ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
2
blob.go
2
blob.go
|
@ -84,7 +84,7 @@ func (repo *Repository) CreateBlobFromChunks(hintPath string, callback BlobChunk
|
||||||
|
|
||||||
var chintPath *C.char = nil
|
var chintPath *C.char = nil
|
||||||
if len(hintPath) > 0 {
|
if len(hintPath) > 0 {
|
||||||
C.CString(hintPath)
|
chintPath = C.CString(hintPath)
|
||||||
defer C.free(unsafe.Pointer(chintPath))
|
defer C.free(unsafe.Pointer(chintPath))
|
||||||
}
|
}
|
||||||
oid := C.git_oid{}
|
oid := C.git_oid{}
|
||||||
|
|
|
@ -45,6 +45,7 @@ type CheckoutOpts struct {
|
||||||
FileOpenFlags int // Default is O_CREAT | O_TRUNC | O_WRONLY
|
FileOpenFlags int // Default is O_CREAT | O_TRUNC | O_WRONLY
|
||||||
TargetDirectory string // Alternative checkout path to workdir
|
TargetDirectory string // Alternative checkout path to workdir
|
||||||
Paths []string
|
Paths []string
|
||||||
|
Baseline *Tree
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOpts {
|
func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOpts {
|
||||||
|
@ -90,6 +91,10 @@ func populateCheckoutOpts(ptr *C.git_checkout_options, opts *CheckoutOpts) *C.gi
|
||||||
ptr.paths.count = C.size_t(len(opts.Paths))
|
ptr.paths.count = C.size_t(len(opts.Paths))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.Baseline != nil {
|
||||||
|
ptr.baseline = opts.Baseline.cast_ptr
|
||||||
|
}
|
||||||
|
|
||||||
return ptr
|
return ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
29
config.go
29
config.go
|
@ -12,6 +12,9 @@ import (
|
||||||
type ConfigLevel int
|
type ConfigLevel int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// System-wide on Windows, for compatibility with portable git
|
||||||
|
ConfigLevelProgramdata ConfigLevel = C.GIT_CONFIG_LEVEL_PROGRAMDATA
|
||||||
|
|
||||||
// System-wide configuration file; /etc/gitconfig on Linux systems
|
// System-wide configuration file; /etc/gitconfig on Linux systems
|
||||||
ConfigLevelSystem ConfigLevel = C.GIT_CONFIG_LEVEL_SYSTEM
|
ConfigLevelSystem ConfigLevel = C.GIT_CONFIG_LEVEL_SYSTEM
|
||||||
|
|
||||||
|
@ -115,18 +118,20 @@ func (c *Config) LookupInt64(name string) (int64, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) LookupString(name string) (string, error) {
|
func (c *Config) LookupString(name string) (string, error) {
|
||||||
var ptr *C.char
|
|
||||||
cname := C.CString(name)
|
cname := C.CString(name)
|
||||||
defer C.free(unsafe.Pointer(cname))
|
defer C.free(unsafe.Pointer(cname))
|
||||||
|
|
||||||
|
valBuf := C.git_buf{}
|
||||||
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
if ret := C.git_config_get_string(&ptr, c.ptr, cname); ret < 0 {
|
if ret := C.git_config_get_string_buf(&valBuf, c.ptr, cname); ret < 0 {
|
||||||
return "", MakeGitError(ret)
|
return "", MakeGitError(ret)
|
||||||
}
|
}
|
||||||
|
defer C.git_buf_free(&valBuf)
|
||||||
|
|
||||||
return C.GoString(ptr), nil
|
return C.GoString(valBuf.ptr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) LookupBool(name string) (bool, error) {
|
func (c *Config) LookupBool(name string) (bool, error) {
|
||||||
|
@ -410,3 +415,21 @@ func ConfigFindXDG() (string, error) {
|
||||||
|
|
||||||
return C.GoString(buf.ptr), nil
|
return C.GoString(buf.ptr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConfigFindProgramdata locate the path to the configuration file in ProgramData.
|
||||||
|
//
|
||||||
|
// Look for the file in %PROGRAMDATA%\Git\config used by portable git.
|
||||||
|
func ConfigFindProgramdata() (string, error) {
|
||||||
|
var buf C.git_buf
|
||||||
|
defer C.git_buf_free(&buf)
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
ret := C.git_config_find_programdata(&buf)
|
||||||
|
if ret < 0 {
|
||||||
|
return "", MakeGitError(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
return C.GoString(buf.ptr), nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tempConfig = "./temp.gitconfig"
|
||||||
|
|
||||||
|
func setupConfig() (*Config, error) {
|
||||||
|
var (
|
||||||
|
c *Config
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
c, err = OpenOndisk(nil, tempConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.SetString("foo.bar", "baz")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = c.SetBool("foo.bool", true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = c.SetInt32("foo.int32", 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = c.SetInt64("foo.int64", 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanupConfig() {
|
||||||
|
os.Remove(tempConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestRunner func(*Config, *testing.T)
|
||||||
|
|
||||||
|
var tests = []TestRunner{
|
||||||
|
// LookupString
|
||||||
|
func(c *Config, t *testing.T) {
|
||||||
|
val, err := c.LookupString("foo.bar")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Got LookupString error: '%v', expected none\n", err)
|
||||||
|
}
|
||||||
|
if val != "baz" {
|
||||||
|
t.Errorf("Got '%s' from LookupString, expected 'bar'\n", val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// LookupBool
|
||||||
|
func(c *Config, t *testing.T) {
|
||||||
|
val, err := c.LookupBool("foo.bool")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Got LookupBool error: '%v', expected none\n", err)
|
||||||
|
}
|
||||||
|
if !val {
|
||||||
|
t.Errorf("Got %b from LookupBool, expected 'false'\n", val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// LookupInt32
|
||||||
|
func(c *Config, t *testing.T) {
|
||||||
|
val, err := c.LookupInt32("foo.int32")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Got LookupInt32 error: '%v', expected none\n", err)
|
||||||
|
}
|
||||||
|
if val != 32 {
|
||||||
|
t.Errorf("Got %v, expected 32\n", val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// LookupInt64
|
||||||
|
func(c *Config, t *testing.T) {
|
||||||
|
val, err := c.LookupInt64("foo.int64")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Got LookupInt64 error: '%v', expected none\n", err)
|
||||||
|
}
|
||||||
|
if val != 64 {
|
||||||
|
t.Errorf("Got %v, expected 64\n", val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigLookups(t *testing.T) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
c *Config
|
||||||
|
)
|
||||||
|
|
||||||
|
c, err = setupConfig()
|
||||||
|
defer cleanupConfig()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Setup error: '%v'. Expected none\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Free()
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
test(c, t)
|
||||||
|
}
|
||||||
|
}
|
6
diff.go
6
diff.go
|
@ -550,7 +550,7 @@ func diffOptionsToC(opts *DiffOptions) (copts *C.git_diff_options, notifyData *d
|
||||||
|
|
||||||
if opts.NotifyCallback != nil {
|
if opts.NotifyCallback != nil {
|
||||||
C._go_git_setup_diff_notify_callbacks(copts)
|
C._go_git_setup_diff_notify_callbacks(copts)
|
||||||
copts.notify_payload = pointerHandles.Track(notifyData)
|
copts.payload = pointerHandles.Track(notifyData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -562,8 +562,8 @@ func freeDiffOptions(copts *C.git_diff_options) {
|
||||||
freeStrarray(&cpathspec)
|
freeStrarray(&cpathspec)
|
||||||
C.free(unsafe.Pointer(copts.old_prefix))
|
C.free(unsafe.Pointer(copts.old_prefix))
|
||||||
C.free(unsafe.Pointer(copts.new_prefix))
|
C.free(unsafe.Pointer(copts.new_prefix))
|
||||||
if copts.notify_payload != nil {
|
if copts.payload != nil {
|
||||||
pointerHandles.Untrack(copts.notify_payload)
|
pointerHandles.Untrack(copts.payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
22
git.go
22
git.go
|
@ -52,6 +52,7 @@ const (
|
||||||
|
|
||||||
// No error
|
// No error
|
||||||
ErrOk ErrorCode = C.GIT_OK
|
ErrOk ErrorCode = C.GIT_OK
|
||||||
|
|
||||||
// Generic error
|
// Generic error
|
||||||
ErrGeneric ErrorCode = C.GIT_ERROR
|
ErrGeneric ErrorCode = C.GIT_ERROR
|
||||||
// Requested object could not be found
|
// Requested object could not be found
|
||||||
|
@ -62,10 +63,12 @@ const (
|
||||||
ErrAmbigious ErrorCode = C.GIT_EAMBIGUOUS
|
ErrAmbigious ErrorCode = C.GIT_EAMBIGUOUS
|
||||||
// Output buffer too short to hold data
|
// Output buffer too short to hold data
|
||||||
ErrBuffs ErrorCode = C.GIT_EBUFS
|
ErrBuffs ErrorCode = C.GIT_EBUFS
|
||||||
|
|
||||||
// GIT_EUSER is a special error that is never generated by libgit2
|
// GIT_EUSER is a special error that is never generated by libgit2
|
||||||
// code. You can return it from a callback (e.g to stop an iteration)
|
// code. You can return it from a callback (e.g to stop an iteration)
|
||||||
// to know that it was generated by the callback and not by libgit2.
|
// to know that it was generated by the callback and not by libgit2.
|
||||||
ErrUser ErrorCode = C.GIT_EUSER
|
ErrUser ErrorCode = C.GIT_EUSER
|
||||||
|
|
||||||
// Operation not allowed on bare repository
|
// Operation not allowed on bare repository
|
||||||
ErrBareRepo ErrorCode = C.GIT_EBAREREPO
|
ErrBareRepo ErrorCode = C.GIT_EBAREREPO
|
||||||
// HEAD refers to branch with no commits
|
// HEAD refers to branch with no commits
|
||||||
|
@ -82,12 +85,27 @@ const (
|
||||||
ErrLocked ErrorCode = C.GIT_ELOCKED
|
ErrLocked ErrorCode = C.GIT_ELOCKED
|
||||||
// Reference value does not match expected
|
// Reference value does not match expected
|
||||||
ErrModified ErrorCode = C.GIT_EMODIFIED
|
ErrModified ErrorCode = C.GIT_EMODIFIED
|
||||||
|
// Authentication failed
|
||||||
|
ErrAuth ErrorCode = C.GIT_EAUTH
|
||||||
|
// Server certificate is invalid
|
||||||
|
ErrCertificate ErrorCode = C.GIT_ECERTIFICATE
|
||||||
|
// Patch/merge has already been applied
|
||||||
|
ErrApplied ErrorCode = C.GIT_EAPPLIED
|
||||||
|
// The requested peel operation is not possible
|
||||||
|
ErrPeel ErrorCode = C.GIT_EPEEL
|
||||||
|
// Unexpected EOF
|
||||||
|
ErrEOF ErrorCode = C.GIT_EEOF
|
||||||
|
// Uncommitted changes in index prevented operation
|
||||||
|
ErrUncommitted ErrorCode = C.GIT_EUNCOMMITTED
|
||||||
|
// The operation is not valid for a directory
|
||||||
|
ErrDirectory ErrorCode = C.GIT_EDIRECTORY
|
||||||
|
// A merge conflict exists and cannot continue
|
||||||
|
ErrMergeConflict ErrorCode = C.GIT_EMERGECONFLICT
|
||||||
|
|
||||||
// Internal only
|
// Internal only
|
||||||
ErrPassthrough ErrorCode = C.GIT_PASSTHROUGH
|
ErrPassthrough ErrorCode = C.GIT_PASSTHROUGH
|
||||||
// Signals end of iteration with iterator
|
// Signals end of iteration with iterator
|
||||||
ErrIterOver ErrorCode = C.GIT_ITEROVER
|
ErrIterOver ErrorCode = C.GIT_ITEROVER
|
||||||
// Authentication failed
|
|
||||||
ErrAuth ErrorCode = C.GIT_EAUTH
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
58
handles.go
58
handles.go
|
@ -1,5 +1,9 @@
|
||||||
package git
|
package git
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdlib.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -9,77 +13,45 @@ import (
|
||||||
type HandleList struct {
|
type HandleList struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
// stores the Go pointers
|
// stores the Go pointers
|
||||||
handles []interface{}
|
handles map[unsafe.Pointer]interface{}
|
||||||
// Indicates which indices are in use, and keeps a pointer to slot int variable (the handle)
|
|
||||||
// in the Go world, so that the Go garbage collector does not free it.
|
|
||||||
set map[int]*int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandleList() *HandleList {
|
func NewHandleList() *HandleList {
|
||||||
return &HandleList{
|
return &HandleList{
|
||||||
handles: make([]interface{}, 5),
|
handles: make(map[unsafe.Pointer]interface{}),
|
||||||
set: make(map[int]*int),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// findUnusedSlot finds the smallest-index empty space in our
|
|
||||||
// list. You must only run this function while holding a write lock.
|
|
||||||
func (v *HandleList) findUnusedSlot() int {
|
|
||||||
for i := 1; i < len(v.handles); i++ {
|
|
||||||
_, isUsed := v.set[i]
|
|
||||||
if !isUsed {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reaching here means we've run out of entries so append and
|
|
||||||
// return the new index, which is equal to the old length.
|
|
||||||
slot := len(v.handles)
|
|
||||||
v.handles = append(v.handles, nil)
|
|
||||||
|
|
||||||
return slot
|
|
||||||
}
|
|
||||||
|
|
||||||
// Track adds the given pointer to the list of pointers to track and
|
// Track adds the given pointer to the list of pointers to track and
|
||||||
// returns a pointer value which can be passed to C as an opaque
|
// returns a pointer value which can be passed to C as an opaque
|
||||||
// pointer.
|
// pointer.
|
||||||
func (v *HandleList) Track(pointer interface{}) unsafe.Pointer {
|
func (v *HandleList) Track(pointer interface{}) unsafe.Pointer {
|
||||||
|
handle := C.malloc(1)
|
||||||
|
|
||||||
v.Lock()
|
v.Lock()
|
||||||
|
v.handles[handle] = pointer
|
||||||
slot := v.findUnusedSlot()
|
|
||||||
v.handles[slot] = pointer
|
|
||||||
v.set[slot] = &slot // Keep a pointer to slot in Go world, so it's not freed by GC.
|
|
||||||
|
|
||||||
v.Unlock()
|
v.Unlock()
|
||||||
|
|
||||||
return unsafe.Pointer(&slot)
|
return handle
|
||||||
}
|
}
|
||||||
|
|
||||||
// Untrack stops tracking the pointer given by the handle
|
// Untrack stops tracking the pointer given by the handle
|
||||||
func (v *HandleList) Untrack(handle unsafe.Pointer) {
|
func (v *HandleList) Untrack(handle unsafe.Pointer) {
|
||||||
slot := *(*int)(handle)
|
|
||||||
|
|
||||||
v.Lock()
|
v.Lock()
|
||||||
|
delete(v.handles, handle)
|
||||||
v.handles[slot] = nil
|
C.free(handle)
|
||||||
delete(v.set, slot)
|
|
||||||
|
|
||||||
v.Unlock()
|
v.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get retrieves the pointer from the given handle
|
// Get retrieves the pointer from the given handle
|
||||||
func (v *HandleList) Get(handle unsafe.Pointer) interface{} {
|
func (v *HandleList) Get(handle unsafe.Pointer) interface{} {
|
||||||
slot := *(*int)(handle)
|
|
||||||
|
|
||||||
v.RLock()
|
v.RLock()
|
||||||
|
defer v.RUnlock()
|
||||||
|
|
||||||
if _, ok := v.set[slot]; !ok {
|
ptr, ok := v.handles[handle]
|
||||||
|
if !ok {
|
||||||
panic(fmt.Sprintf("invalid pointer handle: %p", handle))
|
panic(fmt.Sprintf("invalid pointer handle: %p", handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr := v.handles[slot]
|
|
||||||
|
|
||||||
v.RUnlock()
|
|
||||||
|
|
||||||
return ptr
|
return ptr
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <git2.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *Repository) AddIgnoreRule(rules string) error {
|
||||||
|
crules := C.CString(rules)
|
||||||
|
defer C.free(unsafe.Pointer(crules))
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
ret := C.git_ignore_add_rule(v.ptr, crules)
|
||||||
|
if ret < 0 {
|
||||||
|
return MakeGitError(ret)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Repository) ClearInternalIgnoreRules() error {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
ret := C.git_ignore_clear_internal_rules(v.ptr)
|
||||||
|
if ret < 0 {
|
||||||
|
return MakeGitError(ret)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Repository) IsPathIgnored(path string) (bool, error) {
|
||||||
|
var ignored C.int
|
||||||
|
|
||||||
|
cpath := C.CString(path)
|
||||||
|
defer C.free(unsafe.Pointer(cpath))
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
ret := C.git_ignore_path_is_ignored(&ignored, v.ptr, cpath)
|
||||||
|
if ret < 0 {
|
||||||
|
return false, MakeGitError(ret)
|
||||||
|
}
|
||||||
|
return ignored == 1, nil
|
||||||
|
}
|
66
index.go
66
index.go
|
@ -26,6 +26,24 @@ const (
|
||||||
IndexAddCheckPathspec IndexAddOpts = C.GIT_INDEX_ADD_CHECK_PATHSPEC
|
IndexAddCheckPathspec IndexAddOpts = C.GIT_INDEX_ADD_CHECK_PATHSPEC
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type IndexStageOpts int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// IndexStageAny matches any index stage.
|
||||||
|
//
|
||||||
|
// Some index APIs take a stage to match; pass this value to match
|
||||||
|
// any entry matching the path regardless of stage.
|
||||||
|
IndexStageAny IndexStageOpts = C.GIT_INDEX_STAGE_ANY
|
||||||
|
// IndexStageNormal is a normal staged file in the index.
|
||||||
|
IndexStageNormal IndexStageOpts = C.GIT_INDEX_STAGE_NORMAL
|
||||||
|
// IndexStageAncestor is the ancestor side of a conflict.
|
||||||
|
IndexStageAncestor IndexStageOpts = C.GIT_INDEX_STAGE_ANCESTOR
|
||||||
|
// IndexStageOurs is the "ours" side of a conflict.
|
||||||
|
IndexStageOurs IndexStageOpts = C.GIT_INDEX_STAGE_OURS
|
||||||
|
// IndexStageTheirs is the "theirs" side of a conflict.
|
||||||
|
IndexStageTheirs IndexStageOpts = C.GIT_INDEX_STAGE_THEIRS
|
||||||
|
)
|
||||||
|
|
||||||
type Index struct {
|
type Index struct {
|
||||||
ptr *C.git_index
|
ptr *C.git_index
|
||||||
}
|
}
|
||||||
|
@ -97,7 +115,7 @@ func NewIndex() (*Index, error) {
|
||||||
return nil, MakeGitError(err)
|
return nil, MakeGitError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Index{ptr: ptr}, nil
|
return newIndexFromC(ptr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenIndex creates a new index at the given path. If the file does
|
// OpenIndex creates a new index at the given path. If the file does
|
||||||
|
@ -115,7 +133,7 @@ func OpenIndex(path string) (*Index, error) {
|
||||||
return nil, MakeGitError(err)
|
return nil, MakeGitError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Index{ptr: ptr}, nil
|
return newIndexFromC(ptr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path returns the index' path on disk or an empty string if it
|
// Path returns the index' path on disk or an empty string if it
|
||||||
|
@ -331,6 +349,50 @@ func (v *Index) EntryByIndex(index uint) (*IndexEntry, error) {
|
||||||
return newIndexEntryFromC(centry), nil
|
return newIndexEntryFromC(centry), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Index) EntryByPath(path string, stage int) (*IndexEntry, error) {
|
||||||
|
cpath := C.CString(path)
|
||||||
|
defer C.free(unsafe.Pointer(cpath))
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
centry := C.git_index_get_bypath(v.ptr, cpath, C.int(stage))
|
||||||
|
if centry == nil {
|
||||||
|
return nil, MakeGitError(C.GIT_ENOTFOUND)
|
||||||
|
}
|
||||||
|
return newIndexEntryFromC(centry), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Index) Find(path string) (uint, error) {
|
||||||
|
cpath := C.CString(path)
|
||||||
|
defer C.free(unsafe.Pointer(cpath))
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
var pos C.size_t
|
||||||
|
ret := C.git_index_find(&pos, v.ptr, cpath)
|
||||||
|
if ret < 0 {
|
||||||
|
return uint(0), MakeGitError(ret)
|
||||||
|
}
|
||||||
|
return uint(pos), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Index) FindPrefix(prefix string) (uint, error) {
|
||||||
|
cprefix := C.CString(prefix)
|
||||||
|
defer C.free(unsafe.Pointer(cprefix))
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
var pos C.size_t
|
||||||
|
ret := C.git_index_find_prefix(&pos, v.ptr, cprefix)
|
||||||
|
if ret < 0 {
|
||||||
|
return uint(0), MakeGitError(ret)
|
||||||
|
}
|
||||||
|
return uint(pos), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Index) HasConflicts() bool {
|
func (v *Index) HasConflicts() bool {
|
||||||
return C.git_index_has_conflicts(v.ptr) != 0
|
return C.git_index_has_conflicts(v.ptr) != 0
|
||||||
}
|
}
|
||||||
|
|
67
merge.go
67
merge.go
|
@ -6,6 +6,7 @@ package git
|
||||||
extern git_annotated_commit** _go_git_make_merge_head_array(size_t len);
|
extern git_annotated_commit** _go_git_make_merge_head_array(size_t len);
|
||||||
extern void _go_git_annotated_commit_array_set(git_annotated_commit** array, git_annotated_commit* ptr, size_t n);
|
extern void _go_git_annotated_commit_array_set(git_annotated_commit** array, git_annotated_commit* ptr, size_t n);
|
||||||
extern git_annotated_commit* _go_git_annotated_commit_array_get(git_annotated_commit** array, size_t n);
|
extern git_annotated_commit* _go_git_annotated_commit_array_get(git_annotated_commit** array, size_t n);
|
||||||
|
extern int _go_git_merge_file(git_merge_file_result*, char*, size_t, char*, unsigned int, char*, size_t, char*, unsigned int, char*, size_t, char*, unsigned int, git_merge_file_options*);
|
||||||
|
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
@ -81,7 +82,14 @@ func (r *Repository) AnnotatedCommitFromRef(ref *Reference) (*AnnotatedCommit, e
|
||||||
type MergeTreeFlag int
|
type MergeTreeFlag int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MergeTreeFindRenames MergeTreeFlag = C.GIT_MERGE_TREE_FIND_RENAMES
|
// Detect renames that occur between the common ancestor and the "ours"
|
||||||
|
// side or the common ancestor and the "theirs" side. This will enable
|
||||||
|
// the ability to merge between a modified and renamed file.
|
||||||
|
MergeTreeFindRenames MergeTreeFlag = C.GIT_MERGE_FIND_RENAMES
|
||||||
|
// If a conflict occurs, exit immediately instead of attempting to
|
||||||
|
// continue resolving conflicts. The merge operation will fail with
|
||||||
|
// GIT_EMERGECONFLICT and no index will be returned.
|
||||||
|
MergeTreeFailOnConflict MergeTreeFlag = C.GIT_MERGE_FAIL_ON_CONFLICT
|
||||||
)
|
)
|
||||||
|
|
||||||
type MergeOptions struct {
|
type MergeOptions struct {
|
||||||
|
@ -98,7 +106,7 @@ type MergeOptions struct {
|
||||||
func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions {
|
func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions {
|
||||||
return MergeOptions{
|
return MergeOptions{
|
||||||
Version: uint(opts.version),
|
Version: uint(opts.version),
|
||||||
TreeFlags: MergeTreeFlag(opts.tree_flags),
|
TreeFlags: MergeTreeFlag(opts.flags),
|
||||||
RenameThreshold: uint(opts.rename_threshold),
|
RenameThreshold: uint(opts.rename_threshold),
|
||||||
TargetLimit: uint(opts.target_limit),
|
TargetLimit: uint(opts.target_limit),
|
||||||
FileFavor: MergeFileFavor(opts.file_favor),
|
FileFavor: MergeFileFavor(opts.file_favor),
|
||||||
|
@ -124,7 +132,7 @@ func (mo *MergeOptions) toC() *C.git_merge_options {
|
||||||
}
|
}
|
||||||
return &C.git_merge_options{
|
return &C.git_merge_options{
|
||||||
version: C.uint(mo.Version),
|
version: C.uint(mo.Version),
|
||||||
tree_flags: C.git_merge_tree_flag_t(mo.TreeFlags),
|
flags: C.git_merge_flag_t(mo.TreeFlags),
|
||||||
rename_threshold: C.uint(mo.RenameThreshold),
|
rename_threshold: C.uint(mo.RenameThreshold),
|
||||||
target_limit: C.uint(mo.TargetLimit),
|
target_limit: C.uint(mo.TargetLimit),
|
||||||
file_favor: C.git_merge_file_favor_t(mo.FileFavor),
|
file_favor: C.git_merge_file_favor_t(mo.FileFavor),
|
||||||
|
@ -178,6 +186,9 @@ const (
|
||||||
MergePreferenceFastForwardOnly MergePreference = C.GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY
|
MergePreferenceFastForwardOnly MergePreference = C.GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MergeAnalysis returns the possible actions which could be taken by
|
||||||
|
// a 'git-merge' command. There may be multiple answers, so the first
|
||||||
|
// return value is a bitmask of MergeAnalysis values.
|
||||||
func (r *Repository) MergeAnalysis(theirHeads []*AnnotatedCommit) (MergeAnalysis, MergePreference, error) {
|
func (r *Repository) MergeAnalysis(theirHeads []*AnnotatedCommit) (MergeAnalysis, MergePreference, error) {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
@ -317,20 +328,6 @@ type MergeFileInput struct {
|
||||||
Contents []byte
|
Contents []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// populate a C struct with merge file input, make sure to use freeMergeFileInput to clean up allocs
|
|
||||||
func populateCMergeFileInput(c *C.git_merge_file_input, input MergeFileInput) {
|
|
||||||
c.path = C.CString(input.Path)
|
|
||||||
if input.Contents != nil {
|
|
||||||
c.ptr = (*C.char)(unsafe.Pointer(&input.Contents[0]))
|
|
||||||
c.size = C.size_t(len(input.Contents))
|
|
||||||
}
|
|
||||||
c.mode = C.uint(input.Mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func freeCMergeFileInput(c *C.git_merge_file_input) {
|
|
||||||
C.free(unsafe.Pointer(c.path))
|
|
||||||
}
|
|
||||||
|
|
||||||
type MergeFileFlags int
|
type MergeFileFlags int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -364,7 +361,7 @@ func populateCMergeFileOptions(c *C.git_merge_file_options, options MergeFileOpt
|
||||||
c.our_label = C.CString(options.OurLabel)
|
c.our_label = C.CString(options.OurLabel)
|
||||||
c.their_label = C.CString(options.TheirLabel)
|
c.their_label = C.CString(options.TheirLabel)
|
||||||
c.favor = C.git_merge_file_favor_t(options.Favor)
|
c.favor = C.git_merge_file_favor_t(options.Favor)
|
||||||
c.flags = C.uint(options.Flags)
|
c.flags = C.git_merge_file_flag_t(options.Flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func freeCMergeFileOptions(c *C.git_merge_file_options) {
|
func freeCMergeFileOptions(c *C.git_merge_file_options) {
|
||||||
|
@ -375,16 +372,26 @@ func freeCMergeFileOptions(c *C.git_merge_file_options) {
|
||||||
|
|
||||||
func MergeFile(ancestor MergeFileInput, ours MergeFileInput, theirs MergeFileInput, options *MergeFileOptions) (*MergeFileResult, error) {
|
func MergeFile(ancestor MergeFileInput, ours MergeFileInput, theirs MergeFileInput, options *MergeFileOptions) (*MergeFileResult, error) {
|
||||||
|
|
||||||
var cancestor C.git_merge_file_input
|
ancestorPath := C.CString(ancestor.Path)
|
||||||
var cours C.git_merge_file_input
|
defer C.free(unsafe.Pointer(ancestorPath))
|
||||||
var ctheirs C.git_merge_file_input
|
var ancestorContents *byte
|
||||||
|
if len(ancestor.Contents) > 0 {
|
||||||
|
ancestorContents = &ancestor.Contents[0]
|
||||||
|
}
|
||||||
|
|
||||||
populateCMergeFileInput(&cancestor, ancestor)
|
oursPath := C.CString(ours.Path)
|
||||||
defer freeCMergeFileInput(&cancestor)
|
defer C.free(unsafe.Pointer(oursPath))
|
||||||
populateCMergeFileInput(&cours, ours)
|
var oursContents *byte
|
||||||
defer freeCMergeFileInput(&cours)
|
if len(ours.Contents) > 0 {
|
||||||
populateCMergeFileInput(&ctheirs, theirs)
|
oursContents = &ours.Contents[0]
|
||||||
defer freeCMergeFileInput(&ctheirs)
|
}
|
||||||
|
|
||||||
|
theirsPath := C.CString(theirs.Path)
|
||||||
|
defer C.free(unsafe.Pointer(theirsPath))
|
||||||
|
var theirsContents *byte
|
||||||
|
if len(theirs.Contents) > 0 {
|
||||||
|
theirsContents = &theirs.Contents[0]
|
||||||
|
}
|
||||||
|
|
||||||
var copts *C.git_merge_file_options
|
var copts *C.git_merge_file_options
|
||||||
if options != nil {
|
if options != nil {
|
||||||
|
@ -401,7 +408,11 @@ func MergeFile(ancestor MergeFileInput, ours MergeFileInput, theirs MergeFileInp
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
var result C.git_merge_file_result
|
var result C.git_merge_file_result
|
||||||
ecode := C.git_merge_file(&result, &cancestor, &cours, &ctheirs, copts)
|
ecode := C._go_git_merge_file(&result,
|
||||||
|
(*C.char)(unsafe.Pointer(ancestorContents)), C.size_t(len(ancestor.Contents)), ancestorPath, C.uint(ancestor.Mode),
|
||||||
|
(*C.char)(unsafe.Pointer(oursContents)), C.size_t(len(ours.Contents)), oursPath, C.uint(ours.Mode),
|
||||||
|
(*C.char)(unsafe.Pointer(theirsContents)), C.size_t(len(theirs.Contents)), theirsPath, C.uint(theirs.Mode),
|
||||||
|
copts)
|
||||||
if ecode < 0 {
|
if ecode < 0 {
|
||||||
return nil, MakeGitError(ecode)
|
return nil, MakeGitError(ecode)
|
||||||
}
|
}
|
||||||
|
|
26
odb.go
26
odb.go
|
@ -8,7 +8,6 @@ extern void _go_git_odb_backend_free(git_odb_backend *backend);
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
@ -55,6 +54,21 @@ func (v *Odb) AddBackend(backend *OdbBackend, priority int) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Odb) ReadHeader(oid *Oid) (uint64, ObjectType, error) {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
var sz C.size_t
|
||||||
|
var cotype C.git_otype
|
||||||
|
|
||||||
|
ret := C.git_odb_read_header(&sz, &cotype, v.ptr, oid.toC())
|
||||||
|
if ret < 0 {
|
||||||
|
return 0, C.GIT_OBJ_BAD, MakeGitError(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint64(sz), ObjectType(cotype), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Odb) Exists(oid *Oid) bool {
|
func (v *Odb) Exists(oid *Oid) bool {
|
||||||
ret := C.git_odb_exists(v.ptr, oid.toC())
|
ret := C.git_odb_exists(v.ptr, oid.toC())
|
||||||
return ret != 0
|
return ret != 0
|
||||||
|
@ -62,12 +76,15 @@ func (v *Odb) Exists(oid *Oid) bool {
|
||||||
|
|
||||||
func (v *Odb) Write(data []byte, otype ObjectType) (oid *Oid, err error) {
|
func (v *Odb) Write(data []byte, otype ObjectType) (oid *Oid, err error) {
|
||||||
oid = new(Oid)
|
oid = new(Oid)
|
||||||
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&data))
|
var cptr unsafe.Pointer
|
||||||
|
if len(data) > 0 {
|
||||||
|
cptr = unsafe.Pointer(&data[0])
|
||||||
|
}
|
||||||
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
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))
|
ret := C.git_odb_write(oid.toC(), v.ptr, cptr, C.size_t(len(data)), C.git_otype(otype))
|
||||||
|
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return nil, MakeGitError(ret)
|
return nil, MakeGitError(ret)
|
||||||
|
@ -107,9 +124,7 @@ func odbForEachCb(id *C.git_oid, handle unsafe.Pointer) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
err := data.callback(newOidFromC(id))
|
err := data.callback(newOidFromC(id))
|
||||||
fmt.Println("err %v", err)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("returning EUSER")
|
|
||||||
data.err = err
|
data.err = err
|
||||||
return C.GIT_EUSER
|
return C.GIT_EUSER
|
||||||
}
|
}
|
||||||
|
@ -130,7 +145,6 @@ func (v *Odb) ForEach(callback OdbForEachCallback) error {
|
||||||
defer pointerHandles.Untrack(handle)
|
defer pointerHandles.Untrack(handle)
|
||||||
|
|
||||||
ret := C._go_git_odb_foreach(v.ptr, handle)
|
ret := C._go_git_odb_foreach(v.ptr, handle)
|
||||||
fmt.Println("ret %v", ret)
|
|
||||||
if ret == C.GIT_EUSER {
|
if ret == C.GIT_EUSER {
|
||||||
return data.err
|
return data.err
|
||||||
} else if ret < 0 {
|
} else if ret < 0 {
|
||||||
|
|
28
odb_test.go
28
odb_test.go
|
@ -6,6 +6,34 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestOdbReadHeader(t *testing.T) {
|
||||||
|
repo := createTestRepo(t)
|
||||||
|
defer cleanupTestRepo(t, repo)
|
||||||
|
|
||||||
|
_, _ = seedTestRepo(t, repo)
|
||||||
|
odb, err := repo.Odb()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Odb: %v", err)
|
||||||
|
}
|
||||||
|
data := []byte("hello")
|
||||||
|
id, err := odb.Write(data, ObjectBlob)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("odb.Write: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sz, typ, err := odb.ReadHeader(id)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ReadHeader: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sz != uint64(len(data)) {
|
||||||
|
t.Errorf("ReadHeader got size %d, want %d", sz, len(data))
|
||||||
|
}
|
||||||
|
if typ != ObjectBlob {
|
||||||
|
t.Errorf("ReadHeader got object type %s", typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestOdbStream(t *testing.T) {
|
func TestOdbStream(t *testing.T) {
|
||||||
repo := createTestRepo(t)
|
repo := createTestRepo(t)
|
||||||
defer cleanupTestRepo(t, repo)
|
defer cleanupTestRepo(t, repo)
|
||||||
|
|
56
remote.go
56
remote.go
|
@ -112,6 +112,9 @@ type FetchOptions struct {
|
||||||
//
|
//
|
||||||
// The default is to auto-follow tags.
|
// The default is to auto-follow tags.
|
||||||
DownloadTags DownloadTags
|
DownloadTags DownloadTags
|
||||||
|
|
||||||
|
// Headers are extra headers for the fetch operation.
|
||||||
|
Headers []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Remote struct {
|
type Remote struct {
|
||||||
|
@ -157,6 +160,9 @@ type PushOptions struct {
|
||||||
RemoteCallbacks RemoteCallbacks
|
RemoteCallbacks RemoteCallbacks
|
||||||
|
|
||||||
PbParallelism uint
|
PbParallelism uint
|
||||||
|
|
||||||
|
// Headers are extra headers for the push operation.
|
||||||
|
Headers []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type RemoteHead struct {
|
type RemoteHead struct {
|
||||||
|
@ -594,6 +600,10 @@ func populateFetchOptions(options *C.git_fetch_options, opts *FetchOptions) {
|
||||||
options.prune = C.git_fetch_prune_t(opts.Prune)
|
options.prune = C.git_fetch_prune_t(opts.Prune)
|
||||||
options.update_fetchhead = cbool(opts.UpdateFetchhead)
|
options.update_fetchhead = cbool(opts.UpdateFetchhead)
|
||||||
options.download_tags = C.git_remote_autotag_option_t(opts.DownloadTags)
|
options.download_tags = C.git_remote_autotag_option_t(opts.DownloadTags)
|
||||||
|
|
||||||
|
options.custom_headers = C.git_strarray{}
|
||||||
|
options.custom_headers.count = C.size_t(len(opts.Headers))
|
||||||
|
options.custom_headers.strings = makeCStringsFromStrings(opts.Headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func populatePushOptions(options *C.git_push_options, opts *PushOptions) {
|
func populatePushOptions(options *C.git_push_options, opts *PushOptions) {
|
||||||
|
@ -604,6 +614,10 @@ func populatePushOptions(options *C.git_push_options, opts *PushOptions) {
|
||||||
|
|
||||||
options.pb_parallelism = C.uint(opts.PbParallelism)
|
options.pb_parallelism = C.uint(opts.PbParallelism)
|
||||||
|
|
||||||
|
options.custom_headers = C.git_strarray{}
|
||||||
|
options.custom_headers.count = C.size_t(len(opts.Headers))
|
||||||
|
options.custom_headers.strings = makeCStringsFromStrings(opts.Headers)
|
||||||
|
|
||||||
populateRemoteCallbacks(&options.callbacks, &opts.RemoteCallbacks)
|
populateRemoteCallbacks(&options.callbacks, &opts.RemoteCallbacks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,36 +637,51 @@ func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error
|
||||||
crefspecs.strings = makeCStringsFromStrings(refspecs)
|
crefspecs.strings = makeCStringsFromStrings(refspecs)
|
||||||
defer freeStrarray(&crefspecs)
|
defer freeStrarray(&crefspecs)
|
||||||
|
|
||||||
var coptions C.git_fetch_options
|
coptions := (*C.git_fetch_options)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_fetch_options{}))))
|
||||||
populateFetchOptions(&coptions, opts)
|
defer C.free(unsafe.Pointer(coptions))
|
||||||
|
|
||||||
|
populateFetchOptions(coptions, opts)
|
||||||
defer untrackCalbacksPayload(&coptions.callbacks)
|
defer untrackCalbacksPayload(&coptions.callbacks)
|
||||||
|
defer freeStrarray(&coptions.custom_headers)
|
||||||
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
ret := C.git_remote_fetch(o.ptr, &crefspecs, &coptions, cmsg)
|
ret := C.git_remote_fetch(o.ptr, &crefspecs, coptions, cmsg)
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return MakeGitError(ret)
|
return MakeGitError(ret)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Remote) ConnectFetch(callbacks *RemoteCallbacks) error {
|
func (o *Remote) ConnectFetch(callbacks *RemoteCallbacks, headers []string) error {
|
||||||
return o.Connect(ConnectDirectionFetch, callbacks)
|
return o.Connect(ConnectDirectionFetch, callbacks, headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Remote) ConnectPush(callbacks *RemoteCallbacks) error {
|
func (o *Remote) ConnectPush(callbacks *RemoteCallbacks, headers []string) error {
|
||||||
return o.Connect(ConnectDirectionPush, callbacks)
|
return o.Connect(ConnectDirectionPush, callbacks, headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks) error {
|
// Connect opens a connection to a remote.
|
||||||
|
//
|
||||||
|
// The transport is selected based on the URL. The direction argument
|
||||||
|
// is due to a limitation of the git protocol (over TCP or SSH) which
|
||||||
|
// starts up a specific binary which can only do the one or the other.
|
||||||
|
//
|
||||||
|
// 'headers' are extra HTTP headers to use in this connection.
|
||||||
|
func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks, headers []string) error {
|
||||||
var ccallbacks C.git_remote_callbacks
|
var ccallbacks C.git_remote_callbacks
|
||||||
populateRemoteCallbacks(&ccallbacks, callbacks)
|
populateRemoteCallbacks(&ccallbacks, callbacks)
|
||||||
|
|
||||||
|
cheaders := C.git_strarray{}
|
||||||
|
cheaders.count = C.size_t(len(headers))
|
||||||
|
cheaders.strings = makeCStringsFromStrings(headers)
|
||||||
|
defer freeStrarray(&cheaders)
|
||||||
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
if ret := C.git_remote_connect(o.ptr, C.git_direction(direction), &ccallbacks); ret != 0 {
|
if ret := C.git_remote_connect(o.ptr, C.git_direction(direction), &ccallbacks, &cheaders); ret != 0 {
|
||||||
return MakeGitError(ret)
|
return MakeGitError(ret)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -710,14 +739,17 @@ func (o *Remote) Push(refspecs []string, opts *PushOptions) error {
|
||||||
crefspecs.strings = makeCStringsFromStrings(refspecs)
|
crefspecs.strings = makeCStringsFromStrings(refspecs)
|
||||||
defer freeStrarray(&crefspecs)
|
defer freeStrarray(&crefspecs)
|
||||||
|
|
||||||
var coptions C.git_push_options
|
coptions := (*C.git_push_options)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_push_options{}))))
|
||||||
populatePushOptions(&coptions, opts)
|
defer C.free(unsafe.Pointer(coptions))
|
||||||
|
|
||||||
|
populatePushOptions(coptions, opts)
|
||||||
defer untrackCalbacksPayload(&coptions.callbacks)
|
defer untrackCalbacksPayload(&coptions.callbacks)
|
||||||
|
defer freeStrarray(&coptions.custom_headers)
|
||||||
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
ret := C.git_remote_push(o.ptr, &crefspecs, &coptions)
|
ret := C.git_remote_push(o.ptr, &crefspecs, coptions)
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return MakeGitError(ret)
|
return MakeGitError(ret)
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ func TestRemoteConnect(t *testing.T) {
|
||||||
remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository")
|
remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository")
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
|
|
||||||
err = remote.ConnectFetch(nil)
|
err = remote.ConnectFetch(nil, nil)
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ func TestRemoteLs(t *testing.T) {
|
||||||
remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository")
|
remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository")
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
|
|
||||||
err = remote.ConnectFetch(nil)
|
err = remote.ConnectFetch(nil, nil)
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
|
|
||||||
heads, err := remote.Ls()
|
heads, err := remote.Ls()
|
||||||
|
@ -87,7 +87,7 @@ func TestRemoteLsFiltering(t *testing.T) {
|
||||||
remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository")
|
remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository")
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
|
|
||||||
err = remote.ConnectFetch(nil)
|
err = remote.ConnectFetch(nil, nil)
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
|
|
||||||
heads, err := remote.Ls("master")
|
heads, err := remote.Ls("master")
|
||||||
|
@ -166,7 +166,7 @@ func TestRemotePrune(t *testing.T) {
|
||||||
rr, err := repo.Remotes.Lookup("origin")
|
rr, err := repo.Remotes.Lookup("origin")
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
|
|
||||||
err = rr.ConnectFetch(nil)
|
err = rr.ConnectFetch(nil, nil)
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
|
|
||||||
err = rr.Prune(nil)
|
err = rr.Prune(nil)
|
||||||
|
|
|
@ -66,15 +66,29 @@ func OpenRepository(path string) (*Repository, error) {
|
||||||
return newRepositoryFromC(ptr), nil
|
return newRepositoryFromC(ptr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func OpenRepositoryExtended(path string) (*Repository, error) {
|
type RepositoryOpenFlag int
|
||||||
|
|
||||||
|
const (
|
||||||
|
RepositoryOpenNoSearch RepositoryOpenFlag = C.GIT_REPOSITORY_OPEN_NO_SEARCH
|
||||||
|
RepositoryOpenCrossFs RepositoryOpenFlag = C.GIT_REPOSITORY_OPEN_CROSS_FS
|
||||||
|
RepositoryOpenBare RepositoryOpenFlag = C.GIT_REPOSITORY_OPEN_BARE
|
||||||
|
)
|
||||||
|
|
||||||
|
func OpenRepositoryExtended(path string, flags RepositoryOpenFlag, ceiling string) (*Repository, error) {
|
||||||
cpath := C.CString(path)
|
cpath := C.CString(path)
|
||||||
defer C.free(unsafe.Pointer(cpath))
|
defer C.free(unsafe.Pointer(cpath))
|
||||||
|
|
||||||
|
var cceiling *C.char = nil
|
||||||
|
if len(ceiling) > 0 {
|
||||||
|
cceiling = C.CString(ceiling)
|
||||||
|
defer C.free(unsafe.Pointer(cceiling))
|
||||||
|
}
|
||||||
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
var ptr *C.git_repository
|
var ptr *C.git_repository
|
||||||
ret := C.git_repository_open_ext(&ptr, cpath, 0, nil)
|
ret := C.git_repository_open_ext(&ptr, cpath, C.uint(flags), cceiling)
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return nil, MakeGitError(ret)
|
return nil, MakeGitError(ret)
|
||||||
}
|
}
|
||||||
|
@ -437,3 +451,24 @@ func (r *Repository) StateCleanup() error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (r *Repository) AddGitIgnoreRules(rules string) error {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
crules := C.CString(rules)
|
||||||
|
defer C.free(unsafe.Pointer(crules))
|
||||||
|
if ret := C.git_ignore_add_rule(r.ptr, crules); ret < 0 {
|
||||||
|
return MakeGitError(ret)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Repository) ClearGitIgnoreRules() error {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
if ret := C.git_ignore_clear_internal_rules(r.ptr); ret < 0 {
|
||||||
|
return MakeGitError(ret)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
2
tree.go
2
tree.go
|
@ -149,7 +149,7 @@ func (v *TreeBuilder) Free() {
|
||||||
C.git_treebuilder_free(v.ptr)
|
C.git_treebuilder_free(v.ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TreeBuilder) Insert(filename string, id *Oid, filemode int) error {
|
func (v *TreeBuilder) Insert(filename string, id *Oid, filemode Filemode) error {
|
||||||
cfilename := C.CString(filename)
|
cfilename := C.CString(filename)
|
||||||
defer C.free(unsafe.Pointer(cfilename))
|
defer C.free(unsafe.Pointer(cfilename))
|
||||||
|
|
||||||
|
|
41
tree_test.go
41
tree_test.go
|
@ -20,3 +20,44 @@ func TestTreeEntryById(t *testing.T) {
|
||||||
t.Fatalf("entry id %v was not found", id)
|
t.Fatalf("entry id %v was not found", id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTreeBuilderInsert(t *testing.T) {
|
||||||
|
repo := createTestRepo(t)
|
||||||
|
defer cleanupTestRepo(t, repo)
|
||||||
|
|
||||||
|
subTree, err := repo.TreeBuilder()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("TreeBuilder: %v", err)
|
||||||
|
}
|
||||||
|
defer subTree.Free()
|
||||||
|
|
||||||
|
odb, err := repo.Odb()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("repo.Odb: %v", err)
|
||||||
|
}
|
||||||
|
blobId, err := odb.Write([]byte("hello"), ObjectBlob)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("odb.Write: %v", err)
|
||||||
|
}
|
||||||
|
if err = subTree.Insert("subfile", blobId, FilemodeBlobExecutable); err != nil {
|
||||||
|
t.Fatalf("TreeBuilder.Insert: %v", err)
|
||||||
|
}
|
||||||
|
treeID, err := subTree.Write()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("TreeBuilder.Write: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tree, err := repo.LookupTree(treeID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("LookupTree: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry, err := tree.EntryByPath("subfile")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("tree.EntryByPath(%q): %v", "subfile", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !entry.Id.Equal(blobId) {
|
||||||
|
t.Fatalf("got oid %v, want %v", entry.Id, blobId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit ed38e26db5435b519d8b796e4b6c2c660fe982b5
|
Subproject commit f596946f09f3c1e51239a24ff41e27f2c1ffa2b7
|
4
walk.go
4
walk.go
|
@ -194,6 +194,10 @@ func (v *RevWalk) Iterate(fun RevWalkIterator) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *RevWalk) SimplifyFirstParent() {
|
||||||
|
C.git_revwalk_simplify_first_parent(v.ptr)
|
||||||
|
}
|
||||||
|
|
||||||
func (v *RevWalk) Sorting(sm SortType) {
|
func (v *RevWalk) Sorting(sm SortType) {
|
||||||
C.git_revwalk_sorting(v.ptr, C.uint(sm))
|
C.git_revwalk_sorting(v.ptr, C.uint(sm))
|
||||||
}
|
}
|
||||||
|
|
23
wrapper.c
23
wrapper.c
|
@ -141,6 +141,29 @@ int _go_git_tag_foreach(git_repository *repo, void *payload)
|
||||||
return git_tag_foreach(repo, (git_tag_foreach_cb)&gitTagForeachCb, payload);
|
return git_tag_foreach(repo, (git_tag_foreach_cb)&gitTagForeachCb, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int _go_git_merge_file(git_merge_file_result* out, char* ancestorContents, size_t ancestorLen, char* ancestorPath, unsigned int ancestorMode, char* oursContents, size_t oursLen, char* oursPath, unsigned int oursMode, char* theirsContents, size_t theirsLen, char* theirsPath, unsigned int theirsMode, git_merge_file_options* copts) {
|
||||||
|
git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT;
|
||||||
|
git_merge_file_input ours = GIT_MERGE_FILE_INPUT_INIT;
|
||||||
|
git_merge_file_input theirs = GIT_MERGE_FILE_INPUT_INIT;
|
||||||
|
|
||||||
|
ancestor.ptr = ancestorContents;
|
||||||
|
ancestor.size = ancestorLen;
|
||||||
|
ancestor.path = ancestorPath;
|
||||||
|
ancestor.mode = ancestorMode;
|
||||||
|
|
||||||
|
ours.ptr = oursContents;
|
||||||
|
ours.size = oursLen;
|
||||||
|
ours.path = oursPath;
|
||||||
|
ours.mode = oursMode;
|
||||||
|
|
||||||
|
theirs.ptr = theirsContents;
|
||||||
|
theirs.size = theirsLen;
|
||||||
|
theirs.path = theirsPath;
|
||||||
|
theirs.mode = theirsMode;
|
||||||
|
|
||||||
|
return git_merge_file(out, &ancestor, &ours, &theirs, copts);
|
||||||
|
}
|
||||||
|
|
||||||
void _go_git_setup_stash_apply_progress_callbacks(git_stash_apply_options *opts) {
|
void _go_git_setup_stash_apply_progress_callbacks(git_stash_apply_options *opts) {
|
||||||
opts->progress_cb = (git_stash_apply_progress_cb)stashApplyProgressCb;
|
opts->progress_cb = (git_stash_apply_progress_cb)stashApplyProgressCb;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue