Merge branch 'master' into v22
This commit is contained in:
commit
137c4fc3c8
19
README.md
19
README.md
|
@ -3,15 +3,28 @@ 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=v22)](https://travis-ci.org/libgit2/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=v22)](https://travis-ci.org/libgit2/git2go)
|
||||||
|
|
||||||
|
|
||||||
Go bindings for [libgit2](http://libgit2.github.com/). The master branch follows the latest libgit2 release.
|
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.
|
||||||
|
|
||||||
Installing
|
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 if you want to use HTTPS and SSH respectively.
|
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 `next` 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.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
git checkout next
|
||||||
git submodule update --init # get libgit2
|
git submodule update --init # get libgit2
|
||||||
make install
|
make install
|
||||||
|
|
||||||
|
@ -25,7 +38,7 @@ libgit2 uses OpenSSL and LibSSH2 for performing encrypted network connections. F
|
||||||
Running the tests
|
Running the tests
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
Similarly to installing, running the tests requires linking against the local libgit2 library, so the Makefile provides a wrapper
|
For the stable version, `go test` will work as usual. For the `next` branch, similarly to installing, running the tests requires linking against the local libgit2 library, so the Makefile provides a wrapper
|
||||||
|
|
||||||
make test
|
make test
|
||||||
|
|
||||||
|
|
29
branch.go
29
branch.go
|
@ -30,10 +30,7 @@ type BranchIterator struct {
|
||||||
repo *Repository
|
repo *Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
type BranchInfo struct {
|
type BranchIteratorFunc func(*Branch, BranchType) error
|
||||||
Branch *Branch
|
|
||||||
Type BranchType
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBranchIteratorFromC(repo *Repository, ptr *C.git_branch_iterator) *BranchIterator {
|
func newBranchIteratorFromC(repo *Repository, ptr *C.git_branch_iterator) *BranchIterator {
|
||||||
i := &BranchIterator{repo: repo, ptr: ptr}
|
i := &BranchIterator{repo: repo, ptr: ptr}
|
||||||
|
@ -65,8 +62,20 @@ func (i *BranchIterator) Free() {
|
||||||
C.git_branch_iterator_free(i.ptr)
|
C.git_branch_iterator_free(i.ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) NewBranchIterator(flags BranchType) (*BranchIterator, error) {
|
func (i *BranchIterator) ForEach(f BranchIteratorFunc) error {
|
||||||
|
b, t, err := i.Next()
|
||||||
|
|
||||||
|
for err == nil {
|
||||||
|
err = f(b, t)
|
||||||
|
if err == nil {
|
||||||
|
b, t, err = i.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) NewBranchIterator(flags BranchType) (*BranchIterator, error) {
|
||||||
refType := C.git_branch_t(flags)
|
refType := C.git_branch_t(flags)
|
||||||
var ptr *C.git_branch_iterator
|
var ptr *C.git_branch_iterator
|
||||||
|
|
||||||
|
@ -87,7 +96,10 @@ func (repo *Repository) CreateBranch(branchName string, target *Commit, force bo
|
||||||
cBranchName := C.CString(branchName)
|
cBranchName := C.CString(branchName)
|
||||||
cForce := cbool(force)
|
cForce := cbool(force)
|
||||||
|
|
||||||
cSignature := signature.toC()
|
cSignature, err := signature.toC()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer C.git_signature_free(cSignature)
|
defer C.git_signature_free(cSignature)
|
||||||
|
|
||||||
var cmsg *C.char
|
var cmsg *C.char
|
||||||
|
@ -124,7 +136,10 @@ func (b *Branch) Move(newBranchName string, force bool, signature *Signature, ms
|
||||||
cNewBranchName := C.CString(newBranchName)
|
cNewBranchName := C.CString(newBranchName)
|
||||||
cForce := cbool(force)
|
cForce := cbool(force)
|
||||||
|
|
||||||
cSignature := signature.toC()
|
cSignature, err := signature.toC()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer C.git_signature_free(cSignature)
|
defer C.git_signature_free(cSignature)
|
||||||
|
|
||||||
var cmsg *C.char
|
var cmsg *C.char
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import "testing"
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBranchIterator(t *testing.T) {
|
func TestBranchIterator(t *testing.T) {
|
||||||
|
|
||||||
repo := createTestRepo(t)
|
repo := createTestRepo(t)
|
||||||
seedTestRepo(t, repo)
|
seedTestRepo(t, repo)
|
||||||
|
|
||||||
|
@ -24,3 +21,35 @@ func TestBranchIterator(t *testing.T) {
|
||||||
t.Fatal("expected iterover")
|
t.Fatal("expected iterover")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBranchIteratorEach(t *testing.T) {
|
||||||
|
repo := createTestRepo(t)
|
||||||
|
seedTestRepo(t, repo)
|
||||||
|
|
||||||
|
i, err := repo.NewBranchIterator(BranchLocal)
|
||||||
|
checkFatal(t, err)
|
||||||
|
|
||||||
|
var names []string
|
||||||
|
f := func(b *Branch, t BranchType) error {
|
||||||
|
name, err := b.Name()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
names = append(names, name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = i.ForEach(f)
|
||||||
|
if err != nil && !IsErrorCode(err, ErrIterOver) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(names) != 1 {
|
||||||
|
t.Fatalf("expect 1 branch, but it was %d\n", len(names))
|
||||||
|
}
|
||||||
|
|
||||||
|
if names[0] != "master" {
|
||||||
|
t.Fatalf("expect branch master, but it was %s\n", names[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
55
checkout.go
55
checkout.go
|
@ -7,6 +7,7 @@ import "C"
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CheckoutStrategy uint
|
type CheckoutStrategy uint
|
||||||
|
@ -19,10 +20,10 @@ const (
|
||||||
CheckoutAllowConflicts CheckoutStrategy = C.GIT_CHECKOUT_ALLOW_CONFLICTS // Allow checkout to make safe updates even if conflicts are found
|
CheckoutAllowConflicts CheckoutStrategy = C.GIT_CHECKOUT_ALLOW_CONFLICTS // Allow checkout to make safe updates even if conflicts are found
|
||||||
CheckoutRemoveUntracked CheckoutStrategy = C.GIT_CHECKOUT_REMOVE_UNTRACKED // Remove untracked files not in index (that are not ignored)
|
CheckoutRemoveUntracked CheckoutStrategy = C.GIT_CHECKOUT_REMOVE_UNTRACKED // Remove untracked files not in index (that are not ignored)
|
||||||
CheckoutRemoveIgnored CheckoutStrategy = C.GIT_CHECKOUT_REMOVE_IGNORED // Remove ignored files not in index
|
CheckoutRemoveIgnored CheckoutStrategy = C.GIT_CHECKOUT_REMOVE_IGNORED // Remove ignored files not in index
|
||||||
CheckotUpdateOnly CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_ONLY // Only update existing files, don't create new ones
|
CheckoutUpdateOnly CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_ONLY // Only update existing files, don't create new ones
|
||||||
CheckoutDontUpdateIndex CheckoutStrategy = C.GIT_CHECKOUT_DONT_UPDATE_INDEX // Normally checkout updates index entries as it goes; this stops that
|
CheckoutDontUpdateIndex CheckoutStrategy = C.GIT_CHECKOUT_DONT_UPDATE_INDEX // Normally checkout updates index entries as it goes; this stops that
|
||||||
CheckoutNoRefresh CheckoutStrategy = C.GIT_CHECKOUT_NO_REFRESH // Don't refresh index/config/etc before doing checkout
|
CheckoutNoRefresh CheckoutStrategy = C.GIT_CHECKOUT_NO_REFRESH // Don't refresh index/config/etc before doing checkout
|
||||||
CheckooutDisablePathspecMatch CheckoutStrategy = C.GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH // Treat pathspec as simple list of exact match file paths
|
CheckoutDisablePathspecMatch CheckoutStrategy = C.GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH // Treat pathspec as simple list of exact match file paths
|
||||||
CheckoutSkipUnmerged CheckoutStrategy = C.GIT_CHECKOUT_SKIP_UNMERGED // Allow checkout to skip unmerged files (NOT IMPLEMENTED)
|
CheckoutSkipUnmerged CheckoutStrategy = C.GIT_CHECKOUT_SKIP_UNMERGED // Allow checkout to skip unmerged files (NOT IMPLEMENTED)
|
||||||
CheckoutUserOurs CheckoutStrategy = C.GIT_CHECKOUT_USE_OURS // For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED)
|
CheckoutUserOurs CheckoutStrategy = C.GIT_CHECKOUT_USE_OURS // For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED)
|
||||||
CheckoutUseTheirs CheckoutStrategy = C.GIT_CHECKOUT_USE_THEIRS // For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED)
|
CheckoutUseTheirs CheckoutStrategy = C.GIT_CHECKOUT_USE_THEIRS // For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED)
|
||||||
|
@ -31,11 +32,25 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type CheckoutOpts struct {
|
type CheckoutOpts struct {
|
||||||
Strategy CheckoutStrategy // Default will be a dry run
|
Strategy CheckoutStrategy // Default will be a dry run
|
||||||
DisableFilters bool // Don't apply filters like CRLF conversion
|
DisableFilters bool // Don't apply filters like CRLF conversion
|
||||||
DirMode os.FileMode // Default is 0755
|
DirMode os.FileMode // Default is 0755
|
||||||
FileMode os.FileMode // Default is 0644 or 0755 as dictated by blob
|
FileMode os.FileMode // Default is 0644 or 0755 as dictated by blob
|
||||||
FileOpenFlags int // Default is O_CREAT | O_TRUNC | O_WRONLY
|
FileOpenFlags int // Default is O_CREAT | O_TRUNC | O_WRONLY
|
||||||
|
TargetDirectory string // Alternative checkout path to workdir
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOpts {
|
||||||
|
opts := CheckoutOpts{}
|
||||||
|
opts.Strategy = CheckoutStrategy(c.checkout_strategy)
|
||||||
|
opts.DisableFilters = c.disable_filters != 0
|
||||||
|
opts.DirMode = os.FileMode(c.dir_mode)
|
||||||
|
opts.FileMode = os.FileMode(c.file_mode)
|
||||||
|
opts.FileOpenFlags = int(c.file_open_flags)
|
||||||
|
if c.target_directory != nil {
|
||||||
|
opts.TargetDirectory = C.GoString(c.target_directory)
|
||||||
|
}
|
||||||
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opts *CheckoutOpts) toC() *C.git_checkout_options {
|
func (opts *CheckoutOpts) toC() *C.git_checkout_options {
|
||||||
|
@ -60,17 +75,29 @@ func populateCheckoutOpts(ptr *C.git_checkout_options, opts *CheckoutOpts) *C.gi
|
||||||
ptr.disable_filters = cbool(opts.DisableFilters)
|
ptr.disable_filters = cbool(opts.DisableFilters)
|
||||||
ptr.dir_mode = C.uint(opts.DirMode.Perm())
|
ptr.dir_mode = C.uint(opts.DirMode.Perm())
|
||||||
ptr.file_mode = C.uint(opts.FileMode.Perm())
|
ptr.file_mode = C.uint(opts.FileMode.Perm())
|
||||||
|
if opts.TargetDirectory != "" {
|
||||||
|
ptr.target_directory = C.CString(opts.TargetDirectory)
|
||||||
|
}
|
||||||
return ptr
|
return ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func freeCheckoutOpts(ptr *C.git_checkout_options) {
|
||||||
|
if ptr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
C.free(unsafe.Pointer(ptr.target_directory))
|
||||||
|
}
|
||||||
|
|
||||||
// Updates files in the index and the working tree to match the content of
|
// Updates files in the index and the working tree to match the content of
|
||||||
// the commit pointed at by HEAD. opts may be nil.
|
// the commit pointed at by HEAD. opts may be nil.
|
||||||
func (v *Repository) CheckoutHead(opts *CheckoutOpts) error {
|
func (v *Repository) CheckoutHead(opts *CheckoutOpts) error {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
ret := C.git_checkout_head(v.ptr, opts.toC())
|
cOpts := opts.toC()
|
||||||
|
defer freeCheckoutOpts(cOpts)
|
||||||
|
|
||||||
|
ret := C.git_checkout_head(v.ptr, cOpts)
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return MakeGitError(ret)
|
return MakeGitError(ret)
|
||||||
}
|
}
|
||||||
|
@ -90,7 +117,10 @@ func (v *Repository) CheckoutIndex(index *Index, opts *CheckoutOpts) error {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
ret := C.git_checkout_index(v.ptr, iptr, opts.toC())
|
cOpts := opts.toC()
|
||||||
|
defer freeCheckoutOpts(cOpts)
|
||||||
|
|
||||||
|
ret := C.git_checkout_index(v.ptr, iptr, cOpts)
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return MakeGitError(ret)
|
return MakeGitError(ret)
|
||||||
}
|
}
|
||||||
|
@ -102,7 +132,10 @@ func (v *Repository) CheckoutTree(tree *Tree, opts *CheckoutOpts) error {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
ret := C.git_checkout_tree(v.ptr, tree.ptr, opts.toC())
|
cOpts := opts.toC()
|
||||||
|
defer freeCheckoutOpts(cOpts)
|
||||||
|
|
||||||
|
ret := C.git_checkout_tree(v.ptr, tree.ptr, cOpts)
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return MakeGitError(ret)
|
return MakeGitError(ret)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <git2.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CherrypickOptions struct {
|
||||||
|
Version uint
|
||||||
|
Mainline uint
|
||||||
|
MergeOpts MergeOptions
|
||||||
|
CheckoutOpts CheckoutOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
func cherrypickOptionsFromC(c *C.git_cherrypick_options) CherrypickOptions {
|
||||||
|
opts := CherrypickOptions{
|
||||||
|
Version: uint(c.version),
|
||||||
|
Mainline: uint(c.mainline),
|
||||||
|
MergeOpts: mergeOptionsFromC(&c.merge_opts),
|
||||||
|
CheckoutOpts: checkoutOptionsFromC(&c.checkout_opts),
|
||||||
|
}
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *CherrypickOptions) toC() *C.git_cherrypick_options {
|
||||||
|
if opts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c := C.git_cherrypick_options{}
|
||||||
|
c.version = C.uint(opts.Version)
|
||||||
|
c.mainline = C.uint(opts.Mainline)
|
||||||
|
c.merge_opts = *opts.MergeOpts.toC()
|
||||||
|
c.checkout_opts = *opts.CheckoutOpts.toC()
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
func freeCherrypickOpts(ptr *C.git_cherrypick_options) {
|
||||||
|
if ptr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
freeCheckoutOpts(&ptr.checkout_opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultCherrypickOptions() (CherrypickOptions, error) {
|
||||||
|
c := C.git_cherrypick_options{}
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
ecode := C.git_cherrypick_init_options(&c, C.GIT_CHERRYPICK_OPTIONS_VERSION)
|
||||||
|
if ecode < 0 {
|
||||||
|
return CherrypickOptions{}, MakeGitError(ecode)
|
||||||
|
}
|
||||||
|
defer freeCherrypickOpts(&c)
|
||||||
|
return cherrypickOptionsFromC(&c), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Repository) Cherrypick(commit *Commit, opts CherrypickOptions) error {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
cOpts := opts.toC()
|
||||||
|
defer freeCherrypickOpts(cOpts)
|
||||||
|
|
||||||
|
ecode := C.git_cherrypick(v.ptr, commit.cast_ptr, cOpts)
|
||||||
|
if ecode < 0 {
|
||||||
|
return MakeGitError(ecode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkout(t *testing.T, repo *Repository, commit *Commit) {
|
||||||
|
tree, err := commit.Tree()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = repo.CheckoutTree(tree, &CheckoutOpts{Strategy: CheckoutSafe})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = repo.SetHeadDetached(commit.Id(), commit.Author(), "checkout")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = "Herro, Worrd!"
|
||||||
|
|
||||||
|
func readReadme(t *testing.T, repo *Repository) string {
|
||||||
|
bytes, err := ioutil.ReadFile(pathInRepo(repo, "README"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCherrypick(t *testing.T) {
|
||||||
|
repo := createTestRepo(t)
|
||||||
|
c1, _ := seedTestRepo(t, repo)
|
||||||
|
c2, _ := updateReadme(t, repo, content)
|
||||||
|
|
||||||
|
commit1, err := repo.LookupCommit(c1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
commit2, err := repo.LookupCommit(c2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkout(t, repo, commit1)
|
||||||
|
|
||||||
|
if readReadme(t, repo) == content {
|
||||||
|
t.Fatalf("README has wrong content after checking out initial commit")
|
||||||
|
}
|
||||||
|
|
||||||
|
opts, err := DefaultCherrypickOptions()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = repo.Cherrypick(commit2, opts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if readReadme(t, repo) != content {
|
||||||
|
t.Fatalf("README has wrong contents after cherry-picking")
|
||||||
|
}
|
||||||
|
|
||||||
|
state := repo.State()
|
||||||
|
if state != RepositoryStateCherrypick {
|
||||||
|
t.Fatal("Incorrect repository state: ", state)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = repo.StateCleanup()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
state = repo.State()
|
||||||
|
if state != RepositoryStateNone {
|
||||||
|
t.Fatal("Incorrect repository state: ", state)
|
||||||
|
}
|
||||||
|
}
|
1
clone.go
1
clone.go
|
@ -30,6 +30,7 @@ func Clone(url string, path string, options *CloneOptions) (*Repository, error)
|
||||||
|
|
||||||
var copts C.git_clone_options
|
var copts C.git_clone_options
|
||||||
populateCloneOptions(&copts, options)
|
populateCloneOptions(&copts, options)
|
||||||
|
defer freeCheckoutOpts(&copts.checkout_opts)
|
||||||
|
|
||||||
if len(options.CheckoutBranch) != 0 {
|
if len(options.CheckoutBranch) != 0 {
|
||||||
copts.checkout_branch = C.CString(options.CheckoutBranch)
|
copts.checkout_branch = C.CString(options.CheckoutBranch)
|
||||||
|
|
52
commit.go
52
commit.go
|
@ -9,8 +9,6 @@ import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Commit
|
// Commit
|
||||||
|
@ -23,6 +21,10 @@ func (c Commit) Message() string {
|
||||||
return C.GoString(C.git_commit_message(c.cast_ptr))
|
return C.GoString(C.git_commit_message(c.cast_ptr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c Commit) Summary() string {
|
||||||
|
return C.GoString(C.git_commit_summary(c.cast_ptr))
|
||||||
|
}
|
||||||
|
|
||||||
func (c Commit) Tree() (*Tree, error) {
|
func (c Commit) Tree() (*Tree, error) {
|
||||||
var ptr *C.git_tree
|
var ptr *C.git_tree
|
||||||
|
|
||||||
|
@ -68,49 +70,3 @@ func (c *Commit) ParentId(n uint) *Oid {
|
||||||
func (c *Commit) ParentCount() uint {
|
func (c *Commit) ParentCount() uint {
|
||||||
return uint(C.git_commit_parentcount(c.cast_ptr))
|
return uint(C.git_commit_parentcount(c.cast_ptr))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signature
|
|
||||||
|
|
||||||
type Signature struct {
|
|
||||||
Name string
|
|
||||||
Email string
|
|
||||||
When time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSignatureFromC(sig *C.git_signature) *Signature {
|
|
||||||
// git stores minutes, go wants seconds
|
|
||||||
loc := time.FixedZone("", int(sig.when.offset)*60)
|
|
||||||
return &Signature{
|
|
||||||
C.GoString(sig.name),
|
|
||||||
C.GoString(sig.email),
|
|
||||||
time.Unix(int64(sig.when.time), 0).In(loc),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the offset in mintes, which is what git wants
|
|
||||||
func (v *Signature) Offset() int {
|
|
||||||
_, offset := v.When.Zone()
|
|
||||||
return offset / 60
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sig *Signature) toC() *C.git_signature {
|
|
||||||
|
|
||||||
if sig == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var out *C.git_signature
|
|
||||||
|
|
||||||
name := C.CString(sig.Name)
|
|
||||||
defer C.free(unsafe.Pointer(name))
|
|
||||||
|
|
||||||
email := C.CString(sig.Email)
|
|
||||||
defer C.free(unsafe.Pointer(email))
|
|
||||||
|
|
||||||
ret := C.git_signature_new(&out, name, email, C.git_time_t(sig.When.Unix()), C.int(sig.Offset()))
|
|
||||||
if ret < 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
52
config.go
52
config.go
|
@ -35,14 +35,14 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigEntry struct {
|
type ConfigEntry struct {
|
||||||
Name string
|
Name string
|
||||||
Value string
|
Value string
|
||||||
Level ConfigLevel
|
Level ConfigLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
func newConfigEntryFromC(centry *C.git_config_entry) *ConfigEntry {
|
func newConfigEntryFromC(centry *C.git_config_entry) *ConfigEntry {
|
||||||
return &ConfigEntry{
|
return &ConfigEntry{
|
||||||
Name: C.GoString(centry.name),
|
Name: C.GoString(centry.name),
|
||||||
Value: C.GoString(centry.value),
|
Value: C.GoString(centry.value),
|
||||||
Level: ConfigLevel(centry.level),
|
Level: ConfigLevel(centry.level),
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,6 @@ func (c *Config) AddFile(path string, level ConfigLevel, force bool) error {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
|
||||||
ret := C.git_config_add_file_ondisk(c.ptr, cpath, C.git_config_level_t(level), cbool(force))
|
ret := C.git_config_add_file_ondisk(c.ptr, cpath, C.git_config_level_t(level), cbool(force))
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return MakeGitError(ret)
|
return MakeGitError(ret)
|
||||||
|
@ -130,7 +129,6 @@ func (c *Config) LookupString(name string) (string, error) {
|
||||||
return C.GoString(ptr), nil
|
return C.GoString(ptr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (c *Config) LookupBool(name string) (bool, error) {
|
func (c *Config) LookupBool(name string) (bool, error) {
|
||||||
var out C.int
|
var out C.int
|
||||||
cname := C.CString(name)
|
cname := C.CString(name)
|
||||||
|
@ -234,7 +232,6 @@ func (c *Config) SetInt32(name string, value int32) (err error) {
|
||||||
cname := C.CString(name)
|
cname := C.CString(name)
|
||||||
defer C.free(unsafe.Pointer(cname))
|
defer C.free(unsafe.Pointer(cname))
|
||||||
|
|
||||||
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
@ -368,3 +365,48 @@ func (iter *ConfigIterator) Free() {
|
||||||
runtime.SetFinalizer(iter, nil)
|
runtime.SetFinalizer(iter, nil)
|
||||||
C.free(unsafe.Pointer(iter.ptr))
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -57,6 +57,10 @@ func seedTestRepo(t *testing.T, repo *Repository) (*Oid, *Oid) {
|
||||||
return commitId, treeId
|
return commitId, treeId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pathInRepo(repo *Repository, name string) string {
|
||||||
|
return path.Join(path.Dir(path.Dir(repo.Path())), name)
|
||||||
|
}
|
||||||
|
|
||||||
func updateReadme(t *testing.T, repo *Repository, content string) (*Oid, *Oid) {
|
func updateReadme(t *testing.T, repo *Repository, content string) (*Oid, *Oid) {
|
||||||
loc, err := time.LoadLocation("Europe/Berlin")
|
loc, err := time.LoadLocation("Europe/Berlin")
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
|
@ -67,7 +71,7 @@ func updateReadme(t *testing.T, repo *Repository, content string) (*Oid, *Oid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpfile := "README"
|
tmpfile := "README"
|
||||||
err = ioutil.WriteFile(path.Join(path.Dir(path.Dir(repo.Path())), tmpfile), []byte(content), 0644)
|
err = ioutil.WriteFile(pathInRepo(repo, tmpfile), []byte(content), 0644)
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
|
|
||||||
idx, err := repo.Index()
|
idx, err := repo.Index()
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <git2.h>
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
}
|
1
merge.go
1
merge.go
|
@ -145,6 +145,7 @@ func (r *Repository) Merge(theirHeads []*AnnotatedCommit, mergeOptions *MergeOpt
|
||||||
|
|
||||||
cMergeOpts := mergeOptions.toC()
|
cMergeOpts := mergeOptions.toC()
|
||||||
cCheckoutOpts := checkoutOptions.toC()
|
cCheckoutOpts := checkoutOptions.toC()
|
||||||
|
defer freeCheckoutOpts(cCheckoutOpts)
|
||||||
|
|
||||||
gmerge_head_array := make([]*C.git_annotated_commit, len(theirHeads))
|
gmerge_head_array := make([]*C.git_annotated_commit, len(theirHeads))
|
||||||
for i := 0; i < len(theirHeads); i++ {
|
for i := 0; i < len(theirHeads); i++ {
|
||||||
|
|
|
@ -25,12 +25,12 @@ type Object interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type gitObject struct {
|
type gitObject struct {
|
||||||
ptr *C.git_object
|
ptr *C.git_object
|
||||||
repo *Repository
|
repo *Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t ObjectType) String() (string) {
|
func (t ObjectType) String() string {
|
||||||
switch (t) {
|
switch t {
|
||||||
case ObjectAny:
|
case ObjectAny:
|
||||||
return "Any"
|
return "Any"
|
||||||
case ObjectBad:
|
case ObjectBad:
|
||||||
|
@ -71,7 +71,7 @@ func (o *gitObject) Free() {
|
||||||
|
|
||||||
func allocObject(cobj *C.git_object, repo *Repository) Object {
|
func allocObject(cobj *C.git_object, repo *Repository) Object {
|
||||||
obj := gitObject{
|
obj := gitObject{
|
||||||
ptr: cobj,
|
ptr: cobj,
|
||||||
repo: repo,
|
repo: repo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
8
odb.go
8
odb.go
|
@ -94,7 +94,7 @@ type OdbForEachCallback func(id *Oid) error
|
||||||
|
|
||||||
type foreachData struct {
|
type foreachData struct {
|
||||||
callback OdbForEachCallback
|
callback OdbForEachCallback
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
//export odbForEachCb
|
//export odbForEachCb
|
||||||
|
@ -111,9 +111,9 @@ func odbForEachCb(id *C.git_oid, payload unsafe.Pointer) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Odb) ForEach(callback OdbForEachCallback) error {
|
func (v *Odb) ForEach(callback OdbForEachCallback) error {
|
||||||
data := foreachData {
|
data := foreachData{
|
||||||
callback: callback,
|
callback: callback,
|
||||||
err: nil,
|
err: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
|
@ -138,7 +138,7 @@ func (v *Odb) Hash(data []byte, otype ObjectType) (oid *Oid, err error) {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
ret := C.git_odb_hash(oid.toC(), ptr, C.size_t(header.Len), C.git_otype(otype));
|
ret := C.git_odb_hash(oid.toC(), ptr, C.size_t(header.Len), C.git_otype(otype))
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return nil, MakeGitError(ret)
|
return nil, MakeGitError(ret)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ func TestOdbStream(t *testing.T) {
|
||||||
|
|
||||||
func TestOdbHash(t *testing.T) {
|
func TestOdbHash(t *testing.T) {
|
||||||
|
|
||||||
repo := createTestRepo(t)
|
repo := createTestRepo(t)
|
||||||
defer os.RemoveAll(repo.Workdir())
|
defer os.RemoveAll(repo.Workdir())
|
||||||
_, _ = seedTestRepo(t, repo)
|
_, _ = seedTestRepo(t, repo)
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ func packbuilderForEachCb(buf unsafe.Pointer, size C.size_t, payload unsafe.Poin
|
||||||
func (pb *Packbuilder) ForEach(callback PackbuilderForeachCallback) error {
|
func (pb *Packbuilder) ForEach(callback PackbuilderForeachCallback) error {
|
||||||
data := packbuilderCbData{
|
data := packbuilderCbData{
|
||||||
callback: callback,
|
callback: callback,
|
||||||
err: nil,
|
err: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
|
|
|
@ -20,7 +20,11 @@ func TestPatch(t *testing.T) {
|
||||||
newTree, err := repo.LookupTree(newTreeId)
|
newTree, err := repo.LookupTree(newTreeId)
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
|
|
||||||
diff, err := repo.DiffTreeToTree(originalTree, newTree, nil)
|
opts := &DiffOptions{
|
||||||
|
OldPrefix: "a",
|
||||||
|
NewPrefix: "b",
|
||||||
|
}
|
||||||
|
diff, err := repo.DiffTreeToTree(originalTree, newTree, opts)
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
|
|
||||||
patch, err := diff.Patch(0)
|
patch, err := diff.Patch(0)
|
||||||
|
|
15
reference.go
15
reference.go
|
@ -36,7 +36,10 @@ func (v *Reference) SetSymbolicTarget(target string, sig *Signature, msg string)
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
csig := sig.toC()
|
csig, err := sig.toC()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer C.free(unsafe.Pointer(csig))
|
defer C.free(unsafe.Pointer(csig))
|
||||||
|
|
||||||
var cmsg *C.char
|
var cmsg *C.char
|
||||||
|
@ -61,7 +64,10 @@ func (v *Reference) SetTarget(target *Oid, sig *Signature, msg string) (*Referen
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
csig := sig.toC()
|
csig, err := sig.toC()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer C.free(unsafe.Pointer(csig))
|
defer C.free(unsafe.Pointer(csig))
|
||||||
|
|
||||||
var cmsg *C.char
|
var cmsg *C.char
|
||||||
|
@ -99,7 +105,10 @@ func (v *Reference) Rename(name string, force bool, sig *Signature, msg string)
|
||||||
cname := C.CString(name)
|
cname := C.CString(name)
|
||||||
defer C.free(unsafe.Pointer(cname))
|
defer C.free(unsafe.Pointer(cname))
|
||||||
|
|
||||||
csig := sig.toC()
|
csig, err := sig.toC()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer C.free(unsafe.Pointer(csig))
|
defer C.free(unsafe.Pointer(csig))
|
||||||
|
|
||||||
var cmsg *C.char
|
var cmsg *C.char
|
||||||
|
|
10
remote.go
10
remote.go
|
@ -604,7 +604,10 @@ func (o *Remote) Fetch(refspecs []string, sig *Signature, msg string) error {
|
||||||
|
|
||||||
var csig *C.git_signature = nil
|
var csig *C.git_signature = nil
|
||||||
if sig != nil {
|
if sig != nil {
|
||||||
csig = sig.toC()
|
csig, err := sig.toC()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
defer C.free(unsafe.Pointer(csig))
|
defer C.free(unsafe.Pointer(csig))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,7 +699,10 @@ func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) {
|
||||||
func (o *Remote) Push(refspecs []string, opts *PushOptions, sig *Signature, msg string) error {
|
func (o *Remote) Push(refspecs []string, opts *PushOptions, sig *Signature, msg string) error {
|
||||||
var csig *C.git_signature = nil
|
var csig *C.git_signature = nil
|
||||||
if sig != nil {
|
if sig != nil {
|
||||||
csig = sig.toC()
|
csig, err := sig.toC()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
defer C.free(unsafe.Pointer(csig))
|
defer C.free(unsafe.Pointer(csig))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -210,7 +210,10 @@ func (v *Repository) SetHead(refname string, sig *Signature, msg string) error {
|
||||||
cname := C.CString(refname)
|
cname := C.CString(refname)
|
||||||
defer C.free(unsafe.Pointer(cname))
|
defer C.free(unsafe.Pointer(cname))
|
||||||
|
|
||||||
csig := sig.toC()
|
csig, err := sig.toC()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
defer C.free(unsafe.Pointer(csig))
|
defer C.free(unsafe.Pointer(csig))
|
||||||
|
|
||||||
var cmsg *C.char
|
var cmsg *C.char
|
||||||
|
@ -230,7 +233,10 @@ func (v *Repository) SetHead(refname string, sig *Signature, msg string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Repository) SetHeadDetached(id *Oid, sig *Signature, msg string) error {
|
func (v *Repository) SetHeadDetached(id *Oid, sig *Signature, msg string) error {
|
||||||
csig := sig.toC()
|
csig, err := sig.toC()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
defer C.free(unsafe.Pointer(csig))
|
defer C.free(unsafe.Pointer(csig))
|
||||||
|
|
||||||
var cmsg *C.char
|
var cmsg *C.char
|
||||||
|
@ -253,7 +259,10 @@ func (v *Repository) CreateReference(name string, id *Oid, force bool, sig *Sign
|
||||||
cname := C.CString(name)
|
cname := C.CString(name)
|
||||||
defer C.free(unsafe.Pointer(cname))
|
defer C.free(unsafe.Pointer(cname))
|
||||||
|
|
||||||
csig := sig.toC()
|
csig, err := sig.toC()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer C.free(unsafe.Pointer(csig))
|
defer C.free(unsafe.Pointer(csig))
|
||||||
|
|
||||||
var cmsg *C.char
|
var cmsg *C.char
|
||||||
|
@ -284,7 +293,10 @@ func (v *Repository) CreateSymbolicReference(name, target string, force bool, si
|
||||||
ctarget := C.CString(target)
|
ctarget := C.CString(target)
|
||||||
defer C.free(unsafe.Pointer(ctarget))
|
defer C.free(unsafe.Pointer(ctarget))
|
||||||
|
|
||||||
csig := sig.toC()
|
csig, err := sig.toC()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer C.free(unsafe.Pointer(csig))
|
defer C.free(unsafe.Pointer(csig))
|
||||||
|
|
||||||
var cmsg *C.char
|
var cmsg *C.char
|
||||||
|
@ -352,10 +364,16 @@ func (v *Repository) CreateCommit(
|
||||||
parentsarg = &cparents[0]
|
parentsarg = &cparents[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
authorSig := author.toC()
|
authorSig, err := author.toC()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer C.git_signature_free(authorSig)
|
defer C.git_signature_free(authorSig)
|
||||||
|
|
||||||
committerSig := committer.toC()
|
committerSig, err := committer.toC()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer C.git_signature_free(committerSig)
|
defer C.git_signature_free(committerSig)
|
||||||
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
|
@ -384,7 +402,10 @@ func (v *Repository) CreateTag(
|
||||||
cmessage := C.CString(message)
|
cmessage := C.CString(message)
|
||||||
defer C.free(unsafe.Pointer(cmessage))
|
defer C.free(unsafe.Pointer(cmessage))
|
||||||
|
|
||||||
taggerSig := tagger.toC()
|
taggerSig, err := tagger.toC()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer C.git_signature_free(taggerSig)
|
defer C.git_signature_free(taggerSig)
|
||||||
|
|
||||||
ctarget := commit.gitObject.ptr
|
ctarget := commit.gitObject.ptr
|
||||||
|
@ -546,10 +567,16 @@ func (v *Repository) CreateNote(
|
||||||
defer C.free(unsafe.Pointer(cref))
|
defer C.free(unsafe.Pointer(cref))
|
||||||
}
|
}
|
||||||
|
|
||||||
authorSig := author.toC()
|
authorSig, err := author.toC()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer C.git_signature_free(authorSig)
|
defer C.git_signature_free(authorSig)
|
||||||
|
|
||||||
committerSig := committer.toC()
|
committerSig, err := committer.toC()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer C.git_signature_free(committerSig)
|
defer C.git_signature_free(committerSig)
|
||||||
|
|
||||||
cnote := C.CString(note)
|
cnote := C.CString(note)
|
||||||
|
@ -601,10 +628,16 @@ func (v *Repository) RemoveNote(ref string, author, committer *Signature, id *Oi
|
||||||
defer C.free(unsafe.Pointer(cref))
|
defer C.free(unsafe.Pointer(cref))
|
||||||
}
|
}
|
||||||
|
|
||||||
authorSig := author.toC()
|
authorSig, err := author.toC()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
defer C.git_signature_free(authorSig)
|
defer C.git_signature_free(authorSig)
|
||||||
|
|
||||||
committerSig := committer.toC()
|
committerSig, err := committer.toC()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
defer C.git_signature_free(committerSig)
|
defer C.git_signature_free(committerSig)
|
||||||
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
|
@ -630,3 +663,36 @@ func (v *Repository) DefaultNoteRef() (string, error) {
|
||||||
|
|
||||||
return C.GoString(ptr), nil
|
return C.GoString(ptr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RepositoryState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
RepositoryStateNone RepositoryState = C.GIT_REPOSITORY_STATE_NONE
|
||||||
|
RepositoryStateMerge RepositoryState = C.GIT_REPOSITORY_STATE_MERGE
|
||||||
|
RepositoryStateRevert RepositoryState = C.GIT_REPOSITORY_STATE_REVERT
|
||||||
|
RepositoryStateCherrypick RepositoryState = C.GIT_REPOSITORY_STATE_CHERRYPICK
|
||||||
|
RepositoryStateBisect RepositoryState = C.GIT_REPOSITORY_STATE_BISECT
|
||||||
|
RepositoryStateRebase RepositoryState = C.GIT_REPOSITORY_STATE_REBASE
|
||||||
|
RepositoryStateRebaseInteractive RepositoryState = C.GIT_REPOSITORY_STATE_REBASE_INTERACTIVE
|
||||||
|
RepositoryStateRebaseMerge RepositoryState = C.GIT_REPOSITORY_STATE_REBASE_MERGE
|
||||||
|
RepositoryStateApplyMailbox RepositoryState = C.GIT_REPOSITORY_STATE_APPLY_MAILBOX
|
||||||
|
RepositoryStateApplyMailboxOrRebase RepositoryState = C.GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *Repository) State() RepositoryState {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
return RepositoryState(C.git_repository_state(r.ptr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Repository) StateCleanup() error {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
cErr := C.git_repository_state_cleanup(r.ptr)
|
||||||
|
if cErr < 0 {
|
||||||
|
return MakeGitError(cErr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <git2.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Signature struct {
|
||||||
|
Name string
|
||||||
|
Email string
|
||||||
|
When time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSignatureFromC(sig *C.git_signature) *Signature {
|
||||||
|
// git stores minutes, go wants seconds
|
||||||
|
loc := time.FixedZone("", int(sig.when.offset)*60)
|
||||||
|
return &Signature{
|
||||||
|
C.GoString(sig.name),
|
||||||
|
C.GoString(sig.email),
|
||||||
|
time.Unix(int64(sig.when.time), 0).In(loc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the offset in mintes, which is what git wants
|
||||||
|
func (v *Signature) Offset() int {
|
||||||
|
_, offset := v.When.Zone()
|
||||||
|
return offset / 60
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sig *Signature) toC() (*C.git_signature, error) {
|
||||||
|
if sig == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var out *C.git_signature
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
name := C.CString(sig.Name)
|
||||||
|
defer C.free(unsafe.Pointer(name))
|
||||||
|
|
||||||
|
email := C.CString(sig.Email)
|
||||||
|
defer C.free(unsafe.Pointer(email))
|
||||||
|
|
||||||
|
ret := C.git_signature_new(&out, name, email, C.git_time_t(sig.When.Unix()), C.int(sig.Offset()))
|
||||||
|
if ret < 0 {
|
||||||
|
return nil, MakeGitError(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) DefaultSignature() (*Signature, error) {
|
||||||
|
var out *C.git_signature
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
cErr := C.git_signature_default(&out, repo.ptr)
|
||||||
|
if cErr < 0 {
|
||||||
|
return nil, MakeGitError(cErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer C.git_signature_free(out)
|
||||||
|
|
||||||
|
return newSignatureFromC(out), nil
|
||||||
|
}
|
31
status.go
31
status.go
|
@ -45,7 +45,7 @@ func statusEntryFromC(statusEntry *C.git_status_entry) StatusEntry {
|
||||||
indexToWorkdir = diffDeltaFromC(statusEntry.index_to_workdir)
|
indexToWorkdir = diffDeltaFromC(statusEntry.index_to_workdir)
|
||||||
}
|
}
|
||||||
|
|
||||||
return StatusEntry {
|
return StatusEntry{
|
||||||
Status: Status(statusEntry.status),
|
Status: Status(statusEntry.status),
|
||||||
HeadToIndex: headToIndex,
|
HeadToIndex: headToIndex,
|
||||||
IndexToWorkdir: indexToWorkdir,
|
IndexToWorkdir: indexToWorkdir,
|
||||||
|
@ -96,20 +96,20 @@ func (statusList *StatusList) EntryCount() (int, error) {
|
||||||
type StatusOpt int
|
type StatusOpt int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StatusOptIncludeUntracked StatusOpt = C.GIT_STATUS_OPT_INCLUDE_UNTRACKED
|
StatusOptIncludeUntracked StatusOpt = C.GIT_STATUS_OPT_INCLUDE_UNTRACKED
|
||||||
StatusOptIncludeIgnored StatusOpt = C.GIT_STATUS_OPT_INCLUDE_IGNORED
|
StatusOptIncludeIgnored StatusOpt = C.GIT_STATUS_OPT_INCLUDE_IGNORED
|
||||||
StatusOptIncludeUnmodified StatusOpt = C.GIT_STATUS_OPT_INCLUDE_UNMODIFIED
|
StatusOptIncludeUnmodified StatusOpt = C.GIT_STATUS_OPT_INCLUDE_UNMODIFIED
|
||||||
StatusOptExcludeSubmodules StatusOpt = C.GIT_STATUS_OPT_EXCLUDE_SUBMODULES
|
StatusOptExcludeSubmodules StatusOpt = C.GIT_STATUS_OPT_EXCLUDE_SUBMODULES
|
||||||
StatusOptRecurseUntrackedDirs StatusOpt = C.GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS
|
StatusOptRecurseUntrackedDirs StatusOpt = C.GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS
|
||||||
StatusOptDisablePathspecMatch StatusOpt = C.GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH
|
StatusOptDisablePathspecMatch StatusOpt = C.GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH
|
||||||
StatusOptRecurseIgnoredDirs StatusOpt = C.GIT_STATUS_OPT_RECURSE_IGNORED_DIRS
|
StatusOptRecurseIgnoredDirs StatusOpt = C.GIT_STATUS_OPT_RECURSE_IGNORED_DIRS
|
||||||
StatusOptRenamesHeadToIndex StatusOpt = C.GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX
|
StatusOptRenamesHeadToIndex StatusOpt = C.GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX
|
||||||
StatusOptRenamesIndexToWorkdir StatusOpt = C.GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR
|
StatusOptRenamesIndexToWorkdir StatusOpt = C.GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR
|
||||||
StatusOptSortCaseSensitively StatusOpt = C.GIT_STATUS_OPT_SORT_CASE_SENSITIVELY
|
StatusOptSortCaseSensitively StatusOpt = C.GIT_STATUS_OPT_SORT_CASE_SENSITIVELY
|
||||||
StatusOptSortCaseInsensitively StatusOpt = C.GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY
|
StatusOptSortCaseInsensitively StatusOpt = C.GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY
|
||||||
StatusOptRenamesFromRewrites StatusOpt = C.GIT_STATUS_OPT_RENAMES_FROM_REWRITES
|
StatusOptRenamesFromRewrites StatusOpt = C.GIT_STATUS_OPT_RENAMES_FROM_REWRITES
|
||||||
StatusOptNoRefresh StatusOpt = C.GIT_STATUS_OPT_NO_REFRESH
|
StatusOptNoRefresh StatusOpt = C.GIT_STATUS_OPT_NO_REFRESH
|
||||||
StatusOptUpdateIndex StatusOpt = C.GIT_STATUS_OPT_UPDATE_INDEX
|
StatusOptUpdateIndex StatusOpt = C.GIT_STATUS_OPT_UPDATE_INDEX
|
||||||
)
|
)
|
||||||
|
|
||||||
type StatusShow int
|
type StatusShow int
|
||||||
|
@ -173,7 +173,6 @@ func (v *Repository) StatusList(opts *StatusOptions) (*StatusList, error) {
|
||||||
return newStatusListFromC(ptr), nil
|
return newStatusListFromC(ptr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (v *Repository) StatusFile(path string) (Status, error) {
|
func (v *Repository) StatusFile(path string) (Status, error) {
|
||||||
var statusFlags C.uint
|
var statusFlags C.uint
|
||||||
cPath := C.CString(path)
|
cPath := C.CString(path)
|
||||||
|
|
|
@ -12,6 +12,11 @@ func TestStatusFile(t *testing.T) {
|
||||||
defer repo.Free()
|
defer repo.Free()
|
||||||
defer os.RemoveAll(repo.Workdir())
|
defer os.RemoveAll(repo.Workdir())
|
||||||
|
|
||||||
|
state := repo.State()
|
||||||
|
if state != RepositoryStateNone {
|
||||||
|
t.Fatal("Incorrect repository state: ", state)
|
||||||
|
}
|
||||||
|
|
||||||
err := ioutil.WriteFile(path.Join(path.Dir(repo.Workdir()), "hello.txt"), []byte("Hello, World"), 0644)
|
err := ioutil.WriteFile(path.Join(path.Dir(repo.Workdir()), "hello.txt"), []byte("Hello, World"), 0644)
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
|
|
||||||
|
|
22
submodule.go
22
submodule.go
|
@ -97,10 +97,10 @@ func (repo *Repository) LookupSubmodule(name string) (*Submodule, error) {
|
||||||
type SubmoduleCbk func(sub *Submodule, name string) int
|
type SubmoduleCbk func(sub *Submodule, name string) int
|
||||||
|
|
||||||
//export SubmoduleVisitor
|
//export SubmoduleVisitor
|
||||||
func SubmoduleVisitor(csub unsafe.Pointer, name string, cfct unsafe.Pointer) int {
|
func SubmoduleVisitor(csub unsafe.Pointer, name *C.char, cfct unsafe.Pointer) C.int {
|
||||||
sub := &Submodule{(*C.git_submodule)(csub)}
|
sub := &Submodule{(*C.git_submodule)(csub)}
|
||||||
fct := *(*SubmoduleCbk)(cfct)
|
fct := *(*SubmoduleCbk)(cfct)
|
||||||
return fct(sub, name)
|
return (C.int)(fct(sub, C.GoString(name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) ForeachSubmodule(cbk SubmoduleCbk) error {
|
func (repo *Repository) ForeachSubmodule(cbk SubmoduleCbk) error {
|
||||||
|
@ -318,7 +318,10 @@ func (repo *Repository) ReloadAllSubmodules(force bool) error {
|
||||||
|
|
||||||
func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error {
|
func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error {
|
||||||
var copts C.git_submodule_update_options
|
var copts C.git_submodule_update_options
|
||||||
populateSubmoduleUpdateOptions(&copts, opts)
|
err := populateSubmoduleUpdateOptions(&copts, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
@ -331,15 +334,22 @@ func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func populateSubmoduleUpdateOptions(ptr *C.git_submodule_update_options, opts *SubmoduleUpdateOptions) {
|
func populateSubmoduleUpdateOptions(ptr *C.git_submodule_update_options, opts *SubmoduleUpdateOptions) error {
|
||||||
C.git_submodule_update_init_options(ptr, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION)
|
C.git_submodule_update_init_options(ptr, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION)
|
||||||
|
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
populateCheckoutOpts(&ptr.checkout_opts, opts.CheckoutOpts)
|
populateCheckoutOpts(&ptr.checkout_opts, opts.CheckoutOpts)
|
||||||
populateRemoteCallbacks(&ptr.remote_callbacks, opts.RemoteCallbacks)
|
populateRemoteCallbacks(&ptr.remote_callbacks, opts.RemoteCallbacks)
|
||||||
ptr.clone_checkout_strategy = C.uint(opts.CloneCheckoutStrategy)
|
ptr.clone_checkout_strategy = C.uint(opts.CloneCheckoutStrategy)
|
||||||
ptr.signature = opts.Signature.toC()
|
|
||||||
|
sig, err := opts.Signature.toC()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ptr.signature = sig
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,8 @@ package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateTag(t *testing.T) {
|
func TestCreateTag(t *testing.T) {
|
||||||
|
|
Loading…
Reference in New Issue