Merge branch 'next'

This commit is contained in:
Carlos Martín Nieto 2016-03-07 11:17:10 +01:00
commit 2ae7d13ba1
24 changed files with 460 additions and 145 deletions

View File

@ -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
} }

View File

@ -18,7 +18,7 @@ import (
) )
type Blob struct { type Blob struct {
gitObject Object
cast_ptr *C.git_blob cast_ptr *C.git_blob
} }

View File

@ -14,7 +14,7 @@ import (
// Commit // Commit
type Commit struct { type Commit struct {
gitObject Object
cast_ptr *C.git_commit cast_ptr *C.git_commit
} }
@ -37,7 +37,7 @@ func (c Commit) Tree() (*Tree, error) {
return nil, MakeGitError(err) 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 { func (c Commit) TreeId() *Oid {
@ -61,7 +61,7 @@ func (c *Commit) Parent(n uint) *Commit {
return nil return nil
} }
return allocObject((*C.git_object)(cobj), c.repo).(*Commit) return allocCommit(cobj, c.repo)
} }
func (c *Commit) ParentId(n uint) *Oid { func (c *Commit) ParentId(n uint) *Oid {

View File

@ -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
@ -412,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
}

View File

@ -127,7 +127,7 @@ func (c *Commit) Describe(opts *DescribeOptions) (*DescribeResult, error) {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() 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 { if ecode < 0 {
return nil, MakeGitError(ecode) return nil, MakeGitError(ecode)
} }

View File

@ -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
View File

@ -53,6 +53,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
@ -63,10 +64,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
@ -83,12 +86,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 (

51
ignore.go Normal file
View File

@ -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
}

View File

@ -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
} }
@ -280,7 +298,7 @@ func (v *Index) ReadTree(tree *Tree) error {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() 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 { if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)
} }
@ -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
} }

View File

