Merge remote-tracking branch 'upstream/master' into next
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..8eb5872
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "vendor/libgit2"]
+ path = vendor/libgit2
+ url = https://github.com/libgit2/libgit2
diff --git a/Makefile b/Makefile
index 9c42283..3040857 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,11 @@
default: test
-test:
- go run script/check-MakeGitError-thread-lock.go
- go test ./...
+build-libgit2:
+ ./script/build-libgit2-static.sh
-install:
- go install ./...
+test: build-libgit2
+ go run script/check-MakeGitError-thread-lock.go
+ ./script/with-static.sh go test ./...
+
+install: build-libgit2
+ ./script/with-static.sh go install ./...
diff --git a/blob.go b/blob.go
index 382bb9e..1a86e60 100644
--- a/blob.go
+++ b/blob.go
@@ -18,7 +18,7 @@
)
type Blob struct {
- gitObject
+ Object
cast_ptr *C.git_blob
}
diff --git a/commit.go b/commit.go
index 52f7c01..6830da3 100644
--- a/commit.go
+++ b/commit.go
@@ -14,7 +14,7 @@
// Commit
type Commit struct {
- gitObject
+ Object
cast_ptr *C.git_commit
}
@@ -37,7 +37,7 @@
return nil, MakeGitError(err)
}
- return allocObject((*C.git_object)(ptr), c.repo).(*Tree), nil
+ return allocTree(ptr, c.repo), nil
}
func (c Commit) TreeId() *Oid {
@@ -61,7 +61,7 @@
return nil
}
- return allocObject((*C.git_object)(cobj), c.repo).(*Commit)
+ return allocCommit(cobj, c.repo)
}
func (c *Commit) ParentId(n uint) *Oid {
diff --git a/config.go b/config.go
index c4c4028..7408fbc 100644
--- a/config.go
+++ b/config.go
@@ -12,6 +12,9 @@
type ConfigLevel int
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
ConfigLevelSystem ConfigLevel = C.GIT_CONFIG_LEVEL_SYSTEM
@@ -412,3 +415,21 @@
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
+}
diff --git a/describe.go b/describe.go
index c6f9a79..d75dbcb 100644
--- a/describe.go
+++ b/describe.go
@@ -127,7 +127,7 @@
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- ecode := C.git_describe_commit(&resultPtr, c.gitObject.ptr, cDescribeOpts)
+ ecode := C.git_describe_commit(&resultPtr, c.ptr, cDescribeOpts)
if ecode < 0 {
return nil, MakeGitError(ecode)
}
diff --git a/git.go b/git.go
index 7c7f99c..2b2a909 100644
--- a/git.go
+++ b/git.go
@@ -3,7 +3,6 @@
/*
#include <git2.h>
#include <git2/sys/openssl.h>
-#cgo pkg-config: libgit2
*/
import "C"
import (
@@ -53,6 +52,7 @@
// No error
ErrOk ErrorCode = C.GIT_OK
+
// Generic error
ErrGeneric ErrorCode = C.GIT_ERROR
// Requested object could not be found
@@ -63,10 +63,12 @@
ErrAmbigious ErrorCode = C.GIT_EAMBIGUOUS
// Output buffer too short to hold data
ErrBuffs ErrorCode = C.GIT_EBUFS
+
// 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)
// to know that it was generated by the callback and not by libgit2.
ErrUser ErrorCode = C.GIT_EUSER
+
// Operation not allowed on bare repository
ErrBareRepo ErrorCode = C.GIT_EBAREREPO
// HEAD refers to branch with no commits
@@ -83,12 +85,27 @@
ErrLocked ErrorCode = C.GIT_ELOCKED
// Reference value does not match expected
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
ErrPassthrough ErrorCode = C.GIT_PASSTHROUGH
// Signals end of iteration with iterator
ErrIterOver ErrorCode = C.GIT_ITEROVER
- // Authentication failed
- ErrAuth ErrorCode = C.GIT_EAUTH
)
var (
diff --git a/ignore.go b/ignore.go
new file mode 100644
index 0000000..6b12348
--- /dev/null
+++ b/ignore.go
@@ -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
+}
diff --git a/index.go b/index.go
index 1eb5e9d..ae94864 100644
--- a/index.go
+++ b/index.go
@@ -26,6 +26,24 @@
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 {
ptr *C.git_index
}
@@ -51,8 +69,8 @@
return nil
}
return &IndexEntry{
- IndexTime { int32(entry.ctime.seconds), uint32(entry.ctime.nanoseconds) },
- IndexTime { int32(entry.mtime.seconds), uint32(entry.mtime.nanoseconds) },
+ IndexTime{int32(entry.ctime.seconds), uint32(entry.ctime.nanoseconds)},
+ IndexTime{int32(entry.mtime.seconds), uint32(entry.mtime.nanoseconds)},
Filemode(entry.mode),
uint32(entry.uid),
uint32(entry.gid),
@@ -280,7 +298,7 @@
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- ret := C.git_index_read_tree(v.ptr, tree.cast_ptr);
+ ret := C.git_index_read_tree(v.ptr, tree.cast_ptr)
if ret < 0 {
return MakeGitError(ret)
}
@@ -331,6 +349,50 @@
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 {
return C.git_index_has_conflicts(v.ptr) != 0
}
diff --git a/index_test.go b/index_test.go
index 7c65f4f..5f6b375 100644
--- a/index_test.go
+++ b/index_test.go
@@ -32,10 +32,11 @@
ref, err := repo.Head()
checkFatal(t, err)
- obj, err := ref.Peel(ObjectTree);
+ obj, err := ref.Peel(ObjectTree)
checkFatal(t, err)
- tree := obj.(*Tree)
+ tree, err := obj.AsTree()
+ checkFatal(t, err)
idx, err := NewIndex()
checkFatal(t, err)
diff --git a/merge.go b/merge.go
index 272bf6a..0b0a8f1 100644
--- a/merge.go
+++ b/merge.go
@@ -81,12 +81,19 @@
type MergeTreeFlag int
const (
+ // 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_TREE_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_TREE_FAIL_ON_CONFLICT
)
type MergeOptions struct {
- Version uint
- TreeFlags MergeTreeFlag
+ Version uint
+ TreeFlags MergeTreeFlag
RenameThreshold uint
TargetLimit uint
@@ -98,7 +105,7 @@
func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions {
return MergeOptions{
Version: uint(opts.version),
- TreeFlags: MergeTreeFlag(opts.tree_flags),
+ TreeFlags: MergeTreeFlag(opts.tree_flags),
RenameThreshold: uint(opts.rename_threshold),
TargetLimit: uint(opts.target_limit),
FileFavor: MergeFileFavor(opts.file_favor),
@@ -262,10 +269,10 @@
}
oids := make([]*Oid, coids.count)
- hdr := reflect.SliceHeader {
+ hdr := reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(coids.ids)),
- Len: int(coids.count),
- Cap: int(coids.count),
+ Len: int(coids.count),
+ Cap: int(coids.count),
}
goSlice := *(*[]C.git_oid)(unsafe.Pointer(&hdr))
diff --git a/merge_test.go b/merge_test.go
index ad01319..8059727 100644
--- a/merge_test.go
+++ b/merge_test.go
@@ -115,7 +115,10 @@
parent, err := ref.Peel(ObjectCommit)
checkFatal(t, err)
- commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree, parent.(*Commit))
+ parentCommit, err := parent.AsCommit()
+ checkFatal(t, err)
+
+ commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree, parentCommit)
checkFatal(t, err)
return commitId, treeId
diff --git a/object.go b/object.go
index 6ecebf8..1981980 100644
--- a/object.go
+++ b/object.go
@@ -4,7 +4,11 @@
#include <git2.h>
*/
import "C"
-import "runtime"
+import (
+ "errors"
+ "fmt"
+ "runtime"
+)
type ObjectType int
@@ -17,15 +21,7 @@
ObjectTag ObjectType = C.GIT_OBJ_TAG
)
-type Object interface {
- Free()
- Id() *Oid
- Type() ObjectType
- Owner() *Repository
- Peel(t ObjectType) (Object, error)
-}
-
-type gitObject struct {
+type Object struct {
ptr *C.git_object
repo *Repository
}
@@ -49,23 +45,128 @@
return ""
}
-func (o gitObject) Id() *Oid {
+func (o *Object) Id() *Oid {
return newOidFromC(C.git_object_id(o.ptr))
}
-func (o gitObject) Type() ObjectType {
+func (o *Object) Type() ObjectType {
return ObjectType(C.git_object_type(o.ptr))
}
// Owner returns a weak reference to the repository which owns this
-// object
-func (o gitObject) Owner() *Repository {
+// object. This won't keep the underlying repository alive.
+func (o *Object) Owner() *Repository {
return &Repository{
ptr: C.git_object_owner(o.ptr),
}
}
-func (o *gitObject) Free() {
+func dupObject(obj *Object, kind ObjectType) (*C.git_object, error) {
+ if obj.Type() != kind {
+ return nil, errors.New(fmt.Sprintf("object is not a %v", kind))
+ }
+
+ var cobj *C.git_object
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if err := C.git_object_dup(&cobj, obj.ptr); err < 0 {
+ return nil, MakeGitError(err)
+ }
+
+ return cobj, nil
+}
+
+func allocTree(ptr *C.git_tree, repo *Repository) *Tree {
+ tree := &Tree{
+ Object: Object{
+ ptr: (*C.git_object)(ptr),
+ repo: repo,
+ },
+ cast_ptr: ptr,
+ }
+ runtime.SetFinalizer(tree, (*Tree).Free)
+
+ return tree
+}
+
+func (o *Object) AsTree() (*Tree, error) {
+ cobj, err := dupObject(o, ObjectTree)
+ if err != nil {
+ return nil, err
+ }
+
+ return allocTree((*C.git_tree)(cobj), o.repo), nil
+}
+
+func allocCommit(ptr *C.git_commit, repo *Repository) *Commit {
+ commit := &Commit{
+ Object: Object{
+ ptr: (*C.git_object)(ptr),
+ repo: repo,
+ },
+ cast_ptr: ptr,
+ }
+ runtime.SetFinalizer(commit, (*Commit).Free)
+
+ return commit
+}
+
+func (o *Object) AsCommit() (*Commit, error) {
+ cobj, err := dupObject(o, ObjectCommit)
+ if err != nil {
+ return nil, err
+ }
+
+ return allocCommit((*C.git_commit)(cobj), o.repo), nil
+}
+
+func allocBlob(ptr *C.git_blob, repo *Repository) *Blob {
+ blob := &Blob{
+ Object: Object{
+ ptr: (*C.git_object)(ptr),
+ repo: repo,
+ },
+ cast_ptr: ptr,
+ }
+ runtime.SetFinalizer(blob, (*Blob).Free)
+
+ return blob
+}
+
+func (o *Object) AsBlob() (*Blob, error) {
+ cobj, err := dupObject(o, ObjectBlob)
+ if err != nil {
+ return nil, err
+ }
+
+ return allocBlob((*C.git_blob)(cobj), o.repo), nil
+}
+
+func allocTag(ptr *C.git_tag, repo *Repository) *Tag {
+ tag := &Tag{
+ Object: Object{
+ ptr: (*C.git_object)(ptr),
+ repo: repo,
+ },
+ cast_ptr: ptr,
+ }
+ runtime.SetFinalizer(tag, (*Tag).Free)
+
+ return tag
+}
+
+func (o *Object) AsTag() (*Tag, error) {
+ cobj, err := dupObject(o, ObjectTag)
+ if err != nil {
+ return nil, err
+ }
+
+ return allocTag((*C.git_tag)(cobj), o.repo), nil
+}
+
+func (o *Object) Free() {
runtime.SetFinalizer(o, nil)
C.git_object_free(o.ptr)
}
@@ -82,7 +183,7 @@
//
// If peeling a tag we discover an object which cannot be peeled to the target
// type due to the object model, an error will be returned.
-func (o *gitObject) Peel(t ObjectType) (Object, error) {
+func (o *Object) Peel(t ObjectType) (*Object, error) {
var cobj *C.git_object
runtime.LockOSThread()
@@ -95,44 +196,12 @@
return allocObject(cobj, o.repo), nil
}
-func allocObject(cobj *C.git_object, repo *Repository) Object {
- obj := gitObject{
+func allocObject(cobj *C.git_object, repo *Repository) *Object {
+ obj := &Object{
ptr: cobj,
repo: repo,
}
+ runtime.SetFinalizer(obj, (*Object).Free)
- switch ObjectType(C.git_object_type(cobj)) {
- case ObjectCommit:
- commit := &Commit{
- gitObject: obj,
- cast_ptr: (*C.git_commit)(cobj),
- }
- runtime.SetFinalizer(commit, (*Commit).Free)
- return commit
-
- case ObjectTree:
- tree := &Tree{
- gitObject: obj,
- cast_ptr: (*C.git_tree)(cobj),
- }
- runtime.SetFinalizer(tree, (*Tree).Free)
- return tree
-
- case ObjectBlob:
- blob := &Blob{
- gitObject: obj,
- cast_ptr: (*C.git_blob)(cobj),
- }
- runtime.SetFinalizer(blob, (*Blob).Free)
- return blob
- case ObjectTag:
- tag := &Tag{
- gitObject: obj,
- cast_ptr: (*C.git_tag)(cobj),
- }
- runtime.SetFinalizer(tag, (*Tag).Free)
- return tag
- }
-
- return nil
+ return obj
}
diff --git a/object_test.go b/object_test.go
index ef6c5a1..2ae2a6a 100644
--- a/object_test.go
+++ b/object_test.go
@@ -10,12 +10,12 @@
commitId, treeId := seedTestRepo(t, repo)
- var obj Object
+ var obj *Object
commit, err := repo.LookupCommit(commitId)
checkFatal(t, err)
- obj = commit
+ obj = &commit.Object
if obj.Type() != ObjectCommit {
t.Fatalf("Wrong object type, expected commit, have %v", obj.Type())
}
@@ -27,13 +27,13 @@
tree, err := repo.LookupTree(treeId)
checkFatal(t, err)
- obj = tree
+ obj = &tree.Object
if obj.Type() != ObjectTree {
t.Fatalf("Wrong object type, expected tree, have %v", obj.Type())
}
- tree2, ok := obj.(*Tree)
- if !ok {
+ tree2, err := obj.AsTree()
+ if err != nil {
t.Fatalf("Converting back to *Tree is not ok")
}
@@ -46,16 +46,16 @@
t.Fatal("Wrong filemode for \"README\"")
}
- _, ok = obj.(*Commit)
- if ok {
+ _, err = obj.AsCommit()
+ if err == nil {
t.Fatalf("*Tree is somehow the same as *Commit")
}
obj, err = repo.Lookup(tree.Id())
checkFatal(t, err)
- _, ok = obj.(*Tree)
- if !ok {
+ _, err = obj.AsTree()
+ if err != nil {
t.Fatalf("Lookup creates the wrong type")
}
@@ -99,8 +99,8 @@
tree, err := repo.LookupTree(treeId)
checkFatal(t, err)
- checkOwner(t, repo, commit)
- checkOwner(t, repo, tree)
+ checkOwner(t, repo, commit.Object)
+ checkOwner(t, repo, tree.Object)
}
func TestObjectPeel(t *testing.T) {
@@ -109,7 +109,7 @@
commitID, treeID := seedTestRepo(t, repo)
- var obj Object
+ var obj *Object
commit, err := repo.LookupCommit(commitID)
checkFatal(t, err)
diff --git a/reference.go b/reference.go
index 140082f..463f2fc 100644
--- a/reference.go
+++ b/reference.go
@@ -263,7 +263,7 @@
return nil
}
-func (v *Reference) Peel(t ObjectType) (Object, error) {
+func (v *Reference) Peel(t ObjectType) (*Object, error) {
var cobj *C.git_object
runtime.LockOSThread()
diff --git a/remote.go b/remote.go
index 330f202..8a57280 100644
--- a/remote.go
+++ b/remote.go
@@ -72,12 +72,12 @@
type FetchPrune uint
const (
- // Use the setting from the configuration
+ // Use the setting from the configuration
FetchPruneUnspecified FetchPrune = C.GIT_FETCH_PRUNE_UNSPECIFIED
// Force pruning on
- FetchPruneOn FetchPrune = C.GIT_FETCH_PRUNE
+ FetchPruneOn FetchPrune = C.GIT_FETCH_PRUNE
// Force pruning off
- FetchNoPrune FetchPrune = C.GIT_FETCH_NO_PRUNE
+ FetchNoPrune FetchPrune = C.GIT_FETCH_NO_PRUNE
)
type DownloadTags uint
@@ -88,20 +88,20 @@
DownloadTagsUnspecified DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED
// Ask the server for tags pointing to objects we're already
// downloading.
- DownloadTagsAuto DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_AUTO
+ DownloadTagsAuto DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_AUTO
// Don't ask for any tags beyond the refspecs.
- DownloadTagsNone DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_NONE
+ DownloadTagsNone DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_NONE
// Ask for the all the tags.
- DownloadTagsAll DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_ALL
+ DownloadTagsAll DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_ALL
)
type FetchOptions struct {
// Callbacks to use for this fetch operation
RemoteCallbacks RemoteCallbacks
// Whether to perform a prune after the fetch
- Prune FetchPrune
+ Prune FetchPrune
// Whether to write the results to FETCH_HEAD. Defaults to
// on. Leave this default in order to behave like git.
UpdateFetchhead bool
@@ -111,7 +111,10 @@
// downloading all of them.
//
// The default is to auto-follow tags.
- DownloadTags DownloadTags
+ DownloadTags DownloadTags
+
+ // Headers are extra headers for the fetch operation.
+ Headers []string
}
type Remote struct {
@@ -157,6 +160,9 @@
RemoteCallbacks RemoteCallbacks
PbParallelism uint
+
+ // Headers are extra headers for the push operation.
+ Headers []string
}
type RemoteHead struct {
@@ -588,12 +594,16 @@
func populateFetchOptions(options *C.git_fetch_options, opts *FetchOptions) {
C.git_fetch_init_options(options, C.GIT_FETCH_OPTIONS_VERSION)
if opts == nil {
- return;
+ return
}
populateRemoteCallbacks(&options.callbacks, &opts.RemoteCallbacks)
options.prune = C.git_fetch_prune_t(opts.Prune)
options.update_fetchhead = cbool(opts.UpdateFetchhead)
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) {
@@ -604,6 +614,10 @@
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)
}
@@ -611,7 +625,7 @@
// to use for this fetch, use an empty list to use the refspecs from
// the configuration; msg specifies what to use for the reflog
// entries. Leave "" to use defaults.
-func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error {
+func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error {
var cmsg *C.char = nil
if msg != "" {
cmsg = C.CString(msg)
@@ -628,6 +642,7 @@
populateFetchOptions(coptions, opts)
defer untrackCalbacksPayload(&coptions.callbacks)
+ defer freeStrarray(&coptions.custom_headers)
runtime.LockOSThread()
defer runtime.UnlockOSThread()
@@ -639,22 +654,34 @@
return nil
}
-func (o *Remote) ConnectFetch(callbacks *RemoteCallbacks) error {
- return o.Connect(ConnectDirectionFetch, callbacks)
+func (o *Remote) ConnectFetch(callbacks *RemoteCallbacks, headers []string) error {
+ return o.Connect(ConnectDirectionFetch, callbacks, headers)
}
-func (o *Remote) ConnectPush(callbacks *RemoteCallbacks) error {
- return o.Connect(ConnectDirectionPush, callbacks)
+func (o *Remote) ConnectPush(callbacks *RemoteCallbacks, headers []string) error {
+ return o.Connect(ConnectDirectionPush, callbacks, headers)
}
-func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks) error {
- var ccallbacks C.git_remote_callbacks;
+// 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
populateRemoteCallbacks(&ccallbacks, callbacks)
+ cheaders := C.git_strarray{}
+ cheaders.count = C.size_t(len(headers))
+ cheaders.strings = makeCStringsFromStrings(headers)
+ defer freeStrarray(&cheaders)
+
runtime.LockOSThread()
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 nil
@@ -717,6 +744,7 @@
populatePushOptions(coptions, opts)
defer untrackCalbacksPayload(&coptions.callbacks)
+ defer freeStrarray(&coptions.custom_headers)
runtime.LockOSThread()
defer runtime.UnlockOSThread()
@@ -733,7 +761,7 @@
}
func (o *Remote) Prune(callbacks *RemoteCallbacks) error {
- var ccallbacks C.git_remote_callbacks;
+ var ccallbacks C.git_remote_callbacks
populateRemoteCallbacks(&ccallbacks, callbacks)
runtime.LockOSThread()
diff --git a/remote_test.go b/remote_test.go
index 73c637f..978b803 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -39,7 +39,7 @@
remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository")
checkFatal(t, err)
- options := FetchOptions {
+ options := FetchOptions{
RemoteCallbacks: RemoteCallbacks{
CertificateCheckCallback: func(cert *Certificate, valid bool, hostname string) ErrorCode {
return assertHostname(cert, valid, hostname, t)
@@ -58,7 +58,7 @@
remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository")
checkFatal(t, err)
- err = remote.ConnectFetch(nil)
+ err = remote.ConnectFetch(nil, nil)
checkFatal(t, err)
}
@@ -69,7 +69,7 @@
remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository")
checkFatal(t, err)
- err = remote.ConnectFetch(nil)
+ err = remote.ConnectFetch(nil, nil)
checkFatal(t, err)
heads, err := remote.Ls()
@@ -87,7 +87,7 @@
remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository")
checkFatal(t, err)
- err = remote.ConnectFetch(nil)
+ err = remote.ConnectFetch(nil, nil)
checkFatal(t, err)
heads, err := remote.Ls("master")
@@ -166,7 +166,7 @@
rr, err := repo.Remotes.Lookup("origin")
checkFatal(t, err)
- err = rr.ConnectFetch(nil)
+ err = rr.ConnectFetch(nil, nil)
checkFatal(t, err)
err = rr.Prune(nil)
diff --git a/repository.go b/repository.go
index 12638e1..77e9f9c 100644
--- a/repository.go
+++ b/repository.go
@@ -12,11 +12,11 @@
// Repository
type Repository struct {
- ptr *C.git_repository
+ ptr *C.git_repository
// Remotes represents the collection of remotes and can be
// used to add, remove and configure remotes for this
// repository.
- Remotes RemoteCollection
+ Remotes RemoteCollection
// Submodules represents the collection of submodules and can
// be used to add, remove and configure submodules in this
// repostiory.
@@ -26,7 +26,7 @@
References ReferenceCollection
// Notes represents the collection of notes and can be used to
// read, write and delete notes from this repository.
- Notes NoteCollection
+ Notes NoteCollection
// Tags represents the collection of tags and can be used to create,
// list and iterate tags in this repository.
Tags TagsCollection
@@ -35,10 +35,10 @@
func newRepositoryFromC(ptr *C.git_repository) *Repository {
repo := &Repository{ptr: ptr}
- repo.Remotes.repo = repo
+ repo.Remotes.repo = repo
repo.Submodules.repo = repo
repo.References.repo = repo
- repo.Notes.repo = repo
+ repo.Notes.repo = repo
repo.Tags.repo = repo
runtime.SetFinalizer(repo, (*Repository).Free)
@@ -62,15 +62,29 @@
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)
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()
defer runtime.UnlockOSThread()
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 {
return nil, MakeGitError(ret)
}
@@ -145,7 +159,7 @@
return newIndexFromC(ptr), nil
}
-func (v *Repository) lookupType(id *Oid, t ObjectType) (Object, error) {
+func (v *Repository) lookupType(id *Oid, t ObjectType) (*Object, error) {
var ptr *C.git_object
runtime.LockOSThread()
@@ -159,7 +173,7 @@
return allocObject(ptr, v), nil
}
-func (v *Repository) Lookup(id *Oid) (Object, error) {
+func (v *Repository) Lookup(id *Oid) (*Object, error) {
return v.lookupType(id, ObjectAny)
}
@@ -169,7 +183,7 @@
return nil, err
}
- return obj.(*Tree), nil
+ return obj.AsTree()
}
func (v *Repository) LookupCommit(id *Oid) (*Commit, error) {
@@ -178,7 +192,7 @@
return nil, err
}
- return obj.(*Commit), nil
+ return obj.AsCommit()
}
func (v *Repository) LookupBlob(id *Oid) (*Blob, error) {
@@ -187,7 +201,7 @@
return nil, err
}
- return obj.(*Blob), nil
+ return obj.AsBlob()
}
func (v *Repository) LookupTag(id *Oid) (*Tag, error) {
@@ -196,7 +210,7 @@
return nil, err
}
- return obj.(*Tag), nil
+ return obj.AsTag()
}
func (v *Repository) Head() (*Reference, error) {
diff --git a/reset.go b/reset.go
index b5b7435..9da7625 100644
--- a/reset.go
+++ b/reset.go
@@ -17,7 +17,7 @@
func (r *Repository) ResetToCommit(commit *Commit, resetType ResetType, opts *CheckoutOpts) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- ret := C.git_reset(r.ptr, commit.gitObject.ptr, C.git_reset_t(resetType), opts.toC())
+ ret := C.git_reset(r.ptr, commit.ptr, C.git_reset_t(resetType), opts.toC())
if ret < 0 {
return MakeGitError(ret)
diff --git a/revparse.go b/revparse.go
index 7eb04f1..950932b 100644
--- a/revparse.go
+++ b/revparse.go
@@ -20,16 +20,16 @@
)
type Revspec struct {
- to Object
- from Object
+ to *Object
+ from *Object
flags RevparseFlag
}
-func (rs *Revspec) To() Object {
+func (rs *Revspec) To() *Object {
return rs.to
}
-func (rs *Revspec) From() Object {
+func (rs *Revspec) From() *Object {
return rs.from
}
@@ -38,8 +38,8 @@
}
func newRevspecFromC(ptr *C.git_revspec, repo *Repository) *Revspec {
- var to Object
- var from Object
+ var to *Object
+ var from *Object
if ptr.to != nil {
to = allocObject(ptr.to, repo)
@@ -73,7 +73,7 @@
return newRevspecFromC(&crevspec, r), nil
}
-func (v *Repository) RevparseSingle(spec string) (Object, error) {
+func (v *Repository) RevparseSingle(spec string) (*Object, error) {
cspec := C.CString(spec)
defer C.free(unsafe.Pointer(cspec))
@@ -90,7 +90,7 @@
return allocObject(ptr, v), nil
}
-func (r *Repository) RevparseExt(spec string) (Object, *Reference, error) {
+func (r *Repository) RevparseExt(spec string) (*Object, *Reference, error) {
cspec := C.CString(spec)
defer C.free(unsafe.Pointer(cspec))
diff --git a/revparse_test.go b/revparse_test.go
index 091a76b..75e9ffd 100644
--- a/revparse_test.go
+++ b/revparse_test.go
@@ -46,7 +46,7 @@
}
}
-func checkObject(t *testing.T, obj Object, id *Oid) {
+func checkObject(t *testing.T, obj *Object, id *Oid) {
if obj == nil {
t.Fatalf("bad object")
}
diff --git a/tag.go b/tag.go
index ca85156..8957430 100644
--- a/tag.go
+++ b/tag.go
@@ -13,7 +13,7 @@
// Tag
type Tag struct {
- gitObject
+ Object
cast_ptr *C.git_tag
}
@@ -30,7 +30,7 @@
return newSignatureFromC(cast_ptr)
}
-func (t Tag) Target() Object {
+func (t Tag) Target() *Object {
var ptr *C.git_object
ret := C.git_tag_target(&ptr, t.cast_ptr)
@@ -70,7 +70,7 @@
}
defer C.git_signature_free(taggerSig)
- ctarget := commit.gitObject.ptr
+ ctarget := commit.ptr
runtime.LockOSThread()
defer runtime.UnlockOSThread()
@@ -102,7 +102,7 @@
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
- ctarget := commit.gitObject.ptr
+ ctarget := commit.ptr
runtime.LockOSThread()
defer runtime.UnlockOSThread()
diff --git a/tree.go b/tree.go
index f543c11..8288176 100644
--- a/tree.go
+++ b/tree.go
@@ -23,7 +23,7 @@
)
type Tree struct {
- gitObject
+ Object
cast_ptr *C.git_tree
}
diff --git a/vendor/libgit2 b/vendor/libgit2
new file mode 160000
index 0000000..821131f
--- /dev/null
+++ b/vendor/libgit2
@@ -0,0 +1 @@
+Subproject commit 821131fdaee74526d84aaf1c6ceddc2139c551df