diff --git a/Makefile b/Makefile index 3040857..4ecc8a4 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,6 @@ build-libgit2: ./script/build-libgit2-static.sh test: build-libgit2 - go run script/check-MakeGitError-thread-lock.go ./script/with-static.sh go test ./... install: build-libgit2 diff --git a/README.md b/README.md index faeb5f5..d10c46e 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,14 @@ git2go ====== -[![GoDoc](https://godoc.org/github.com/libgit2/git2go?status.svg)](http://godoc.org/github.com/libgit2/git2go) [![Build Status](https://travis-ci.org/libgit2/git2go.svg?branch=master)](https://travis-ci.org/libgit2/git2go) +[![GoDoc](https://godoc.org/github.com/libgit2/git2go?status.svg)](http://godoc.org/github.com/libgit2/git2go) -Go bindings for [libgit2](http://libgit2.github.com/). The master branch follows the latest libgit2 release. The versioned branches indicate which libgit2 version they work against. +Go bindings for [libgit2](http://libgit2.github.com/). The master branch follows the latest libgit2 release. Installing ---------- -This project needs libgit2, which is written in C so we need to build that as well. In order to build libgit2, you need `cmake`, `pkg-config` and a C compiler. You will also need the development packages for OpenSSL and LibSSH2 installed if you want libgit2 to support HTTPS and SSH respectively. - -### Stable version - -git2go has versioned branches which indicate which version of libgit2 they work against. Install the development package it on your system via your favourite package manager or from source and you can use a service like gopkg.in to use the appropriate version. For the libgit2 v0.22 case, you can use - - import "gopkg.in/libgit2/git2go.v22" - -to use a version of git2go which will work against libgit2 v0.22 and dynamically link to the library. - -### From master - -The master branch follows libgit2's master branch, which means there is no stable API or ABI to link against. git2go can statically link against a vendored version of libgit2. +This project needs libgit2, which is written in C so we need to build that as well. In order to build libgit2, you need `cmake`, `pkg-config` and a C compiler. You will also need the development packages for OpenSSL and LibSSH2 if you want to use HTTPS and SSH respectively. Run `go get -d github.com/libgit2/git2go` to download the code and go to your `$GOPATH/src/github.com/libgit2/git2go` dir. From there, we need to build the C code and put it into the resulting go binary. @@ -29,15 +17,10 @@ Run `go get -d github.com/libgit2/git2go` to download the code and go to your `$ will compile libgit2 and run `go install` such that it's statically linked to the git2go package. -Paralellism and network operations ----------------------------------- - -libgit2 uses OpenSSL and LibSSH2 for performing encrypted network connections. For now, git2go asks libgit2 to set locking for OpenSSL. This makes HTTPS connections thread-safe, but it is fragile and will likely stop doing it soon. This may also make SSH connections thread-safe if your copy of libssh2 is linked against OpenSSL. Check libgit2's `THREADSAFE.md` for more information. - Running the tests ----------------- -For the stable version, `go test` will work as usual. For the master branch, similarly to installing, running the tests requires linking against the local libgit2 library, so the Makefile provides a wrapper +Similarly to installing, running the tests requires linking against the local libgit2 library, so the Makefile provides a wrapper make test diff --git a/blame.go b/blame.go deleted file mode 100644 index c24c934..0000000 --- a/blame.go +++ /dev/null @@ -1,157 +0,0 @@ -package git - -/* -#include -*/ -import "C" -import ( - "runtime" - "unsafe" -) - -type BlameOptions struct { - Flags BlameOptionsFlag - MinMatchCharacters uint16 - NewestCommit *Oid - OldestCommit *Oid - MinLine uint32 - MaxLine uint32 -} - -func DefaultBlameOptions() (BlameOptions, error) { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - opts := C.git_blame_options{} - ecode := C.git_blame_init_options(&opts, C.GIT_BLAME_OPTIONS_VERSION) - if ecode < 0 { - return BlameOptions{}, MakeGitError(ecode) - } - - return BlameOptions{ - Flags: BlameOptionsFlag(opts.flags), - MinMatchCharacters: uint16(opts.min_match_characters), - NewestCommit: newOidFromC(&opts.newest_commit), - OldestCommit: newOidFromC(&opts.oldest_commit), - MinLine: uint32(opts.min_line), - MaxLine: uint32(opts.max_line), - }, nil -} - -type BlameOptionsFlag uint32 - -const ( - BlameNormal BlameOptionsFlag = C.GIT_BLAME_NORMAL - BlameTrackCopiesSameFile BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_FILE - BlameTrackCopiesSameCommitMoves BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES - BlameTrackCopiesSameCommitCopies BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES - BlameTrackCopiesAnyCommitCopies BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES - BlameFirstParent BlameOptionsFlag = C.GIT_BLAME_FIRST_PARENT -) - -func (v *Repository) BlameFile(path string, opts *BlameOptions) (*Blame, error) { - var blamePtr *C.git_blame - - var copts *C.git_blame_options - if opts != nil { - copts = &C.git_blame_options{ - version: C.GIT_BLAME_OPTIONS_VERSION, - flags: C.uint32_t(opts.Flags), - min_match_characters: C.uint16_t(opts.MinMatchCharacters), - min_line: C.uint32_t(opts.MinLine), - max_line: C.uint32_t(opts.MaxLine), - } - if opts.NewestCommit != nil { - copts.newest_commit = *opts.NewestCommit.toC() - } - if opts.OldestCommit != nil { - copts.oldest_commit = *opts.OldestCommit.toC() - } - } - - cpath := C.CString(path) - defer C.free(unsafe.Pointer(cpath)) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ecode := C.git_blame_file(&blamePtr, v.ptr, cpath, copts) - if ecode < 0 { - return nil, MakeGitError(ecode) - } - - return newBlameFromC(blamePtr), nil -} - -type Blame struct { - ptr *C.git_blame -} - -func (blame *Blame) HunkCount() int { - return int(C.git_blame_get_hunk_count(blame.ptr)) -} - -func (blame *Blame) HunkByIndex(index int) (BlameHunk, error) { - ptr := C.git_blame_get_hunk_byindex(blame.ptr, C.uint32_t(index)) - if ptr == nil { - return BlameHunk{}, ErrInvalid - } - return blameHunkFromC(ptr), nil -} - -func (blame *Blame) HunkByLine(lineno int) (BlameHunk, error) { - ptr := C.git_blame_get_hunk_byline(blame.ptr, C.uint32_t(lineno)) - if ptr == nil { - return BlameHunk{}, ErrInvalid - } - return blameHunkFromC(ptr), nil -} - -func newBlameFromC(ptr *C.git_blame) *Blame { - if ptr == nil { - return nil - } - - blame := &Blame{ - ptr: ptr, - } - - runtime.SetFinalizer(blame, (*Blame).Free) - return blame -} - -func (blame *Blame) Free() error { - if blame.ptr == nil { - return ErrInvalid - } - runtime.SetFinalizer(blame, nil) - C.git_blame_free(blame.ptr) - blame.ptr = nil - return nil -} - -type BlameHunk struct { - LinesInHunk uint16 - FinalCommitId *Oid - FinalStartLineNumber uint16 - FinalSignature *Signature - OrigCommitId *Oid - OrigPath string - OrigStartLineNumber uint16 - OrigSignature *Signature - Boundary bool -} - -func blameHunkFromC(hunk *C.git_blame_hunk) BlameHunk { - return BlameHunk{ - LinesInHunk: uint16(hunk.lines_in_hunk), - FinalCommitId: newOidFromC(&hunk.final_commit_id), - FinalStartLineNumber: uint16(hunk.final_start_line_number), - FinalSignature: newSignatureFromC(hunk.final_signature), - OrigCommitId: newOidFromC(&hunk.orig_commit_id), - OrigPath: C.GoString(hunk.orig_path), - OrigStartLineNumber: uint16(hunk.orig_start_line_number), - OrigSignature: newSignatureFromC(hunk.orig_signature), - Boundary: hunk.boundary == 1, - } -} diff --git a/blame_test.go b/blame_test.go deleted file mode 100644 index 1785042..0000000 --- a/blame_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package git - -import ( - "os" - "reflect" - "testing" -) - -func TestBlame(t *testing.T) { - repo := createTestRepo(t) - defer repo.Free() - defer os.RemoveAll(repo.Workdir()) - - commitId1, _ := seedTestRepo(t, repo) - commitId2, _ := updateReadme(t, repo, "foo\nbar\nbaz\n") - - opts := BlameOptions{ - NewestCommit: commitId2, - OldestCommit: nil, - MinLine: 1, - MaxLine: 3, - } - blame, err := repo.BlameFile("README", &opts) - checkFatal(t, err) - defer blame.Free() - if blame.HunkCount() != 2 { - t.Errorf("got hunk count %d, want 2", blame.HunkCount()) - } - - wantHunk1 := BlameHunk{ - LinesInHunk: 1, - FinalCommitId: commitId1, - FinalStartLineNumber: 1, - OrigCommitId: commitId1, - OrigPath: "README", - OrigStartLineNumber: 1, - Boundary: true, - } - wantHunk2 := BlameHunk{ - LinesInHunk: 2, - FinalCommitId: commitId2, - FinalStartLineNumber: 2, - OrigCommitId: commitId2, - OrigPath: "README", - OrigStartLineNumber: 2, - Boundary: false, - } - - hunk1, err := blame.HunkByIndex(0) - checkFatal(t, err) - checkHunk(t, "index 0", hunk1, wantHunk1) - - hunk2, err := blame.HunkByIndex(1) - checkFatal(t, err) - checkHunk(t, "index 1", hunk2, wantHunk2) - - hunkLine1, err := blame.HunkByLine(1) - checkFatal(t, err) - checkHunk(t, "line 1", hunkLine1, wantHunk1) - - hunkLine2, err := blame.HunkByLine(3) - checkFatal(t, err) - checkHunk(t, "line 2", hunkLine2, wantHunk2) -} - -func checkHunk(t *testing.T, label string, hunk, want BlameHunk) { - hunk.FinalSignature = nil - want.FinalSignature = nil - hunk.OrigSignature = nil - want.OrigSignature = nil - if !reflect.DeepEqual(hunk, want) { - t.Fatalf("%s: got hunk %+v, want %+v", label, hunk, want) - } -} diff --git a/blob.go b/blob.go index 5a33bd8..58258fd 100644 --- a/blob.go +++ b/blob.go @@ -2,6 +2,7 @@ package git /* #include +#include #include extern int _go_git_blob_create_fromchunks(git_oid *id, diff --git a/branch.go b/branch.go index bb231c3..95c3807 100644 --- a/branch.go +++ b/branch.go @@ -2,6 +2,7 @@ package git /* #include +#include */ import "C" diff --git a/checkout.go b/checkout.go index 06d010c..9c7188e 100644 --- a/checkout.go +++ b/checkout.go @@ -7,7 +7,6 @@ import "C" import ( "os" "runtime" - "unsafe" ) type CheckoutStrategy uint @@ -32,12 +31,11 @@ const ( ) type CheckoutOpts struct { - Strategy CheckoutStrategy // Default will be a dry run - DisableFilters bool // Don't apply filters like CRLF conversion - DirMode os.FileMode // Default is 0755 - FileMode os.FileMode // Default is 0644 or 0755 as dictated by blob - FileOpenFlags int // Default is O_CREAT | O_TRUNC | O_WRONLY - TargetDirectory string // Alternative checkout path to workdir + Strategy CheckoutStrategy // Default will be a dry run + DisableFilters bool // Don't apply filters like CRLF conversion + DirMode os.FileMode // Default is 0755 + FileMode os.FileMode // Default is 0644 or 0755 as dictated by blob + FileOpenFlags int // Default is O_CREAT | O_TRUNC | O_WRONLY } func (opts *CheckoutOpts) toC() *C.git_checkout_options { @@ -62,17 +60,8 @@ func populateCheckoutOpts(ptr *C.git_checkout_options, opts *CheckoutOpts) *C.gi ptr.disable_filters = cbool(opts.DisableFilters) ptr.dir_mode = C.uint(opts.DirMode.Perm()) ptr.file_mode = C.uint(opts.FileMode.Perm()) - if opts.TargetDirectory != "" { - ptr.target_directory = C.CString(opts.TargetDirectory) - } - return ptr -} -func freeCheckoutOpts(ptr *C.git_checkout_options) { - if ptr == nil { - return - } - C.free(unsafe.Pointer(ptr.target_directory)) + return ptr } // Updates files in the index and the working tree to match the content of @@ -81,10 +70,7 @@ func (v *Repository) CheckoutHead(opts *CheckoutOpts) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - cOpts := opts.toC() - defer freeCheckoutOpts(cOpts) - - ret := C.git_checkout_head(v.ptr, cOpts) + ret := C.git_checkout_head(v.ptr, opts.toC()) if ret < 0 { return MakeGitError(ret) } @@ -104,10 +90,7 @@ func (v *Repository) CheckoutIndex(index *Index, opts *CheckoutOpts) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - cOpts := opts.toC() - defer freeCheckoutOpts(cOpts) - - ret := C.git_checkout_index(v.ptr, iptr, cOpts) + ret := C.git_checkout_index(v.ptr, iptr, opts.toC()) if ret < 0 { return MakeGitError(ret) } @@ -119,10 +102,7 @@ func (v *Repository) CheckoutTree(tree *Tree, opts *CheckoutOpts) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - cOpts := opts.toC() - defer freeCheckoutOpts(cOpts) - - ret := C.git_checkout_tree(v.ptr, tree.ptr, cOpts) + ret := C.git_checkout_tree(v.ptr, tree.ptr, opts.toC()) if ret < 0 { return MakeGitError(ret) } diff --git a/clone.go b/clone.go index b796b6e..958e65d 100644 --- a/clone.go +++ b/clone.go @@ -2,6 +2,7 @@ package git /* #include +#include */ import "C" @@ -30,7 +31,6 @@ func Clone(url string, path string, options *CloneOptions) (*Repository, error) var copts C.git_clone_options populateCloneOptions(&copts, options) - defer freeCheckoutOpts(&copts.checkout_opts) if len(options.CheckoutBranch) != 0 { copts.checkout_branch = C.CString(options.CheckoutBranch) diff --git a/commit.go b/commit.go index 559a1bd..ed026a4 100644 --- a/commit.go +++ b/commit.go @@ -2,6 +2,7 @@ package git /* #include +#include extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr); */ diff --git a/config.go b/config.go index 9d25e35..5bcb0f8 100644 --- a/config.go +++ b/config.go @@ -2,6 +2,7 @@ package git /* #include +#include */ import "C" import ( @@ -35,14 +36,14 @@ const ( ) type ConfigEntry struct { - Name string + Name string Value string Level ConfigLevel } func newConfigEntryFromC(centry *C.git_config_entry) *ConfigEntry { return &ConfigEntry{ - Name: C.GoString(centry.name), + Name: C.GoString(centry.name), Value: C.GoString(centry.value), Level: ConfigLevel(centry.level), } @@ -74,6 +75,7 @@ func (c *Config) AddFile(path string, level ConfigLevel, force bool) error { runtime.LockOSThread() defer runtime.UnlockOSThread() + ret := C.git_config_add_file_ondisk(c.ptr, cpath, C.git_config_level_t(level), cbool(force)) if ret < 0 { return MakeGitError(ret) @@ -129,6 +131,7 @@ func (c *Config) LookupString(name string) (string, error) { return C.GoString(ptr), nil } + func (c *Config) LookupBool(name string) (bool, error) { var out C.int cname := C.CString(name) @@ -232,9 +235,6 @@ func (c *Config) SetInt32(name string, value int32) (err error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_config_set_int32(c.ptr, cname, C.int32_t(value)) if ret < 0 { return MakeGitError(ret) @@ -350,9 +350,6 @@ type ConfigIterator struct { func (iter *ConfigIterator) Next() (*ConfigEntry, error) { var centry *C.git_config_entry - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_config_next(¢ry, iter.ptr) if ret < 0 { return nil, MakeGitError(ret) @@ -365,48 +362,3 @@ func (iter *ConfigIterator) Free() { runtime.SetFinalizer(iter, nil) C.free(unsafe.Pointer(iter.ptr)) } - -func ConfigFindGlobal() (string, error) { - var buf C.git_buf - defer C.git_buf_free(&buf) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_config_find_global(&buf) - if ret < 0 { - return "", MakeGitError(ret) - } - - return C.GoString(buf.ptr), nil -} - -func ConfigFindSystem() (string, error) { - var buf C.git_buf - defer C.git_buf_free(&buf) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_config_find_system(&buf) - if ret < 0 { - return "", MakeGitError(ret) - } - - return C.GoString(buf.ptr), nil -} - -func ConfigFindXDG() (string, error) { - var buf C.git_buf - defer C.git_buf_free(&buf) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_config_find_xdg(&buf) - if ret < 0 { - return "", MakeGitError(ret) - } - - return C.GoString(buf.ptr), nil -} diff --git a/credentials.go b/credentials.go index bb0ec41..b04bf98 100644 --- a/credentials.go +++ b/credentials.go @@ -2,6 +2,7 @@ package git /* #include +#include */ import "C" import "unsafe" diff --git a/diff.go b/diff.go index 63fa867..f762a56 100644 --- a/diff.go +++ b/diff.go @@ -164,72 +164,6 @@ func (diff *Diff) Free() error { return nil } -func (diff *Diff) FindSimilar(opts *DiffFindOptions) error { - - var copts *C.git_diff_find_options - if opts != nil { - copts = &C.git_diff_find_options{ - version: C.GIT_DIFF_FIND_OPTIONS_VERSION, - flags: C.uint32_t(opts.Flags), - rename_threshold: C.uint16_t(opts.RenameThreshold), - copy_threshold: C.uint16_t(opts.CopyThreshold), - rename_from_rewrite_threshold: C.uint16_t(opts.RenameFromRewriteThreshold), - break_rewrite_threshold: C.uint16_t(opts.BreakRewriteThreshold), - rename_limit: C.size_t(opts.RenameLimit), - } - } - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ecode := C.git_diff_find_similar(diff.ptr, copts) - if ecode < 0 { - return MakeGitError(ecode) - } - - return nil -} - -type DiffStats struct { - ptr *C.git_diff_stats -} - -func (stats *DiffStats) Free() error { - if stats.ptr == nil { - return ErrInvalid - } - runtime.SetFinalizer(stats, nil) - C.git_diff_stats_free(stats.ptr) - stats.ptr = nil - return nil -} - -func (stats *DiffStats) Insertions() int { - return int(C.git_diff_stats_insertions(stats.ptr)) -} - -func (stats *DiffStats) Deletions() int { - return int(C.git_diff_stats_deletions(stats.ptr)) -} - -func (stats *DiffStats) FilesChanged() int { - return int(C.git_diff_stats_files_changed(stats.ptr)) -} - -func (diff *Diff) Stats() (*DiffStats, error) { - stats := new(DiffStats) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - if ecode := C.git_diff_get_stats(&stats.ptr, diff.ptr); ecode < 0 { - return nil, MakeGitError(ecode) - } - runtime.SetFinalizer(stats, (*DiffStats).Free) - - return stats, nil -} - type diffForEachData struct { FileCallback DiffForEachFileCallback HunkCallback DiffForEachHunkCallback @@ -330,9 +264,6 @@ func (diff *Diff) Patch(deltaIndex int) (*Patch, error) { } var patchPtr *C.git_patch - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ecode := C.git_patch_from_diff(&patchPtr, diff.ptr, C.size_t(deltaIndex)) if ecode < 0 { return nil, MakeGitError(ecode) @@ -382,8 +313,8 @@ type DiffOptions struct { Pathspec []string NotifyCallback DiffNotifyCallback - ContextLines uint32 - InterhunkLines uint32 + ContextLines uint16 + InterhunkLines uint16 IdAbbrev uint16 MaxSize int @@ -394,10 +325,6 @@ type DiffOptions struct { func DefaultDiffOptions() (DiffOptions, error) { opts := C.git_diff_options{} - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ecode := C.git_diff_init_options(&opts, C.GIT_DIFF_OPTIONS_VERSION) if ecode < 0 { return DiffOptions{}, MakeGitError(ecode) @@ -407,64 +334,10 @@ func DefaultDiffOptions() (DiffOptions, error) { Flags: DiffOptionsFlag(opts.flags), IgnoreSubmodules: SubmoduleIgnore(opts.ignore_submodules), Pathspec: makeStringsFromCStrings(opts.pathspec.strings, int(opts.pathspec.count)), - ContextLines: uint32(opts.context_lines), - InterhunkLines: uint32(opts.interhunk_lines), + ContextLines: uint16(opts.context_lines), + InterhunkLines: uint16(opts.interhunk_lines), IdAbbrev: uint16(opts.id_abbrev), MaxSize: int(opts.max_size), - OldPrefix: "a", - NewPrefix: "b", - }, nil -} - -type DiffFindOptionsFlag int - -const ( - DiffFindByConfig DiffFindOptionsFlag = C.GIT_DIFF_FIND_BY_CONFIG - DiffFindRenames DiffFindOptionsFlag = C.GIT_DIFF_FIND_RENAMES - DiffFindRenamesFromRewrites DiffFindOptionsFlag = C.GIT_DIFF_FIND_RENAMES_FROM_REWRITES - DiffFindCopies DiffFindOptionsFlag = C.GIT_DIFF_FIND_COPIES - DiffFindCopiesFromUnmodified DiffFindOptionsFlag = C.GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED - DiffFindRewrites DiffFindOptionsFlag = C.GIT_DIFF_FIND_REWRITES - DiffFindBreakRewrites DiffFindOptionsFlag = C.GIT_DIFF_BREAK_REWRITES - DiffFindAndBreakRewrites DiffFindOptionsFlag = C.GIT_DIFF_FIND_AND_BREAK_REWRITES - DiffFindForUntracked DiffFindOptionsFlag = C.GIT_DIFF_FIND_FOR_UNTRACKED - DiffFindAll DiffFindOptionsFlag = C.GIT_DIFF_FIND_ALL - DiffFindIgnoreLeadingWhitespace DiffFindOptionsFlag = C.GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE - DiffFindIgnoreWhitespace DiffFindOptionsFlag = C.GIT_DIFF_FIND_IGNORE_WHITESPACE - DiffFindDontIgnoreWhitespace DiffFindOptionsFlag = C.GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE - DiffFindExactMatchOnly DiffFindOptionsFlag = C.GIT_DIFF_FIND_EXACT_MATCH_ONLY - DiffFindBreakRewritesForRenamesOnly DiffFindOptionsFlag = C.GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY - DiffFindRemoveUnmodified DiffFindOptionsFlag = C.GIT_DIFF_FIND_REMOVE_UNMODIFIED -) - -//TODO implement git_diff_similarity_metric -type DiffFindOptions struct { - Flags DiffFindOptionsFlag - RenameThreshold uint16 - CopyThreshold uint16 - RenameFromRewriteThreshold uint16 - BreakRewriteThreshold uint16 - RenameLimit uint -} - -func DefaultDiffFindOptions() (DiffFindOptions, error) { - opts := C.git_diff_find_options{} - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ecode := C.git_diff_find_init_options(&opts, C.GIT_DIFF_FIND_OPTIONS_VERSION) - if ecode < 0 { - return DiffFindOptions{}, MakeGitError(ecode) - } - - return DiffFindOptions{ - Flags: DiffFindOptionsFlag(opts.flags), - RenameThreshold: uint16(opts.rename_threshold), - CopyThreshold: uint16(opts.copy_threshold), - RenameFromRewriteThreshold: uint16(opts.rename_from_rewrite_threshold), - BreakRewriteThreshold: uint16(opts.break_rewrite_threshold), - RenameLimit: uint(opts.rename_limit), }, nil } @@ -501,47 +374,6 @@ func diffNotifyCb(_diff_so_far unsafe.Pointer, delta_to_add *C.git_diff_delta, m return 0 } -func diffOptionsToC(opts *DiffOptions) (copts *C.git_diff_options, notifyData *diffNotifyData) { - cpathspec := C.git_strarray{} - if opts != nil { - notifyData = &diffNotifyData{ - Callback: opts.NotifyCallback, - } - if opts.Pathspec != nil { - cpathspec.count = C.size_t(len(opts.Pathspec)) - cpathspec.strings = makeCStringsFromStrings(opts.Pathspec) - } - - copts = &C.git_diff_options{ - version: C.GIT_DIFF_OPTIONS_VERSION, - flags: C.uint32_t(opts.Flags), - ignore_submodules: C.git_submodule_ignore_t(opts.IgnoreSubmodules), - pathspec: cpathspec, - context_lines: C.uint32_t(opts.ContextLines), - interhunk_lines: C.uint32_t(opts.InterhunkLines), - id_abbrev: C.uint16_t(opts.IdAbbrev), - max_size: C.git_off_t(opts.MaxSize), - old_prefix: C.CString(opts.OldPrefix), - new_prefix: C.CString(opts.NewPrefix), - } - - if opts.NotifyCallback != nil { - C._go_git_setup_diff_notify_callbacks(copts) - copts.notify_payload = unsafe.Pointer(notifyData) - } - } - return -} - -func freeDiffOptions(copts *C.git_diff_options) { - if copts != nil { - cpathspec := copts.pathspec - freeStrarray(&cpathspec) - C.free(unsafe.Pointer(copts.old_prefix)) - C.free(unsafe.Pointer(copts.new_prefix)) - } -} - func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) (*Diff, error) { var diffPtr *C.git_diff var oldPtr, newPtr *C.git_tree @@ -554,11 +386,35 @@ func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) ( newPtr = newTree.cast_ptr } - copts, notifyData := diffOptionsToC(opts) - defer freeDiffOptions(copts) + cpathspec := C.git_strarray{} + var copts *C.git_diff_options + var notifyData *diffNotifyData + if opts != nil { + notifyData = &diffNotifyData{ + Callback: opts.NotifyCallback, + } + if opts.Pathspec != nil { + cpathspec.count = C.size_t(len(opts.Pathspec)) + cpathspec.strings = makeCStringsFromStrings(opts.Pathspec) + defer freeStrarray(&cpathspec) + } - runtime.LockOSThread() - defer runtime.UnlockOSThread() + copts = &C.git_diff_options{ + version: C.GIT_DIFF_OPTIONS_VERSION, + flags: C.uint32_t(opts.Flags), + ignore_submodules: C.git_submodule_ignore_t(opts.IgnoreSubmodules), + pathspec: cpathspec, + context_lines: C.uint16_t(opts.ContextLines), + interhunk_lines: C.uint16_t(opts.InterhunkLines), + id_abbrev: C.uint16_t(opts.IdAbbrev), + max_size: C.git_off_t(opts.MaxSize), + } + + if opts.NotifyCallback != nil { + C._go_git_setup_diff_notify_callbacks(copts) + copts.notify_payload = unsafe.Pointer(notifyData) + } + } ecode := C.git_diff_tree_to_tree(&diffPtr, v.ptr, oldPtr, newPtr, copts) if ecode < 0 { @@ -579,11 +435,35 @@ func (v *Repository) DiffTreeToWorkdir(oldTree *Tree, opts *DiffOptions) (*Diff, oldPtr = oldTree.cast_ptr } - copts, notifyData := diffOptionsToC(opts) - defer freeDiffOptions(copts) + cpathspec := C.git_strarray{} + var copts *C.git_diff_options + var notifyData *diffNotifyData + if opts != nil { + notifyData = &diffNotifyData{ + Callback: opts.NotifyCallback, + } + if opts.Pathspec != nil { + cpathspec.count = C.size_t(len(opts.Pathspec)) + cpathspec.strings = makeCStringsFromStrings(opts.Pathspec) + defer freeStrarray(&cpathspec) + } - runtime.LockOSThread() - defer runtime.UnlockOSThread() + copts = &C.git_diff_options{ + version: C.GIT_DIFF_OPTIONS_VERSION, + flags: C.uint32_t(opts.Flags), + ignore_submodules: C.git_submodule_ignore_t(opts.IgnoreSubmodules), + pathspec: cpathspec, + context_lines: C.uint16_t(opts.ContextLines), + interhunk_lines: C.uint16_t(opts.InterhunkLines), + id_abbrev: C.uint16_t(opts.IdAbbrev), + max_size: C.git_off_t(opts.MaxSize), + } + + if opts.NotifyCallback != nil { + C._go_git_setup_diff_notify_callbacks(copts) + copts.notify_payload = unsafe.Pointer(notifyData) + } + } ecode := C.git_diff_tree_to_workdir(&diffPtr, v.ptr, oldPtr, copts) if ecode < 0 { @@ -594,4 +474,5 @@ func (v *Repository) DiffTreeToWorkdir(oldTree *Tree, opts *DiffOptions) (*Diff, return notifyData.Diff, nil } return newDiffFromC(diffPtr), nil + } diff --git a/diff_test.go b/diff_test.go index fc6fed9..b688294 100644 --- a/diff_test.go +++ b/diff_test.go @@ -3,72 +3,23 @@ package git import ( "errors" "os" - "strings" "testing" ) -func TestFindSimilar(t *testing.T) { - repo := createTestRepo(t) - defer repo.Free() - defer os.RemoveAll(repo.Workdir()) - - originalTree, newTree := createTestTrees(t, repo) - - diffOpt, _ := DefaultDiffOptions() - - diff, err := repo.DiffTreeToTree(originalTree, newTree, &diffOpt) - checkFatal(t, err) - if diff == nil { - t.Fatal("no diff returned") - } - - findOpts, err := DefaultDiffFindOptions() - checkFatal(t, err) - findOpts.Flags = DiffFindBreakRewrites - - err = diff.FindSimilar(&findOpts) - checkFatal(t, err) - - numDiffs := 0 - numAdded := 0 - numDeleted := 0 - - err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) { - numDiffs++ - - switch file.Status { - case DeltaAdded: - numAdded++ - case DeltaDeleted: - numDeleted++ - } - - return func(hunk DiffHunk) (DiffForEachLineCallback, error) { - return func(line DiffLine) error { - return nil - }, nil - }, nil - }, DiffDetailLines) - - if numDiffs != 2 { - t.Fatal("Incorrect number of files in diff") - } - if numAdded != 1 { - t.Fatal("Incorrect number of new files in diff") - } - if numDeleted != 1 { - t.Fatal("Incorrect number of deleted files in diff") - } - -} - func TestDiffTreeToTree(t *testing.T) { - repo := createTestRepo(t) defer repo.Free() defer os.RemoveAll(repo.Workdir()) - originalTree, newTree := createTestTrees(t, repo) + _, originalTreeId := seedTestRepo(t, repo) + originalTree, err := repo.LookupTree(originalTreeId) + + checkFatal(t, err) + + _, newTreeId := updateReadme(t, repo, "file changed\n") + + newTree, err := repo.LookupTree(newTreeId) + checkFatal(t, err) callbackInvoked := false opts := DiffOptions{ @@ -76,8 +27,6 @@ func TestDiffTreeToTree(t *testing.T) { callbackInvoked = true return nil }, - OldPrefix: "x1/", - NewPrefix: "y1/", } diff, err := repo.DiffTreeToTree(originalTree, newTree, &opts) @@ -93,19 +42,7 @@ func TestDiffTreeToTree(t *testing.T) { files := make([]string, 0) hunks := make([]DiffHunk, 0) lines := make([]DiffLine, 0) - patches := make([]string, 0) err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) { - patch, err := diff.Patch(len(patches)) - if err != nil { - return nil, err - } - defer patch.Free() - patchStr, err := patch.String() - if err != nil { - return nil, err - } - patches = append(patches, patchStr) - files = append(files, file.OldFile.Path) return func(hunk DiffHunk) (DiffForEachLineCallback, error) { hunks = append(hunks, hunk) @@ -146,24 +83,6 @@ func TestDiffTreeToTree(t *testing.T) { t.Fatal("Incorrect lines in diff") } - if want1, want2 := "x1/README", "y1/README"; !strings.Contains(patches[0], want1) || !strings.Contains(patches[0], want2) { - t.Errorf("Diff patch doesn't contain %q or %q\n\n%s", want1, want2, patches[0]) - - } - - stats, err := diff.Stats() - checkFatal(t, err) - - if stats.Insertions() != 1 { - t.Fatal("Incorrect number of insertions in diff") - } - if stats.Deletions() != 1 { - t.Fatal("Incorrect number of deletions in diff") - } - if stats.FilesChanged() != 1 { - t.Fatal("Incorrect number of changed files in diff") - } - errTest := errors.New("test error") err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) { @@ -175,18 +94,3 @@ func TestDiffTreeToTree(t *testing.T) { } } - -func createTestTrees(t *testing.T, repo *Repository) (originalTree *Tree, newTree *Tree) { - var err error - _, originalTreeId := seedTestRepo(t, repo) - originalTree, err = repo.LookupTree(originalTreeId) - - checkFatal(t, err) - - _, newTreeId := updateReadme(t, repo, "file changed\n") - - newTree, err = repo.LookupTree(newTreeId) - checkFatal(t, err) - - return originalTree, newTree -} diff --git a/git.go b/git.go index 8e78710..adfa3b0 100644 --- a/git.go +++ b/git.go @@ -2,7 +2,7 @@ package git /* #include -#include +#include */ import "C" import ( @@ -93,14 +93,7 @@ var ( ) func init() { - C.git_libgit2_init() - - // This is not something we should be doing, as we may be - // stomping all over someone else's setup. The user should do - // this themselves or use some binding/wrapper which does it - // in such a way that they can be sure they're the only ones - // setting it up. - C.git_openssl_set_locking() + C.git_threads_init() } // Oid represents the id for a Git object. diff --git a/graph.go b/graph.go new file mode 100644 index 0000000..e5d7732 --- /dev/null +++ b/graph.go @@ -0,0 +1,36 @@ +package git + +/* +#include +*/ +import "C" +import ( + "runtime" +) + +func (repo *Repository) DescendantOf(commit, ancestor *Oid) (bool, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_graph_descendant_of(repo.ptr, commit.toC(), ancestor.toC()) + if ret < 0 { + return false, MakeGitError(ret) + } + + return (ret > 0), nil +} + +func (repo *Repository) AheadBehind(local, upstream *Oid) (ahead, behind int, err error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + var aheadT C.size_t + var behindT C.size_t + + ret := C.git_graph_ahead_behind(&aheadT, &behindT, repo.ptr, local.toC(), upstream.toC()) + if ret < 0 { + return 0, 0, MakeGitError(ret) + } + + return int(aheadT), int(behindT), nil +} diff --git a/index.go b/index.go index 6b90758..230e159 100644 --- a/index.go +++ b/index.go @@ -2,6 +2,7 @@ package git /* #include +#include extern int _go_git_index_add_all(git_index*, const git_strarray*, unsigned int, void*); extern int _go_git_index_update_all(git_index*, const git_strarray*, void*); diff --git a/merge.go b/merge.go index 5b68a8b..93ac71b 100644 --- a/merge.go +++ b/merge.go @@ -2,10 +2,11 @@ package git /* #include +#include -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 git_annotated_commit* _go_git_annotated_commit_array_get(git_annotated_commit** array, size_t n); +extern git_merge_head** _go_git_make_merge_head_array(size_t len); +extern void _go_git_merge_head_array_set(git_merge_head** array, git_merge_head* ptr, size_t n); +extern git_merge_head* _go_git_merge_head_array_get(git_merge_head** array, size_t n); */ import "C" @@ -14,23 +15,23 @@ import ( "unsafe" ) -type AnnotatedCommit struct { - ptr *C.git_annotated_commit +type MergeHead struct { + ptr *C.git_merge_head } -func newAnnotatedCommitFromC(c *C.git_annotated_commit) *AnnotatedCommit { - mh := &AnnotatedCommit{ptr: c} - runtime.SetFinalizer(mh, (*AnnotatedCommit).Free) +func newMergeHeadFromC(c *C.git_merge_head) *MergeHead { + mh := &MergeHead{ptr: c} + runtime.SetFinalizer(mh, (*MergeHead).Free) return mh } -func (mh *AnnotatedCommit) Free() { +func (mh *MergeHead) Free() { runtime.SetFinalizer(mh, nil) - C.git_annotated_commit_free(mh.ptr) + C.git_merge_head_free(mh.ptr) } -func (r *Repository) AnnotatedCommitFromFetchHead(branchName string, remoteURL string, oid *Oid) (*AnnotatedCommit, error) { - mh := &AnnotatedCommit{} +func (r *Repository) MergeHeadFromFetchHead(branchName string, remoteURL string, oid *Oid) (*MergeHead, error) { + mh := &MergeHead{} cbranchName := C.CString(branchName) defer C.free(unsafe.Pointer(cbranchName)) @@ -38,42 +39,33 @@ func (r *Repository) AnnotatedCommitFromFetchHead(branchName string, remoteURL s cremoteURL := C.CString(remoteURL) defer C.free(unsafe.Pointer(cremoteURL)) - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_annotated_commit_from_fetchhead(&mh.ptr, r.ptr, cbranchName, cremoteURL, oid.toC()) + ret := C.git_merge_head_from_fetchhead(&mh.ptr, r.ptr, cbranchName, cremoteURL, oid.toC()) if ret < 0 { return nil, MakeGitError(ret) } - runtime.SetFinalizer(mh, (*AnnotatedCommit).Free) + runtime.SetFinalizer(mh, (*MergeHead).Free) return mh, nil } -func (r *Repository) LookupAnnotatedCommit(oid *Oid) (*AnnotatedCommit, error) { - mh := &AnnotatedCommit{} +func (r *Repository) MergeHeadFromId(oid *Oid) (*MergeHead, error) { + mh := &MergeHead{} - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_annotated_commit_lookup(&mh.ptr, r.ptr, oid.toC()) + ret := C.git_merge_head_from_id(&mh.ptr, r.ptr, oid.toC()) if ret < 0 { return nil, MakeGitError(ret) } - runtime.SetFinalizer(mh, (*AnnotatedCommit).Free) + runtime.SetFinalizer(mh, (*MergeHead).Free) return mh, nil } -func (r *Repository) AnnotatedCommitFromRef(ref *Reference) (*AnnotatedCommit, error) { - mh := &AnnotatedCommit{} +func (r *Repository) MergeHeadFromRef(ref *Reference) (*MergeHead, error) { + mh := &MergeHead{} - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_annotated_commit_from_ref(&mh.ptr, r.ptr, ref.ptr) + ret := C.git_merge_head_from_ref(&mh.ptr, r.ptr, ref.ptr) if ret < 0 { return nil, MakeGitError(ret) } - runtime.SetFinalizer(mh, (*AnnotatedCommit).Free) + runtime.SetFinalizer(mh, (*MergeHead).Free) return mh, nil } @@ -106,10 +98,6 @@ func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions { func DefaultMergeOptions() (MergeOptions, error) { opts := C.git_merge_options{} - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ecode := C.git_merge_init_options(&opts, C.GIT_MERGE_OPTIONS_VERSION) if ecode < 0 { return MergeOptions{}, MakeGitError(ecode) @@ -139,20 +127,19 @@ const ( MergeFileFavorUnion MergeFileFavor = C.GIT_MERGE_FILE_FAVOR_UNION ) -func (r *Repository) Merge(theirHeads []*AnnotatedCommit, mergeOptions *MergeOptions, checkoutOptions *CheckoutOpts) error { +func (r *Repository) Merge(theirHeads []*MergeHead, mergeOptions *MergeOptions, checkoutOptions *CheckoutOpts) error { runtime.LockOSThread() defer runtime.UnlockOSThread() cMergeOpts := mergeOptions.toC() cCheckoutOpts := checkoutOptions.toC() - defer freeCheckoutOpts(cCheckoutOpts) - gmerge_head_array := make([]*C.git_annotated_commit, len(theirHeads)) + gmerge_head_array := make([]*C.git_merge_head, len(theirHeads)) for i := 0; i < len(theirHeads); i++ { gmerge_head_array[i] = theirHeads[i].ptr } ptr := unsafe.Pointer(&gmerge_head_array[0]) - err := C.git_merge(r.ptr, (**C.git_annotated_commit)(ptr), C.size_t(len(theirHeads)), cMergeOpts, cCheckoutOpts) + err := C.git_merge(r.ptr, (**C.git_merge_head)(ptr), C.size_t(len(theirHeads)), cMergeOpts, cCheckoutOpts) if err < 0 { return MakeGitError(err) } @@ -177,18 +164,18 @@ const ( MergePreferenceFastForwardOnly MergePreference = C.GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY ) -func (r *Repository) MergeAnalysis(theirHeads []*AnnotatedCommit) (MergeAnalysis, MergePreference, error) { +func (r *Repository) MergeAnalysis(theirHeads []*MergeHead) (MergeAnalysis, MergePreference, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - gmerge_head_array := make([]*C.git_annotated_commit, len(theirHeads)) + gmerge_head_array := make([]*C.git_merge_head, len(theirHeads)) for i := 0; i < len(theirHeads); i++ { gmerge_head_array[i] = theirHeads[i].ptr } ptr := unsafe.Pointer(&gmerge_head_array[0]) var analysis C.git_merge_analysis_t var preference C.git_merge_preference_t - err := C.git_merge_analysis(&analysis, &preference, r.ptr, (**C.git_annotated_commit)(ptr), C.size_t(len(theirHeads))) + err := C.git_merge_analysis(&analysis, &preference, r.ptr, (**C.git_merge_head)(ptr), C.size_t(len(theirHeads))) if err < 0 { return MergeAnalysisNone, MergePreferenceNone, MakeGitError(err) } diff --git a/merge_test.go b/merge_test.go index 1eba806..7e884c0 100644 --- a/merge_test.go +++ b/merge_test.go @@ -13,10 +13,10 @@ func TestMergeWithSelf(t *testing.T) { master, err := repo.LookupReference("refs/heads/master") checkFatal(t, err) - mergeHead, err := repo.AnnotatedCommitFromRef(master) + mergeHead, err := repo.MergeHeadFromRef(master) checkFatal(t, err) - mergeHeads := make([]*AnnotatedCommit, 1) + mergeHeads := make([]*MergeHead, 1) mergeHeads[0] = mergeHead err = repo.Merge(mergeHeads, nil, nil) checkFatal(t, err) @@ -30,10 +30,10 @@ func TestMergeAnalysisWithSelf(t *testing.T) { master, err := repo.LookupReference("refs/heads/master") checkFatal(t, err) - mergeHead, err := repo.AnnotatedCommitFromRef(master) + mergeHead, err := repo.MergeHeadFromRef(master) checkFatal(t, err) - mergeHeads := make([]*AnnotatedCommit, 1) + mergeHeads := make([]*MergeHead, 1) mergeHeads[0] = mergeHead a, _, err := repo.MergeAnalysis(mergeHeads) checkFatal(t, err) diff --git a/note.go b/note.go deleted file mode 100644 index 3cdd340..0000000 --- a/note.go +++ /dev/null @@ -1,99 +0,0 @@ -package git - -/* -#include -*/ -import "C" - -import ( - "runtime" - "unsafe" -) - -// Note -type Note struct { - ptr *C.git_note -} - -// Free frees a git_note object -func (n *Note) Free() error { - if n.ptr == nil { - return ErrInvalid - } - runtime.SetFinalizer(n, nil) - C.git_note_free(n.ptr) - n.ptr = nil - return nil -} - -// Author returns the signature of the note author -func (n *Note) Author() *Signature { - ptr := C.git_note_author(n.ptr) - return newSignatureFromC(ptr) -} - -// Id returns the note object's id -func (n *Note) Id() *Oid { - ptr := C.git_note_id(n.ptr) - return newOidFromC(ptr) -} - -// Committer returns the signature of the note committer -func (n *Note) Committer() *Signature { - ptr := C.git_note_committer(n.ptr) - return newSignatureFromC(ptr) -} - -// Message returns the note message -func (n *Note) Message() string { - return C.GoString(C.git_note_message(n.ptr)) -} - -// NoteIterator -type NoteIterator struct { - ptr *C.git_note_iterator -} - -// NewNoteIterator creates a new iterator for notes -func (repo *Repository) NewNoteIterator(ref string) (*NoteIterator, error) { - var cref *C.char - if ref == "" { - cref = nil - } else { - cref = C.CString(ref) - defer C.free(unsafe.Pointer(cref)) - } - - var ptr *C.git_note_iterator - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - if ret := C.git_note_iterator_new(&ptr, repo.ptr, cref); ret < 0 { - return nil, MakeGitError(ret) - } - - iter := &NoteIterator{ptr: ptr} - runtime.SetFinalizer(iter, (*NoteIterator).Free) - return iter, nil -} - -// Free frees the note interator -func (v *NoteIterator) Free() { - runtime.SetFinalizer(v, nil) - C.git_note_iterator_free(v.ptr) -} - -// Next returns the current item (note id & annotated id) and advances the -// iterator internally to the next item -func (it *NoteIterator) Next() (noteId, annotatedId *Oid, err error) { - noteId, annotatedId = new(Oid), new(Oid) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - if ret := C.git_note_next(noteId.toC(), annotatedId.toC(), it.ptr); ret < 0 { - err = MakeGitError(ret) - } - return -} diff --git a/note_test.go b/note_test.go deleted file mode 100644 index f5e9c01..0000000 --- a/note_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package git - -import ( - "fmt" - "os" - "reflect" - "testing" - "time" -) - -func TestCreateNote(t *testing.T) { - repo := createTestRepo(t) - defer os.RemoveAll(repo.Workdir()) - - commitId, _ := seedTestRepo(t, repo) - - commit, err := repo.LookupCommit(commitId) - checkFatal(t, err) - - note, noteId := createTestNote(t, repo, commit) - - compareStrings(t, "I am a note\n", note.Message()) - compareStrings(t, noteId.String(), note.Id().String()) - compareStrings(t, "alice", note.Author().Name) - compareStrings(t, "alice@example.com", note.Author().Email) - compareStrings(t, "alice", note.Committer().Name) - compareStrings(t, "alice@example.com", note.Committer().Email) -} - -func TestNoteIterator(t *testing.T) { - repo := createTestRepo(t) - defer os.RemoveAll(repo.Workdir()) - seedTestRepo(t, repo) - - notes := make([]*Note, 5) - for i := range notes { - commitId, _ := updateReadme(t, repo, fmt.Sprintf("README v%d\n", i+1)) - commit, err := repo.LookupCommit(commitId) - checkFatal(t, err) - - note, _ := createTestNote(t, repo, commit) - notes[i] = note - } - - iter, err := repo.NewNoteIterator("") - checkFatal(t, err) - for { - noteId, commitId, err := iter.Next() - if err != nil { - if !IsErrorCode(err, ErrIterOver) { - checkFatal(t, err) - } - break - } - - note, err := repo.ReadNote("", commitId) - checkFatal(t, err) - - if !reflect.DeepEqual(note.Id(), noteId) { - t.Errorf("expected note oid '%v', actual '%v'", note.Id(), noteId) - } - } -} - -func TestRemoveNote(t *testing.T) { - repo := createTestRepo(t) - defer os.RemoveAll(repo.Workdir()) - - commitId, _ := seedTestRepo(t, repo) - - commit, err := repo.LookupCommit(commitId) - checkFatal(t, err) - - note, _ := createTestNote(t, repo, commit) - - _, err = repo.ReadNote("", commit.Id()) - checkFatal(t, err) - - err = repo.RemoveNote("", note.Author(), note.Committer(), commitId) - checkFatal(t, err) - - _, err = repo.ReadNote("", commit.Id()) - if err == nil { - t.Fatal("note remove failed") - } -} - -func TestDefaultNoteRef(t *testing.T) { - repo := createTestRepo(t) - defer os.RemoveAll(repo.Workdir()) - - ref, err := repo.DefaultNoteRef() - checkFatal(t, err) - - compareStrings(t, "refs/notes/commits", ref) -} - -func createTestNote(t *testing.T, repo *Repository, commit *Commit) (*Note, *Oid) { - loc, err := time.LoadLocation("Europe/Berlin") - sig := &Signature{ - Name: "alice", - Email: "alice@example.com", - When: time.Date(2015, 01, 05, 13, 0, 0, 0, loc), - } - - noteId, err := repo.CreateNote("", sig, sig, commit.Id(), "I am a note\n", false) - checkFatal(t, err) - - note, err := repo.ReadNote("", commit.Id()) - checkFatal(t, err) - - return note, noteId -} diff --git a/object.go b/object.go index 7428e0f..a3bf652 100644 --- a/object.go +++ b/object.go @@ -2,6 +2,7 @@ package git /* #include +#include */ import "C" import "runtime" diff --git a/odb.go b/odb.go index 19bb71c..7076e20 100644 --- a/odb.go +++ b/odb.go @@ -2,6 +2,7 @@ package git /* #include +#include extern int _go_git_odb_foreach(git_odb *db, void *payload); extern void _go_git_odb_backend_free(git_odb_backend *backend); @@ -24,9 +25,6 @@ type OdbBackend struct { func NewOdb() (odb *Odb, err error) { odb = new(Odb) - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_odb_new(&odb.ptr) if ret < 0 { return nil, MakeGitError(ret) @@ -42,10 +40,6 @@ func NewOdbBackendFromC(ptr *C.git_odb_backend) (backend *OdbBackend) { } func (v *Odb) AddBackend(backend *OdbBackend, priority int) (err error) { - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_odb_add_backend(v.ptr, backend.ptr, C.int(priority)) if ret < 0 { backend.Free() @@ -116,9 +110,6 @@ func (v *Odb) ForEach(callback OdbForEachCallback) error { err: nil, } - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C._go_git_odb_foreach(v.ptr, unsafe.Pointer(&data)) if ret == C.GIT_EUSER { return data.err @@ -149,10 +140,6 @@ func (v *Odb) Hash(data []byte, otype ObjectType) (oid *Oid, err error) { // contents of the object. func (v *Odb) NewReadStream(id *Oid) (*OdbReadStream, error) { stream := new(OdbReadStream) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_odb_open_rstream(&stream.ptr, v.ptr, id.toC()) if ret < 0 { return nil, MakeGitError(ret) @@ -167,10 +154,6 @@ func (v *Odb) NewReadStream(id *Oid) (*OdbReadStream, error) { // known in advance func (v *Odb) NewWriteStream(size int, otype ObjectType) (*OdbWriteStream, error) { stream := new(OdbWriteStream) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_odb_open_wstream(&stream.ptr, v.ptr, C.size_t(size), C.git_otype(otype)) if ret < 0 { return nil, MakeGitError(ret) @@ -224,10 +207,6 @@ func (stream *OdbReadStream) Read(data []byte) (int, error) { header := (*reflect.SliceHeader)(unsafe.Pointer(&data)) ptr := (*C.char)(unsafe.Pointer(header.Data)) size := C.size_t(header.Cap) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_odb_stream_read(stream.ptr, ptr, size) if ret < 0 { return 0, MakeGitError(ret) @@ -260,9 +239,6 @@ func (stream *OdbWriteStream) Write(data []byte) (int, error) { ptr := (*C.char)(unsafe.Pointer(header.Data)) size := C.size_t(header.Len) - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_odb_stream_write(stream.ptr, ptr, size) if ret < 0 { return 0, MakeGitError(ret) @@ -274,9 +250,6 @@ func (stream *OdbWriteStream) Write(data []byte) (int, error) { // Close signals that all the data has been written and stores the // resulting object id in the stream's Id field. func (stream *OdbWriteStream) Close() error { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_odb_stream_finalize_write(stream.Id.toC(), stream.ptr) if ret < 0 { return MakeGitError(ret) diff --git a/packbuilder.go b/packbuilder.go index 7c94926..666f5c4 100644 --- a/packbuilder.go +++ b/packbuilder.go @@ -2,6 +2,7 @@ package git /* #include +#include #include #include @@ -131,9 +132,6 @@ func (pb *Packbuilder) ForEach(callback PackbuilderForeachCallback) error { err: nil, } - runtime.LockOSThread() - defer runtime.UnlockOSThread() - err := C._go_git_packbuilder_foreach(pb.ptr, unsafe.Pointer(&data)) if err == C.GIT_EUSER { return data.err diff --git a/patch.go b/patch.go index 45e14ac..0665501 100644 --- a/patch.go +++ b/patch.go @@ -6,7 +6,6 @@ package git import "C" import ( "runtime" - "unsafe" ) type Patch struct { @@ -41,47 +40,9 @@ func (patch *Patch) String() (string, error) { return "", ErrInvalid } var buf C.git_buf - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ecode := C.git_patch_to_buf(&buf, patch.ptr) if ecode < 0 { return "", MakeGitError(ecode) } return C.GoString(buf.ptr), nil } - -func toPointer(data []byte) (ptr unsafe.Pointer) { - if len(data) > 0 { - ptr = unsafe.Pointer(&data[0]) - } else { - ptr = unsafe.Pointer(nil) - } - return -} - -func (v *Repository) PatchFromBuffers(oldPath, newPath string, oldBuf, newBuf []byte, opts *DiffOptions) (*Patch, error) { - var patchPtr *C.git_patch - - oldPtr := toPointer(oldBuf) - newPtr := (*C.char)(toPointer(newBuf)) - - cOldPath := C.CString(oldPath) - defer C.free(unsafe.Pointer(cOldPath)) - - cNewPath := C.CString(newPath) - defer C.free(unsafe.Pointer(cNewPath)) - - copts, _ := diffOptionsToC(opts) - defer freeDiffOptions(copts) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ecode := C.git_patch_from_buffers(&patchPtr, oldPtr, C.size_t(len(oldBuf)), cOldPath, newPtr, C.size_t(len(newBuf)), cNewPath, copts) - if ecode < 0 { - return nil, MakeGitError(ecode) - } - return newPatchFromC(patchPtr), nil -} diff --git a/patch_test.go b/patch_test.go index a061142..569eac2 100644 --- a/patch_test.go +++ b/patch_test.go @@ -20,11 +20,7 @@ func TestPatch(t *testing.T) { newTree, err := repo.LookupTree(newTreeId) checkFatal(t, err) - opts := &DiffOptions{ - OldPrefix: "a", - NewPrefix: "b", - } - diff, err := repo.DiffTreeToTree(originalTree, newTree, opts) + diff, err := repo.DiffTreeToTree(originalTree, newTree, nil) checkFatal(t, err) patch, err := diff.Patch(0) diff --git a/push.go b/push.go new file mode 100644 index 0000000..5fb7f07 --- /dev/null +++ b/push.go @@ -0,0 +1,182 @@ +package git + +/* +#include +#include + +int _go_git_push_status_foreach(git_push *push, void *data); +int _go_git_push_set_callbacks(git_push *push, void *packbuilder_progress_data, void *transfer_progress_data); + +*/ +import "C" +import ( + "runtime" + "unsafe" +) + +type Push struct { + ptr *C.git_push + + packbuilderProgress *PackbuilderProgressCallback + transferProgress *PushTransferProgressCallback +} + +func newPushFromC(cpush *C.git_push) *Push { + p := &Push{ptr: cpush} + runtime.SetFinalizer(p, (*Push).Free) + return p +} + +func (p *Push) Free() { + runtime.SetFinalizer(p, nil) + C.git_push_free(p.ptr) +} + +func (remote *Remote) NewPush() (*Push, error) { + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + var cpush *C.git_push + ret := C.git_push_new(&cpush, remote.ptr) + if ret < 0 { + return nil, MakeGitError(ret) + } + return newPushFromC(cpush), nil +} + +func (p *Push) Finish() error { + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_push_finish(p.ptr) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +func (p *Push) UnpackOk() bool { + + ret := C.git_push_unpack_ok(p.ptr) + if ret == 0 { + return false + } + return true + +} + +func (p *Push) UpdateTips(sig *Signature, msg string) error { + + var csig *C.git_signature = nil + if sig != nil { + csig = sig.toC() + defer C.free(unsafe.Pointer(csig)) + } + + var cmsg *C.char + if msg == "" { + cmsg = nil + } else { + cmsg = C.CString(msg) + defer C.free(unsafe.Pointer(cmsg)) + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_push_update_tips(p.ptr, csig, cmsg) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +func (p *Push) AddRefspec(refspec string) error { + + crefspec := C.CString(refspec) + defer C.free(unsafe.Pointer(crefspec)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_push_add_refspec(p.ptr, crefspec) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +type PushOptions struct { + Version uint + PbParallelism uint +} + +func (p *Push) SetOptions(opts PushOptions) error { + copts := C.git_push_options{version: C.uint(opts.Version), pb_parallelism: C.uint(opts.PbParallelism)} + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_push_set_options(p.ptr, &copts) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +type StatusForeachFunc func(ref string, msg string) int + +//export statusForeach +func statusForeach(_ref *C.char, _msg *C.char, _data unsafe.Pointer) C.int { + ref := C.GoString(_ref) + msg := C.GoString(_msg) + + cb := (*StatusForeachFunc)(_data) + + return C.int((*cb)(ref, msg)) +} + +func (p *Push) StatusForeach(callback StatusForeachFunc) error { + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C._go_git_push_status_foreach(p.ptr, unsafe.Pointer(&callback)) + if ret < 0 { + return MakeGitError(ret) + } + return nil + +} + +type PushCallbacks struct { + PackbuilderProgress *PackbuilderProgressCallback + TransferProgress *PushTransferProgressCallback +} + +type PackbuilderProgressCallback func(stage int, current uint, total uint) int +type PushTransferProgressCallback func(current uint, total uint, bytes uint) int + +//export packbuilderProgress +func packbuilderProgress(stage C.int, current C.uint, total C.uint, data unsafe.Pointer) C.int { + return C.int((*(*PackbuilderProgressCallback)(data))(int(stage), uint(current), uint(total))) +} + +//export pushTransferProgress +func pushTransferProgress(current C.uint, total C.uint, bytes C.size_t, data unsafe.Pointer) C.int { + return C.int((*(*PushTransferProgressCallback)(data))(uint(current), uint(total), uint(bytes))) +} + +func (p *Push) SetCallbacks(callbacks PushCallbacks) { + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + // save callbacks so they don't get GC'd + p.packbuilderProgress = callbacks.PackbuilderProgress + p.transferProgress = callbacks.TransferProgress + + C._go_git_push_set_callbacks(p.ptr, unsafe.Pointer(p.packbuilderProgress), unsafe.Pointer(p.transferProgress)) +} diff --git a/push_test.go b/push_test.go index cd708c6..65f4dd2 100644 --- a/push_test.go +++ b/push_test.go @@ -3,25 +3,55 @@ package git import ( "os" "testing" + "time" ) -func TestRemotePush(t *testing.T) { +func Test_Push_ToRemote(t *testing.T) { repo := createBareTestRepo(t) defer os.RemoveAll(repo.Path()) - localRepo := createTestRepo(t) - defer os.RemoveAll(localRepo.Workdir()) + repo2 := createTestRepo(t) + defer os.RemoveAll(repo2.Workdir()) - remote, err := localRepo.CreateRemote("test_push", repo.Path()) + remote, err := repo2.CreateRemote("test_push", repo.Path()) checkFatal(t, err) - seedTestRepo(t, localRepo) - - err = remote.Push([]string{"refs/heads/master"}, nil, nil, "") + index, err := repo2.Index() checkFatal(t, err) - _, err = localRepo.LookupReference("refs/remotes/test_push/master") + index.AddByPath("README") + + err = index.Write() checkFatal(t, err) - _, err = repo.LookupReference("refs/heads/master") + newTreeId, err := index.WriteTree() checkFatal(t, err) + + tree, err := repo2.LookupTree(newTreeId) + checkFatal(t, err) + + sig := &Signature{Name: "Rand Om Hacker", Email: "random@hacker.com", When: time.Now()} + // this should cause master branch to be created if it does not already exist + _, err = repo2.CreateCommit("HEAD", sig, sig, "message", tree) + checkFatal(t, err) + + push, err := remote.NewPush() + checkFatal(t, err) + + err = push.AddRefspec("refs/heads/master") + checkFatal(t, err) + + err = push.Finish() + checkFatal(t, err) + + err = push.StatusForeach(func(ref string, msg string) int { + return 0 + }) + checkFatal(t, err) + + if !push.UnpackOk() { + t.Fatalf("unable to unpack") + } + + defer remote.Free() + defer repo.Free() } diff --git a/refdb.go b/refdb.go index 0d1e241..46fbb63 100644 --- a/refdb.go +++ b/refdb.go @@ -2,6 +2,7 @@ package git /* #include +#include #include extern void _go_git_refdb_backend_free(git_refdb_backend *backend); @@ -22,9 +23,6 @@ type RefdbBackend struct { func (v *Repository) NewRefdb() (refdb *Refdb, err error) { refdb = new(Refdb) - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_refdb_new(&refdb.ptr, v.ptr) if ret < 0 { return nil, MakeGitError(ret) @@ -40,9 +38,6 @@ func NewRefdbBackendFromC(ptr *C.git_refdb_backend) (backend *RefdbBackend) { } func (v *Refdb) SetBackend(backend *RefdbBackend) (err error) { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_refdb_set_backend(v.ptr, backend.ptr) if ret < 0 { backend.Free() diff --git a/reference.go b/reference.go index 46436a6..ce9d722 100644 --- a/reference.go +++ b/reference.go @@ -2,6 +2,7 @@ package git /* #include +#include */ import "C" import ( @@ -293,10 +294,6 @@ func (v *ReferenceNameIterator) Next() (string, error) { // returned error is git.ErrIterOver func (v *ReferenceIterator) Next() (*Reference, error) { var ptr *C.git_reference - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_reference_next(&ptr, v.ptr) if ret < 0 { return nil, MakeGitError(ret) diff --git a/remote.go b/remote.go index 2684c20..a2288fa 100644 --- a/remote.go +++ b/remote.go @@ -53,9 +53,6 @@ type CredentialsCallback func(url string, username_from_url string, allowed_type type TransferProgressCallback func(stats TransferProgress) ErrorCode type UpdateTipsCallback func(refname string, a *Oid, b *Oid) ErrorCode type CertificateCheckCallback func(cert *Certificate, valid bool, hostname string) ErrorCode -type PackbuilderProgressCallback func(stage int32, current, total uint32) ErrorCode -type PushTransferProgressCallback func(current, total uint32, bytes uint) ErrorCode -type PushUpdateReferenceCallback func(refname, status string) ErrorCode type RemoteCallbacks struct { SidebandProgressCallback TransportMessageCallback @@ -64,9 +61,6 @@ type RemoteCallbacks struct { TransferProgressCallback UpdateTipsCallback CertificateCheckCallback - PackProgressCallback PackbuilderProgressCallback - PushTransferProgressCallback - PushUpdateReferenceCallback } type Remote struct { @@ -107,10 +101,6 @@ type HostkeyCertificate struct { HashSHA1 [20]byte } -type PushOptions struct { - PbParallelism uint -} - type RemoteHead struct { Id *Oid Name string @@ -226,38 +216,6 @@ func certificateCheckCallback(_cert *C.git_cert, _valid C.int, _host *C.char, da return int(callbacks.CertificateCheckCallback(&cert, valid, host)) } -//export packProgressCallback -func packProgressCallback(stage C.int, current, total C.uint, data unsafe.Pointer) int { - callbacks := (*RemoteCallbacks)(data) - - if callbacks.PackProgressCallback == nil { - return 0 - } - - return int(callbacks.PackProgressCallback(int32(stage), uint32(current), uint32(total))) -} - -//export pushTransferProgressCallback -func pushTransferProgressCallback(current, total C.uint, bytes C.size_t, data unsafe.Pointer) int { - callbacks := (*RemoteCallbacks)(data) - if callbacks.PushTransferProgressCallback == nil { - return 0 - } - - return int(callbacks.PushTransferProgressCallback(uint32(current), uint32(total), uint(bytes))) -} - -//export pushUpdateReferenceCallback -func pushUpdateReferenceCallback(refname, status *C.char, data unsafe.Pointer) int { - callbacks := (*RemoteCallbacks)(data) - - if callbacks.PushUpdateReferenceCallback == nil { - return 0 - } - - return int(callbacks.PushUpdateReferenceCallback(C.GoString(refname), C.GoString(status))) -} - func RemoteIsValidName(name string) bool { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) @@ -291,10 +249,6 @@ func (r *Remote) Free() { func (repo *Repository) ListRemotes() ([]string, error) { var r C.git_strarray - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ecode := C.git_remote_list(&r, repo.ptr) if ecode < 0 { return nil, MakeGitError(ecode) @@ -324,20 +278,6 @@ func (repo *Repository) CreateRemote(name string, url string) (*Remote, error) { return remote, nil } -func (repo *Repository) DeleteRemote(name string) error { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_remote_delete(repo.ptr, cname) - if ret < 0 { - return MakeGitError(ret) - } - return nil -} - func (repo *Repository) CreateRemoteWithFetchspec(name string, url string, fetch string) (*Remote, error) { remote := &Remote{} @@ -378,7 +318,7 @@ func (repo *Repository) CreateAnonymousRemote(url, fetch string) (*Remote, error return remote, nil } -func (repo *Repository) LookupRemote(name string) (*Remote, error) { +func (repo *Repository) LoadRemote(name string) (*Remote, error) { remote := &Remote{} cname := C.CString(name) @@ -387,7 +327,7 @@ func (repo *Repository) LookupRemote(name string) (*Remote, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_lookup(&remote.ptr, repo.ptr, cname) + ret := C.git_remote_load(&remote.ptr, repo.ptr, cname) if ret < 0 { return nil, MakeGitError(ret) } @@ -619,9 +559,6 @@ func (o *Remote) Fetch(refspecs []string, sig *Signature, msg string) error { crefspecs.strings = makeCStringsFromStrings(refspecs) defer freeStrarray(&crefspecs) - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_remote_fetch(o.ptr, &crefspecs, csig, cmsg) if ret < 0 { return MakeGitError(ret) @@ -652,9 +589,6 @@ func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) { var refs **C.git_remote_head var length C.size_t - runtime.LockOSThread() - defer runtime.UnlockOSThread() - if ret := C.git_remote_ls(&refs, &length, o.ptr); ret != 0 { return nil, MakeGitError(ret) } @@ -692,54 +626,3 @@ func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) { return heads, nil } - -func (o *Remote) Push(refspecs []string, opts *PushOptions, sig *Signature, msg string) error { - var csig *C.git_signature = nil - if sig != nil { - csig = sig.toC() - defer C.free(unsafe.Pointer(csig)) - } - - var cmsg *C.char - if msg == "" { - cmsg = nil - } else { - cmsg = C.CString(msg) - defer C.free(unsafe.Pointer(cmsg)) - } - - var copts C.git_push_options - C.git_push_init_options(&copts, C.GIT_PUSH_OPTIONS_VERSION) - if opts != nil { - copts.pb_parallelism = C.uint(opts.PbParallelism) - } - - crefspecs := C.git_strarray{} - crefspecs.count = C.size_t(len(refspecs)) - crefspecs.strings = makeCStringsFromStrings(refspecs) - defer freeStrarray(&crefspecs) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_remote_push(o.ptr, &crefspecs, &copts, csig, cmsg) - if ret < 0 { - return MakeGitError(ret) - } - return nil -} - -func (o *Remote) PruneRefs() bool { - return C.git_remote_prune_refs(o.ptr) > 0 -} - -func (o *Remote) Prune() error { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_remote_prune(o.ptr) - if ret < 0 { - return MakeGitError(ret) - } - return nil -} diff --git a/remote_test.go b/remote_test.go index 54a66ed..631a6cd 100644 --- a/remote_test.go +++ b/remote_test.go @@ -1,10 +1,8 @@ package git import ( - "fmt" "os" "testing" - "time" ) func TestRefspecs(t *testing.T) { @@ -134,84 +132,3 @@ func TestRemoteLsFiltering(t *testing.T) { t.Fatalf("Expected head to have a name, but it's empty") } } - -func TestRemotePruneRefs(t *testing.T) { - repo := createTestRepo(t) - defer os.RemoveAll(repo.Workdir()) - defer repo.Free() - - config, err := repo.Config() - checkFatal(t, err) - defer config.Free() - - err = config.SetBool("remote.origin.prune", true) - checkFatal(t, err) - - _, err = repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") - checkFatal(t, err) - - remote, err := repo.LookupRemote("origin") - checkFatal(t, err) - - if !remote.PruneRefs() { - t.Fatal("Expected remote to be configured to prune references") - } -} - -func TestRemotePrune(t *testing.T) { - remoteRepo := createTestRepo(t) - defer os.RemoveAll(remoteRepo.Workdir()) - defer remoteRepo.Free() - - head, _ := seedTestRepo(t, remoteRepo) - commit, err := remoteRepo.LookupCommit(head) - checkFatal(t, err) - defer commit.Free() - - sig := &Signature{ - Name: "Rand Om Hacker", - Email: "random@hacker.com", - When: time.Now(), - } - - remoteRef, err := remoteRepo.CreateBranch("test-prune", commit, true, sig, "branch test-prune") - checkFatal(t, err) - - repo := createTestRepo(t) - defer os.RemoveAll(repo.Workdir()) - defer repo.Free() - - config, err := repo.Config() - checkFatal(t, err) - defer config.Free() - - remoteUrl := fmt.Sprintf("file://%s", remoteRepo.Workdir()) - remote, err := repo.CreateRemote("origin", remoteUrl) - checkFatal(t, err) - - err = remote.Fetch([]string{"test-prune"}, sig, "") - checkFatal(t, err) - - _, err = repo.CreateReference("refs/remotes/origin/test-prune", head, true, sig, "remote reference") - checkFatal(t, err) - - err = remoteRef.Delete() - checkFatal(t, err) - - err = config.SetBool("remote.origin.prune", true) - checkFatal(t, err) - - rr, err := repo.LookupRemote("origin") - checkFatal(t, err) - - err = rr.ConnectFetch() - checkFatal(t, err) - - err = rr.Prune() - checkFatal(t, err) - - _, err = repo.LookupReference("refs/remotes/origin/test-prune") - if err == nil { - t.Fatal("Expected error getting a pruned reference") - } -} diff --git a/repository.go b/repository.go index 7760c3a..09f5fef 100644 --- a/repository.go +++ b/repository.go @@ -72,9 +72,6 @@ func InitRepository(path string, isbare bool) (*Repository, error) { func NewRepositoryWrapOdb(odb *Odb) (repo *Repository, err error) { repo = new(Repository) - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_repository_wrap_odb(&repo.ptr, odb.ptr) if ret < 0 { return nil, MakeGitError(ret) @@ -389,9 +386,6 @@ func (v *Repository) CreateTag( ctarget := commit.gitObject.ptr - runtime.LockOSThread() - defer runtime.UnlockOSThread() - ret := C.git_tag_create(oid.toC(), v.ptr, cname, ctarget, taggerSig, cmessage, 0) if ret < 0 { return nil, MakeGitError(ret) @@ -456,7 +450,7 @@ func (v *Repository) TreeBuilder() (*TreeBuilder, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - if ret := C.git_treebuilder_new(&bld.ptr, v.ptr, nil); ret < 0 { + if ret := C.git_treebuilder_create(&bld.ptr, nil); ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(bld, (*TreeBuilder).Free) @@ -471,7 +465,7 @@ func (v *Repository) TreeBuilderFromTree(tree *Tree) (*TreeBuilder, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - if ret := C.git_treebuilder_new(&bld.ptr, v.ptr, tree.cast_ptr); ret < 0 { + if ret := C.git_treebuilder_create(&bld.ptr, tree.cast_ptr); ret < 0 { return nil, MakeGitError(ret) } runtime.SetFinalizer(bld, (*TreeBuilder).Free) @@ -530,103 +524,3 @@ func (v *Repository) DwimReference(name string) (*Reference, error) { return newReferenceFromC(ptr, v), nil } - -// CreateNote adds a note for an object -func (v *Repository) CreateNote( - ref string, author, committer *Signature, id *Oid, - note string, force bool) (*Oid, error) { - - oid := new(Oid) - - var cref *C.char - if ref == "" { - cref = nil - } else { - cref = C.CString(ref) - defer C.free(unsafe.Pointer(cref)) - } - - authorSig := author.toC() - defer C.git_signature_free(authorSig) - - committerSig := committer.toC() - defer C.git_signature_free(committerSig) - - cnote := C.CString(note) - defer C.free(unsafe.Pointer(cnote)) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_note_create( - oid.toC(), v.ptr, cref, authorSig, - committerSig, id.toC(), cnote, cbool(force)) - - if ret < 0 { - return nil, MakeGitError(ret) - } - return oid, nil -} - -// ReadNote reads the note for an object -func (v *Repository) ReadNote(ref string, id *Oid) (*Note, error) { - var cref *C.char - if ref == "" { - cref = nil - } else { - cref = C.CString(ref) - defer C.free(unsafe.Pointer(cref)) - } - - note := new(Note) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - if ret := C.git_note_read(¬e.ptr, v.ptr, cref, id.toC()); ret < 0 { - return nil, MakeGitError(ret) - } - - runtime.SetFinalizer(note, (*Note).Free) - return note, nil -} - -// RemoveNote removes the note for an object -func (v *Repository) RemoveNote(ref string, author, committer *Signature, id *Oid) error { - var cref *C.char - if ref == "" { - cref = nil - } else { - cref = C.CString(ref) - defer C.free(unsafe.Pointer(cref)) - } - - authorSig := author.toC() - defer C.git_signature_free(authorSig) - - committerSig := committer.toC() - defer C.git_signature_free(committerSig) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_note_remove(v.ptr, cref, authorSig, committerSig, id.toC()) - if ret < 0 { - return MakeGitError(ret) - } - return nil -} - -// DefaultNoteRef returns the default notes reference for a repository -func (v *Repository) DefaultNoteRef() (string, error) { - var ptr *C.char - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - if ret := C.git_note_default_ref(&ptr, v.ptr); ret < 0 { - return "", MakeGitError(ret) - } - - return C.GoString(ptr), nil -} diff --git a/revparse.go b/revparse.go index 7eb04f1..308da4c 100644 --- a/revparse.go +++ b/revparse.go @@ -2,6 +2,7 @@ package git /* #include +#include extern void _go_git_revspec_free(git_revspec *revspec); */ diff --git a/script/check-MakeGitError-thread-lock.go b/script/check-MakeGitError-thread-lock.go deleted file mode 100644 index f6b01b3..0000000 --- a/script/check-MakeGitError-thread-lock.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/printer" - "go/token" - "log" - "os" - "path/filepath" - "strings" -) - -var ( - fset = token.NewFileSet() -) - -func main() { - log.SetFlags(0) - - bpkg, err := build.ImportDir(".", 0) - if err != nil { - log.Fatal(err) - } - - pkgs, err := parser.ParseDir(fset, bpkg.Dir, func(fi os.FileInfo) bool { return filepath.Ext(fi.Name()) == ".go" }, 0) - if err != nil { - log.Fatal(err) - } - - for _, pkg := range pkgs { - if err := checkPkg(pkg); err != nil { - log.Fatal(err) - } - } - if len(pkgs) == 0 { - log.Fatal("No packages to check.") - } -} - -var ignoreViolationsInFunc = map[string]bool{ - "MakeGitError": true, - "MakeGitError2": true, -} - -func checkPkg(pkg *ast.Package) error { - var violations []string - ast.Inspect(pkg, func(node ast.Node) bool { - switch node := node.(type) { - case *ast.FuncDecl: - var b bytes.Buffer - if err := printer.Fprint(&b, fset, node); err != nil { - log.Fatal(err) - } - src := b.String() - - if strings.Contains(src, "MakeGitError") && !strings.Contains(src, "runtime.LockOSThread()") && !strings.Contains(src, "defer runtime.UnlockOSThread()") && !ignoreViolationsInFunc[node.Name.Name] { - pos := fset.Position(node.Pos()) - violations = append(violations, fmt.Sprintf("%s at %s:%d", node.Name.Name, pos.Filename, pos.Line)) - } - } - return true - }) - if len(violations) > 0 { - return fmt.Errorf("%d non-thread-locked calls to MakeGitError found. To fix, add the following to each func below that calls MakeGitError, before the cgo call that might produce the error:\n\n\truntime.LockOSThread()\n\tdefer runtime.UnlockOSThread()\n\n%s", len(violations), strings.Join(violations, "\n")) - } - return nil -} diff --git a/status.go b/status.go index c849aca..1d1d098 100644 --- a/status.go +++ b/status.go @@ -2,6 +2,7 @@ package git /* #include +#include */ import "C" diff --git a/submodule.go b/submodule.go index 6923c61..35ceeb0 100644 --- a/submodule.go +++ b/submodule.go @@ -2,6 +2,7 @@ package git /* #include +#include extern int _go_git_visit_submodule(git_repository *repo, void *fct); */ @@ -11,14 +12,6 @@ import ( "unsafe" ) -// SubmoduleUpdateOptions -type SubmoduleUpdateOptions struct { - *CheckoutOpts - *RemoteCallbacks - CloneCheckoutStrategy CheckoutStrategy - Signature *Signature -} - // Submodule type Submodule struct { ptr *C.git_submodule @@ -28,10 +21,10 @@ type SubmoduleUpdate int const ( SubmoduleUpdateReset SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_RESET - SubmoduleUpdateCheckout SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_CHECKOUT - SubmoduleUpdateRebase SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_REBASE - SubmoduleUpdateMerge SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_MERGE - SubmoduleUpdateNone SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_NONE + SubmoduleUpdateCheckout SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_CHECKOUT + SubmoduleUpdateRebase SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_REBASE + SubmoduleUpdateMerge SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_MERGE + SubmoduleUpdateNone SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_NONE ) type SubmoduleIgnore int @@ -97,10 +90,10 @@ func (repo *Repository) LookupSubmodule(name string) (*Submodule, error) { type SubmoduleCbk func(sub *Submodule, name string) int //export SubmoduleVisitor -func SubmoduleVisitor(csub unsafe.Pointer, name *C.char, cfct unsafe.Pointer) C.int { +func SubmoduleVisitor(csub unsafe.Pointer, name string, cfct unsafe.Pointer) int { sub := &Submodule{(*C.git_submodule)(csub)} fct := *(*SubmoduleCbk)(cfct) - return (C.int)(fct(sub, C.GoString(name))) + return fct(sub, name) } func (repo *Repository) ForeachSubmodule(cbk SubmoduleCbk) error { @@ -234,8 +227,8 @@ func (sub *Submodule) SetIgnore(ignore SubmoduleIgnore) SubmoduleIgnore { return SubmoduleIgnore(o) } -func (sub *Submodule) UpdateStrategy() SubmoduleUpdate { - o := C.git_submodule_update_strategy(sub.ptr) +func (sub *Submodule) Update() SubmoduleUpdate { + o := C.git_submodule_update(sub.ptr) return SubmoduleUpdate(o) } @@ -315,31 +308,3 @@ func (repo *Repository) ReloadAllSubmodules(force bool) error { } return nil } - -func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error { - var copts C.git_submodule_update_options - populateSubmoduleUpdateOptions(&copts, opts) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_submodule_update(sub.ptr, cbool(init), &copts) - if ret < 0 { - return MakeGitError(ret) - } - - return nil -} - -func populateSubmoduleUpdateOptions(ptr *C.git_submodule_update_options, opts *SubmoduleUpdateOptions) { - C.git_submodule_update_init_options(ptr, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION) - - if opts == nil { - return - } - - populateCheckoutOpts(&ptr.checkout_opts, opts.CheckoutOpts) - populateRemoteCallbacks(&ptr.remote_callbacks, opts.RemoteCallbacks) - ptr.clone_checkout_strategy = C.uint(opts.CloneCheckoutStrategy) - ptr.signature = opts.Signature.toC() -} diff --git a/submodule_test.go b/submodule_test.go deleted file mode 100644 index 1c8f471..0000000 --- a/submodule_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package git - -import ( - "testing" -) - -func TestSubmoduleForeach(t *testing.T) { - repo := createTestRepo(t) - seedTestRepo(t, repo) - - _, err := repo.AddSubmodule("http://example.org/submodule", "submodule", true) - checkFatal(t, err) - - i := 0 - err = repo.ForeachSubmodule(func(sub *Submodule, name string) int { - i++ - return 0 - }) - checkFatal(t, err) - - if i != 1 { - t.Fatalf("expected one submodule found but got %i", i) - } -} diff --git a/tree.go b/tree.go index c18d02a..45de9f1 100644 --- a/tree.go +++ b/tree.go @@ -2,6 +2,7 @@ package git /* #include +#include extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr); */ @@ -161,7 +162,7 @@ func (v *TreeBuilder) Write() (*Oid, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - err := C.git_treebuilder_write(oid.toC(), v.ptr) + err := C.git_treebuilder_write(oid.toC(), v.repo.ptr, v.ptr) if err < 0 { return nil, MakeGitError(err) diff --git a/vendor/libgit2 b/vendor/libgit2 index 04bdd97..d09458f 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 04bdd97f2b63793a8720fd19007911e946ba3c55 +Subproject commit d09458f3e9f24afa0689ce90b7d4191872372634 diff --git a/walk.go b/walk.go index d02044a..f243e21 100644 --- a/walk.go +++ b/walk.go @@ -2,6 +2,7 @@ package git /* #include +#include */ import "C" diff --git a/wrapper.c b/wrapper.c index 938fd17..6e33fa2 100644 --- a/wrapper.c +++ b/wrapper.c @@ -1,7 +1,9 @@ #include "_cgo_export.h" -#include -#include -#include +#include "git2.h" +#include "git2/sys/odb_backend.h" +#include "git2/sys/refdb_backend.h" +#include "git2/submodule.h" +#include "git2/pack.h" typedef int (*gogit_submodule_cbk)(git_submodule *sm, const char *name, void *payload); @@ -69,17 +71,24 @@ void _go_git_setup_diff_notify_callbacks(git_diff_options *opts) { void _go_git_setup_callbacks(git_remote_callbacks *callbacks) { typedef int (*completion_cb)(git_remote_completion_type type, void *data); typedef int (*update_tips_cb)(const char *refname, const git_oid *a, const git_oid *b, void *data); - typedef int (*push_update_reference_cb)(const char *refname, const char *status, void *data); - callbacks->sideband_progress = (git_transport_message_cb)sidebandProgressCallback; callbacks->completion = (completion_cb)completionCallback; callbacks->credentials = (git_cred_acquire_cb)credentialsCallback; callbacks->transfer_progress = (git_transfer_progress_cb)transferProgressCallback; callbacks->update_tips = (update_tips_cb)updateTipsCallback; callbacks->certificate_check = (git_transport_certificate_check_cb) certificateCheckCallback; - callbacks->pack_progress = (git_packbuilder_progress) packProgressCallback; - callbacks->push_transfer_progress = (git_push_transfer_progress) pushTransferProgressCallback; - callbacks->push_update_reference = (push_update_reference_cb) pushUpdateReferenceCallback; +} + +typedef int (*status_foreach_cb)(const char *ref, const char *msg, void *data); + +int _go_git_push_status_foreach(git_push *push, void *data) +{ + return git_push_status_foreach(push, (status_foreach_cb)statusForeach, data); +} + +int _go_git_push_set_callbacks(git_push *push, void *packbuilder_progress_data, void *transfer_progress_data) +{ + return git_push_set_callbacks(push, packbuilderProgress, packbuilder_progress_data, pushTransferProgress, transfer_progress_data); } int _go_blob_chunk_cb(char *buffer, size_t maxLen, void *payload)