@ -32,10 +32,11 @@ func TestIndexReadTree(t *testing.T) {
ref, err := repo.Head() ref, err := repo.Head()
checkFatal(t, err) checkFatal(t, err)
obj, err := ref.Peel(ObjectTree); obj, err := ref.Peel(ObjectTree)
checkFatal(t, err) checkFatal(t, err)
tree := obj.(*Tree) tree, err := obj.AsTree()
checkFatal(t, err)
idx, err := NewIndex() idx, err := NewIndex()
checkFatal(t, err) checkFatal(t, err)

View File

@ -82,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 {
@ -99,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),
@ -125,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),
@ -354,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) {

View File

@ -115,7 +115,10 @@ func appendCommit(t *testing.T, repo *Repository) (*Oid, *Oid) {
parent, err := ref.Peel(ObjectCommit) parent, err := ref.Peel(ObjectCommit)
checkFatal(t, err) 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) checkFatal(t, err)
return commitId, treeId return commitId, treeId

173
object.go
View File

@ -4,7 +4,11 @@ package git
#include <git2.h> #include <git2.h>
*/ */
import "C" import "C"
import "runtime" import (
"errors"
"fmt"
"runtime"
)
type ObjectType int type ObjectType int
@ -17,15 +21,7 @@ const (
ObjectTag ObjectType = C.GIT_OBJ_TAG ObjectTag ObjectType = C.GIT_OBJ_TAG
) )
type Object interface { type Object struct {
Free()
Id() *Oid
Type() ObjectType
Owner() *Repository
Peel(t ObjectType) (Object, error)
}
type gitObject struct {
ptr *C.git_object ptr *C.git_object
repo *Repository repo *Repository
} }
@ -49,23 +45,128 @@ func (t ObjectType) String() string {
return "" return ""
} }
func (o gitObject) Id() *Oid { func (o *Object) Id() *Oid {
return newOidFromC(C.git_object_id(o.ptr)) 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)) return ObjectType(C.git_object_type(o.ptr))
} }
// Owner returns a weak reference to the repository which owns this // Owner returns a weak reference to the repository which owns this
// object // object. This won't keep the underlying repository alive.
func (o gitObject) Owner() *Repository { func (o *Object) Owner() *Repository {
return &Repository{ return &Repository{
ptr: C.git_object_owner(o.ptr), 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) runtime.SetFinalizer(o, nil)
C.git_object_free(o.ptr) C.git_object_free(o.ptr)
} }
@ -82,7 +183,7 @@ func (o *gitObject) Free() {
// //
// If peeling a tag we discover an object which cannot be peeled to the target // 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. // 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 var cobj *C.git_object
runtime.LockOSThread() runtime.LockOSThread()
@ -95,44 +196,12 @@ func (o *gitObject) Peel(t ObjectType) (Object, error) {
return allocObject(cobj, o.repo), nil return allocObject(cobj, o.repo), nil
} }
func allocObject(cobj *C.git_object, repo *Repository) Object { func allocObject(cobj *C.git_object, repo *Repository) *Object {
obj := gitObject{ obj := &Object{
ptr: cobj, ptr: cobj,
repo: repo, repo: repo,
} }
runtime.SetFinalizer(obj, (*Object).Free)
switch ObjectType(C.git_object_type(cobj)) { return obj
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
} }

View File

@ -10,12 +10,12 @@ func TestObjectPoymorphism(t *testing.T) {
commitId, treeId := seedTestRepo(t, repo) commitId, treeId := seedTestRepo(t, repo)
var obj Object var obj *Object
commit, err := repo.LookupCommit(commitId) commit, err := repo.LookupCommit(commitId)
checkFatal(t, err) checkFatal(t, err)
obj = commit obj = &commit.Object
if obj.Type() != ObjectCommit { if obj.Type() != ObjectCommit {
t.Fatalf("Wrong object type, expected commit, have %v", obj.Type()) t.Fatalf("Wrong object type, expected commit, have %v", obj.Type())
} }
@ -27,13 +27,13 @@ func TestObjectPoymorphism(t *testing.T) {
tree, err := repo.LookupTree(treeId) tree, err := repo.LookupTree(treeId)
checkFatal(t, err) checkFatal(t, err)
obj = tree obj = &tree.Object
if obj.Type() != ObjectTree { if obj.Type() != ObjectTree {
t.Fatalf("Wrong object type, expected tree, have %v", obj.Type()) t.Fatalf("Wrong object type, expected tree, have %v", obj.Type())
} }
tree2, ok := obj.(*Tree) tree2, err := obj.AsTree()
if !ok { if err != nil {
t.Fatalf("Converting back to *Tree is not ok") t.Fatalf("Converting back to *Tree is not ok")
} }
@ -46,16 +46,16 @@ func TestObjectPoymorphism(t *testing.T) {
t.Fatal("Wrong filemode for \"README\"") t.Fatal("Wrong filemode for \"README\"")
} }
_, ok = obj.(*Commit) _, err = obj.AsCommit()
if ok { if err == nil {
t.Fatalf("*Tree is somehow the same as *Commit") t.Fatalf("*Tree is somehow the same as *Commit")
} }
obj, err = repo.Lookup(tree.Id()) obj, err = repo.Lookup(tree.Id())
checkFatal(t, err) checkFatal(t, err)
_, ok = obj.(*Tree) _, err = obj.AsTree()
if !ok { if err != nil {
t.Fatalf("Lookup creates the wrong type") t.Fatalf("Lookup creates the wrong type")
} }
@ -99,8 +99,8 @@ func TestObjectOwner(t *testing.T) {
tree, err := repo.LookupTree(treeId) tree, err := repo.LookupTree(treeId)
checkFatal(t, err) checkFatal(t, err)
checkOwner(t, repo, commit) checkOwner(t, repo, commit.Object)
checkOwner(t, repo, tree) checkOwner(t, repo, tree.Object)
} }
func TestObjectPeel(t *testing.T) { func TestObjectPeel(t *testing.T) {
@ -109,7 +109,7 @@ func TestObjectPeel(t *testing.T) {
commitID, treeID := seedTestRepo(t, repo) commitID, treeID := seedTestRepo(t, repo)
var obj Object var obj *Object
commit, err := repo.LookupCommit(commitID) commit, err := repo.LookupCommit(commitID)
checkFatal(t, err) checkFatal(t, err)

View File

@ -263,7 +263,7 @@ func (v *Reference) Delete() error {
return nil return nil
} }
func (v *Reference) Peel(t ObjectType) (Object, error) { func (v *Reference) Peel(t ObjectType) (*Object, error) {
var cobj *C.git_object var cobj *C.git_object
runtime.LockOSThread() runtime.LockOSThread()

View File

@ -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 {
@ -588,12 +594,16 @@ func (o *Remote) RefspecCount() uint {
func populateFetchOptions(options *C.git_fetch_options, opts *FetchOptions) { func populateFetchOptions(options *C.git_fetch_options, opts *FetchOptions) {
C.git_fetch_init_options(options, C.GIT_FETCH_OPTIONS_VERSION) C.git_fetch_init_options(options, C.GIT_FETCH_OPTIONS_VERSION)
if opts == nil { if opts == nil {
return; return
} }
populateRemoteCallbacks(&options.callbacks, &opts.RemoteCallbacks) populateRemoteCallbacks(&options.callbacks, &opts.RemoteCallbacks)
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)
} }
@ -628,6 +642,7 @@ func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error
populateFetchOptions(coptions, opts) 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()
@ -639,22 +654,34 @@ func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error
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.
var ccallbacks C.git_remote_callbacks; //
// 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) 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
@ -717,6 +744,7 @@ func (o *Remote) Push(refspecs []string, opts *PushOptions) error {
populatePushOptions(coptions, opts) 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()
@ -733,7 +761,7 @@ func (o *Remote) PruneRefs() bool {
} }
func (o *Remote) Prune(callbacks *RemoteCallbacks) error { func (o *Remote) Prune(callbacks *RemoteCallbacks) error {
var ccallbacks C.git_remote_callbacks; var ccallbacks C.git_remote_callbacks
populateRemoteCallbacks(&ccallbacks, callbacks) populateRemoteCallbacks(&ccallbacks, callbacks)
runtime.LockOSThread() runtime.LockOSThread()

View File

@ -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)

View File

@ -62,15 +62,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)
} }
@ -145,7 +159,7 @@ func (v *Repository) Index() (*Index, error) {
return newIndexFromC(ptr), nil 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 var ptr *C.git_object
runtime.LockOSThread() runtime.LockOSThread()
@ -159,7 +173,7 @@ func (v *Repository) lookupType(id *Oid, t ObjectType) (Object, error) {
return allocObject(ptr, v), nil 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) return v.lookupType(id, ObjectAny)
} }
@ -169,7 +183,7 @@ func (v *Repository) LookupTree(id *Oid) (*Tree, error) {
return nil, err return nil, err
} }
return obj.(*Tree), nil return obj.AsTree()
} }
func (v *Repository) LookupCommit(id *Oid) (*Commit, error) { func (v *Repository) LookupCommit(id *Oid) (*Commit, error) {
@ -178,7 +192,7 @@ func (v *Repository) LookupCommit(id *Oid) (*Commit, error) {
return nil, err return nil, err
} }
return obj.(*Commit), nil return obj.AsCommit()
} }
func (v *Repository) LookupBlob(id *Oid) (*Blob, error) { func (v *Repository) LookupBlob(id *Oid) (*Blob, error) {
@ -187,7 +201,7 @@ func (v *Repository) LookupBlob(id *Oid) (*Blob, error) {
return nil, err return nil, err
} }
return obj.(*Blob), nil return obj.AsBlob()
} }
func (v *Repository) LookupTag(id *Oid) (*Tag, error) { func (v *Repository) LookupTag(id *Oid) (*Tag, error) {
@ -196,7 +210,7 @@ func (v *Repository) LookupTag(id *Oid) (*Tag, error) {
return nil, err return nil, err
} }
return obj.(*Tag), nil return obj.AsTag()
} }
func (v *Repository) Head() (*Reference, error) { func (v *Repository) Head() (*Reference, error) {

View File

@ -17,7 +17,7 @@ const (
func (r *Repository) ResetToCommit(commit *Commit, resetType ResetType, opts *CheckoutOpts) error { func (r *Repository) ResetToCommit(commit *Commit, resetType ResetType, opts *CheckoutOpts) error {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() 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 { if ret < 0 {
return MakeGitError(ret) return MakeGitError(ret)

View File

@ -20,16 +20,16 @@ const (
) )
type Revspec struct { type Revspec struct {
to Object to *Object
from Object from *Object
flags RevparseFlag flags RevparseFlag
} }
func (rs *Revspec) To() Object { func (rs *Revspec) To() *Object {
return rs.to return rs.to
} }
func (rs *Revspec) From() Object { func (rs *Revspec) From() *Object {
return rs.from return rs.from
} }
@ -38,8 +38,8 @@ func (rs *Revspec) Flags() RevparseFlag {
} }
func newRevspecFromC(ptr *C.git_revspec, repo *Repository) *Revspec { func newRevspecFromC(ptr *C.git_revspec, repo *Repository) *Revspec {
var to Object var to *Object
var from Object var from *Object
if ptr.to != nil { if ptr.to != nil {
to = allocObject(ptr.to, repo) to = allocObject(ptr.to, repo)
@ -73,7 +73,7 @@ func (r *Repository) Revparse(spec string) (*Revspec, error) {
return newRevspecFromC(&crevspec, r), nil 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) cspec := C.CString(spec)
defer C.free(unsafe.Pointer(cspec)) defer C.free(unsafe.Pointer(cspec))
@ -90,7 +90,7 @@ func (v *Repository) RevparseSingle(spec string) (Object, error) {
return allocObject(ptr, v), nil 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) cspec := C.CString(spec)
defer C.free(unsafe.Pointer(cspec)) defer C.free(unsafe.Pointer(cspec))

View File

@ -46,7 +46,7 @@ func TestRevparseExt(t *testing.T) {
} }
} }
func checkObject(t *testing.T, obj Object, id *Oid) { func checkObject(t *testing.T, obj *Object, id *Oid) {
if obj == nil { if obj == nil {
t.Fatalf("bad object") t.Fatalf("bad object")
} }

8
tag.go
View File

@ -13,7 +13,7 @@ import (
// Tag // Tag
type Tag struct { type Tag struct {
gitObject Object
cast_ptr *C.git_tag cast_ptr *C.git_tag
} }
@ -30,7 +30,7 @@ func (t Tag) Tagger() *Signature {
return newSignatureFromC(cast_ptr) return newSignatureFromC(cast_ptr)
} }
func (t Tag) Target() Object { func (t Tag) Target() *Object {
var ptr *C.git_object var ptr *C.git_object
ret := C.git_tag_target(&ptr, t.cast_ptr) ret := C.git_tag_target(&ptr, t.cast_ptr)
@ -70,7 +70,7 @@ func (c *TagsCollection) Create(
} }
defer C.git_signature_free(taggerSig) defer C.git_signature_free(taggerSig)
ctarget := commit.gitObject.ptr ctarget := commit.ptr
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
@ -102,7 +102,7 @@ func (c *TagsCollection) CreateLightweight(name string, commit *Commit, force bo
cname := C.CString(name) cname := C.CString(name)
defer C.free(unsafe.Pointer(cname)) defer C.free(unsafe.Pointer(cname))
ctarget := commit.gitObject.ptr ctarget := commit.ptr
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()

View File

@ -23,7 +23,7 @@ const (
) )
type Tree struct { type Tree struct {
gitObject Object
cast_ptr *C.git_tree cast_ptr *C.git_tree
} }
@ -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))

View File

@ -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)
}
}