From 050e6fbc49ac1a173af5e3462b3c2d330cadd113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 15 Mar 2015 00:45:30 +0100 Subject: [PATCH 001/128] Correct README on what master tracks The second mention still said that master tracks master. Add a mention of next which will become the branch to track upstream tip. --- README.md | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index faeb5f5..c604eaf 100644 --- a/README.md +++ b/README.md @@ -3,27 +3,34 @@ 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) -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. The versioned branches indicate which libgit2 version they work against. 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. +This project wraps the functionality provided by libgit2. If you're using a stable version, install it to your system via your system's package manger and then install git2go as usual. + +Otherwise (`next` which tracks an unstable version), we need to build libgit2 as well. In order to build it, you need `cmake`, `pkg-config` and a C compiler. You will also need the development packages for OpenSSL and LibSSH2 installed if you want libgit2 to support HTTPS and SSH respectively. ### 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 +git2go has `master` which tracks the latest release of libgit2, and 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. +to use a version of git2go which will work against libgit2 v0.22 and dynamically link to the library. You can use -### From master + import "github.com/libgit2/git2go" -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. +to use the version which works against the latest release. + +### From `next` + +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. + git checkout next git submodule update --init # get libgit2 make install @@ -33,11 +40,11 @@ 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 +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 -- 2.45.2 From c4fce1a218fd33938c0be90e939531c0a00ebf7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 15 Mar 2015 01:49:32 +0100 Subject: [PATCH 002/128] Update to libgit2 d675982a153 There's been some changes to the checkout strategy, especially the SAFE_CREATE mode, which is now the RECREATE_MISSING flag, though that shouldn't be necessary to use in the general case. The largest changes come from the removal of the signture from ref-modifying functions/methods and the removal of the reflog string in all but those directly related to moving references. --- branch.go | 36 ++++---------------------------- checkout.go | 15 ++++++++----- cherrypick_test.go | 2 +- push_test.go | 2 +- reference.go | 30 ++++++-------------------- reference_test.go | 21 +++++++------------ remote.go | 38 ++++++--------------------------- remote_test.go | 15 ++++--------- repository.go | 52 +++++++--------------------------------------- revparse_test.go | 2 +- submodule.go | 7 ------- vendor/libgit2 | 2 +- 12 files changed, 49 insertions(+), 173 deletions(-) diff --git a/branch.go b/branch.go index 22b767e..0daa99d 100644 --- a/branch.go +++ b/branch.go @@ -90,30 +90,16 @@ func (repo *Repository) NewBranchIterator(flags BranchType) (*BranchIterator, er return newBranchIteratorFromC(repo, ptr), nil } -func (repo *Repository) CreateBranch(branchName string, target *Commit, force bool, signature *Signature, msg string) (*Branch, error) { +func (repo *Repository) CreateBranch(branchName string, target *Commit, force bool) (*Branch, error) { ref := new(Reference) cBranchName := C.CString(branchName) cForce := cbool(force) - cSignature, err := signature.toC() - if err != nil { - return nil, err - } - defer C.git_signature_free(cSignature) - - 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_branch_create(&ref.ptr, repo.ptr, cBranchName, target.cast_ptr, cForce, cSignature, cmsg) + ret := C.git_branch_create(&ref.ptr, repo.ptr, cBranchName, target.cast_ptr, cForce) if ret < 0 { return nil, MakeGitError(ret) } @@ -131,29 +117,15 @@ func (b *Branch) Delete() error { return nil } -func (b *Branch) Move(newBranchName string, force bool, signature *Signature, msg string) (*Branch, error) { +func (b *Branch) Move(newBranchName string, force bool) (*Branch, error) { var ptr *C.git_reference cNewBranchName := C.CString(newBranchName) cForce := cbool(force) - cSignature, err := signature.toC() - if err != nil { - return nil, err - } - defer C.git_signature_free(cSignature) - - 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_branch_move(&ptr, b.Reference.ptr, cNewBranchName, cForce, cSignature, cmsg) + ret := C.git_branch_move(&ptr, b.Reference.ptr, cNewBranchName, cForce) if ret < 0 { return nil, MakeGitError(ret) } diff --git a/checkout.go b/checkout.go index 6eb6098..98c1ee6 100644 --- a/checkout.go +++ b/checkout.go @@ -15,18 +15,23 @@ type CheckoutStrategy uint const ( CheckoutNone CheckoutStrategy = C.GIT_CHECKOUT_NONE // Dry run, no actual updates CheckoutSafe CheckoutStrategy = C.GIT_CHECKOUT_SAFE // Allow safe updates that cannot overwrite uncommitted data - CheckoutSafeCreate CheckoutStrategy = C.GIT_CHECKOUT_SAFE_CREATE // Allow safe updates plus creation of missing files - CheckoutForce CheckoutStrategy = C.GIT_CHECKOUT_FORCE // Allow all updates to force working directory to look like index + CheckoutRecreateMissing CheckoutStrategy = C.GIT_CHECKOUT_RECREATE_MISSING // Allow checkout to recreate missing files 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) CheckoutRemoveIgnored CheckoutStrategy = C.GIT_CHECKOUT_REMOVE_IGNORED // Remove ignored files not in index 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 CheckoutNoRefresh CheckoutStrategy = C.GIT_CHECKOUT_NO_REFRESH // Don't refresh index/config/etc before doing checkout + CheckoutSkipUnmerged CheckoutStrategy = C.GIT_CHECKOUT_SKIP_UNMERGED // Allow checkout to skip unmerged files + CheckoutUserOurs CheckoutStrategy = C.GIT_CHECKOUT_USE_OURS // For unmerged files, checkout stage 2 from index + CheckoutUseTheirs CheckoutStrategy = C.GIT_CHECKOUT_USE_THEIRS // For unmerged files, checkout stage 3 from index 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) - 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) + CheckoutSkipLockedDirectories CheckoutStrategy = C.GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES // Ignore directories in use, they will be left empty + CheckoutDontOverwriteIgnored CheckoutStrategy = C.GIT_CHECKOUT_DONT_OVERWRITE_IGNORED // Don't overwrite ignored files that exist in the checkout target + CheckoutConflictStyleMerge CheckoutStrategy = C.GIT_CHECKOUT_CONFLICT_STYLE_MERGE // Write normal merge files for conflicts + CheckoutConflictStyleDiff3 CheckoutStrategy = C.GIT_CHECKOUT_CONFLICT_STYLE_DIFF3 // Include common ancestor data in diff3 format files for conflicts + CheckoutDontRemoveExisting CheckoutStrategy = C.GIT_CHECKOUT_DONT_REMOVE_EXISTING // Don't overwrite existing files or folders + CheckoutDontWriteIndex CheckoutStrategy = C.GIT_CHECKOUT_DONT_WRITE_INDEX // Normally checkout writes the index upon completion; this prevents that CheckoutUpdateSubmodules CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_SUBMODULES // Recursively checkout submodules with same options (NOT IMPLEMENTED) CheckoutUpdateSubmodulesIfChanged CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED // Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) ) diff --git a/cherrypick_test.go b/cherrypick_test.go index f06dbdd..141a55d 100644 --- a/cherrypick_test.go +++ b/cherrypick_test.go @@ -16,7 +16,7 @@ func checkout(t *testing.T, repo *Repository, commit *Commit) { t.Fatal(err) } - err = repo.SetHeadDetached(commit.Id(), commit.Author(), "checkout") + err = repo.SetHeadDetached(commit.Id()) if err != nil { t.Fatal(err) } diff --git a/push_test.go b/push_test.go index cd708c6..ad72e4d 100644 --- a/push_test.go +++ b/push_test.go @@ -16,7 +16,7 @@ func TestRemotePush(t *testing.T) { seedTestRepo(t, localRepo) - err = remote.Push([]string{"refs/heads/master"}, nil, nil, "") + err = remote.Push([]string{"refs/heads/master"}, nil) checkFatal(t, err) _, err = localRepo.LookupReference("refs/remotes/test_push/master") diff --git a/reference.go b/reference.go index ef12d0b..61e2b26 100644 --- a/reference.go +++ b/reference.go @@ -27,7 +27,7 @@ func newReferenceFromC(ptr *C.git_reference, repo *Repository) *Reference { return ref } -func (v *Reference) SetSymbolicTarget(target string, sig *Signature, msg string) (*Reference, error) { +func (v *Reference) SetSymbolicTarget(target string, msg string) (*Reference, error) { var ptr *C.git_reference ctarget := C.CString(target) @@ -36,12 +36,6 @@ func (v *Reference) SetSymbolicTarget(target string, sig *Signature, msg string) runtime.LockOSThread() defer runtime.UnlockOSThread() - csig, err := sig.toC() - if err != nil { - return nil, err - } - defer C.free(unsafe.Pointer(csig)) - var cmsg *C.char if msg == "" { cmsg = nil @@ -50,7 +44,7 @@ func (v *Reference) SetSymbolicTarget(target string, sig *Signature, msg string) defer C.free(unsafe.Pointer(cmsg)) } - ret := C.git_reference_symbolic_set_target(&ptr, v.ptr, ctarget, csig, cmsg) + ret := C.git_reference_symbolic_set_target(&ptr, v.ptr, ctarget, cmsg) if ret < 0 { return nil, MakeGitError(ret) } @@ -58,18 +52,12 @@ func (v *Reference) SetSymbolicTarget(target string, sig *Signature, msg string) return newReferenceFromC(ptr, v.repo), nil } -func (v *Reference) SetTarget(target *Oid, sig *Signature, msg string) (*Reference, error) { +func (v *Reference) SetTarget(target *Oid, msg string) (*Reference, error) { var ptr *C.git_reference runtime.LockOSThread() defer runtime.UnlockOSThread() - csig, err := sig.toC() - if err != nil { - return nil, err - } - defer C.free(unsafe.Pointer(csig)) - var cmsg *C.char if msg == "" { cmsg = nil @@ -78,7 +66,7 @@ func (v *Reference) SetTarget(target *Oid, sig *Signature, msg string) (*Referen defer C.free(unsafe.Pointer(cmsg)) } - ret := C.git_reference_set_target(&ptr, v.ptr, target.toC(), csig, cmsg) + ret := C.git_reference_set_target(&ptr, v.ptr, target.toC(), cmsg) if ret < 0 { return nil, MakeGitError(ret) } @@ -100,17 +88,11 @@ func (v *Reference) Resolve() (*Reference, error) { return newReferenceFromC(ptr, v.repo), nil } -func (v *Reference) Rename(name string, force bool, sig *Signature, msg string) (*Reference, error) { +func (v *Reference) Rename(name string, force bool, msg string) (*Reference, error) { var ptr *C.git_reference cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) - csig, err := sig.toC() - if err != nil { - return nil, err - } - defer C.free(unsafe.Pointer(csig)) - var cmsg *C.char if msg == "" { cmsg = nil @@ -122,7 +104,7 @@ func (v *Reference) Rename(name string, force bool, sig *Signature, msg string) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_reference_rename(&ptr, v.ptr, cname, cbool(force), csig, cmsg) + ret := C.git_reference_rename(&ptr, v.ptr, cname, cbool(force), cmsg) if ret < 0 { return nil, MakeGitError(ret) diff --git a/reference_test.go b/reference_test.go index c7d52fb..562e276 100644 --- a/reference_test.go +++ b/reference_test.go @@ -14,14 +14,7 @@ func TestRefModification(t *testing.T) { commitId, treeId := seedTestRepo(t, repo) - loc, err := time.LoadLocation("Europe/Berlin") - checkFatal(t, err) - sig := &Signature{ - Name: "Rand Om Hacker", - Email: "random@hacker.com", - When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc), - } - _, err = repo.CreateReference("refs/tags/tree", treeId, true, sig, "testTreeTag") + _, err := repo.CreateReference("refs/tags/tree", treeId, true, "testTreeTag") checkFatal(t, err) tag, err := repo.LookupReference("refs/tags/tree") @@ -52,7 +45,7 @@ func TestRefModification(t *testing.T) { t.Fatalf("Wrong ref target") } - _, err = tag.Rename("refs/tags/renamed", false, nil, "") + _, err = tag.Rename("refs/tags/renamed", false, "") checkFatal(t, err) tag, err = repo.LookupReference("refs/tags/renamed") checkFatal(t, err) @@ -85,13 +78,13 @@ func TestReferenceIterator(t *testing.T) { commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree) checkFatal(t, err) - _, err = repo.CreateReference("refs/heads/one", commitId, true, sig, "headOne") + _, err = repo.CreateReference("refs/heads/one", commitId, true, "headOne") checkFatal(t, err) - _, err = repo.CreateReference("refs/heads/two", commitId, true, sig, "headTwo") + _, err = repo.CreateReference("refs/heads/two", commitId, true, "headTwo") checkFatal(t, err) - _, err = repo.CreateReference("refs/heads/three", commitId, true, sig, "headThree") + _, err = repo.CreateReference("refs/heads/three", commitId, true, "headThree") checkFatal(t, err) iter, err := repo.NewReferenceIterator() @@ -143,7 +136,7 @@ func TestReferenceOwner(t *testing.T) { defer os.RemoveAll(repo.Workdir()) commitId, _ := seedTestRepo(t, repo) - ref, err := repo.CreateReference("refs/heads/foo", commitId, true, nil, "") + ref, err := repo.CreateReference("refs/heads/foo", commitId, true, "") checkFatal(t, err) owner := ref.Owner() @@ -162,7 +155,7 @@ func TestUtil(t *testing.T) { commitId, _ := seedTestRepo(t, repo) - ref, err := repo.CreateReference("refs/heads/foo", commitId, true, nil, "") + ref, err := repo.CreateReference("refs/heads/foo", commitId, true, "") checkFatal(t, err) ref2, err := repo.DwimReference("foo") diff --git a/remote.go b/remote.go index 84750d3..72d3934 100644 --- a/remote.go +++ b/remote.go @@ -598,18 +598,9 @@ func (o *Remote) UpdateFetchHead() bool { // Fetch performs a fetch operation. refspecs specifies which refspecs // to use for this fetch, use an empty list to use the refspecs from -// the configuration; sig and msg specify what to use for the reflog -// entries. Leave nil and "" to use defaults. -func (o *Remote) Fetch(refspecs []string, sig *Signature, msg string) error { - - var csig *C.git_signature = nil - if sig != nil { - csig, err := sig.toC() - if err != nil { - return err - } - defer C.free(unsafe.Pointer(csig)) - } +// the configuration; msg specifies what to use for the reflog +// entries. Leave "" to use defaults. +func (o *Remote) Fetch(refspecs []string, msg string) error { var cmsg *C.char = nil if msg != "" { @@ -625,7 +616,7 @@ func (o *Remote) Fetch(refspecs []string, sig *Signature, msg string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_fetch(o.ptr, &crefspecs, csig, cmsg) + ret := C.git_remote_fetch(o.ptr, &crefspecs, cmsg) if ret < 0 { return MakeGitError(ret) } @@ -696,24 +687,7 @@ 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, err := sig.toC() - if err != nil { - return err - } - 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)) - } - +func (o *Remote) Push(refspecs []string, opts *PushOptions) error { var copts C.git_push_options C.git_push_init_options(&copts, C.GIT_PUSH_OPTIONS_VERSION) if opts != nil { @@ -728,7 +702,7 @@ func (o *Remote) Push(refspecs []string, opts *PushOptions, sig *Signature, msg runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_push(o.ptr, &crefspecs, &copts, csig, cmsg) + ret := C.git_remote_push(o.ptr, &crefspecs, &copts) if ret < 0 { return MakeGitError(ret) } diff --git a/remote_test.go b/remote_test.go index 54a66ed..bbbdeb9 100644 --- a/remote_test.go +++ b/remote_test.go @@ -4,7 +4,6 @@ import ( "fmt" "os" "testing" - "time" ) func TestRefspecs(t *testing.T) { @@ -73,7 +72,7 @@ func TestCertificateCheck(t *testing.T) { err = remote.SetCallbacks(&callbacks) checkFatal(t, err) - err = remote.Fetch([]string{}, nil, "") + err = remote.Fetch([]string{}, "") checkFatal(t, err) } @@ -168,13 +167,7 @@ func TestRemotePrune(t *testing.T) { 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") + remoteRef, err := remoteRepo.CreateBranch("test-prune", commit, true) checkFatal(t, err) repo := createTestRepo(t) @@ -189,10 +182,10 @@ func TestRemotePrune(t *testing.T) { remote, err := repo.CreateRemote("origin", remoteUrl) checkFatal(t, err) - err = remote.Fetch([]string{"test-prune"}, sig, "") + err = remote.Fetch([]string{"test-prune"}, "") checkFatal(t, err) - _, err = repo.CreateReference("refs/remotes/origin/test-prune", head, true, sig, "remote reference") + _, err = repo.CreateReference("refs/remotes/origin/test-prune", head, true, "remote reference") checkFatal(t, err) err = remoteRef.Delete() diff --git a/repository.go b/repository.go index 7fac277..b65867b 100644 --- a/repository.go +++ b/repository.go @@ -206,65 +206,35 @@ func (v *Repository) Head() (*Reference, error) { return newReferenceFromC(ptr, v), nil } -func (v *Repository) SetHead(refname string, sig *Signature, msg string) error { +func (v *Repository) SetHead(refname string) error { cname := C.CString(refname) defer C.free(unsafe.Pointer(cname)) - csig, err := sig.toC() - if err != nil { - return err - } - defer C.free(unsafe.Pointer(csig)) - - var cmsg *C.char - if msg != "" { - cmsg = C.CString(msg) - defer C.free(unsafe.Pointer(cmsg)) - } - runtime.LockOSThread() defer runtime.UnlockOSThread() - ecode := C.git_repository_set_head(v.ptr, cname, csig, cmsg) + ecode := C.git_repository_set_head(v.ptr, cname) if ecode != 0 { return MakeGitError(ecode) } return nil } -func (v *Repository) SetHeadDetached(id *Oid, sig *Signature, msg string) error { - csig, err := sig.toC() - if err != nil { - return err - } - defer C.free(unsafe.Pointer(csig)) - - var cmsg *C.char - if msg != "" { - cmsg = C.CString(msg) - defer C.free(unsafe.Pointer(cmsg)) - } - +func (v *Repository) SetHeadDetached(id *Oid) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ecode := C.git_repository_set_head_detached(v.ptr, id.toC(), csig, cmsg) + ecode := C.git_repository_set_head_detached(v.ptr, id.toC()) if ecode != 0 { return MakeGitError(ecode) } return nil } -func (v *Repository) CreateReference(name string, id *Oid, force bool, sig *Signature, msg string) (*Reference, error) { +func (v *Repository) CreateReference(name string, id *Oid, force bool, msg string) (*Reference, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) - csig, err := sig.toC() - if err != nil { - return nil, err - } - defer C.free(unsafe.Pointer(csig)) - var cmsg *C.char if msg == "" { cmsg = nil @@ -278,7 +248,7 @@ func (v *Repository) CreateReference(name string, id *Oid, force bool, sig *Sign runtime.LockOSThread() defer runtime.UnlockOSThread() - ecode := C.git_reference_create(&ptr, v.ptr, cname, id.toC(), cbool(force), csig, cmsg) + ecode := C.git_reference_create(&ptr, v.ptr, cname, id.toC(), cbool(force), cmsg) if ecode < 0 { return nil, MakeGitError(ecode) } @@ -286,19 +256,13 @@ func (v *Repository) CreateReference(name string, id *Oid, force bool, sig *Sign return newReferenceFromC(ptr, v), nil } -func (v *Repository) CreateSymbolicReference(name, target string, force bool, sig *Signature, msg string) (*Reference, error) { +func (v *Repository) CreateSymbolicReference(name, target string, force bool, msg string) (*Reference, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) ctarget := C.CString(target) defer C.free(unsafe.Pointer(ctarget)) - csig, err := sig.toC() - if err != nil { - return nil, err - } - defer C.free(unsafe.Pointer(csig)) - var cmsg *C.char if msg == "" { cmsg = nil @@ -312,7 +276,7 @@ func (v *Repository) CreateSymbolicReference(name, target string, force bool, si runtime.LockOSThread() defer runtime.UnlockOSThread() - ecode := C.git_reference_symbolic_create(&ptr, v.ptr, cname, ctarget, cbool(force), csig, cmsg) + ecode := C.git_reference_symbolic_create(&ptr, v.ptr, cname, ctarget, cbool(force), cmsg) if ecode < 0 { return nil, MakeGitError(ecode) } diff --git a/revparse_test.go b/revparse_test.go index c046a20..8c3a352 100644 --- a/revparse_test.go +++ b/revparse_test.go @@ -35,7 +35,7 @@ func TestRevparseExt(t *testing.T) { _, treeId := seedTestRepo(t, repo) - ref, err := repo.CreateReference("refs/heads/master", treeId, true, nil, "") + ref, err := repo.CreateReference("refs/heads/master", treeId, true, "") checkFatal(t, err) obj, ref, err := repo.RevparseExt("master") diff --git a/submodule.go b/submodule.go index 7c6c922..fdd38cc 100644 --- a/submodule.go +++ b/submodule.go @@ -16,7 +16,6 @@ type SubmoduleUpdateOptions struct { *CheckoutOpts *RemoteCallbacks CloneCheckoutStrategy CheckoutStrategy - Signature *Signature } // Submodule @@ -345,11 +344,5 @@ func populateSubmoduleUpdateOptions(ptr *C.git_submodule_update_options, opts *S populateRemoteCallbacks(&ptr.remote_callbacks, opts.RemoteCallbacks) ptr.clone_checkout_strategy = C.uint(opts.CloneCheckoutStrategy) - sig, err := opts.Signature.toC() - if err != nil { - return err - } - ptr.signature = sig - return nil } diff --git a/vendor/libgit2 b/vendor/libgit2 index 04bdd97..d675982 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 04bdd97f2b63793a8720fd19007911e946ba3c55 +Subproject commit d675982a15388d8c413acda139b4662062cf3286 -- 2.45.2 From 89d67328f3682c49db296831538c8638fcfe53fe Mon Sep 17 00:00:00 2001 From: Dylan Griffin Date: Sat, 21 Mar 2015 18:37:00 -0400 Subject: [PATCH 003/128] Add support for libgit2's git_reset. Adds a new method to *Repository called ResetToCommit as well as constants for the three reset modes that libgit2 currently supports. This does not need to be limited to Commit, we actually just need something with a gitObject, which blobs and other Objects have, they will just require different methods. I only need to be able to reset to commits, so that's all I'm implementing right now. Also adds a test which updates the test repository README twice and then resets to the first commit. --- reset.go | 26 ++++++++++++++++++++++++++ reset_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 reset.go create mode 100644 reset_test.go diff --git a/reset.go b/reset.go new file mode 100644 index 0000000..b5b7435 --- /dev/null +++ b/reset.go @@ -0,0 +1,26 @@ +package git + +/* +#include +*/ +import "C" +import "runtime" + +type ResetType int + +const ( + ResetSoft ResetType = C.GIT_RESET_SOFT + ResetMixed ResetType = C.GIT_RESET_MIXED + ResetHard ResetType = C.GIT_RESET_HARD +) + +func (r *Repository) ResetToCommit(commit *Commit, resetType ResetType, opts *CheckoutOpts) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_reset(r.ptr, commit.gitObject.ptr, C.git_reset_t(resetType), opts.toC()) + + if ret < 0 { + return MakeGitError(ret) + } + return nil +} diff --git a/reset_test.go b/reset_test.go new file mode 100644 index 0000000..ec578bd --- /dev/null +++ b/reset_test.go @@ -0,0 +1,45 @@ +package git + +import ( + "io/ioutil" + "testing" +) + +func TestResetToCommit(t *testing.T) { + repo := createTestRepo(t) + seedTestRepo(t, repo) + // create commit to reset to + commitId, _ := updateReadme(t, repo, "testing reset") + // create commit to reset from + nextCommitId, _ := updateReadme(t, repo, "will be reset") + + // confirm that we wrote "will be reset" to the readme + newBytes, err := ioutil.ReadFile(pathInRepo(repo, "README")) + checkFatal(t, err) + if string(newBytes) != "will be reset" { + t.Fatalf("expected %s to equal 'will be reset'", string(newBytes)) + } + + // confirm that the head of the repo is the next commit id + head, err := repo.Head() + checkFatal(t, err) + if head.Target().String() != nextCommitId.String() { + t.Fatalf( + "expected to be at latest commit %s, but was %s", + nextCommitId.String(), + head.Target().String(), + ) + } + + commitToResetTo, err := repo.LookupCommit(commitId) + checkFatal(t, err) + + repo.ResetToCommit(commitToResetTo, ResetHard, &CheckoutOpts{}) + + // check that the file now reads "testing reset" like it did before + bytes, err := ioutil.ReadFile(pathInRepo(repo, "README")) + checkFatal(t, err) + if string(bytes) != "testing reset" { + t.Fatalf("expected %s to equal 'testing reset'", string(bytes)) + } +} -- 2.45.2 From acc05926f7d21547a8c9de0bb08d3b0b498a6769 Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Thu, 16 Apr 2015 18:45:53 -0700 Subject: [PATCH 004/128] Avoid installing script binary to user's $GOPATH/bin. --- script/check-MakeGitError-thread-lock.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/check-MakeGitError-thread-lock.go b/script/check-MakeGitError-thread-lock.go index f6b01b3..77411f7 100644 --- a/script/check-MakeGitError-thread-lock.go +++ b/script/check-MakeGitError-thread-lock.go @@ -1,3 +1,5 @@ +// +build ignore + package main import ( -- 2.45.2 From b374e16db84c451cddc49d064cae69ca00993982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 18 Apr 2015 04:50:39 +0200 Subject: [PATCH 005/128] Update to libgit2 4c02d3937 --- merge.go | 10 +++++----- repository.go | 9 ++++++--- vendor/libgit2 | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/merge.go b/merge.go index 5b68a8b..65dc03e 100644 --- a/merge.go +++ b/merge.go @@ -84,8 +84,8 @@ const ( ) type MergeOptions struct { - Version uint - Flags MergeTreeFlag + Version uint + TreeFlags MergeTreeFlag RenameThreshold uint TargetLimit uint @@ -97,7 +97,7 @@ type MergeOptions struct { func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions { return MergeOptions{ Version: uint(opts.version), - Flags: MergeTreeFlag(opts.flags), + TreeFlags: MergeTreeFlag(opts.tree_flags), RenameThreshold: uint(opts.rename_threshold), TargetLimit: uint(opts.target_limit), FileFavor: MergeFileFavor(opts.file_favor), @@ -123,7 +123,7 @@ func (mo *MergeOptions) toC() *C.git_merge_options { } return &C.git_merge_options{ version: C.uint(mo.Version), - flags: C.git_merge_tree_flag_t(mo.Flags), + tree_flags: C.git_merge_tree_flag_t(mo.TreeFlags), rename_threshold: C.uint(mo.RenameThreshold), target_limit: C.uint(mo.TargetLimit), file_favor: C.git_merge_file_favor_t(mo.FileFavor), @@ -333,7 +333,7 @@ func populateCMergeFileOptions(c *C.git_merge_file_options, options MergeFileOpt c.our_label = C.CString(options.OurLabel) c.their_label = C.CString(options.TheirLabel) c.favor = C.git_merge_file_favor_t(options.Favor) - c.flags = C.git_merge_file_flags_t(options.Flags) + c.flags = C.uint(options.Flags) } func freeCMergeFileOptions(c *C.git_merge_file_options) { diff --git a/repository.go b/repository.go index b65867b..9917c60 100644 --- a/repository.go +++ b/repository.go @@ -616,16 +616,19 @@ func (v *Repository) RemoveNote(ref string, author, committer *Signature, id *Oi // DefaultNoteRef returns the default notes reference for a repository func (v *Repository) DefaultNoteRef() (string, error) { - var ptr *C.char + buf := C.git_buf{} runtime.LockOSThread() defer runtime.UnlockOSThread() - if ret := C.git_note_default_ref(&ptr, v.ptr); ret < 0 { + if ret := C.git_note_default_ref(&buf, v.ptr); ret < 0 { return "", MakeGitError(ret) } - return C.GoString(ptr), nil + ret := C.GoString(buf.ptr) + C.git_buf_free(&buf) + + return ret, nil } type RepositoryState int diff --git a/vendor/libgit2 b/vendor/libgit2 index d675982..4c02d39 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit d675982a15388d8c413acda139b4662062cf3286 +Subproject commit 4c02d393748d0db382450871ad9ef6898a2ce360 -- 2.45.2 From 36e0a256fe79f87447bb730fda53e5cbc90eb47c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 8 Jun 2015 04:11:21 +0200 Subject: [PATCH 006/128] Update to libgit2 b6011e29 --- clone.go | 4 +- git.go | 4 +- index.go | 42 ++++++----- odb.go | 8 ++- odb_test.go | 2 +- remote.go | 184 ++++++++++++++++++++++++------------------------- remote_test.go | 45 ++++-------- submodule.go | 4 +- vendor/libgit2 | 2 +- 9 files changed, 140 insertions(+), 155 deletions(-) diff --git a/clone.go b/clone.go index b796b6e..233300d 100644 --- a/clone.go +++ b/clone.go @@ -12,7 +12,7 @@ import ( type CloneOptions struct { *CheckoutOpts - *RemoteCallbacks + *FetchOptions Bare bool CheckoutBranch string RemoteCreateCallback C.git_remote_create_cb @@ -55,7 +55,7 @@ func populateCloneOptions(ptr *C.git_clone_options, opts *CloneOptions) { return } populateCheckoutOpts(&ptr.checkout_opts, opts.CheckoutOpts) - populateRemoteCallbacks(&ptr.remote_callbacks, opts.RemoteCallbacks) + populateFetchOptions(&ptr.fetch_opts, opts.FetchOptions) ptr.bare = cbool(opts.Bare) if opts.RemoteCreateCallback != nil { diff --git a/git.go b/git.go index 5f69837..ae65cb7 100644 --- a/git.go +++ b/git.go @@ -76,8 +76,8 @@ const ( ErrNonFastForward ErrorCode = C.GIT_ENONFASTFORWARD // Name/ref spec was not in a valid format ErrInvalidSpec ErrorCode = C.GIT_EINVALIDSPEC - // Merge conflicts prevented operation - ErrMergeConflict ErrorCode = C.GIT_EMERGECONFLICT + // Checkout conflicts prevented operation + ErrConflict ErrorCode = C.GIT_ECONFLICT // Lock file prevented operation ErrLocked ErrorCode = C.GIT_ELOCKED // Reference value does not match expected diff --git a/index.go b/index.go index c1bfb74..0174dc1 100644 --- a/index.go +++ b/index.go @@ -12,7 +12,6 @@ import "C" import ( "fmt" "runtime" - "time" "unsafe" ) @@ -31,13 +30,18 @@ type Index struct { ptr *C.git_index } +type IndexTime struct { + seconds int32 + nanoseconds uint32 +} + type IndexEntry struct { - Ctime time.Time - Mtime time.Time + Ctime IndexTime + Mtime IndexTime Mode Filemode - Uid uint - Gid uint - Size uint + Uid uint32 + Gid uint32 + Size uint32 Id *Oid Path string } @@ -47,26 +51,26 @@ func newIndexEntryFromC(entry *C.git_index_entry) *IndexEntry { return nil } return &IndexEntry{ - time.Unix(int64(entry.ctime.seconds), int64(entry.ctime.nanoseconds)), - time.Unix(int64(entry.mtime.seconds), int64(entry.mtime.nanoseconds)), + IndexTime { int32(entry.ctime.seconds), uint32(entry.ctime.nanoseconds) }, + IndexTime { int32(entry.mtime.seconds), uint32(entry.mtime.nanoseconds) }, Filemode(entry.mode), - uint(entry.uid), - uint(entry.gid), - uint(entry.file_size), + uint32(entry.uid), + uint32(entry.gid), + uint32(entry.file_size), newOidFromC(&entry.id), C.GoString(entry.path), } } func populateCIndexEntry(source *IndexEntry, dest *C.git_index_entry) { - dest.ctime.seconds = C.git_time_t(source.Ctime.Unix()) - dest.ctime.nanoseconds = C.uint(source.Ctime.UnixNano()) - dest.mtime.seconds = C.git_time_t(source.Mtime.Unix()) - dest.mtime.nanoseconds = C.uint(source.Mtime.UnixNano()) - dest.mode = C.uint(source.Mode) - dest.uid = C.uint(source.Uid) - dest.gid = C.uint(source.Gid) - dest.file_size = C.git_off_t(source.Size) + dest.ctime.seconds = C.int32_t(source.Ctime.seconds) + dest.ctime.nanoseconds = C.uint32_t(source.Ctime.nanoseconds) + dest.mtime.seconds = C.int32_t(source.Mtime.seconds) + dest.mtime.nanoseconds = C.uint32_t(source.Mtime.nanoseconds) + dest.mode = C.uint32_t(source.Mode) + dest.uid = C.uint32_t(source.Uid) + dest.gid = C.uint32_t(source.Gid) + dest.file_size = C.uint32_t(source.Size) dest.id = *source.Id.toC() dest.path = C.CString(source.Path) } diff --git a/odb.go b/odb.go index 6b21329..be0870e 100644 --- a/odb.go +++ b/odb.go @@ -11,6 +11,7 @@ import ( "reflect" "runtime" "unsafe" + "fmt" ) type Odb struct { @@ -106,7 +107,9 @@ func odbForEachCb(id *C.git_oid, handle unsafe.Pointer) int { } err := data.callback(newOidFromC(id)) + fmt.Println("err %v", err) if err != nil { + fmt.Println("returning EUSER") data.err = err return C.GIT_EUSER } @@ -127,6 +130,7 @@ func (v *Odb) ForEach(callback OdbForEachCallback) error { defer pointerHandles.Untrack(handle) ret := C._go_git_odb_foreach(v.ptr, handle) + fmt.Println("ret %v", ret); if ret == C.GIT_EUSER { return data.err } else if ret < 0 { @@ -172,13 +176,13 @@ func (v *Odb) NewReadStream(id *Oid) (*OdbReadStream, error) { // NewWriteStream opens a write stream to the ODB, which allows you to // create a new object in the database. The size and type must be // known in advance -func (v *Odb) NewWriteStream(size int, otype ObjectType) (*OdbWriteStream, error) { +func (v *Odb) NewWriteStream(size int64, 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)) + ret := C.git_odb_open_wstream(&stream.ptr, v.ptr, C.git_off_t(size), C.git_otype(otype)) if ret < 0 { return nil, MakeGitError(ret) } diff --git a/odb_test.go b/odb_test.go index 2fb6840..0d765b9 100644 --- a/odb_test.go +++ b/odb_test.go @@ -17,7 +17,7 @@ func TestOdbStream(t *testing.T) { str := "hello, world!" - stream, error := odb.NewWriteStream(len(str), ObjectBlob) + stream, error := odb.NewWriteStream(int64(len(str)), ObjectBlob) checkFatal(t, error) n, error := io.WriteString(stream, str) checkFatal(t, error) diff --git a/remote.go b/remote.go index 72d3934..705bc0c 100644 --- a/remote.go +++ b/remote.go @@ -69,6 +69,51 @@ type RemoteCallbacks struct { PushUpdateReferenceCallback } +type FetchPrune uint + +const ( + // Use the setting from the configuration + FetchPruneFallback FetchPrune = C.GIT_FETCH_PRUNE_FALLBACK + // Force pruning on + FetchPruneOn FetchPrune = C.GIT_FETCH_PRUNE + // Force pruning off + FetchNoPrune FetchPrune = C.GIT_FETCH_NO_PRUNE +) + +type DownloadTags uint + +const ( + + // Use the setting from the configuration. + DownloadTagsFallback DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_FALLBACK + // Ask the server for tags pointing to objects we're already + // downloading. + DownloadTagsAuto DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_AUTO + + // Don't ask for any tags beyond the refspecs. + DownloadTagsNone DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_NONE + + // Ask for the all the tags. + DownloadTagsAll DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_ALL +) + +type FetchOptions struct { + // Callbacks to use for this fetch operation + RemoteCallbacks RemoteCallbacks + // Whether to perform a prune after the fetch + Prune FetchPrune + // Whether to write the results to FETCH_HEAD. Defaults to + // on. Leave this default in order to behave like git. + UpdateFetchhead bool + + // Determines how to behave regarding tags on the remote, such + // as auto-downloading tags for objects we're downloading or + // downloading all of them. + // + // The default is to auto-follow tags. + DownloadTags DownloadTags +} + type Remote struct { ptr *C.git_remote callbacks RemoteCallbacks @@ -267,23 +312,6 @@ func RemoteIsValidName(name string) bool { return false } -func (r *Remote) SetCallbacks(callbacks *RemoteCallbacks) error { - r.callbacks = *callbacks - - var ccallbacks C.git_remote_callbacks - populateRemoteCallbacks(&ccallbacks, &r.callbacks) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ecode := C.git_remote_set_callbacks(r.ptr, &ccallbacks) - if ecode < 0 { - return MakeGitError(ecode) - } - - return nil -} - func (r *Remote) Free() { runtime.SetFinalizer(r, nil) C.git_remote_free(r.ptr) @@ -359,18 +387,16 @@ func (repo *Repository) CreateRemoteWithFetchspec(name string, url string, fetch return remote, nil } -func (repo *Repository) CreateAnonymousRemote(url, fetch string) (*Remote, error) { +func (repo *Repository) CreateAnonymousRemote(url string) (*Remote, error) { remote := &Remote{} curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) - cfetch := C.CString(fetch) - defer C.free(unsafe.Pointer(cfetch)) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_create_anonymous(&remote.ptr, repo.ptr, curl, cfetch) + ret := C.git_remote_create_anonymous(&remote.ptr, repo.ptr, curl) if ret < 0 { return nil, MakeGitError(ret) } @@ -395,18 +421,6 @@ func (repo *Repository) LookupRemote(name string) (*Remote, error) { return remote, nil } -func (o *Remote) Save() error { - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_remote_save(o.ptr) - if ret < 0 { - return MakeGitError(ret) - } - return nil -} - func (o *Remote) Owner() Repository { return Repository{C.git_remote_owner(o.ptr)} } @@ -423,42 +437,48 @@ func (o *Remote) PushUrl() string { return C.GoString(C.git_remote_pushurl(o.ptr)) } -func (o *Remote) SetUrl(url string) error { +func (o *Repository) RemoteSetUrl(remote, url string) error { curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) + cremote := C.CString(remote) + defer C.free(unsafe.Pointer(cremote)) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_set_url(o.ptr, curl) + ret := C.git_remote_set_url(o.ptr, cremote, curl) if ret < 0 { return MakeGitError(ret) } return nil } -func (o *Remote) SetPushUrl(url string) error { +func (o *Repository) RemoteSetPushUrl(remote, url string) error { curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) + cremote := C.CString(remote) + defer C.free(unsafe.Pointer(cremote)) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_set_pushurl(o.ptr, curl) + ret := C.git_remote_set_pushurl(o.ptr, cremote, curl) if ret < 0 { return MakeGitError(ret) } return nil } -func (o *Remote) AddFetch(refspec string) error { +func (o *Repository) RemoteAddFetch(remote, refspec string) error { crefspec := C.CString(refspec) defer C.free(unsafe.Pointer(crefspec)) + cremote := C.CString(remote) + defer C.free(unsafe.Pointer(cremote)) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_add_fetch(o.ptr, crefspec) + ret := C.git_remote_add_fetch(o.ptr, cremote, crefspec) if ret < 0 { return MakeGitError(ret) } @@ -519,30 +539,16 @@ func (o *Remote) FetchRefspecs() ([]string, error) { return refspecs, nil } -func (o *Remote) SetFetchRefspecs(refspecs []string) error { - 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_set_fetch_refspecs(o.ptr, &crefspecs) - if ret < 0 { - return MakeGitError(ret) - } - return nil -} - -func (o *Remote) AddPush(refspec string) error { +func (o *Repository) RemoteAddPush(remote, refspec string) error { crefspec := C.CString(refspec) defer C.free(unsafe.Pointer(crefspec)) + cremote := C.CString(remote) + defer C.free(unsafe.Pointer(cremote)) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_add_push(o.ptr, crefspec) + ret := C.git_remote_add_push(o.ptr, cremote, crefspec) if ret < 0 { return MakeGitError(ret) } @@ -564,43 +570,26 @@ func (o *Remote) PushRefspecs() ([]string, error) { return refspecs, nil } -func (o *Remote) SetPushRefspecs(refspecs []string) error { - 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_set_push_refspecs(o.ptr, &crefspecs) - if ret < 0 { - return MakeGitError(ret) - } - return nil -} - -func (o *Remote) ClearRefspecs() { - C.git_remote_clear_refspecs(o.ptr) -} - func (o *Remote) RefspecCount() uint { return uint(C.git_remote_refspec_count(o.ptr)) } -func (o *Remote) SetUpdateFetchHead(val bool) { - C.git_remote_set_update_fetchhead(o.ptr, cbool(val)) -} - -func (o *Remote) UpdateFetchHead() bool { - return C.git_remote_update_fetchhead(o.ptr) > 0 +func populateFetchOptions(options *C.git_fetch_options, opts *FetchOptions) { + C.git_fetch_init_options(options, C.GIT_FETCH_OPTIONS_VERSION) + if opts == nil { + return; + } + populateRemoteCallbacks(&options.callbacks, &opts.RemoteCallbacks) + options.prune = C.git_fetch_prune_t(opts.Prune) + options.update_fetchhead = cbool(opts.UpdateFetchhead) + options.download_tags = C.git_remote_autotag_option_t(opts.DownloadTags) } // Fetch performs a fetch operation. refspecs specifies which refspecs // to use for this fetch, use an empty list to use the refspecs from // the configuration; msg specifies what to use for the reflog // entries. Leave "" to use defaults. -func (o *Remote) Fetch(refspecs []string, msg string) error { +func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error { var cmsg *C.char = nil if msg != "" { @@ -613,29 +602,35 @@ func (o *Remote) Fetch(refspecs []string, msg string) error { crefspecs.strings = makeCStringsFromStrings(refspecs) defer freeStrarray(&crefspecs) + var coptions C.git_fetch_options + populateFetchOptions(&coptions, opts); + runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_fetch(o.ptr, &crefspecs, cmsg) + ret := C.git_remote_fetch(o.ptr, &crefspecs, &coptions, cmsg) if ret < 0 { return MakeGitError(ret) } return nil } -func (o *Remote) ConnectFetch() error { - return o.Connect(ConnectDirectionFetch) +func (o *Remote) ConnectFetch(callbacks *RemoteCallbacks) error { + return o.Connect(ConnectDirectionFetch, callbacks) } -func (o *Remote) ConnectPush() error { - return o.Connect(ConnectDirectionPush) +func (o *Remote) ConnectPush(callbacks *RemoteCallbacks) error { + return o.Connect(ConnectDirectionPush, callbacks) } -func (o *Remote) Connect(direction ConnectDirection) error { +func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks) error { + var ccallbacks C.git_remote_callbacks; + populateRemoteCallbacks(&ccallbacks, callbacks) + runtime.LockOSThread() defer runtime.UnlockOSThread() - if ret := C.git_remote_connect(o.ptr, C.git_direction(direction)); ret != 0 { + if ret := C.git_remote_connect(o.ptr, C.git_direction(direction), &ccallbacks); ret != 0 { return MakeGitError(ret) } return nil @@ -713,11 +708,14 @@ func (o *Remote) PruneRefs() bool { return C.git_remote_prune_refs(o.ptr) > 0 } -func (o *Remote) Prune() error { +func (o *Remote) Prune(callbacks *RemoteCallbacks) error { + var ccallbacks C.git_remote_callbacks; + populateRemoteCallbacks(&ccallbacks, callbacks) + runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_prune(o.ptr) + ret := C.git_remote_prune(o.ptr, &ccallbacks) if ret < 0 { return MakeGitError(ret) } diff --git a/remote_test.go b/remote_test.go index 23c80f5..cbb52aa 100644 --- a/remote_test.go +++ b/remote_test.go @@ -5,27 +5,6 @@ import ( "testing" ) -func TestRefspecs(t *testing.T) { - repo := createTestRepo(t) - defer cleanupTestRepo(t, repo) - - remote, err := repo.CreateAnonymousRemote("git://foo/bar", "refs/heads/*:refs/heads/*") - checkFatal(t, err) - - expected := []string{ - "refs/heads/*:refs/remotes/origin/*", - "refs/pull/*/head:refs/remotes/origin/*", - } - - err = remote.SetFetchRefspecs(expected) - checkFatal(t, err) - - actual, err := remote.FetchRefspecs() - checkFatal(t, err) - - compareStringList(t, expected, actual) -} - func TestListRemotes(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -60,15 +39,15 @@ func TestCertificateCheck(t *testing.T) { remote, err := repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) - callbacks := RemoteCallbacks{ - CertificateCheckCallback: func(cert *Certificate, valid bool, hostname string) ErrorCode { - return assertHostname(cert, valid, hostname, t) + options := FetchOptions { + RemoteCallbacks: RemoteCallbacks{ + CertificateCheckCallback: func(cert *Certificate, valid bool, hostname string) ErrorCode { + return assertHostname(cert, valid, hostname, t) + }, }, } - err = remote.SetCallbacks(&callbacks) - checkFatal(t, err) - err = remote.Fetch([]string{}, "") + err = remote.Fetch([]string{}, &options, "") checkFatal(t, err) } @@ -79,7 +58,7 @@ func TestRemoteConnect(t *testing.T) { remote, err := repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) - err = remote.ConnectFetch() + err = remote.ConnectFetch(nil) checkFatal(t, err) } @@ -90,7 +69,7 @@ func TestRemoteLs(t *testing.T) { remote, err := repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) - err = remote.ConnectFetch() + err = remote.ConnectFetch(nil) checkFatal(t, err) heads, err := remote.Ls() @@ -108,7 +87,7 @@ func TestRemoteLsFiltering(t *testing.T) { remote, err := repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) - err = remote.ConnectFetch() + err = remote.ConnectFetch(nil) checkFatal(t, err) heads, err := remote.Ls("master") @@ -172,7 +151,7 @@ func TestRemotePrune(t *testing.T) { remote, err := repo.CreateRemote("origin", remoteUrl) checkFatal(t, err) - err = remote.Fetch([]string{"test-prune"}, "") + err = remote.Fetch([]string{"test-prune"}, nil, "") checkFatal(t, err) _, err = repo.CreateReference("refs/remotes/origin/test-prune", head, true, "remote reference") @@ -187,10 +166,10 @@ func TestRemotePrune(t *testing.T) { rr, err := repo.LookupRemote("origin") checkFatal(t, err) - err = rr.ConnectFetch() + err = rr.ConnectFetch(nil) checkFatal(t, err) - err = rr.Prune() + err = rr.Prune(nil) checkFatal(t, err) _, err = repo.LookupReference("refs/remotes/origin/test-prune") diff --git a/submodule.go b/submodule.go index 6edc1d7..05cfd73 100644 --- a/submodule.go +++ b/submodule.go @@ -14,7 +14,7 @@ import ( // SubmoduleUpdateOptions type SubmoduleUpdateOptions struct { *CheckoutOpts - *RemoteCallbacks + *FetchOptions CloneCheckoutStrategy CheckoutStrategy } @@ -348,7 +348,7 @@ func populateSubmoduleUpdateOptions(ptr *C.git_submodule_update_options, opts *S } populateCheckoutOpts(&ptr.checkout_opts, opts.CheckoutOpts) - populateRemoteCallbacks(&ptr.remote_callbacks, opts.RemoteCallbacks) + populateFetchOptions(&ptr.fetch_opts, opts.FetchOptions) ptr.clone_checkout_strategy = C.uint(opts.CloneCheckoutStrategy) return nil diff --git a/vendor/libgit2 b/vendor/libgit2 index 4c02d39..b6011e2 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 4c02d393748d0db382450871ad9ef6898a2ce360 +Subproject commit b6011e296e4db6942d71f5a8548d1308353927ee -- 2.45.2 From d400f1d5b2154f00f676b6b4e3c510f37de47573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 28 Jun 2015 00:34:54 +0200 Subject: [PATCH 007/128] Update to libgit2 fa39975 --- remote.go | 4 +-- submodule.go | 80 ++++++++++++++++++++++---------------------------- vendor/libgit2 | 2 +- wrapper.c | 2 +- 4 files changed, 39 insertions(+), 49 deletions(-) diff --git a/remote.go b/remote.go index 705bc0c..a52ff06 100644 --- a/remote.go +++ b/remote.go @@ -73,7 +73,7 @@ type FetchPrune uint const ( // Use the setting from the configuration - FetchPruneFallback FetchPrune = C.GIT_FETCH_PRUNE_FALLBACK + FetchPruneUnspecified FetchPrune = C.GIT_FETCH_PRUNE_UNSPECIFIED // Force pruning on FetchPruneOn FetchPrune = C.GIT_FETCH_PRUNE // Force pruning off @@ -85,7 +85,7 @@ type DownloadTags uint const ( // Use the setting from the configuration. - DownloadTagsFallback DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_FALLBACK + DownloadTagsUnspecified DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED // Ask the server for tags pointing to objects we're already // downloading. DownloadTagsAuto DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_AUTO diff --git a/submodule.go b/submodule.go index 05cfd73..fb7c5e6 100644 --- a/submodule.go +++ b/submodule.go @@ -26,7 +26,6 @@ type Submodule struct { 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 @@ -36,7 +35,6 @@ const ( type SubmoduleIgnore int const ( - SubmoduleIgnoreReset SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_RESET SubmoduleIgnoreNone SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_NONE SubmoduleIgnoreUntracked SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_UNTRACKED SubmoduleIgnoreDirty SubmoduleIgnore = C.GIT_SUBMODULE_IGNORE_DIRTY @@ -160,17 +158,6 @@ func (sub *Submodule) AddToIndex(write_index bool) error { return nil } -func (sub *Submodule) Save() error { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_submodule_save(sub.ptr) - if ret < 0 { - return MakeGitError(ret) - } - return nil -} - func (sub *Submodule) Owner() *Repository { repo := C.git_submodule_owner(sub.ptr) //FIXME: how to handle dangling references ? @@ -192,14 +179,16 @@ func (sub *Submodule) Url() string { return C.GoString(n) } -func (sub *Submodule) SetUrl(url string) error { +func (o *Repository) SubmoduleSetUrl(submodule, url string) error { + csubmodule := C.CString(submodule) + defer C.free(unsafe.Pointer(csubmodule)) curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_submodule_set_url(sub.ptr, curl) + ret := C.git_submodule_set_url(o.ptr, csubmodule, curl) if ret < 0 { return MakeGitError(ret) } @@ -235,9 +224,19 @@ func (sub *Submodule) Ignore() SubmoduleIgnore { return SubmoduleIgnore(o) } -func (sub *Submodule) SetIgnore(ignore SubmoduleIgnore) SubmoduleIgnore { - o := C.git_submodule_set_ignore(sub.ptr, C.git_submodule_ignore_t(ignore)) - return SubmoduleIgnore(o) +func (o *Repository) SubmoduleSetIgnore(submodule string, ignore SubmoduleIgnore) error { + csubmodule := C.CString(submodule) + defer C.free(unsafe.Pointer(csubmodule)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_submodule_set_ignore(o.ptr, csubmodule, C.git_submodule_ignore_t(ignore)) + if ret < 0 { + return MakeGitError(ret) + } + + return nil } func (sub *Submodule) UpdateStrategy() SubmoduleUpdate { @@ -245,20 +244,33 @@ func (sub *Submodule) UpdateStrategy() SubmoduleUpdate { return SubmoduleUpdate(o) } -func (sub *Submodule) SetUpdate(update SubmoduleUpdate) SubmoduleUpdate { - o := C.git_submodule_set_update(sub.ptr, C.git_submodule_update_t(update)) - return SubmoduleUpdate(o) +func (o *Repository) SubmoduleSetUpdate(submodule string, update SubmoduleUpdate) error { + csubmodule := C.CString(submodule) + defer C.free(unsafe.Pointer(csubmodule)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_submodule_set_update(o.ptr, csubmodule, C.git_submodule_update_t(update)) + if ret < 0 { + return MakeGitError(ret) + } + + return nil } func (sub *Submodule) FetchRecurseSubmodules() SubmoduleRecurse { return SubmoduleRecurse(C.git_submodule_fetch_recurse_submodules(sub.ptr)) } -func (sub *Submodule) SetFetchRecurseSubmodules(recurse SubmoduleRecurse) error { +func (o *Repository) SubmoduleSetFetchRecurseSubmodules(submodule string, recurse SubmoduleRecurse) error { + csubmodule := C.CString(submodule) + defer C.free(unsafe.Pointer(csubmodule)) + runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_submodule_set_fetch_recurse_submodules(sub.ptr, C.git_submodule_recurse_t(recurse)) + ret := C.git_submodule_set_fetch_recurse_submodules(o.ptr, csubmodule, C.git_submodule_recurse_t(recurse)) if ret < 0 { return MakeGitError(C.int(ret)) } @@ -300,28 +312,6 @@ func (sub *Submodule) Open() (*Repository, error) { return repo, nil } -func (sub *Submodule) Reload(force bool) error { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_submodule_reload(sub.ptr, cbool(force)) - if ret < 0 { - return MakeGitError(ret) - } - return nil -} - -func (repo *Repository) ReloadAllSubmodules(force bool) error { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_submodule_reload_all(repo.ptr, cbool(force)) - if ret < 0 { - return MakeGitError(ret) - } - return nil -} - func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error { var copts C.git_submodule_update_options err := populateSubmoduleUpdateOptions(&copts, opts) diff --git a/vendor/libgit2 b/vendor/libgit2 index b6011e2..fa39975 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit b6011e296e4db6942d71f5a8548d1308353927ee +Subproject commit fa399750c680aa254784a40193d73d373df5e3ea diff --git a/wrapper.c b/wrapper.c index 938fd17..4f2de60 100644 --- a/wrapper.c +++ b/wrapper.c @@ -59,7 +59,7 @@ int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLin lcb = (git_diff_line_cb)&diffForEachLineCb; } - return git_diff_foreach(diff, fcb, hcb, lcb, payload); + return git_diff_foreach(diff, fcb, NULL, hcb, lcb, payload); } void _go_git_setup_diff_notify_callbacks(git_diff_options *opts) { -- 2.45.2 From ba0a24087a8cd1a354872c95f3efe0224ea84b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 28 Jun 2015 00:49:56 +0200 Subject: [PATCH 008/128] Get rid of Owner() on Remote and Submdoule These are inherently unsafe. The underlying pointer might get released at any moment. --- remote.go | 4 ---- submodule.go | 6 ------ 2 files changed, 10 deletions(-) diff --git a/remote.go b/remote.go index a52ff06..4ff2040 100644 --- a/remote.go +++ b/remote.go @@ -421,10 +421,6 @@ func (repo *Repository) LookupRemote(name string) (*Remote, error) { return remote, nil } -func (o *Remote) Owner() Repository { - return Repository{C.git_remote_owner(o.ptr)} -} - func (o *Remote) Name() string { return C.GoString(C.git_remote_name(o.ptr)) } diff --git a/submodule.go b/submodule.go index fb7c5e6..971011b 100644 --- a/submodule.go +++ b/submodule.go @@ -158,12 +158,6 @@ func (sub *Submodule) AddToIndex(write_index bool) error { return nil } -func (sub *Submodule) Owner() *Repository { - repo := C.git_submodule_owner(sub.ptr) - //FIXME: how to handle dangling references ? - return &Repository{repo} -} - func (sub *Submodule) Name() string { n := C.git_submodule_name(sub.ptr) return C.GoString(n) -- 2.45.2 From 4b9cbd78fd266767f6bdf55257c4ee2b1611bbe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 28 Jun 2015 00:51:17 +0200 Subject: [PATCH 009/128] Create a RemoteCollection for managing remotes Instead of making the 'Remote' part of the function calls, create a collection object which serves to namespace the operations for the remotes. --- push_test.go | 2 +- remote.go | 44 ++++++++++++++++++++++++-------------------- remote_test.go | 20 ++++++++++---------- repository.go | 19 ++++++++++++++----- 4 files changed, 49 insertions(+), 36 deletions(-) diff --git a/push_test.go b/push_test.go index 4686c65..a9fc90d 100644 --- a/push_test.go +++ b/push_test.go @@ -11,7 +11,7 @@ func TestRemotePush(t *testing.T) { localRepo := createTestRepo(t) defer cleanupTestRepo(t, localRepo) - remote, err := localRepo.CreateRemote("test_push", repo.Path()) + remote, err := localRepo.Remotes.Create("test_push", repo.Path()) checkFatal(t, err) seedTestRepo(t, localRepo) diff --git a/remote.go b/remote.go index 4ff2040..5bed309 100644 --- a/remote.go +++ b/remote.go @@ -317,13 +317,17 @@ func (r *Remote) Free() { C.git_remote_free(r.ptr) } -func (repo *Repository) ListRemotes() ([]string, error) { +type RemoteCollection struct { + repo *Repository +} + +func (c *RemoteCollection) List() ([]string, error) { var r C.git_strarray runtime.LockOSThread() defer runtime.UnlockOSThread() - ecode := C.git_remote_list(&r, repo.ptr) + ecode := C.git_remote_list(&r, c.repo.ptr) if ecode < 0 { return nil, MakeGitError(ecode) } @@ -333,7 +337,7 @@ func (repo *Repository) ListRemotes() ([]string, error) { return remotes, nil } -func (repo *Repository) CreateRemote(name string, url string) (*Remote, error) { +func (c *RemoteCollection) Create(name string, url string) (*Remote, error) { remote := &Remote{} cname := C.CString(name) @@ -344,7 +348,7 @@ func (repo *Repository) CreateRemote(name string, url string) (*Remote, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_create(&remote.ptr, repo.ptr, cname, curl) + ret := C.git_remote_create(&remote.ptr, c.repo.ptr, cname, curl) if ret < 0 { return nil, MakeGitError(ret) } @@ -352,21 +356,21 @@ func (repo *Repository) CreateRemote(name string, url string) (*Remote, error) { return remote, nil } -func (repo *Repository) DeleteRemote(name string) error { +func (c *RemoteCollection) Delete(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) + ret := C.git_remote_delete(c.repo.ptr, cname) if ret < 0 { return MakeGitError(ret) } return nil } -func (repo *Repository) CreateRemoteWithFetchspec(name string, url string, fetch string) (*Remote, error) { +func (c *RemoteCollection) CreateWithFetchspec(name string, url string, fetch string) (*Remote, error) { remote := &Remote{} cname := C.CString(name) @@ -379,7 +383,7 @@ func (repo *Repository) CreateRemoteWithFetchspec(name string, url string, fetch runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_create_with_fetchspec(&remote.ptr, repo.ptr, cname, curl, cfetch) + ret := C.git_remote_create_with_fetchspec(&remote.ptr, c.repo.ptr, cname, curl, cfetch) if ret < 0 { return nil, MakeGitError(ret) } @@ -387,7 +391,7 @@ func (repo *Repository) CreateRemoteWithFetchspec(name string, url string, fetch return remote, nil } -func (repo *Repository) CreateAnonymousRemote(url string) (*Remote, error) { +func (c *RemoteCollection) CreateAnonymous(url string) (*Remote, error) { remote := &Remote{} curl := C.CString(url) @@ -396,7 +400,7 @@ func (repo *Repository) CreateAnonymousRemote(url string) (*Remote, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_create_anonymous(&remote.ptr, repo.ptr, curl) + ret := C.git_remote_create_anonymous(&remote.ptr, c.repo.ptr, curl) if ret < 0 { return nil, MakeGitError(ret) } @@ -404,7 +408,7 @@ func (repo *Repository) CreateAnonymousRemote(url string) (*Remote, error) { return remote, nil } -func (repo *Repository) LookupRemote(name string) (*Remote, error) { +func (c *RemoteCollection) Lookup(name string) (*Remote, error) { remote := &Remote{} cname := C.CString(name) @@ -413,7 +417,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_lookup(&remote.ptr, c.repo.ptr, cname) if ret < 0 { return nil, MakeGitError(ret) } @@ -433,7 +437,7 @@ func (o *Remote) PushUrl() string { return C.GoString(C.git_remote_pushurl(o.ptr)) } -func (o *Repository) RemoteSetUrl(remote, url string) error { +func (c *RemoteCollection) SetUrl(remote, url string) error { curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) cremote := C.CString(remote) @@ -442,14 +446,14 @@ func (o *Repository) RemoteSetUrl(remote, url string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_set_url(o.ptr, cremote, curl) + ret := C.git_remote_set_url(c.repo.ptr, cremote, curl) if ret < 0 { return MakeGitError(ret) } return nil } -func (o *Repository) RemoteSetPushUrl(remote, url string) error { +func (c *RemoteCollection) SetPushUrl(remote, url string) error { curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) cremote := C.CString(remote) @@ -458,14 +462,14 @@ func (o *Repository) RemoteSetPushUrl(remote, url string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_set_pushurl(o.ptr, cremote, curl) + ret := C.git_remote_set_pushurl(c.repo.ptr, cremote, curl) if ret < 0 { return MakeGitError(ret) } return nil } -func (o *Repository) RemoteAddFetch(remote, refspec string) error { +func (c *RemoteCollection) AddFetch(remote, refspec string) error { crefspec := C.CString(refspec) defer C.free(unsafe.Pointer(crefspec)) cremote := C.CString(remote) @@ -474,7 +478,7 @@ func (o *Repository) RemoteAddFetch(remote, refspec string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_add_fetch(o.ptr, cremote, crefspec) + ret := C.git_remote_add_fetch(c.repo.ptr, cremote, crefspec) if ret < 0 { return MakeGitError(ret) } @@ -535,7 +539,7 @@ func (o *Remote) FetchRefspecs() ([]string, error) { return refspecs, nil } -func (o *Repository) RemoteAddPush(remote, refspec string) error { +func (c *RemoteCollection) AddPush(remote, refspec string) error { crefspec := C.CString(refspec) defer C.free(unsafe.Pointer(crefspec)) cremote := C.CString(remote) @@ -544,7 +548,7 @@ func (o *Repository) RemoteAddPush(remote, refspec string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_add_push(o.ptr, cremote, crefspec) + ret := C.git_remote_add_push(c.repo.ptr, cremote, crefspec) if ret < 0 { return MakeGitError(ret) } diff --git a/remote_test.go b/remote_test.go index cbb52aa..4e64489 100644 --- a/remote_test.go +++ b/remote_test.go @@ -9,7 +9,7 @@ func TestListRemotes(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) - _, err := repo.CreateRemote("test", "git://foo/bar") + _, err := repo.Remotes.Create("test", "git://foo/bar") checkFatal(t, err) @@ -17,7 +17,7 @@ func TestListRemotes(t *testing.T) { "test", } - actual, err := repo.ListRemotes() + actual, err := repo.Remotes.List() checkFatal(t, err) compareStringList(t, expected, actual) @@ -36,7 +36,7 @@ func TestCertificateCheck(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) - remote, err := repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") + remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) options := FetchOptions { @@ -55,7 +55,7 @@ func TestRemoteConnect(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) - remote, err := repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") + remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) err = remote.ConnectFetch(nil) @@ -66,7 +66,7 @@ func TestRemoteLs(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) - remote, err := repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") + remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) err = remote.ConnectFetch(nil) @@ -84,7 +84,7 @@ func TestRemoteLsFiltering(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) - remote, err := repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") + remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) err = remote.ConnectFetch(nil) @@ -117,10 +117,10 @@ func TestRemotePruneRefs(t *testing.T) { err = config.SetBool("remote.origin.prune", true) checkFatal(t, err) - _, err = repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository") + _, err = repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) - remote, err := repo.LookupRemote("origin") + remote, err := repo.Remotes.Lookup("origin") checkFatal(t, err) if !remote.PruneRefs() { @@ -148,7 +148,7 @@ func TestRemotePrune(t *testing.T) { defer config.Free() remoteUrl := fmt.Sprintf("file://%s", remoteRepo.Workdir()) - remote, err := repo.CreateRemote("origin", remoteUrl) + remote, err := repo.Remotes.Create("origin", remoteUrl) checkFatal(t, err) err = remote.Fetch([]string{"test-prune"}, nil, "") @@ -163,7 +163,7 @@ func TestRemotePrune(t *testing.T) { err = config.SetBool("remote.origin.prune", true) checkFatal(t, err) - rr, err := repo.LookupRemote("origin") + rr, err := repo.Remotes.Lookup("origin") checkFatal(t, err) err = rr.ConnectFetch(nil) diff --git a/repository.go b/repository.go index 9917c60..860050f 100644 --- a/repository.go +++ b/repository.go @@ -12,7 +12,16 @@ import ( // Repository type Repository struct { - ptr *C.git_repository + ptr *C.git_repository + // Remotes represents the collection of remotes and can be + // used to add, remove and configure remotes for this + // repository. + Remotes RemoteCollection +} + +func initRepositoryObject(repo *Repository) { + repo.Remotes.repo = repo + runtime.SetFinalizer(repo, (*Repository).Free) } func OpenRepository(path string) (*Repository, error) { @@ -29,7 +38,7 @@ func OpenRepository(path string) (*Repository, error) { return nil, MakeGitError(ret) } - runtime.SetFinalizer(repo, (*Repository).Free) + initRepositoryObject(repo) return repo, nil } @@ -47,7 +56,7 @@ func OpenRepositoryExtended(path string) (*Repository, error) { return nil, MakeGitError(ret) } - runtime.SetFinalizer(repo, (*Repository).Free) + initRepositoryObject(repo) return repo, nil } @@ -65,7 +74,7 @@ func InitRepository(path string, isbare bool) (*Repository, error) { return nil, MakeGitError(ret) } - runtime.SetFinalizer(repo, (*Repository).Free) + initRepositoryObject(repo) return repo, nil } @@ -80,7 +89,7 @@ func NewRepositoryWrapOdb(odb *Odb) (repo *Repository, err error) { return nil, MakeGitError(ret) } - runtime.SetFinalizer(repo, (*Repository).Free) + initRepositoryObject(repo) return repo, nil } -- 2.45.2 From d2808d16101e113096b3a1c02e3f91122de74f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 28 Jun 2015 00:58:31 +0200 Subject: [PATCH 010/128] Create a SubmoduleCollection for managing submodules Similarly to RemoteCollection, this allows us to namespace the submodule operations much more concisely and removes API on the Repository. --- repository.go | 7 ++++++- submodule.go | 32 ++++++++++++++++++-------------- submodule_test.go | 4 ++-- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/repository.go b/repository.go index 860050f..0427d85 100644 --- a/repository.go +++ b/repository.go @@ -17,10 +17,15 @@ type Repository struct { // used to add, remove and configure remotes for this // repository. Remotes RemoteCollection + // Submodules represents the collectin of submodules and can + // be used to add, remove and configure submodules in this + // repostiory. + Submodules SubmoduleCollection } func initRepositoryObject(repo *Repository) { - repo.Remotes.repo = repo + repo.Remotes.repo = repo + repo.Submodules.repo = repo runtime.SetFinalizer(repo, (*Repository).Free) } diff --git a/submodule.go b/submodule.go index 971011b..6d4dc28 100644 --- a/submodule.go +++ b/submodule.go @@ -68,13 +68,17 @@ const ( SubmoduleRecurseOndemand SubmoduleRecurse = C.GIT_SUBMODULE_RECURSE_ONDEMAND ) +type SubmoduleCollection struct { + repo *Repository +} + func SubmoduleStatusIsUnmodified(status int) bool { o := SubmoduleStatus(status) & ^(SubmoduleStatusInHead | SubmoduleStatusInIndex | SubmoduleStatusInConfig | SubmoduleStatusInWd) return o == 0 } -func (repo *Repository) LookupSubmodule(name string) (*Submodule, error) { +func (c *SubmoduleCollection) Lookup(name string) (*Submodule, error) { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) @@ -83,7 +87,7 @@ func (repo *Repository) LookupSubmodule(name string) (*Submodule, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_submodule_lookup(&sub.ptr, repo.ptr, cname) + ret := C.git_submodule_lookup(&sub.ptr, c.repo.ptr, cname) if ret < 0 { return nil, MakeGitError(ret) } @@ -104,21 +108,21 @@ func SubmoduleVisitor(csub unsafe.Pointer, name *C.char, handle unsafe.Pointer) } } -func (repo *Repository) ForeachSubmodule(cbk SubmoduleCbk) error { +func (c *SubmoduleCollection) Foreach(cbk SubmoduleCbk) error { runtime.LockOSThread() defer runtime.UnlockOSThread() handle := pointerHandles.Track(cbk) defer pointerHandles.Untrack(handle) - ret := C._go_git_visit_submodule(repo.ptr, handle) + ret := C._go_git_visit_submodule(c.repo.ptr, handle) if ret < 0 { return MakeGitError(ret) } return nil } -func (repo *Repository) AddSubmodule(url, path string, use_git_link bool) (*Submodule, error) { +func (c *SubmoduleCollection) Add(url, path string, use_git_link bool) (*Submodule, error) { curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) cpath := C.CString(path) @@ -129,7 +133,7 @@ func (repo *Repository) AddSubmodule(url, path string, use_git_link bool) (*Subm runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_submodule_add_setup(&sub.ptr, repo.ptr, curl, cpath, cbool(use_git_link)) + ret := C.git_submodule_add_setup(&sub.ptr, c.repo.ptr, curl, cpath, cbool(use_git_link)) if ret < 0 { return nil, MakeGitError(ret) } @@ -173,7 +177,7 @@ func (sub *Submodule) Url() string { return C.GoString(n) } -func (o *Repository) SubmoduleSetUrl(submodule, url string) error { +func (c *SubmoduleCollection) SetUrl(submodule, url string) error { csubmodule := C.CString(submodule) defer C.free(unsafe.Pointer(csubmodule)) curl := C.CString(url) @@ -182,7 +186,7 @@ func (o *Repository) SubmoduleSetUrl(submodule, url string) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_submodule_set_url(o.ptr, csubmodule, curl) + ret := C.git_submodule_set_url(c.repo.ptr, csubmodule, curl) if ret < 0 { return MakeGitError(ret) } @@ -218,14 +222,14 @@ func (sub *Submodule) Ignore() SubmoduleIgnore { return SubmoduleIgnore(o) } -func (o *Repository) SubmoduleSetIgnore(submodule string, ignore SubmoduleIgnore) error { +func (c *SubmoduleCollection) SetIgnore(submodule string, ignore SubmoduleIgnore) error { csubmodule := C.CString(submodule) defer C.free(unsafe.Pointer(csubmodule)) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_submodule_set_ignore(o.ptr, csubmodule, C.git_submodule_ignore_t(ignore)) + ret := C.git_submodule_set_ignore(c.repo.ptr, csubmodule, C.git_submodule_ignore_t(ignore)) if ret < 0 { return MakeGitError(ret) } @@ -238,14 +242,14 @@ func (sub *Submodule) UpdateStrategy() SubmoduleUpdate { return SubmoduleUpdate(o) } -func (o *Repository) SubmoduleSetUpdate(submodule string, update SubmoduleUpdate) error { +func (c *SubmoduleCollection) SetUpdate(submodule string, update SubmoduleUpdate) error { csubmodule := C.CString(submodule) defer C.free(unsafe.Pointer(csubmodule)) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_submodule_set_update(o.ptr, csubmodule, C.git_submodule_update_t(update)) + ret := C.git_submodule_set_update(c.repo.ptr, csubmodule, C.git_submodule_update_t(update)) if ret < 0 { return MakeGitError(ret) } @@ -257,14 +261,14 @@ func (sub *Submodule) FetchRecurseSubmodules() SubmoduleRecurse { return SubmoduleRecurse(C.git_submodule_fetch_recurse_submodules(sub.ptr)) } -func (o *Repository) SubmoduleSetFetchRecurseSubmodules(submodule string, recurse SubmoduleRecurse) error { +func (c *SubmoduleCollection) SetFetchRecurseSubmodules(submodule string, recurse SubmoduleRecurse) error { csubmodule := C.CString(submodule) defer C.free(unsafe.Pointer(csubmodule)) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_submodule_set_fetch_recurse_submodules(o.ptr, csubmodule, C.git_submodule_recurse_t(recurse)) + ret := C.git_submodule_set_fetch_recurse_submodules(c.repo.ptr, csubmodule, C.git_submodule_recurse_t(recurse)) if ret < 0 { return MakeGitError(C.int(ret)) } diff --git a/submodule_test.go b/submodule_test.go index 27bc193..43c890a 100644 --- a/submodule_test.go +++ b/submodule_test.go @@ -10,11 +10,11 @@ func TestSubmoduleForeach(t *testing.T) { seedTestRepo(t, repo) - _, err := repo.AddSubmodule("http://example.org/submodule", "submodule", true) + _, err := repo.Submodules.Add("http://example.org/submodule", "submodule", true) checkFatal(t, err) i := 0 - err = repo.ForeachSubmodule(func(sub *Submodule, name string) int { + err = repo.Submodules.Foreach(func(sub *Submodule, name string) int { i++ return 0 }) -- 2.45.2 From 01a2d8d38d4683c6e29da21240ffff7ae7b3a680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 28 Jun 2015 01:12:32 +0200 Subject: [PATCH 011/128] Create a ReferenceCollection for managing references As with the other commits, this clears up the clutter in naming and around the Repository's API. --- merge_test.go | 4 +- push_test.go | 4 +- reference.go | 124 +++++++++++++++++++++++++++++++++++++++++++++ reference_test.go | 22 ++++---- remote_test.go | 4 +- repository.go | 126 ++-------------------------------------------- revparse_test.go | 2 +- 7 files changed, 147 insertions(+), 139 deletions(-) diff --git a/merge_test.go b/merge_test.go index 0b1faca..5c62f5c 100644 --- a/merge_test.go +++ b/merge_test.go @@ -10,7 +10,7 @@ func TestMergeWithSelf(t *testing.T) { seedTestRepo(t, repo) - master, err := repo.LookupReference("refs/heads/master") + master, err := repo.References.Lookup("refs/heads/master") checkFatal(t, err) mergeHead, err := repo.AnnotatedCommitFromRef(master) @@ -28,7 +28,7 @@ func TestMergeAnalysisWithSelf(t *testing.T) { seedTestRepo(t, repo) - master, err := repo.LookupReference("refs/heads/master") + master, err := repo.References.Lookup("refs/heads/master") checkFatal(t, err) mergeHead, err := repo.AnnotatedCommitFromRef(master) diff --git a/push_test.go b/push_test.go index a9fc90d..8f6e806 100644 --- a/push_test.go +++ b/push_test.go @@ -19,9 +19,9 @@ func TestRemotePush(t *testing.T) { err = remote.Push([]string{"refs/heads/master"}, nil) checkFatal(t, err) - _, err = localRepo.LookupReference("refs/remotes/test_push/master") + _, err = localRepo.References.Lookup("refs/remotes/test_push/master") checkFatal(t, err) - _, err = repo.LookupReference("refs/heads/master") + _, err = repo.References.Lookup("refs/heads/master") checkFatal(t, err) } diff --git a/reference.go b/reference.go index 61e2b26..d24e054 100644 --- a/reference.go +++ b/reference.go @@ -21,6 +21,130 @@ type Reference struct { repo *Repository } +type ReferenceCollection struct { + repo *Repository +} + +func (c *ReferenceCollection) Lookup(name string) (*Reference, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + var ptr *C.git_reference + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ecode := C.git_reference_lookup(&ptr, c.repo.ptr, cname) + if ecode < 0 { + return nil, MakeGitError(ecode) + } + + return newReferenceFromC(ptr, c.repo), nil +} + +func (c *ReferenceCollection) Create(name string, id *Oid, force bool, msg string) (*Reference, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + var cmsg *C.char + if msg == "" { + cmsg = nil + } else { + cmsg = C.CString(msg) + defer C.free(unsafe.Pointer(cmsg)) + } + + var ptr *C.git_reference + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ecode := C.git_reference_create(&ptr, c.repo.ptr, cname, id.toC(), cbool(force), cmsg) + if ecode < 0 { + return nil, MakeGitError(ecode) + } + + return newReferenceFromC(ptr, c.repo), nil +} + +func (c *ReferenceCollection) CreateSymbolic(name, target string, force bool, msg string) (*Reference, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + ctarget := C.CString(target) + defer C.free(unsafe.Pointer(ctarget)) + + var cmsg *C.char + if msg == "" { + cmsg = nil + } else { + cmsg = C.CString(msg) + defer C.free(unsafe.Pointer(cmsg)) + } + + var ptr *C.git_reference + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ecode := C.git_reference_symbolic_create(&ptr, c.repo.ptr, cname, ctarget, cbool(force), cmsg) + if ecode < 0 { + return nil, MakeGitError(ecode) + } + + return newReferenceFromC(ptr, c.repo), nil +} + +// EnsureLog ensures that there is a reflog for the given reference +// name and creates an empty one if necessary. +func (c *ReferenceCollection) EnsureLog(name string) error { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_reference_ensure_log(c.repo.ptr, cname) + if ret < 0 { + return MakeGitError(ret) + } + + return nil +} + +// HasLog returns whether there is a reflog for the given reference +// name +func (c *ReferenceCollection) HasLog(name string) (bool, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_reference_has_log(c.repo.ptr, cname) + if ret < 0 { + return false, MakeGitError(ret) + } + + return ret == 1, nil +} + +// Dwim looks up a reference by DWIMing its short name +func (c *ReferenceCollection) Dwim(name string) (*Reference, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + var ptr *C.git_reference + ret := C.git_reference_dwim(&ptr, c.repo.ptr, cname) + if ret < 0 { + return nil, MakeGitError(ret) + } + + return newReferenceFromC(ptr, c.repo), nil +} + func newReferenceFromC(ptr *C.git_reference, repo *Repository) *Reference { ref := &Reference{ptr: ptr, repo: repo} runtime.SetFinalizer(ref, (*Reference).Free) diff --git a/reference_test.go b/reference_test.go index e891e7a..f1546e2 100644 --- a/reference_test.go +++ b/reference_test.go @@ -13,14 +13,14 @@ func TestRefModification(t *testing.T) { commitId, treeId := seedTestRepo(t, repo) - _, err := repo.CreateReference("refs/tags/tree", treeId, true, "testTreeTag") + _, err := repo.References.Create("refs/tags/tree", treeId, true, "testTreeTag") checkFatal(t, err) - tag, err := repo.LookupReference("refs/tags/tree") + tag, err := repo.References.Lookup("refs/tags/tree") checkFatal(t, err) checkRefType(t, tag, ReferenceOid) - ref, err := repo.LookupReference("HEAD") + ref, err := repo.References.Lookup("HEAD") checkFatal(t, err) checkRefType(t, ref, ReferenceSymbolic) @@ -46,7 +46,7 @@ func TestRefModification(t *testing.T) { _, err = tag.Rename("refs/tags/renamed", false, "") checkFatal(t, err) - tag, err = repo.LookupReference("refs/tags/renamed") + tag, err = repo.References.Lookup("refs/tags/renamed") checkFatal(t, err) checkRefType(t, ref, ReferenceOid) @@ -77,13 +77,13 @@ func TestReferenceIterator(t *testing.T) { commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree) checkFatal(t, err) - _, err = repo.CreateReference("refs/heads/one", commitId, true, "headOne") + _, err = repo.References.Create("refs/heads/one", commitId, true, "headOne") checkFatal(t, err) - _, err = repo.CreateReference("refs/heads/two", commitId, true, "headTwo") + _, err = repo.References.Create("refs/heads/two", commitId, true, "headTwo") checkFatal(t, err) - _, err = repo.CreateReference("refs/heads/three", commitId, true, "headThree") + _, err = repo.References.Create("refs/heads/three", commitId, true, "headThree") checkFatal(t, err) iter, err := repo.NewReferenceIterator() @@ -136,7 +136,7 @@ func TestReferenceOwner(t *testing.T) { commitId, _ := seedTestRepo(t, repo) - ref, err := repo.CreateReference("refs/heads/foo", commitId, true, "") + ref, err := repo.References.Create("refs/heads/foo", commitId, true, "") checkFatal(t, err) owner := ref.Owner() @@ -155,10 +155,10 @@ func TestUtil(t *testing.T) { commitId, _ := seedTestRepo(t, repo) - ref, err := repo.CreateReference("refs/heads/foo", commitId, true, "") + ref, err := repo.References.Create("refs/heads/foo", commitId, true, "") checkFatal(t, err) - ref2, err := repo.DwimReference("foo") + ref2, err := repo.References.Dwim("foo") checkFatal(t, err) if ref.Cmp(ref2) != 0 { @@ -169,7 +169,7 @@ func TestUtil(t *testing.T) { t.Fatalf("refs/heads/foo has no foo shorthand") } - hasLog, err := repo.HasLog("refs/heads/foo") + hasLog, err := repo.References.HasLog("refs/heads/foo") checkFatal(t, err) if !hasLog { t.Fatalf("branches have logs by default") diff --git a/remote_test.go b/remote_test.go index 4e64489..73c637f 100644 --- a/remote_test.go +++ b/remote_test.go @@ -154,7 +154,7 @@ func TestRemotePrune(t *testing.T) { err = remote.Fetch([]string{"test-prune"}, nil, "") checkFatal(t, err) - _, err = repo.CreateReference("refs/remotes/origin/test-prune", head, true, "remote reference") + _, err = repo.References.Create("refs/remotes/origin/test-prune", head, true, "remote reference") checkFatal(t, err) err = remoteRef.Delete() @@ -172,7 +172,7 @@ func TestRemotePrune(t *testing.T) { err = rr.Prune(nil) checkFatal(t, err) - _, err = repo.LookupReference("refs/remotes/origin/test-prune") + _, err = repo.References.Lookup("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 0427d85..8aa39ef 100644 --- a/repository.go +++ b/repository.go @@ -17,15 +17,19 @@ type Repository struct { // used to add, remove and configure remotes for this // repository. Remotes RemoteCollection - // Submodules represents the collectin of submodules and can + // Submodules represents the collection of submodules and can // be used to add, remove and configure submodules in this // repostiory. Submodules SubmoduleCollection + // References represents the collection of references and can + // be used to create, remove or update refernces for this repository. + References ReferenceCollection } func initRepositoryObject(repo *Repository) { repo.Remotes.repo = repo repo.Submodules.repo = repo + repo.References.repo = repo runtime.SetFinalizer(repo, (*Repository).Free) } @@ -190,22 +194,6 @@ func (v *Repository) LookupTag(id *Oid) (*Tag, error) { return obj.(*Tag), nil } -func (v *Repository) LookupReference(name string) (*Reference, error) { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - var ptr *C.git_reference - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ecode := C.git_reference_lookup(&ptr, v.ptr, cname) - if ecode < 0 { - return nil, MakeGitError(ecode) - } - - return newReferenceFromC(ptr, v), nil -} - func (v *Repository) Head() (*Reference, error) { var ptr *C.git_reference @@ -245,59 +233,6 @@ func (v *Repository) SetHeadDetached(id *Oid) error { return nil } -func (v *Repository) CreateReference(name string, id *Oid, force bool, msg string) (*Reference, error) { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - - var cmsg *C.char - if msg == "" { - cmsg = nil - } else { - cmsg = C.CString(msg) - defer C.free(unsafe.Pointer(cmsg)) - } - - var ptr *C.git_reference - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ecode := C.git_reference_create(&ptr, v.ptr, cname, id.toC(), cbool(force), cmsg) - if ecode < 0 { - return nil, MakeGitError(ecode) - } - - return newReferenceFromC(ptr, v), nil -} - -func (v *Repository) CreateSymbolicReference(name, target string, force bool, msg string) (*Reference, error) { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - - ctarget := C.CString(target) - defer C.free(unsafe.Pointer(ctarget)) - - var cmsg *C.char - if msg == "" { - cmsg = nil - } else { - cmsg = C.CString(msg) - defer C.free(unsafe.Pointer(cmsg)) - } - - var ptr *C.git_reference - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ecode := C.git_reference_symbolic_create(&ptr, v.ptr, cname, ctarget, cbool(force), cmsg) - if ecode < 0 { - return nil, MakeGitError(ecode) - } - - return newReferenceFromC(ptr, v), nil -} - func (v *Repository) Walk() (*RevWalk, error) { var walkPtr *C.git_revwalk @@ -479,57 +414,6 @@ func (v *Repository) TreeBuilderFromTree(tree *Tree) (*TreeBuilder, error) { return bld, nil } -// EnsureLog ensures that there is a reflog for the given reference -// name and creates an empty one if necessary. -func (v *Repository) EnsureLog(name string) error { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_reference_ensure_log(v.ptr, cname) - if ret < 0 { - return MakeGitError(ret) - } - - return nil -} - -// HasLog returns whether there is a reflog for the given reference -// name -func (v *Repository) HasLog(name string) (bool, error) { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - ret := C.git_reference_has_log(v.ptr, cname) - if ret < 0 { - return false, MakeGitError(ret) - } - - return ret == 1, nil -} - -// DwimReference looks up a reference by DWIMing its short name -func (v *Repository) DwimReference(name string) (*Reference, error) { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - var ptr *C.git_reference - ret := C.git_reference_dwim(&ptr, v.ptr, cname) - if ret < 0 { - return nil, MakeGitError(ret) - } - - return newReferenceFromC(ptr, v), nil -} - // CreateNote adds a note for an object func (v *Repository) CreateNote( ref string, author, committer *Signature, id *Oid, diff --git a/revparse_test.go b/revparse_test.go index 4bc327c..091a76b 100644 --- a/revparse_test.go +++ b/revparse_test.go @@ -34,7 +34,7 @@ func TestRevparseExt(t *testing.T) { _, treeId := seedTestRepo(t, repo) - ref, err := repo.CreateReference("refs/heads/master", treeId, true, "") + ref, err := repo.References.Create("refs/heads/master", treeId, true, "") checkFatal(t, err) obj, ref, err := repo.RevparseExt("master") -- 2.45.2 From 70c95a7655eddffda4cd8fddd87536c5580136fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 28 Jun 2015 01:19:22 +0200 Subject: [PATCH 012/128] Create a NotesCollection for managing notes As with the others, move these methods into their own namespace. --- note.go | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++ note_test.go | 14 +++--- repository.go | 119 ++----------------------------------------------- 3 files changed, 132 insertions(+), 122 deletions(-) diff --git a/note.go b/note.go index 3cdd340..a1b15d8 100644 --- a/note.go +++ b/note.go @@ -10,6 +10,127 @@ import ( "unsafe" ) +// This object represents the possible operations which can be +// performed on the collection of notes for a repository. +type NoteCollection struct { + repo *Repository +} + +// Create adds a note for an object +func (c *NoteCollection) Create( + 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, err := author.toC() + if err != nil { + return nil, err + } + defer C.git_signature_free(authorSig) + + committerSig, err := committer.toC() + if err != nil { + return nil, err + } + 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(), c.repo.ptr, cref, authorSig, + committerSig, id.toC(), cnote, cbool(force)) + + if ret < 0 { + return nil, MakeGitError(ret) + } + return oid, nil +} + +// Read reads the note for an object +func (c *NoteCollection) Read(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, c.repo.ptr, cref, id.toC()); ret < 0 { + return nil, MakeGitError(ret) + } + + runtime.SetFinalizer(note, (*Note).Free) + return note, nil +} + +// Remove removes the note for an object +func (c *NoteCollection) Remove(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, err := author.toC() + if err != nil { + return err + } + defer C.git_signature_free(authorSig) + + committerSig, err := committer.toC() + if err != nil { + return err + } + defer C.git_signature_free(committerSig) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_note_remove(c.repo.ptr, cref, authorSig, committerSig, id.toC()) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +// DefaultRef returns the default notes reference for a repository +func (c *NoteCollection) DefaultRef() (string, error) { + buf := C.git_buf{} + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if ret := C.git_note_default_ref(&buf, c.repo.ptr); ret < 0 { + return "", MakeGitError(ret) + } + + ret := C.GoString(buf.ptr) + C.git_buf_free(&buf) + + return ret, nil +} + // Note type Note struct { ptr *C.git_note diff --git a/note_test.go b/note_test.go index e6c378d..27e04be 100644 --- a/note_test.go +++ b/note_test.go @@ -53,7 +53,7 @@ func TestNoteIterator(t *testing.T) { break } - note, err := repo.ReadNote("", commitId) + note, err := repo.Notes.Read("", commitId) checkFatal(t, err) if !reflect.DeepEqual(note.Id(), noteId) { @@ -73,13 +73,13 @@ func TestRemoveNote(t *testing.T) { note, _ := createTestNote(t, repo, commit) - _, err = repo.ReadNote("", commit.Id()) + _, err = repo.Notes.Read("", commit.Id()) checkFatal(t, err) - err = repo.RemoveNote("", note.Author(), note.Committer(), commitId) + err = repo.Notes.Remove("", note.Author(), note.Committer(), commitId) checkFatal(t, err) - _, err = repo.ReadNote("", commit.Id()) + _, err = repo.Notes.Read("", commit.Id()) if err == nil { t.Fatal("note remove failed") } @@ -89,7 +89,7 @@ func TestDefaultNoteRef(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) - ref, err := repo.DefaultNoteRef() + ref, err := repo.Notes.DefaultRef() checkFatal(t, err) compareStrings(t, "refs/notes/commits", ref) @@ -103,10 +103,10 @@ func createTestNote(t *testing.T, repo *Repository, commit *Commit) (*Note, *Oid 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) + noteId, err := repo.Notes.Create("", sig, sig, commit.Id(), "I am a note\n", false) checkFatal(t, err) - note, err := repo.ReadNote("", commit.Id()) + note, err := repo.Notes.Read("", commit.Id()) checkFatal(t, err) return note, noteId diff --git a/repository.go b/repository.go index 8aa39ef..5a2b815 100644 --- a/repository.go +++ b/repository.go @@ -24,12 +24,16 @@ type Repository struct { // References represents the collection of references and can // be used to create, remove or update refernces for this repository. References ReferenceCollection + // Notes represents the collection of notes and can be used to + // read, write and delete notes from this repository. + Notes NoteCollection } func initRepositoryObject(repo *Repository) { repo.Remotes.repo = repo repo.Submodules.repo = repo repo.References.repo = repo + repo.Notes.repo = repo runtime.SetFinalizer(repo, (*Repository).Free) } @@ -414,121 +418,6 @@ func (v *Repository) TreeBuilderFromTree(tree *Tree) (*TreeBuilder, error) { return bld, 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, err := author.toC() - if err != nil { - return nil, err - } - defer C.git_signature_free(authorSig) - - committerSig, err := committer.toC() - if err != nil { - return nil, err - } - 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, err := author.toC() - if err != nil { - return err - } - defer C.git_signature_free(authorSig) - - committerSig, err := committer.toC() - if err != nil { - return err - } - 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) { - buf := C.git_buf{} - - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - if ret := C.git_note_default_ref(&buf, v.ptr); ret < 0 { - return "", MakeGitError(ret) - } - - ret := C.GoString(buf.ptr) - C.git_buf_free(&buf) - - return ret, nil -} - type RepositoryState int const ( -- 2.45.2 From 3115b6c76236517ca6d10e54a1e34965c340531f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 28 Jun 2015 01:32:59 +0200 Subject: [PATCH 013/128] travis: allow containerized builds These builds should be quicker and faster. We don't need sudo, so write that down for Travis to move us to these builds. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index f8b7e93..9341639 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: go +sudo: false + install: - cd "${HOME}" - wget -O libgit2-0.22.1.tar.gz https://github.com/libgit2/libgit2/archive/v0.22.1.tar.gz -- 2.45.2 From 5f3a9d76b86efa91d8f1ee26277484419bc95f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 28 Jun 2015 13:17:14 +0200 Subject: [PATCH 014/128] travis: don't install libgit2 for 'next' branch We have our own libgit2 in a submodule, so this is unnecessary. While in the area, update which version of libgit2 would be downloaded and remove unnecessary CMake flags. --- .travis.yml | 9 +-------- script/install-libgit2.sh | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 script/install-libgit2.sh diff --git a/.travis.yml b/.travis.yml index 9341639..fb080b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,14 +2,7 @@ language: go sudo: false -install: - - cd "${HOME}" - - wget -O libgit2-0.22.1.tar.gz https://github.com/libgit2/libgit2/archive/v0.22.1.tar.gz - - tar -xzvf libgit2-0.22.1.tar.gz - - cd libgit2-0.22.1 && mkdir build && cd build - - cmake -DTHREADSAFE=ON -DBUILD_CLAR=OFF -DCMAKE_C_FLAGS=-fPIC -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DCMAKE_INSTALL_PREFIX=/usr/local .. && make && sudo make install - - sudo ldconfig - - cd "${TRAVIS_BUILD_DIR}" +install: ./script/install-libgit2.sh go: - 1.1 diff --git a/script/install-libgit2.sh b/script/install-libgit2.sh new file mode 100644 index 0000000..a6c3202 --- /dev/null +++ b/script/install-libgit2.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +# +# Install libgit2 to git2go in dynamic mode on Travis +# + +set -ex + +# We don't want to build libgit2 on the next branch, as we carry a +# submodule with the exact version we support +if [ "x$TRAVIS_BRANCH" = "xnext" ]; then + exit 0 +fi + +cd "${HOME}" +wget -O libgit2-0.22.3.tar.gz https://github.com/libgit2/libgit2/archive/v0.22.1.tar.gz +tar -xzvf libgit2-0.22.3.tar.gz +cd libgit2-0.22.1 && mkdir build && cd build +cmake -DTHREADSAFE=ON -DBUILD_CLAR=OFF -DCMAKE_BUILD_TYPE="RelWithDebInfo" .. && make && sudo make install +sudo ldconfig +cd "${TRAVIS_BUILD_DIR}" -- 2.45.2 From 2d6b1ebd4c8f98d3946920003796b16b702d70c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 28 Jun 2015 13:19:03 +0200 Subject: [PATCH 015/128] travis: make the script executable --- script/install-libgit2.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 script/install-libgit2.sh diff --git a/script/install-libgit2.sh b/script/install-libgit2.sh old mode 100644 new mode 100755 -- 2.45.2 From 0ce52d9aebdb2bfd0dee91b58296a6c70c7de8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 28 Jun 2015 14:35:57 +0200 Subject: [PATCH 016/128] Update to libgit2 fb84cde8 --- vendor/libgit2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/libgit2 b/vendor/libgit2 index fa39975..fb84cde 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit fa399750c680aa254784a40193d73d373df5e3ea +Subproject commit fb84cde81e11947add4ff8bb9b4084f7d76e6567 -- 2.45.2 From 66d266f97185020fe80f4b573411c39fc354fc91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 30 Jun 2015 19:03:52 +0200 Subject: [PATCH 017/128] Repository: move to use an actual constructor This should further reduce the changes of the creation of the object going badly. --- repository.go | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/repository.go b/repository.go index 5a2b815..2e2e897 100644 --- a/repository.go +++ b/repository.go @@ -29,81 +29,78 @@ type Repository struct { Notes NoteCollection } -func initRepositoryObject(repo *Repository) { +func newRepositoryFromC(ptr *C.git_repository) *Repository { + repo := &Repository{ptr: ptr} + repo.Remotes.repo = repo repo.Submodules.repo = repo repo.References.repo = repo repo.Notes.repo = repo + runtime.SetFinalizer(repo, (*Repository).Free) + + return repo } func OpenRepository(path string) (*Repository, error) { - repo := new(Repository) - cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_repository_open(&repo.ptr, cpath) + var ptr *C.git_repository + ret := C.git_repository_open(&ptr, cpath) if ret < 0 { return nil, MakeGitError(ret) } - initRepositoryObject(repo) - return repo, nil + return newRepositoryFromC(ptr), nil } func OpenRepositoryExtended(path string) (*Repository, error) { - repo := new(Repository) - cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_repository_open_ext(&repo.ptr, cpath, 0, nil) + var ptr *C.git_repository + ret := C.git_repository_open_ext(&ptr, cpath, 0, nil) if ret < 0 { return nil, MakeGitError(ret) } - initRepositoryObject(repo) - return repo, nil + return newRepositoryFromC(ptr), nil } func InitRepository(path string, isbare bool) (*Repository, error) { - repo := new(Repository) - cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_repository_init(&repo.ptr, cpath, ucbool(isbare)) + var ptr *C.git_repository + ret := C.git_repository_init(&ptr, cpath, ucbool(isbare)) if ret < 0 { return nil, MakeGitError(ret) } - initRepositoryObject(repo) - return repo, nil + return newRepositoryFromC(ptr), nil } 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) + var ptr *C.git_repository + ret := C.git_repository_wrap_odb(&ptr, odb.ptr) if ret < 0 { return nil, MakeGitError(ret) } - initRepositoryObject(repo) - return repo, nil + return newRepositoryFromC(ptr), nil } func (v *Repository) SetRefdb(refdb *Refdb) { -- 2.45.2 From b6811196e4f7728b25ab37ce2a0862aa74c22253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 30 Jun 2015 19:08:29 +0200 Subject: [PATCH 018/128] Clone: test we clone something usable and fix constructor Clone was still trying to do its own initialisation, which was missing all of the namespacing changes. --- clone.go | 8 +++----- clone_test.go | 10 ++++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/clone.go b/clone.go index f67f511..b5c5a5b 100644 --- a/clone.go +++ b/clone.go @@ -20,8 +20,6 @@ type CloneOptions struct { } func Clone(url string, path string, options *CloneOptions) (*Repository, error) { - repo := new(Repository) - curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) @@ -37,7 +35,8 @@ func Clone(url string, path string, options *CloneOptions) (*Repository, error) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_clone(&repo.ptr, curl, cpath, copts) + var ptr *C.git_repository + ret := C.git_clone(&ptr, curl, cpath, copts) freeCheckoutOpts(&copts.checkout_opts) C.free(unsafe.Pointer(copts.checkout_branch)) C.free(unsafe.Pointer(copts)) @@ -46,8 +45,7 @@ func Clone(url string, path string, options *CloneOptions) (*Repository, error) return nil, MakeGitError(ret) } - runtime.SetFinalizer(repo, (*Repository).Free) - return repo, nil + return newRepositoryFromC(ptr), nil } func populateCloneOptions(ptr *C.git_clone_options, opts *CloneOptions) { diff --git a/clone_test.go b/clone_test.go index fd83fec..7cdc362 100644 --- a/clone_test.go +++ b/clone_test.go @@ -15,8 +15,18 @@ func TestClone(t *testing.T) { path, err := ioutil.TempDir("", "git2go") checkFatal(t, err) + ref, err := repo.References.Lookup("refs/heads/master") + checkFatal(t, err) + repo2, err := Clone(repo.Path(), path, &CloneOptions{Bare: true}) defer cleanupTestRepo(t, repo2) checkFatal(t, err) + + ref2, err := repo2.References.Lookup("refs/heads/master") + checkFatal(t, err) + + if ref.Cmp(ref2) != 0 { + t.Fatal("reference in clone does not match original ref") + } } -- 2.45.2 From 47191d12746e39a246d8eb089e267340349728b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 30 Jun 2015 19:12:39 +0200 Subject: [PATCH 019/128] Submodule: use the Repository constructor --- submodule.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/submodule.go b/submodule.go index 6d4dc28..4a32ce4 100644 --- a/submodule.go +++ b/submodule.go @@ -298,16 +298,15 @@ func (sub *Submodule) Sync() error { } func (sub *Submodule) Open() (*Repository, error) { - repo := new(Repository) - runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_submodule_open(&repo.ptr, sub.ptr) + var ptr *C.git_repository + ret := C.git_submodule_open(&ptr, sub.ptr) if ret < 0 { return nil, MakeGitError(ret) } - return repo, nil + return newRepositoryFromC(ptr), nil } func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error { -- 2.45.2 From b5693c1429ad7247ce75b23ebb866f9428bde8b6 Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Mon, 6 Jul 2015 19:27:58 -0700 Subject: [PATCH 020/128] Prevent slot int variable from being GCed. Before this change, there were no users of slot int variable in the Go world (just a pointer to it that ended up in C world only), so Go's garbage collector would free it and its value could not retrieved later (once a pointer to it comes back to Go world from C world). Keep a pointer to it in the Go world so that does not happen. Fixes #218. --- handles.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/handles.go b/handles.go index ec62a48..a062231 100644 --- a/handles.go +++ b/handles.go @@ -10,14 +10,15 @@ type HandleList struct { sync.RWMutex // stores the Go pointers handles []interface{} - // indicates which indices are in use - set map[int]bool + // Indicates which indices are in use, and keeps a pointer to slot int variable (the handle) + // in the Go world, so that the Go garbage collector does not free it. + set map[int]*int } func NewHandleList() *HandleList { return &HandleList{ handles: make([]interface{}, 5), - set: make(map[int]bool), + set: make(map[int]*int), } } @@ -25,7 +26,7 @@ func NewHandleList() *HandleList { // list. You must only run this function while holding a write lock. func (v *HandleList) findUnusedSlot() int { for i := 1; i < len(v.handles); i++ { - isUsed := v.set[i] + _, isUsed := v.set[i] if !isUsed { return i } @@ -47,7 +48,7 @@ func (v *HandleList) Track(pointer interface{}) unsafe.Pointer { slot := v.findUnusedSlot() v.handles[slot] = pointer - v.set[slot] = true + v.set[slot] = &slot // Keep a pointer to slot in Go world, so it's not freed by GC. v.Unlock() -- 2.45.2 From 28dee704cafd347599ae55f30441275b219def03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 10 Jul 2015 00:31:32 +0200 Subject: [PATCH 021/128] Bring back CheckoutForce This was mistakenly deleted when the SafeCreate was replaced with RecreateMissing. --- checkout.go | 1 + 1 file changed, 1 insertion(+) diff --git a/checkout.go b/checkout.go index d747344..e0c067e 100644 --- a/checkout.go +++ b/checkout.go @@ -15,6 +15,7 @@ type CheckoutStrategy uint const ( CheckoutNone CheckoutStrategy = C.GIT_CHECKOUT_NONE // Dry run, no actual updates CheckoutSafe CheckoutStrategy = C.GIT_CHECKOUT_SAFE // Allow safe updates that cannot overwrite uncommitted data + CheckoutForce CheckoutStrategy = C.GIT_CHECKOUT_FORCE // Allow all updates to force working directory to look like index CheckoutRecreateMissing CheckoutStrategy = C.GIT_CHECKOUT_RECREATE_MISSING // Allow checkout to recreate missing files 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) -- 2.45.2 From 4b88210cbf495891c8d44c53b3d978e6ff31a5a3 Mon Sep 17 00:00:00 2001 From: Calin Seciu Date: Fri, 24 Jul 2015 12:14:24 +0300 Subject: [PATCH 022/128] Add check if reference is a note --- reference.go | 5 +++++ reference_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/reference.go b/reference.go index d24e054..452de46 100644 --- a/reference.go +++ b/reference.go @@ -315,6 +315,11 @@ func (v *Reference) IsTag() bool { return C.git_reference_is_tag(v.ptr) == 1 } +// IsNote checks if the reference is a note. +func (v *Reference) IsNote() bool { + return C.git_reference_is_note(v.ptr) == 1 +} + func (v *Reference) Free() { runtime.SetFinalizer(v, nil) C.git_reference_free(v.ptr) diff --git a/reference_test.go b/reference_test.go index f1546e2..b69a274 100644 --- a/reference_test.go +++ b/reference_test.go @@ -176,6 +176,38 @@ func TestUtil(t *testing.T) { } } +func TestIsNote(t *testing.T) { + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + commitID, _ := seedTestRepo(t, repo) + + sig := &Signature{ + Name: "Rand Om Hacker", + Email: "random@hacker.com", + When: time.Now(), + } + + refname, err := repo.Notes.DefaultRef() + checkFatal(t, err) + + _, err = repo.Notes.Create(refname, sig, sig, commitID, "This is a note", false) + checkFatal(t, err) + + ref, err := repo.References.Lookup(refname) + checkFatal(t, err) + + if !ref.IsNote() { + t.Fatalf("%s should be a note", ref.Name()) + } + + ref, err = repo.References.Create("refs/heads/foo", commitID, true, "") + checkFatal(t, err) + + if ref.IsNote() { + t.Fatalf("%s should not be a note", ref.Name()) + } +} func compareStringList(t *testing.T, expected, actual []string) { for i, v := range expected { if actual[i] != v { -- 2.45.2 From ec93213f21f57e6b378bf9f6ceb05c9fd1f15daf Mon Sep 17 00:00:00 2001 From: Calin Seciu Date: Fri, 24 Jul 2015 12:14:53 +0300 Subject: [PATCH 023/128] Add ReferenceIsValidName() --- reference.go | 19 +++++++++++++++++++ reference_test.go | 10 ++++++++++ 2 files changed, 29 insertions(+) diff --git a/reference.go b/reference.go index 452de46..140082f 100644 --- a/reference.go +++ b/reference.go @@ -430,3 +430,22 @@ func (v *ReferenceIterator) Free() { runtime.SetFinalizer(v, nil) C.git_reference_iterator_free(v.ptr) } + +// ReferenceIsValidName ensures the reference name is well-formed. +// +// Valid reference names must follow one of two patterns: +// +// 1. Top-level names must contain only capital letters and underscores, +// and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). +// +// 2. Names prefixed with "refs/" can be almost anything. You must avoid +// the characters '~', '^', ':', ' \ ', '?', '[', and '*', and the sequences +// ".." and " @ {" which have special meaning to revparse. +func ReferenceIsValidName(name string) bool { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + if C.git_reference_is_valid_name(cname) == 1 { + return true + } + return false +} diff --git a/reference_test.go b/reference_test.go index b69a274..761daf8 100644 --- a/reference_test.go +++ b/reference_test.go @@ -208,6 +208,16 @@ func TestIsNote(t *testing.T) { t.Fatalf("%s should not be a note", ref.Name()) } } + +func TestReferenceIsValidName(t *testing.T) { + if !ReferenceIsValidName("HEAD") { + t.Errorf("HEAD should be a valid reference name") + } + if ReferenceIsValidName("HEAD1") { + t.Errorf("HEAD1 should not be a valid reference name") + } +} + func compareStringList(t *testing.T, expected, actual []string) { for i, v := range expected { if actual[i] != v { -- 2.45.2 From 64c160f6f2300fc675453761471dc8d4726756e0 Mon Sep 17 00:00:00 2001 From: Calin Seciu Date: Fri, 24 Jul 2015 19:46:57 +0300 Subject: [PATCH 024/128] Find tree entry by id Add support for 'git_tree_entry_byid'. --- tree.go | 18 ++++++++++++++++++ tree_test.go | 22 ++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 tree_test.go diff --git a/tree.go b/tree.go index aad2c8d..f543c11 100644 --- a/tree.go +++ b/tree.go @@ -55,6 +55,24 @@ func (t Tree) EntryByName(filename string) *TreeEntry { return newTreeEntry(entry) } +// EntryById performs a lookup for a tree entry with the given SHA value. +// +// It returns a *TreeEntry that is owned by the Tree. You don't have to +// free it, but you must not use it after the Tree is freed. +// +// Warning: this must examine every entry in the tree, so it is not fast. +func (t Tree) EntryById(id *Oid) *TreeEntry { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + entry := C.git_tree_entry_byid(t.cast_ptr, id.toC()) + if entry == nil { + return nil + } + + return newTreeEntry(entry) +} + // EntryByPath looks up an entry by its full path, recursing into // deeper trees if necessary (i.e. if there are slashes in the path) func (t Tree) EntryByPath(path string) (*TreeEntry, error) { diff --git a/tree_test.go b/tree_test.go new file mode 100644 index 0000000..4c6a4ed --- /dev/null +++ b/tree_test.go @@ -0,0 +1,22 @@ +package git + +import "testing" + +func TestTreeEntryById(t *testing.T) { + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + _, treeID := seedTestRepo(t, repo) + + tree, err := repo.LookupTree(treeID) + checkFatal(t, err) + + id, err := NewOid("257cc5642cb1a054f08cc83f2d943e56fd3ebe99") + checkFatal(t, err) + + entry := tree.EntryById(id) + + if entry == nil { + t.Fatalf("entry id %v was not found", id) + } +} -- 2.45.2 From 12311c8528c577ebb11006f24c026c7a4d2f2de3 Mon Sep 17 00:00:00 2001 From: Calin Seciu Date: Fri, 31 Jul 2015 09:51:19 +0200 Subject: [PATCH 025/128] Add TagsCollection --- repository.go | 4 ++++ tag.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/repository.go b/repository.go index 44509af..b17745d 100644 --- a/repository.go +++ b/repository.go @@ -27,6 +27,9 @@ type Repository struct { // Notes represents the collection of notes and can be used to // read, write and delete notes from this repository. Notes NoteCollection + // Tags represents the collection of tags and can be used to create, + // list and iterate tags in this repository. + Tags TagsCollection } func newRepositoryFromC(ptr *C.git_repository) *Repository { @@ -36,6 +39,7 @@ func newRepositoryFromC(ptr *C.git_repository) *Repository { repo.Submodules.repo = repo repo.References.repo = repo repo.Notes.repo = repo + repo.Tags.repo = repo runtime.SetFinalizer(repo, (*Repository).Free) diff --git a/tag.go b/tag.go index 89ac8bd..74b18d8 100644 --- a/tag.go +++ b/tag.go @@ -42,3 +42,7 @@ func (t Tag) TargetId() *Oid { func (t Tag) TargetType() ObjectType { return ObjectType(C.git_tag_target_type(t.cast_ptr)) } + +type TagsCollection struct { + repo *Repository +} -- 2.45.2 From 6c4af98c5b2763b020e39357f31bcc6d6f1960e1 Mon Sep 17 00:00:00 2001 From: Calin Seciu Date: Fri, 31 Jul 2015 10:07:26 +0200 Subject: [PATCH 026/128] Add more support for tags Implement support for the following libgit2 functions: - 'git_tag_list' and 'git_tag_list_match' - 'git_tag_foreach' - 'git_tag_create_lightweight' --- tag.go | 133 ++++++++++++++++++++++++++++++++++++++++++++ tag_test.go | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++++ wrapper.c | 5 ++ 3 files changed, 293 insertions(+) diff --git a/tag.go b/tag.go index 74b18d8..5801c99 100644 --- a/tag.go +++ b/tag.go @@ -2,8 +2,14 @@ package git /* #include + +extern int _go_git_tag_foreach(git_repository *repo, void *payload); */ import "C" +import ( + "runtime" + "unsafe" +) // Tag type Tag struct { @@ -46,3 +52,130 @@ func (t Tag) TargetType() ObjectType { type TagsCollection struct { repo *Repository } + +// CreateLightweight creates a new lightweight tag pointing to a commit +// and returns the id of the target object. +// +// The name of the tag is validated for consistency (see git_tag_create() for the rules +// https://libgit2.github.com/libgit2/#HEAD/group/tag/git_tag_create) and should +// not conflict with an already existing tag name. +// +// If force is true and a reference already exists with the given name, it'll be replaced. +// +// The created tag is a simple reference and can be queried using +// repo.References.Lookup("refs/tags/"). The name of the tag (eg "v1.0.0") +// is queried with ref.Shorthand(). +func (c *TagsCollection) CreateLightweight(name string, commit *Commit, force bool) (*Oid, error) { + + oid := new(Oid) + + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + ctarget := commit.gitObject.ptr + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + err := C.git_tag_create_lightweight(oid.toC(), c.repo.ptr, cname, ctarget, cbool(force)) + if err < 0 { + return nil, MakeGitError(err) + } + + return oid, nil +} + +// List returns the names of all the tags in the repository, +// eg: ["v1.0.1", "v2.0.0"]. +func (c *TagsCollection) List() ([]string, error) { + var strC C.git_strarray + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ecode := C.git_tag_list(&strC, c.repo.ptr) + if ecode < 0 { + return nil, MakeGitError(ecode) + } + defer C.git_strarray_free(&strC) + + tags := makeStringsFromCStrings(strC.strings, int(strC.count)) + return tags, nil +} + +// ListWithMatch returns the names of all the tags in the repository +// that match a given pattern. +// +// The pattern is a standard fnmatch(3) pattern http://man7.org/linux/man-pages/man3/fnmatch.3.html +func (c *TagsCollection) ListWithMatch(pattern string) ([]string, error) { + var strC C.git_strarray + + patternC := C.CString(pattern) + defer C.free(unsafe.Pointer(patternC)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ecode := C.git_tag_list_match(&strC, patternC, c.repo.ptr) + if ecode < 0 { + return nil, MakeGitError(ecode) + } + defer C.git_strarray_free(&strC) + + tags := makeStringsFromCStrings(strC.strings, int(strC.count)) + return tags, nil +} + +// TagForeachCallback is called for each tag in the repository. +// +// The name is the full ref name eg: "refs/tags/v1.0.0". +// +// Note that the callback is called for lightweight tags as well, +// so repo.LookupTag() will return an error for these tags. Use +// repo.References.Lookup() instead. +type TagForeachCallback func(name string, id *Oid) error +type tagForeachData struct { + callback TagForeachCallback + err error +} + +//export gitTagForeachCb +func gitTagForeachCb(name *C.char, id *C.git_oid, handle unsafe.Pointer) int { + payload := pointerHandles.Get(handle) + data, ok := payload.(*tagForeachData) + if !ok { + panic("could not retrieve tag foreach CB handle") + } + + err := data.callback(C.GoString(name), newOidFromC(id)) + if err != nil { + data.err = err + return C.GIT_EUSER + } + + return 0 +} + +// Foreach calls the callback for each tag in the repository. +func (c *TagsCollection) Foreach(callback TagForeachCallback) error { + data := tagForeachData{ + callback: callback, + err: nil, + } + + handle := pointerHandles.Track(&data) + defer pointerHandles.Untrack(handle) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + err := C._go_git_tag_foreach(c.repo.ptr, handle) + if err == C.GIT_EUSER { + return data.err + } + if err < 0 { + return MakeGitError(err) + } + + return nil +} diff --git a/tag_test.go b/tag_test.go index 74f9fec..4bf3889 100644 --- a/tag_test.go +++ b/tag_test.go @@ -1,6 +1,7 @@ package git import ( + "errors" "testing" "time" ) @@ -24,6 +25,146 @@ func TestCreateTag(t *testing.T) { compareStrings(t, commitId.String(), tag.TargetId().String()) } +func TestCreateTagLightweight(t *testing.T) { + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + commitID, _ := seedTestRepo(t, repo) + + commit, err := repo.LookupCommit(commitID) + checkFatal(t, err) + + tagID, err := repo.Tags.CreateLightweight("v0.1.0", commit, false) + checkFatal(t, err) + + _, err = repo.Tags.CreateLightweight("v0.1.0", commit, true) + checkFatal(t, err) + + ref, err := repo.References.Lookup("refs/tags/v0.1.0") + checkFatal(t, err) + + compareStrings(t, "refs/tags/v0.1.0", ref.Name()) + compareStrings(t, "v0.1.0", ref.Shorthand()) + compareStrings(t, tagID.String(), commitID.String()) + compareStrings(t, commitID.String(), ref.Target().String()) +} + +func TestListTags(t *testing.T) { + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + commitID, _ := seedTestRepo(t, repo) + + commit, err := repo.LookupCommit(commitID) + checkFatal(t, err) + + createTag(t, repo, commit, "v1.0.1", "Release v1.0.1") + + commitID, _ = updateReadme(t, repo, "Release version 2") + + commit, err = repo.LookupCommit(commitID) + checkFatal(t, err) + + createTag(t, repo, commit, "v2.0.0", "Release v2.0.0") + + expected := []string{ + "v1.0.1", + "v2.0.0", + } + + actual, err := repo.Tags.List() + checkFatal(t, err) + + compareStringList(t, expected, actual) +} + +func TestListTagsWithMatch(t *testing.T) { + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + commitID, _ := seedTestRepo(t, repo) + + commit, err := repo.LookupCommit(commitID) + checkFatal(t, err) + + createTag(t, repo, commit, "v1.0.1", "Release v1.0.1") + + commitID, _ = updateReadme(t, repo, "Release version 2") + + commit, err = repo.LookupCommit(commitID) + checkFatal(t, err) + + createTag(t, repo, commit, "v2.0.0", "Release v2.0.0") + + expected := []string{ + "v2.0.0", + } + + actual, err := repo.Tags.ListWithMatch("v2*") + checkFatal(t, err) + + compareStringList(t, expected, actual) + + expected = []string{ + "v1.0.1", + } + + actual, err = repo.Tags.ListWithMatch("v1*") + checkFatal(t, err) + + compareStringList(t, expected, actual) +} + +func TestTagForeach(t *testing.T) { + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + commitID, _ := seedTestRepo(t, repo) + + commit, err := repo.LookupCommit(commitID) + checkFatal(t, err) + + tag1 := createTag(t, repo, commit, "v1.0.1", "Release v1.0.1") + + commitID, _ = updateReadme(t, repo, "Release version 2") + + commit, err = repo.LookupCommit(commitID) + checkFatal(t, err) + + tag2 := createTag(t, repo, commit, "v2.0.0", "Release v2.0.0") + + expectedNames := []string{ + "refs/tags/v1.0.1", + "refs/tags/v2.0.0", + } + actualNames := []string{} + expectedOids := []string{ + tag1.String(), + tag2.String(), + } + actualOids := []string{} + + err = repo.Tags.Foreach(func(name string, id *Oid) error { + actualNames = append(actualNames, name) + actualOids = append(actualOids, id.String()) + return nil + }) + checkFatal(t, err) + + compareStringList(t, expectedNames, actualNames) + compareStringList(t, expectedOids, actualOids) + + fakeErr := errors.New("fake error") + + err = repo.Tags.Foreach(func(name string, id *Oid) error { + return fakeErr + }) + + if err != fakeErr { + t.Fatalf("Tags.Foreach() did not return the expected error, got %v", err) + } +} + func compareStrings(t *testing.T, expected, value string) { if value != expected { t.Fatalf("expected '%v', actual '%v'", expected, value) @@ -43,3 +184,17 @@ func createTestTag(t *testing.T, repo *Repository, commit *Commit) *Oid { checkFatal(t, err) return tagId } + +func createTag(t *testing.T, repo *Repository, commit *Commit, name, message string) *Oid { + loc, err := time.LoadLocation("Europe/Bucharest") + checkFatal(t, err) + sig := &Signature{ + Name: "Rand Om Hacker", + Email: "random@hacker.com", + When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc), + } + + tagId, err := repo.CreateTag(name, commit, sig, message) + checkFatal(t, err) + return tagId +} diff --git a/wrapper.c b/wrapper.c index 75cc03c..1efe5d7 100644 --- a/wrapper.c +++ b/wrapper.c @@ -131,4 +131,9 @@ int _go_git_index_remove_all(git_index *index, const git_strarray *pathspec, voi return git_index_remove_all(index, pathspec, cb, callback); } +int _go_git_tag_foreach(git_repository *repo, void *payload) +{ + return git_tag_foreach(repo, (git_tag_foreach_cb)&gitTagForeachCb, payload); +} + /* EOF */ -- 2.45.2 From def4494b74ec1c8fd12669e3f65bd29d6315c83c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 31 Jul 2015 20:23:05 +0200 Subject: [PATCH 027/128] Move CreateTag to the tags collection --- repository.go | 30 ------------------------------ tag.go | 30 ++++++++++++++++++++++++++++++ tag_test.go | 4 ++-- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/repository.go b/repository.go index b17745d..62fde6d 100644 --- a/repository.go +++ b/repository.go @@ -321,36 +321,6 @@ func (v *Repository) CreateCommit( return oid, nil } -func (v *Repository) CreateTag( - name string, commit *Commit, tagger *Signature, message string) (*Oid, error) { - - oid := new(Oid) - - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - - cmessage := C.CString(message) - defer C.free(unsafe.Pointer(cmessage)) - - taggerSig, err := tagger.toC() - if err != nil { - return nil, err - } - defer C.git_signature_free(taggerSig) - - 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) - } - - return oid, nil -} - func (v *Odb) Free() { runtime.SetFinalizer(v, nil) C.git_odb_free(v.ptr) diff --git a/tag.go b/tag.go index 5801c99..ca85156 100644 --- a/tag.go +++ b/tag.go @@ -53,6 +53,36 @@ type TagsCollection struct { repo *Repository } +func (c *TagsCollection) Create( + name string, commit *Commit, tagger *Signature, message string) (*Oid, error) { + + oid := new(Oid) + + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + cmessage := C.CString(message) + defer C.free(unsafe.Pointer(cmessage)) + + taggerSig, err := tagger.toC() + if err != nil { + return nil, err + } + defer C.git_signature_free(taggerSig) + + ctarget := commit.gitObject.ptr + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_tag_create(oid.toC(), c.repo.ptr, cname, ctarget, taggerSig, cmessage, 0) + if ret < 0 { + return nil, MakeGitError(ret) + } + + return oid, nil +} + // CreateLightweight creates a new lightweight tag pointing to a commit // and returns the id of the target object. // diff --git a/tag_test.go b/tag_test.go index 4bf3889..2fdfe00 100644 --- a/tag_test.go +++ b/tag_test.go @@ -180,7 +180,7 @@ func createTestTag(t *testing.T, repo *Repository, commit *Commit) *Oid { When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc), } - tagId, err := repo.CreateTag("v0.0.0", commit, sig, "This is a tag") + tagId, err := repo.Tags.Create("v0.0.0", commit, sig, "This is a tag") checkFatal(t, err) return tagId } @@ -194,7 +194,7 @@ func createTag(t *testing.T, repo *Repository, commit *Commit, name, message str When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc), } - tagId, err := repo.CreateTag(name, commit, sig, message) + tagId, err := repo.Tags.Create(name, commit, sig, message) checkFatal(t, err) return tagId } -- 2.45.2 From 1018ff76d034662e5bba7665a62d6263cd377ab7 Mon Sep 17 00:00:00 2001 From: Calin Seciu Date: Fri, 24 Jul 2015 01:39:49 +0300 Subject: [PATCH 028/128] Add git-describe support Includes 'git_describe_commit' and 'git_describe_workdir'. --- describe.go | 222 +++++++++++++++++++++++++++++++++++++++++++++++ describe_test.go | 106 ++++++++++++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 describe.go create mode 100644 describe_test.go diff --git a/describe.go b/describe.go new file mode 100644 index 0000000..c6f9a79 --- /dev/null +++ b/describe.go @@ -0,0 +1,222 @@ +package git + +/* +#include +*/ +import "C" +import ( + "runtime" + "unsafe" +) + +// DescribeOptions represents the describe operation configuration. +// +// You can use DefaultDescribeOptions() to get default options. +type DescribeOptions struct { + // How many tags as candidates to consider to describe the input commit-ish. + // Increasing it above 10 will take slightly longer but may produce a more + // accurate result. 0 will cause only exact matches to be output. + MaxCandidatesTags uint // default: 10 + + // By default describe only shows annotated tags. Change this in order + // to show all refs from refs/tags or refs/. + Strategy DescribeOptionsStrategy // default: DescribeDefault + + // Only consider tags matching the given glob(7) pattern, excluding + // the "refs/tags/" prefix. Can be used to avoid leaking private + // tags from the repo. + Pattern string + + // When calculating the distance from the matching tag or + // reference, only walk down the first-parent ancestry. + OnlyFollowFirstParent bool + + // If no matching tag or reference is found, the describe + // operation would normally fail. If this option is set, it + // will instead fall back to showing the full id of the commit. + ShowCommitOidAsFallback bool +} + +// DefaultDescribeOptions returns default options for the describe operation. +func DefaultDescribeOptions() (DescribeOptions, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + opts := C.git_describe_options{} + ecode := C.git_describe_init_options(&opts, C.GIT_DESCRIBE_OPTIONS_VERSION) + if ecode < 0 { + return DescribeOptions{}, MakeGitError(ecode) + } + + return DescribeOptions{ + MaxCandidatesTags: uint(opts.max_candidates_tags), + Strategy: DescribeOptionsStrategy(opts.describe_strategy), + }, nil +} + +// DescribeFormatOptions can be used for formatting the describe string. +// +// You can use DefaultDescribeFormatOptions() to get default options. +type DescribeFormatOptions struct { + // Size of the abbreviated commit id to use. This value is the + // lower bound for the length of the abbreviated string. + AbbreviatedSize uint // default: 7 + + // Set to use the long format even when a shorter name could be used. + AlwaysUseLongFormat bool + + // If the workdir is dirty and this is set, this string will be + // appended to the description string. + DirtySuffix string +} + +// DefaultDescribeFormatOptions returns default options for formatting +// the output. +func DefaultDescribeFormatOptions() (DescribeFormatOptions, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + opts := C.git_describe_format_options{} + ecode := C.git_describe_init_format_options(&opts, C.GIT_DESCRIBE_FORMAT_OPTIONS_VERSION) + if ecode < 0 { + return DescribeFormatOptions{}, MakeGitError(ecode) + } + + return DescribeFormatOptions{ + AbbreviatedSize: uint(opts.abbreviated_size), + AlwaysUseLongFormat: opts.always_use_long_format == 1, + }, nil +} + +// DescribeOptionsStrategy behaves like the --tags and --all options +// to git-describe, namely they say to look for any reference in +// either refs/tags/ or refs/ respectively. +// +// By default it only shows annotated tags. +type DescribeOptionsStrategy uint + +// Describe strategy options. +const ( + DescribeDefault DescribeOptionsStrategy = C.GIT_DESCRIBE_DEFAULT + DescribeTags DescribeOptionsStrategy = C.GIT_DESCRIBE_TAGS + DescribeAll DescribeOptionsStrategy = C.GIT_DESCRIBE_ALL +) + +// Describe performs the describe operation on the commit. +func (c *Commit) Describe(opts *DescribeOptions) (*DescribeResult, error) { + var resultPtr *C.git_describe_result + + var cDescribeOpts *C.git_describe_options + if opts != nil { + var cpattern *C.char + if len(opts.Pattern) > 0 { + cpattern = C.CString(opts.Pattern) + defer C.free(unsafe.Pointer(cpattern)) + } + + cDescribeOpts = &C.git_describe_options{ + version: C.GIT_DESCRIBE_OPTIONS_VERSION, + max_candidates_tags: C.uint(opts.MaxCandidatesTags), + describe_strategy: C.uint(opts.Strategy), + pattern: cpattern, + only_follow_first_parent: cbool(opts.OnlyFollowFirstParent), + show_commit_oid_as_fallback: cbool(opts.ShowCommitOidAsFallback), + } + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ecode := C.git_describe_commit(&resultPtr, c.gitObject.ptr, cDescribeOpts) + if ecode < 0 { + return nil, MakeGitError(ecode) + } + + return newDescribeResultFromC(resultPtr), nil +} + +// DescribeWorkdir describes the working tree. It means describe HEAD +// and appends (-dirty by default) if the working tree is dirty. +func (repo *Repository) DescribeWorkdir(opts *DescribeOptions) (*DescribeResult, error) { + var resultPtr *C.git_describe_result + + var cDescribeOpts *C.git_describe_options + if opts != nil { + var cpattern *C.char + if len(opts.Pattern) > 0 { + cpattern = C.CString(opts.Pattern) + defer C.free(unsafe.Pointer(cpattern)) + } + + cDescribeOpts = &C.git_describe_options{ + version: C.GIT_DESCRIBE_OPTIONS_VERSION, + max_candidates_tags: C.uint(opts.MaxCandidatesTags), + describe_strategy: C.uint(opts.Strategy), + pattern: cpattern, + only_follow_first_parent: cbool(opts.OnlyFollowFirstParent), + show_commit_oid_as_fallback: cbool(opts.ShowCommitOidAsFallback), + } + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ecode := C.git_describe_workdir(&resultPtr, repo.ptr, cDescribeOpts) + if ecode < 0 { + return nil, MakeGitError(ecode) + } + + return newDescribeResultFromC(resultPtr), nil +} + +// DescribeResult represents the output from the 'git_describe_commit' +// and 'git_describe_workdir' functions in libgit2. +// +// Use Format() to get a string out of it. +type DescribeResult struct { + ptr *C.git_describe_result +} + +func newDescribeResultFromC(ptr *C.git_describe_result) *DescribeResult { + result := &DescribeResult{ + ptr: ptr, + } + runtime.SetFinalizer(result, (*DescribeResult).Free) + return result +} + +// Format prints the DescribeResult as a string. +func (result *DescribeResult) Format(opts *DescribeFormatOptions) (string, error) { + resultBuf := C.git_buf{} + + var cFormatOpts *C.git_describe_format_options + if opts != nil { + cDirtySuffix := C.CString(opts.DirtySuffix) + defer C.free(unsafe.Pointer(cDirtySuffix)) + + cFormatOpts = &C.git_describe_format_options{ + version: C.GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, + abbreviated_size: C.uint(opts.AbbreviatedSize), + always_use_long_format: cbool(opts.AlwaysUseLongFormat), + dirty_suffix: cDirtySuffix, + } + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ecode := C.git_describe_format(&resultBuf, result.ptr, cFormatOpts) + if ecode < 0 { + return "", MakeGitError(ecode) + } + defer C.git_buf_free(&resultBuf) + + return C.GoString(resultBuf.ptr), nil +} + +// Free cleans up the C reference. +func (result *DescribeResult) Free() { + runtime.SetFinalizer(result, nil) + C.git_describe_result_free(result.ptr) + result.ptr = nil +} diff --git a/describe_test.go b/describe_test.go new file mode 100644 index 0000000..25af107 --- /dev/null +++ b/describe_test.go @@ -0,0 +1,106 @@ +package git + +import ( + "path" + "runtime" + "strings" + "testing" +) + +func TestDescribeCommit(t *testing.T) { + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + describeOpts, err := DefaultDescribeOptions() + checkFatal(t, err) + + formatOpts, err := DefaultDescribeFormatOptions() + checkFatal(t, err) + + commitID, _ := seedTestRepo(t, repo) + + commit, err := repo.LookupCommit(commitID) + checkFatal(t, err) + + // No annotated tags can be used to describe master + _, err = commit.Describe(&describeOpts) + checkDescribeNoRefsFound(t, err) + + // Fallback + fallback := describeOpts + fallback.ShowCommitOidAsFallback = true + result, err := commit.Describe(&fallback) + checkFatal(t, err) + resultStr, err := result.Format(&formatOpts) + checkFatal(t, err) + compareStrings(t, "473bf77", resultStr) + + // Abbreviated + abbreviated := formatOpts + abbreviated.AbbreviatedSize = 2 + result, err = commit.Describe(&fallback) + checkFatal(t, err) + resultStr, err = result.Format(&abbreviated) + checkFatal(t, err) + compareStrings(t, "473b", resultStr) + + createTestTag(t, repo, commit) + + // Exact tag + patternOpts := describeOpts + patternOpts.Pattern = "v[0-9]*" + result, err = commit.Describe(&patternOpts) + checkFatal(t, err) + resultStr, err = result.Format(&formatOpts) + checkFatal(t, err) + compareStrings(t, "v0.0.0", resultStr) + + // Pattern no match + patternOpts.Pattern = "v[1-9]*" + result, err = commit.Describe(&patternOpts) + checkDescribeNoRefsFound(t, err) + + commitID, _ = updateReadme(t, repo, "update1") + commit, err = repo.LookupCommit(commitID) + checkFatal(t, err) + + // Tag-1 + result, err = commit.Describe(&describeOpts) + checkFatal(t, err) + resultStr, err = result.Format(&formatOpts) + checkFatal(t, err) + compareStrings(t, "v0.0.0-1-gd88ef8d", resultStr) + + // Strategy: All + describeOpts.Strategy = DescribeAll + result, err = commit.Describe(&describeOpts) + checkFatal(t, err) + resultStr, err = result.Format(&formatOpts) + checkFatal(t, err) + compareStrings(t, "heads/master", resultStr) + + repo.CreateBranch("hotfix", commit, false) + + // Workdir (branch) + result, err = repo.DescribeWorkdir(&describeOpts) + checkFatal(t, err) + resultStr, err = result.Format(&formatOpts) + checkFatal(t, err) + compareStrings(t, "heads/hotfix", resultStr) +} + +func checkDescribeNoRefsFound(t *testing.T, err error) { + // The failure happens at wherever we were called, not here + _, file, line, ok := runtime.Caller(1) + if !ok { + t.Fatalf("Unable to get caller") + } + if err == nil || !strings.Contains(err.Error(), "No reference found, cannot describe anything") { + t.Fatalf( + "%s:%v: was expecting error 'No reference found, cannot describe anything', got %v", + path.Base(file), + line, + err, + ) + } +} -- 2.45.2 From 17950c198b0d1495091a5784e454a4ca9800e927 Mon Sep 17 00:00:00 2001 From: Calin Seciu Date: Sat, 1 Aug 2015 14:28:20 +0200 Subject: [PATCH 029/128] Add ability to peel any git object Includes support for 'git_object_peel'. --- object.go | 26 ++++++++++++++++++++++ object_test.go | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/object.go b/object.go index 20cee85..6ecebf8 100644 --- a/object.go +++ b/object.go @@ -22,6 +22,7 @@ type Object interface { Id() *Oid Type() ObjectType Owner() *Repository + Peel(t ObjectType) (Object, error) } type gitObject struct { @@ -69,6 +70,31 @@ func (o *gitObject) Free() { C.git_object_free(o.ptr) } +// Peel recursively peels an object until an object of the specified type is met. +// +// If the query cannot be satisfied due to the object model, ErrInvalidSpec +// will be returned (e.g. trying to peel a blob to a tree). +// +// If you pass ObjectAny as the target type, then the object will be peeled +// until the type changes. A tag will be peeled until the referenced object +// is no longer a tag, and a commit will be peeled to a tree. Any other object +// type will return ErrInvalidSpec. +// +// If peeling a tag we discover an object which cannot be peeled to the target +// type due to the object model, an error will be returned. +func (o *gitObject) Peel(t ObjectType) (Object, error) { + var cobj *C.git_object + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if err := C.git_object_peel(&cobj, o.ptr, C.git_otype(t)); err < 0 { + return nil, MakeGitError(err) + } + + return allocObject(cobj, o.repo), nil +} + func allocObject(cobj *C.git_object, repo *Repository) Object { obj := gitObject{ ptr: cobj, diff --git a/object_test.go b/object_test.go index aa295e5..ef6c5a1 100644 --- a/object_test.go +++ b/object_test.go @@ -102,3 +102,63 @@ func TestObjectOwner(t *testing.T) { checkOwner(t, repo, commit) checkOwner(t, repo, tree) } + +func TestObjectPeel(t *testing.T) { + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + commitID, treeID := seedTestRepo(t, repo) + + var obj Object + + commit, err := repo.LookupCommit(commitID) + checkFatal(t, err) + + obj, err = commit.Peel(ObjectAny) + checkFatal(t, err) + + if obj.Type() != ObjectTree { + t.Fatalf("Wrong object type when peeling a commit, expected tree, have %v", obj.Type()) + } + + obj, err = commit.Peel(ObjectTag) + + if !IsErrorCode(err, ErrInvalidSpec) { + t.Fatalf("Wrong error when peeling a commit to a tag, expected ErrInvalidSpec, have %v", err) + } + + tree, err := repo.LookupTree(treeID) + checkFatal(t, err) + + obj, err = tree.Peel(ObjectAny) + + if !IsErrorCode(err, ErrInvalidSpec) { + t.Fatalf("Wrong error when peeling a tree, expected ErrInvalidSpec, have %v", err) + } + + entry := tree.EntryByName("README") + + blob, err := repo.LookupBlob(entry.Id) + checkFatal(t, err) + + obj, err = blob.Peel(ObjectAny) + + if !IsErrorCode(err, ErrInvalidSpec) { + t.Fatalf("Wrong error when peeling a blob, expected ErrInvalidSpec, have %v", err) + } + + tagID := createTestTag(t, repo, commit) + + tag, err := repo.LookupTag(tagID) + checkFatal(t, err) + + obj, err = tag.Peel(ObjectAny) + checkFatal(t, err) + + if obj.Type() != ObjectCommit { + t.Fatalf("Wrong object type when peeling a tag, expected commit, have %v", obj.Type()) + } + + // TODO: Should test a tag that annotates a different object than a commit + // but it's impossible at the moment to tag such an object. +} -- 2.45.2 From b7159b0cd4b25ee3b1a8eb9e0d4991d297487a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 4 Aug 2015 14:47:10 +0200 Subject: [PATCH 030/128] Move from an Object interface to a type An Object should be about representing a libgit2 object rather than showing which methods it should support. Change any return of Object to *Object and provide methods to convert between this and the particular type. --- blob.go | 2 +- commit.go | 6 +- describe.go | 2 +- index_test.go | 5 +- object.go | 173 +++++++++++++++++++++++++++++++++-------------- object_test.go | 24 +++---- reference.go | 2 +- repository.go | 12 ++-- reset.go | 2 +- revparse.go | 16 ++--- revparse_test.go | 2 +- tag.go | 8 +-- tree.go | 2 +- 13 files changed, 163 insertions(+), 93 deletions(-) diff --git a/blob.go b/blob.go index b1fc78a..16ec183 100644 --- a/blob.go +++ b/blob.go @@ -18,7 +18,7 @@ import ( ) type Blob struct { - gitObject + Object cast_ptr *C.git_blob } diff --git a/commit.go b/commit.go index 52f7c01..6830da3 100644 --- a/commit.go +++ b/commit.go @@ -14,7 +14,7 @@ import ( // Commit type Commit struct { - gitObject + Object cast_ptr *C.git_commit } @@ -37,7 +37,7 @@ func (c Commit) Tree() (*Tree, error) { return nil, MakeGitError(err) } - return allocObject((*C.git_object)(ptr), c.repo).(*Tree), nil + return allocTree(ptr, c.repo), nil } func (c Commit) TreeId() *Oid { @@ -61,7 +61,7 @@ func (c *Commit) Parent(n uint) *Commit { return nil } - return allocObject((*C.git_object)(cobj), c.repo).(*Commit) + return allocCommit(cobj, c.repo) } func (c *Commit) ParentId(n uint) *Oid { diff --git a/describe.go b/describe.go index c6f9a79..d75dbcb 100644 --- a/describe.go +++ b/describe.go @@ -127,7 +127,7 @@ func (c *Commit) Describe(opts *DescribeOptions) (*DescribeResult, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() - ecode := C.git_describe_commit(&resultPtr, c.gitObject.ptr, cDescribeOpts) + ecode := C.git_describe_commit(&resultPtr, c.ptr, cDescribeOpts) if ecode < 0 { return nil, MakeGitError(ecode) } diff --git a/index_test.go b/index_test.go index 7c65f4f..5f6b375 100644 --- a/index_test.go +++ b/index_test.go @@ -32,10 +32,11 @@ func TestIndexReadTree(t *testing.T) { ref, err := repo.Head() checkFatal(t, err) - obj, err := ref.Peel(ObjectTree); + obj, err := ref.Peel(ObjectTree) checkFatal(t, err) - tree := obj.(*Tree) + tree, err := obj.AsTree() + checkFatal(t, err) idx, err := NewIndex() checkFatal(t, err) diff --git a/object.go b/object.go index 6ecebf8..1981980 100644 --- a/object.go +++ b/object.go @@ -4,7 +4,11 @@ package git #include */ import "C" -import "runtime" +import ( + "errors" + "fmt" + "runtime" +) type ObjectType int @@ -17,15 +21,7 @@ const ( ObjectTag ObjectType = C.GIT_OBJ_TAG ) -type Object interface { - Free() - Id() *Oid - Type() ObjectType - Owner() *Repository - Peel(t ObjectType) (Object, error) -} - -type gitObject struct { +type Object struct { ptr *C.git_object repo *Repository } @@ -49,23 +45,128 @@ func (t ObjectType) String() string { return "" } -func (o gitObject) Id() *Oid { +func (o *Object) Id() *Oid { return newOidFromC(C.git_object_id(o.ptr)) } -func (o gitObject) Type() ObjectType { +func (o *Object) Type() ObjectType { return ObjectType(C.git_object_type(o.ptr)) } // Owner returns a weak reference to the repository which owns this -// object -func (o gitObject) Owner() *Repository { +// object. This won't keep the underlying repository alive. +func (o *Object) Owner() *Repository { return &Repository{ ptr: C.git_object_owner(o.ptr), } } -func (o *gitObject) Free() { +func dupObject(obj *Object, kind ObjectType) (*C.git_object, error) { + if obj.Type() != kind { + return nil, errors.New(fmt.Sprintf("object is not a %v", kind)) + } + + var cobj *C.git_object + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if err := C.git_object_dup(&cobj, obj.ptr); err < 0 { + return nil, MakeGitError(err) + } + + return cobj, nil +} + +func allocTree(ptr *C.git_tree, repo *Repository) *Tree { + tree := &Tree{ + Object: Object{ + ptr: (*C.git_object)(ptr), + repo: repo, + }, + cast_ptr: ptr, + } + runtime.SetFinalizer(tree, (*Tree).Free) + + return tree +} + +func (o *Object) AsTree() (*Tree, error) { + cobj, err := dupObject(o, ObjectTree) + if err != nil { + return nil, err + } + + return allocTree((*C.git_tree)(cobj), o.repo), nil +} + +func allocCommit(ptr *C.git_commit, repo *Repository) *Commit { + commit := &Commit{ + Object: Object{ + ptr: (*C.git_object)(ptr), + repo: repo, + }, + cast_ptr: ptr, + } + runtime.SetFinalizer(commit, (*Commit).Free) + + return commit +} + +func (o *Object) AsCommit() (*Commit, error) { + cobj, err := dupObject(o, ObjectCommit) + if err != nil { + return nil, err + } + + return allocCommit((*C.git_commit)(cobj), o.repo), nil +} + +func allocBlob(ptr *C.git_blob, repo *Repository) *Blob { + blob := &Blob{ + Object: Object{ + ptr: (*C.git_object)(ptr), + repo: repo, + }, + cast_ptr: ptr, + } + runtime.SetFinalizer(blob, (*Blob).Free) + + return blob +} + +func (o *Object) AsBlob() (*Blob, error) { + cobj, err := dupObject(o, ObjectBlob) + if err != nil { + return nil, err + } + + return allocBlob((*C.git_blob)(cobj), o.repo), nil +} + +func allocTag(ptr *C.git_tag, repo *Repository) *Tag { + tag := &Tag{ + Object: Object{ + ptr: (*C.git_object)(ptr), + repo: repo, + }, + cast_ptr: ptr, + } + runtime.SetFinalizer(tag, (*Tag).Free) + + return tag +} + +func (o *Object) AsTag() (*Tag, error) { + cobj, err := dupObject(o, ObjectTag) + if err != nil { + return nil, err + } + + return allocTag((*C.git_tag)(cobj), o.repo), nil +} + +func (o *Object) Free() { runtime.SetFinalizer(o, nil) C.git_object_free(o.ptr) } @@ -82,7 +183,7 @@ func (o *gitObject) Free() { // // If peeling a tag we discover an object which cannot be peeled to the target // type due to the object model, an error will be returned. -func (o *gitObject) Peel(t ObjectType) (Object, error) { +func (o *Object) Peel(t ObjectType) (*Object, error) { var cobj *C.git_object runtime.LockOSThread() @@ -95,44 +196,12 @@ func (o *gitObject) Peel(t ObjectType) (Object, error) { return allocObject(cobj, o.repo), nil } -func allocObject(cobj *C.git_object, repo *Repository) Object { - obj := gitObject{ +func allocObject(cobj *C.git_object, repo *Repository) *Object { + obj := &Object{ ptr: cobj, repo: repo, } + runtime.SetFinalizer(obj, (*Object).Free) - switch ObjectType(C.git_object_type(cobj)) { - case ObjectCommit: - commit := &Commit{ - gitObject: obj, - cast_ptr: (*C.git_commit)(cobj), - } - runtime.SetFinalizer(commit, (*Commit).Free) - return commit - - case ObjectTree: - tree := &Tree{ - gitObject: obj, - cast_ptr: (*C.git_tree)(cobj), - } - runtime.SetFinalizer(tree, (*Tree).Free) - return tree - - case ObjectBlob: - blob := &Blob{ - gitObject: obj, - cast_ptr: (*C.git_blob)(cobj), - } - runtime.SetFinalizer(blob, (*Blob).Free) - return blob - case ObjectTag: - tag := &Tag{ - gitObject: obj, - cast_ptr: (*C.git_tag)(cobj), - } - runtime.SetFinalizer(tag, (*Tag).Free) - return tag - } - - return nil + return obj } diff --git a/object_test.go b/object_test.go index ef6c5a1..2ae2a6a 100644 --- a/object_test.go +++ b/object_test.go @@ -10,12 +10,12 @@ func TestObjectPoymorphism(t *testing.T) { commitId, treeId := seedTestRepo(t, repo) - var obj Object + var obj *Object commit, err := repo.LookupCommit(commitId) checkFatal(t, err) - obj = commit + obj = &commit.Object if obj.Type() != ObjectCommit { t.Fatalf("Wrong object type, expected commit, have %v", obj.Type()) } @@ -27,13 +27,13 @@ func TestObjectPoymorphism(t *testing.T) { tree, err := repo.LookupTree(treeId) checkFatal(t, err) - obj = tree + obj = &tree.Object if obj.Type() != ObjectTree { t.Fatalf("Wrong object type, expected tree, have %v", obj.Type()) } - tree2, ok := obj.(*Tree) - if !ok { + tree2, err := obj.AsTree() + if err != nil { t.Fatalf("Converting back to *Tree is not ok") } @@ -46,16 +46,16 @@ func TestObjectPoymorphism(t *testing.T) { t.Fatal("Wrong filemode for \"README\"") } - _, ok = obj.(*Commit) - if ok { + _, err = obj.AsCommit() + if err == nil { t.Fatalf("*Tree is somehow the same as *Commit") } obj, err = repo.Lookup(tree.Id()) checkFatal(t, err) - _, ok = obj.(*Tree) - if !ok { + _, err = obj.AsTree() + if err != nil { t.Fatalf("Lookup creates the wrong type") } @@ -99,8 +99,8 @@ func TestObjectOwner(t *testing.T) { tree, err := repo.LookupTree(treeId) checkFatal(t, err) - checkOwner(t, repo, commit) - checkOwner(t, repo, tree) + checkOwner(t, repo, commit.Object) + checkOwner(t, repo, tree.Object) } func TestObjectPeel(t *testing.T) { @@ -109,7 +109,7 @@ func TestObjectPeel(t *testing.T) { commitID, treeID := seedTestRepo(t, repo) - var obj Object + var obj *Object commit, err := repo.LookupCommit(commitID) checkFatal(t, err) diff --git a/reference.go b/reference.go index 140082f..463f2fc 100644 --- a/reference.go +++ b/reference.go @@ -263,7 +263,7 @@ func (v *Reference) Delete() error { return nil } -func (v *Reference) Peel(t ObjectType) (Object, error) { +func (v *Reference) Peel(t ObjectType) (*Object, error) { var cobj *C.git_object runtime.LockOSThread() diff --git a/repository.go b/repository.go index 62fde6d..2e05780 100644 --- a/repository.go +++ b/repository.go @@ -145,7 +145,7 @@ func (v *Repository) Index() (*Index, error) { return newIndexFromC(ptr), nil } -func (v *Repository) lookupType(id *Oid, t ObjectType) (Object, error) { +func (v *Repository) lookupType(id *Oid, t ObjectType) (*Object, error) { var ptr *C.git_object runtime.LockOSThread() @@ -159,7 +159,7 @@ func (v *Repository) lookupType(id *Oid, t ObjectType) (Object, error) { return allocObject(ptr, v), nil } -func (v *Repository) Lookup(id *Oid) (Object, error) { +func (v *Repository) Lookup(id *Oid) (*Object, error) { return v.lookupType(id, ObjectAny) } @@ -169,7 +169,7 @@ func (v *Repository) LookupTree(id *Oid) (*Tree, error) { return nil, err } - return obj.(*Tree), nil + return obj.AsTree() } func (v *Repository) LookupCommit(id *Oid) (*Commit, error) { @@ -178,7 +178,7 @@ func (v *Repository) LookupCommit(id *Oid) (*Commit, error) { return nil, err } - return obj.(*Commit), nil + return obj.AsCommit() } func (v *Repository) LookupBlob(id *Oid) (*Blob, error) { @@ -187,7 +187,7 @@ func (v *Repository) LookupBlob(id *Oid) (*Blob, error) { return nil, err } - return obj.(*Blob), nil + return obj.AsBlob() } func (v *Repository) LookupTag(id *Oid) (*Tag, error) { @@ -196,7 +196,7 @@ func (v *Repository) LookupTag(id *Oid) (*Tag, error) { return nil, err } - return obj.(*Tag), nil + return obj.AsTag() } func (v *Repository) Head() (*Reference, error) { diff --git a/reset.go b/reset.go index b5b7435..9da7625 100644 --- a/reset.go +++ b/reset.go @@ -17,7 +17,7 @@ const ( func (r *Repository) ResetToCommit(commit *Commit, resetType ResetType, opts *CheckoutOpts) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_reset(r.ptr, commit.gitObject.ptr, C.git_reset_t(resetType), opts.toC()) + ret := C.git_reset(r.ptr, commit.ptr, C.git_reset_t(resetType), opts.toC()) if ret < 0 { return MakeGitError(ret) diff --git a/revparse.go b/revparse.go index 7eb04f1..950932b 100644 --- a/revparse.go +++ b/revparse.go @@ -20,16 +20,16 @@ const ( ) type Revspec struct { - to Object - from Object + to *Object + from *Object flags RevparseFlag } -func (rs *Revspec) To() Object { +func (rs *Revspec) To() *Object { return rs.to } -func (rs *Revspec) From() Object { +func (rs *Revspec) From() *Object { return rs.from } @@ -38,8 +38,8 @@ func (rs *Revspec) Flags() RevparseFlag { } func newRevspecFromC(ptr *C.git_revspec, repo *Repository) *Revspec { - var to Object - var from Object + var to *Object + var from *Object if ptr.to != nil { to = allocObject(ptr.to, repo) @@ -73,7 +73,7 @@ func (r *Repository) Revparse(spec string) (*Revspec, error) { return newRevspecFromC(&crevspec, r), nil } -func (v *Repository) RevparseSingle(spec string) (Object, error) { +func (v *Repository) RevparseSingle(spec string) (*Object, error) { cspec := C.CString(spec) defer C.free(unsafe.Pointer(cspec)) @@ -90,7 +90,7 @@ func (v *Repository) RevparseSingle(spec string) (Object, error) { return allocObject(ptr, v), nil } -func (r *Repository) RevparseExt(spec string) (Object, *Reference, error) { +func (r *Repository) RevparseExt(spec string) (*Object, *Reference, error) { cspec := C.CString(spec) defer C.free(unsafe.Pointer(cspec)) diff --git a/revparse_test.go b/revparse_test.go index 091a76b..75e9ffd 100644 --- a/revparse_test.go +++ b/revparse_test.go @@ -46,7 +46,7 @@ func TestRevparseExt(t *testing.T) { } } -func checkObject(t *testing.T, obj Object, id *Oid) { +func checkObject(t *testing.T, obj *Object, id *Oid) { if obj == nil { t.Fatalf("bad object") } diff --git a/tag.go b/tag.go index ca85156..8957430 100644 --- a/tag.go +++ b/tag.go @@ -13,7 +13,7 @@ import ( // Tag type Tag struct { - gitObject + Object cast_ptr *C.git_tag } @@ -30,7 +30,7 @@ func (t Tag) Tagger() *Signature { return newSignatureFromC(cast_ptr) } -func (t Tag) Target() Object { +func (t Tag) Target() *Object { var ptr *C.git_object ret := C.git_tag_target(&ptr, t.cast_ptr) @@ -70,7 +70,7 @@ func (c *TagsCollection) Create( } defer C.git_signature_free(taggerSig) - ctarget := commit.gitObject.ptr + ctarget := commit.ptr runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -102,7 +102,7 @@ func (c *TagsCollection) CreateLightweight(name string, commit *Commit, force bo cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) - ctarget := commit.gitObject.ptr + ctarget := commit.ptr runtime.LockOSThread() defer runtime.UnlockOSThread() diff --git a/tree.go b/tree.go index f543c11..8288176 100644 --- a/tree.go +++ b/tree.go @@ -23,7 +23,7 @@ const ( ) type Tree struct { - gitObject + Object cast_ptr *C.git_tree } -- 2.45.2 From ff6d4a7dfde18dcf330143980c66f426518b8061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 4 Aug 2015 15:01:17 +0200 Subject: [PATCH 031/128] We do require sudo on Travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fb080b9..209d89f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: go -sudo: false +sudo: required install: ./script/install-libgit2.sh -- 2.45.2 From 0b530c15cfff492e61c7afae55888fe1eeffe214 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Wed, 12 Aug 2015 12:44:58 +0200 Subject: [PATCH 032/128] clone: improve handling of remote create callback The clone options contain fields for ae remote create callback and its payload, which can be used to override the behavior when the default remote is being created for newly cloned repositories. Currently we only accept a C function as callback, though, making it overly complicated to use it. We also unconditionally `free` the payload if its address is non-`nil`, which may cause the program to segfault when the memory is not dynamically allocated. Instead, we want callers to provide a Go function that is subsequently being called by us. To do this, we introduce an indirection such that we are able to extract the provided function and payload when being called by `git_clone` and handle the return values of the user-provided function. --- clone.go | 61 +++++++++++++++++++++++++++++++++++++++++---------- clone_test.go | 45 ++++++++++++++++++++++++++++++++++++- wrapper.c | 5 +++++ 3 files changed, 98 insertions(+), 13 deletions(-) diff --git a/clone.go b/clone.go index 4de4aea..1265d7f 100644 --- a/clone.go +++ b/clone.go @@ -3,6 +3,7 @@ package git /* #include +extern void _go_git_populate_remote_cb(git_clone_options *opts); */ import "C" import ( @@ -10,13 +11,14 @@ import ( "unsafe" ) +type RemoteCreateCallback func(repo Repository, name, url string) (*Remote, ErrorCode) + type CloneOptions struct { *CheckoutOpts *RemoteCallbacks Bare bool CheckoutBranch string - RemoteCreateCallback C.git_remote_create_cb - RemoteCreatePayload unsafe.Pointer + RemoteCreateCallback RemoteCreateCallback } func Clone(url string, path string, options *CloneOptions) (*Repository, error) { @@ -30,6 +32,7 @@ func Clone(url string, path string, options *CloneOptions) (*Repository, error) copts := (*C.git_clone_options)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_clone_options{})))) populateCloneOptions(copts, options) + defer freeCloneOptions(copts) if len(options.CheckoutBranch) != 0 { copts.checkout_branch = C.CString(options.CheckoutBranch) @@ -38,9 +41,6 @@ func Clone(url string, path string, options *CloneOptions) (*Repository, error) runtime.LockOSThread() defer runtime.UnlockOSThread() ret := C.git_clone(&repo.ptr, curl, cpath, copts) - freeCheckoutOpts(&copts.checkout_opts) - C.free(unsafe.Pointer(copts.checkout_branch)) - C.free(unsafe.Pointer(copts)) if ret < 0 { return nil, MakeGitError(ret) @@ -50,6 +50,32 @@ func Clone(url string, path string, options *CloneOptions) (*Repository, error) return repo, nil } +//export remoteCreateCallback +func remoteCreateCallback(cremote unsafe.Pointer, crepo unsafe.Pointer, cname, curl *C.char, payload unsafe.Pointer) C.int { + name := C.GoString(cname) + url := C.GoString(curl) + repo := Repository{(*C.git_repository)(crepo)} + + if opts, ok := pointerHandles.Get(payload).(CloneOptions); ok { + remote, err := opts.RemoteCreateCallback(repo, name, url) + + if err == ErrOk && remote != nil { + // clear finalizer as the calling C function will + // free the remote itself + runtime.SetFinalizer(remote, nil) + + cptr := (**C.git_remote)(cremote) + *cptr = remote.ptr + } else if err == ErrOk && remote == nil { + panic("no remote created by callback") + } + + return C.int(err) + } else { + panic("invalid remote create callback") + } +} + func populateCloneOptions(ptr *C.git_clone_options, opts *CloneOptions) { C.git_clone_init_options(ptr, C.GIT_CLONE_OPTIONS_VERSION) @@ -61,12 +87,23 @@ func populateCloneOptions(ptr *C.git_clone_options, opts *CloneOptions) { ptr.bare = cbool(opts.Bare) if opts.RemoteCreateCallback != nil { - ptr.remote_cb = opts.RemoteCreateCallback - defer C.free(unsafe.Pointer(opts.RemoteCreateCallback)) - - if opts.RemoteCreatePayload != nil { - ptr.remote_cb_payload = opts.RemoteCreatePayload - defer C.free(opts.RemoteCreatePayload) - } + // Go v1.1 does not allow to assign a C function pointer + C._go_git_populate_remote_cb(ptr) + ptr.remote_cb_payload = pointerHandles.Track(*opts) } } + +func freeCloneOptions(ptr *C.git_clone_options) { + if ptr == nil { + return + } + + freeCheckoutOpts(&ptr.checkout_opts) + + if ptr.remote_cb_payload != nil { + pointerHandles.Untrack(ptr.remote_cb_payload) + } + + C.free(unsafe.Pointer(ptr.checkout_branch)) + C.free(unsafe.Pointer(ptr)) +} diff --git a/clone_test.go b/clone_test.go index fd83fec..86fced8 100644 --- a/clone_test.go +++ b/clone_test.go @@ -5,8 +5,11 @@ import ( "testing" ) -func TestClone(t *testing.T) { +const ( + REMOTENAME = "testremote" +) +func TestClone(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -20,3 +23,43 @@ func TestClone(t *testing.T) { checkFatal(t, err) } + +func TestCloneWithCallback(t *testing.T) { + testPayload := 0 + + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + seedTestRepo(t, repo) + + path, err := ioutil.TempDir("", "git2go") + checkFatal(t, err) + + opts := CloneOptions{ + Bare: true, + RemoteCreateCallback: func(r Repository, name, url string) (*Remote, ErrorCode) { + testPayload += 1 + + remote, err := r.CreateRemote(REMOTENAME, url) + if err != nil { + return nil, ErrGeneric + } + + return remote, ErrOk + }, + } + + repo2, err := Clone(repo.Path(), path, &opts) + defer cleanupTestRepo(t, repo2) + + checkFatal(t, err) + + if testPayload != 1 { + t.Fatal("Payload's value has not been changed") + } + + remote, err := repo2.LookupRemote(REMOTENAME) + if err != nil || remote == nil { + t.Fatal("Remote was not created properly") + } +} diff --git a/wrapper.c b/wrapper.c index 017168d..3b88f93 100644 --- a/wrapper.c +++ b/wrapper.c @@ -5,6 +5,11 @@ typedef int (*gogit_submodule_cbk)(git_submodule *sm, const char *name, void *payload); +void _go_git_populate_remote_cb(git_clone_options *opts) +{ + opts->remote_cb = (git_remote_create_cb)remoteCreateCallback; +} + int _go_git_visit_submodule(git_repository *repo, void *fct) { return git_submodule_foreach(repo, (gogit_submodule_cbk)&SubmoduleVisitor, fct); -- 2.45.2 From a572b15df69c4b0350a1134045a7e7a66b06de21 Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Mon, 31 Aug 2015 12:42:34 +0200 Subject: [PATCH 033/128] Add back support for RemoteCallbacks in Remote.Push() --- remote.go | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/remote.go b/remote.go index 6f3c9ea..e1e4845 100644 --- a/remote.go +++ b/remote.go @@ -153,6 +153,9 @@ type HostkeyCertificate struct { } type PushOptions struct { + // Callbacks to use for this push operation + RemoteCallbacks RemoteCallbacks + PbParallelism uint } @@ -591,6 +594,17 @@ func populateFetchOptions(options *C.git_fetch_options, opts *FetchOptions) { options.download_tags = C.git_remote_autotag_option_t(opts.DownloadTags) } +func populatePushOptions(options *C.git_push_options, opts *PushOptions) { + C.git_push_init_options(options, C.GIT_PUSH_OPTIONS_VERSION) + if opts == nil { + return + } + + options.pb_parallelism = C.uint(opts.PbParallelism) + + populateRemoteCallbacks(&options.callbacks, &opts.RemoteCallbacks) +} + // Fetch performs a fetch operation. refspecs specifies which refspecs // to use for this fetch, use an empty list to use the refspecs from // the configuration; msg specifies what to use for the reflog @@ -689,22 +703,19 @@ func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) { } func (o *Remote) Push(refspecs []string, opts *PushOptions) error { - 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) + var coptions C.git_push_options + populatePushOptions(&coptions, opts) + defer untrackCalbacksPayload(&coptions.callbacks) + runtime.LockOSThread() defer runtime.UnlockOSThread() - defer untrackCalbacksPayload(&copts.callbacks) - ret := C.git_remote_push(o.ptr, &crefspecs, &copts) + ret := C.git_remote_push(o.ptr, &crefspecs, &coptions) if ret < 0 { return MakeGitError(ret) } -- 2.45.2 From 803ef7dad548b1b84c86365d6b61c8b6b4a0cce0 Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Mon, 24 Aug 2015 21:37:09 +0200 Subject: [PATCH 034/128] Add nil check on CredentialsCallback wrapper --- remote.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/remote.go b/remote.go index e1e4845..b2fb96f 100644 --- a/remote.go +++ b/remote.go @@ -214,7 +214,9 @@ func credentialsCallback(_cred **C.git_cred, _url *C.char, _username_from_url *C url := C.GoString(_url) username_from_url := C.GoString(_username_from_url) ret, cred := callbacks.CredentialsCallback(url, username_from_url, (CredType)(allowed_types)) - *_cred = cred.ptr + if cred != nil { + *_cred = cred.ptr + } return int(ret) } -- 2.45.2 From 157593f38da780c4f6cb6dc61275b9b36a3327bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 31 Aug 2015 13:13:27 +0200 Subject: [PATCH 035/128] Don't trat a revwalk's ITEROVER as an error --- walk.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/walk.go b/walk.go index d02044a..c314f60 100644 --- a/walk.go +++ b/walk.go @@ -173,6 +173,10 @@ func (v *RevWalk) Iterate(fun RevWalkIterator) (err error) { return nil } if err != nil { + if err.(GitError).Code == ErrIterOver { + err = nil + } + return err } -- 2.45.2 From 337f25d47e9841ea9c5e1cf6bc25720c08538a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 31 Aug 2015 16:05:48 +0200 Subject: [PATCH 036/128] Remove the vendored libgit2 submodule This is a left-over from the merge from 'next'. --- vendor/libgit2 | 1 - 1 file changed, 1 deletion(-) delete mode 160000 vendor/libgit2 diff --git a/vendor/libgit2 b/vendor/libgit2 deleted file mode 160000 index fb84cde..0000000 --- a/vendor/libgit2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fb84cde81e11947add4ff8bb9b4084f7d76e6567 -- 2.45.2 From 1ea99658246673770b21f8848e257132a29e78d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 31 Aug 2015 14:01:06 +0200 Subject: [PATCH 037/128] Install v23 on Travis --- script/install-libgit2.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/install-libgit2.sh b/script/install-libgit2.sh index a6c3202..9bf6b37 100755 --- a/script/install-libgit2.sh +++ b/script/install-libgit2.sh @@ -13,9 +13,9 @@ if [ "x$TRAVIS_BRANCH" = "xnext" ]; then fi cd "${HOME}" -wget -O libgit2-0.22.3.tar.gz https://github.com/libgit2/libgit2/archive/v0.22.1.tar.gz -tar -xzvf libgit2-0.22.3.tar.gz -cd libgit2-0.22.1 && mkdir build && cd build +wget -O libgit2-0.23.1.tar.gz https://github.com/libgit2/libgit2/archive/v0.23.1.tar.gz +tar -xzvf libgit2-0.23.1.tar.gz +cd libgit2-0.23.1 && mkdir build && cd build cmake -DTHREADSAFE=ON -DBUILD_CLAR=OFF -DCMAKE_BUILD_TYPE="RelWithDebInfo" .. && make && sudo make install sudo ldconfig cd "${TRAVIS_BUILD_DIR}" -- 2.45.2 From 2743bbfca3963e2a1cc21513b7ab9e8e8af70bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 31 Aug 2015 15:36:20 +0200 Subject: [PATCH 038/128] Test against Go 1.5 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 209d89f..f796389 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ go: - 1.2 - 1.3 - 1.4 + - 1.5 - tip matrix: -- 2.45.2 From 4090c401c8bf3f062e898f75bde01e2ef27b3911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 31 Aug 2015 16:05:54 +0200 Subject: [PATCH 039/128] Don't call the finalizer on a borrowed repository When libgit2 gives us the repository for us to create the remote in, we do not own it, so we must make sure we don't try to free it. --- clone.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/clone.go b/clone.go index b8579b5..e80d14d 100644 --- a/clone.go +++ b/clone.go @@ -55,15 +55,16 @@ func remoteCreateCallback(cremote unsafe.Pointer, crepo unsafe.Pointer, cname, c name := C.GoString(cname) url := C.GoString(curl) repo := newRepositoryFromC((*C.git_repository)(crepo)) + // We don't own this repository, so make sure we don't try to free it + runtime.SetFinalizer(repo, nil) if opts, ok := pointerHandles.Get(payload).(CloneOptions); ok { remote, err := opts.RemoteCreateCallback(repo, name, url) + // clear finalizer as the calling C function will + // free the remote itself + runtime.SetFinalizer(remote, nil) if err == ErrOk && remote != nil { - // clear finalizer as the calling C function will - // free the remote itself - runtime.SetFinalizer(remote, nil) - cptr := (**C.git_remote)(cremote) *cptr = remote.ptr } else if err == ErrOk && remote == nil { -- 2.45.2 From 876ddd17eba886c8d1cc10d34a8130dd147d63da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 31 Aug 2015 20:21:28 +0200 Subject: [PATCH 040/128] Update missing bit to the new object model --- merge_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/merge_test.go b/merge_test.go index ad01319..8059727 100644 --- a/merge_test.go +++ b/merge_test.go @@ -115,7 +115,10 @@ func appendCommit(t *testing.T, repo *Repository) (*Oid, *Oid) { parent, err := ref.Peel(ObjectCommit) checkFatal(t, err) - commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree, parent.(*Commit)) + parentCommit, err := parent.AsCommit() + checkFatal(t, err) + + commitId, err := repo.CreateCommit("HEAD", sig, sig, message, tree, parentCommit) checkFatal(t, err) return commitId, treeId -- 2.45.2 From d5890f58e884174f1cee13d5a616e818ba2f5049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 31 Aug 2015 20:22:17 +0200 Subject: [PATCH 041/128] Run go fmt As it seems to be something that many people can't get over, reformat all the files; as we're breaking things, whoever depended on 'next' will have to take many changes into account anyway, so let's include this to reduce the noise of incoming patches. --- checkout.go | 4 ++-- index.go | 6 +++--- merge.go | 12 ++++++------ odb.go | 4 ++-- remote.go | 26 +++++++++++++------------- remote_test.go | 2 +- repository.go | 10 +++++----- 7 files changed, 32 insertions(+), 32 deletions(-) diff --git a/checkout.go b/checkout.go index e0c067e..ce2f469 100644 --- a/checkout.go +++ b/checkout.go @@ -44,7 +44,7 @@ type CheckoutOpts struct { 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 - Paths []string + Paths []string } func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOpts { @@ -156,4 +156,4 @@ func (v *Repository) CheckoutTree(tree *Tree, opts *CheckoutOpts) error { } return nil -} \ No newline at end of file +} diff --git a/index.go b/index.go index 0174dc1..f4c0c1e 100644 --- a/index.go +++ b/index.go @@ -51,8 +51,8 @@ func newIndexEntryFromC(entry *C.git_index_entry) *IndexEntry { return nil } return &IndexEntry{ - IndexTime { int32(entry.ctime.seconds), uint32(entry.ctime.nanoseconds) }, - IndexTime { int32(entry.mtime.seconds), uint32(entry.mtime.nanoseconds) }, + IndexTime{int32(entry.ctime.seconds), uint32(entry.ctime.nanoseconds)}, + IndexTime{int32(entry.mtime.seconds), uint32(entry.mtime.nanoseconds)}, Filemode(entry.mode), uint32(entry.uid), uint32(entry.gid), @@ -280,7 +280,7 @@ func (v *Index) ReadTree(tree *Tree) error { runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_index_read_tree(v.ptr, tree.cast_ptr); + ret := C.git_index_read_tree(v.ptr, tree.cast_ptr) if ret < 0 { return MakeGitError(ret) } diff --git a/merge.go b/merge.go index 7307f10..f1a6f40 100644 --- a/merge.go +++ b/merge.go @@ -85,8 +85,8 @@ const ( ) type MergeOptions struct { - Version uint - TreeFlags MergeTreeFlag + Version uint + TreeFlags MergeTreeFlag RenameThreshold uint TargetLimit uint @@ -98,7 +98,7 @@ type MergeOptions struct { func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions { return MergeOptions{ Version: uint(opts.version), - TreeFlags: MergeTreeFlag(opts.tree_flags), + TreeFlags: MergeTreeFlag(opts.tree_flags), RenameThreshold: uint(opts.rename_threshold), TargetLimit: uint(opts.target_limit), FileFavor: MergeFileFavor(opts.file_favor), @@ -259,10 +259,10 @@ func (r *Repository) MergeBases(one, two *Oid) ([]*Oid, error) { } oids := make([]*Oid, coids.count) - hdr := reflect.SliceHeader { + hdr := reflect.SliceHeader{ Data: uintptr(unsafe.Pointer(coids.ids)), - Len: int(coids.count), - Cap: int(coids.count), + Len: int(coids.count), + Cap: int(coids.count), } goSlice := *(*[]C.git_oid)(unsafe.Pointer(&hdr)) diff --git a/odb.go b/odb.go index be0870e..ff8b739 100644 --- a/odb.go +++ b/odb.go @@ -8,10 +8,10 @@ extern void _go_git_odb_backend_free(git_odb_backend *backend); */ import "C" import ( + "fmt" "reflect" "runtime" "unsafe" - "fmt" ) type Odb struct { @@ -130,7 +130,7 @@ func (v *Odb) ForEach(callback OdbForEachCallback) error { defer pointerHandles.Untrack(handle) ret := C._go_git_odb_foreach(v.ptr, handle) - fmt.Println("ret %v", ret); + fmt.Println("ret %v", ret) if ret == C.GIT_EUSER { return data.err } else if ret < 0 { diff --git a/remote.go b/remote.go index b2fb96f..b3aba54 100644 --- a/remote.go +++ b/remote.go @@ -72,12 +72,12 @@ type RemoteCallbacks struct { type FetchPrune uint const ( - // Use the setting from the configuration + // Use the setting from the configuration FetchPruneUnspecified FetchPrune = C.GIT_FETCH_PRUNE_UNSPECIFIED // Force pruning on - FetchPruneOn FetchPrune = C.GIT_FETCH_PRUNE + FetchPruneOn FetchPrune = C.GIT_FETCH_PRUNE // Force pruning off - FetchNoPrune FetchPrune = C.GIT_FETCH_NO_PRUNE + FetchNoPrune FetchPrune = C.GIT_FETCH_NO_PRUNE ) type DownloadTags uint @@ -88,20 +88,20 @@ const ( DownloadTagsUnspecified DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED // Ask the server for tags pointing to objects we're already // downloading. - DownloadTagsAuto DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_AUTO + DownloadTagsAuto DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_AUTO // Don't ask for any tags beyond the refspecs. - DownloadTagsNone DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_NONE + DownloadTagsNone DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_NONE // Ask for the all the tags. - DownloadTagsAll DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_ALL + DownloadTagsAll DownloadTags = C.GIT_REMOTE_DOWNLOAD_TAGS_ALL ) type FetchOptions struct { // Callbacks to use for this fetch operation RemoteCallbacks RemoteCallbacks // Whether to perform a prune after the fetch - Prune FetchPrune + Prune FetchPrune // Whether to write the results to FETCH_HEAD. Defaults to // on. Leave this default in order to behave like git. UpdateFetchhead bool @@ -111,7 +111,7 @@ type FetchOptions struct { // downloading all of them. // // The default is to auto-follow tags. - DownloadTags DownloadTags + DownloadTags DownloadTags } type Remote struct { @@ -588,7 +588,7 @@ func (o *Remote) RefspecCount() uint { func populateFetchOptions(options *C.git_fetch_options, opts *FetchOptions) { C.git_fetch_init_options(options, C.GIT_FETCH_OPTIONS_VERSION) if opts == nil { - return; + return } populateRemoteCallbacks(&options.callbacks, &opts.RemoteCallbacks) options.prune = C.git_fetch_prune_t(opts.Prune) @@ -611,7 +611,7 @@ func populatePushOptions(options *C.git_push_options, opts *PushOptions) { // to use for this fetch, use an empty list to use the refspecs from // the configuration; msg specifies what to use for the reflog // entries. Leave "" to use defaults. -func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error { +func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error { var cmsg *C.char = nil if msg != "" { cmsg = C.CString(msg) @@ -624,7 +624,7 @@ func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error defer freeStrarray(&crefspecs) var coptions C.git_fetch_options - populateFetchOptions(&coptions, opts); + populateFetchOptions(&coptions, opts) defer untrackCalbacksPayload(&coptions.callbacks) runtime.LockOSThread() @@ -646,7 +646,7 @@ func (o *Remote) ConnectPush(callbacks *RemoteCallbacks) error { } func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks) error { - var ccallbacks C.git_remote_callbacks; + var ccallbacks C.git_remote_callbacks populateRemoteCallbacks(&ccallbacks, callbacks) runtime.LockOSThread() @@ -729,7 +729,7 @@ func (o *Remote) PruneRefs() bool { } func (o *Remote) Prune(callbacks *RemoteCallbacks) error { - var ccallbacks C.git_remote_callbacks; + var ccallbacks C.git_remote_callbacks populateRemoteCallbacks(&ccallbacks, callbacks) runtime.LockOSThread() diff --git a/remote_test.go b/remote_test.go index 73c637f..dac3dbe 100644 --- a/remote_test.go +++ b/remote_test.go @@ -39,7 +39,7 @@ func TestCertificateCheck(t *testing.T) { remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) - options := FetchOptions { + options := FetchOptions{ RemoteCallbacks: RemoteCallbacks{ CertificateCheckCallback: func(cert *Certificate, valid bool, hostname string) ErrorCode { return assertHostname(cert, valid, hostname, t) diff --git a/repository.go b/repository.go index 2e05780..d8e398b 100644 --- a/repository.go +++ b/repository.go @@ -12,11 +12,11 @@ import ( // Repository type Repository struct { - ptr *C.git_repository + ptr *C.git_repository // Remotes represents the collection of remotes and can be // used to add, remove and configure remotes for this // repository. - Remotes RemoteCollection + Remotes RemoteCollection // Submodules represents the collection of submodules and can // be used to add, remove and configure submodules in this // repostiory. @@ -26,7 +26,7 @@ type Repository struct { References ReferenceCollection // Notes represents the collection of notes and can be used to // read, write and delete notes from this repository. - Notes NoteCollection + Notes NoteCollection // Tags represents the collection of tags and can be used to create, // list and iterate tags in this repository. Tags TagsCollection @@ -35,10 +35,10 @@ type Repository struct { func newRepositoryFromC(ptr *C.git_repository) *Repository { repo := &Repository{ptr: ptr} - repo.Remotes.repo = repo + repo.Remotes.repo = repo repo.Submodules.repo = repo repo.References.repo = repo - repo.Notes.repo = repo + repo.Notes.repo = repo repo.Tags.repo = repo runtime.SetFinalizer(repo, (*Repository).Free) -- 2.45.2 From b3e7705c48f038ef335204a2a9e1ee829784c30e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 31 Aug 2015 20:24:54 +0200 Subject: [PATCH 042/128] Update vendored libgit2 --- vendor/libgit2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/libgit2 b/vendor/libgit2 index fb84cde..ed38e26 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit fb84cde81e11947add4ff8bb9b4084f7d76e6567 +Subproject commit ed38e26db5435b519d8b796e4b6c2c660fe982b5 -- 2.45.2 From 34fb7e03ecfd3fbe7000ecfac4e30ea719d9879e Mon Sep 17 00:00:00 2001 From: Calin Seciu Date: Thu, 17 Sep 2015 17:07:34 +0300 Subject: [PATCH 043/128] Fix crash when using Pathspec in StatusOptions Using `StatusOptions.Pathspec` results in a fatal error panic with the message 'unexpected signal during runtime execution'. This is because the `&cpathspec` C.git_strarray gets freed in `*StatusOptions.toC()` before being passed to `C.git_status_init_options()` in `*Repository.StatusList()` (see https://github.com/libgit2/git2go/blob/b3e7705c48f038ef335204a2a9e1ee829784c30e/status.go#L138) The relevant panic trace is: ``` fatal error: unexpected signal during runtime execution [signal 0xb code=0x1 addr=0xb01dfacedebac1e pc=0x4062609] runtime stack: runtime.throw(0x469a080, 0x2a) /usr/local/Cellar/go/1.5.1/libexec/src/runtime/panic.go:527 +0x90 runtime.sigpanic() /usr/local/Cellar/go/1.5.1/libexec/src/runtime/sigpanic_unix.go:12 +0x5a goroutine 71 [syscall, locked to thread]: runtime.cgocall(0x400a720, 0xc8204e9998, 0x0) /usr/local/Cellar/go/1.5.1/libexec/src/runtime/cgocall.go:120 +0x11b fp=0xc8204e9968 sp=0xc8204e9938 github.com/libgit2/git2go._Cfunc_git_status_list_new(0xc8204c39c8, 0x5e17780, 0xc820478c40, 0xc800000000) ??:0 +0x39 fp=0xc8204e9998 sp=0xc8204e9968 github.com/libgit2/git2go.(*Repository).StatusList(0xc820013290, 0xc8204e9b58, 0x0, 0x0, 0x0) /Users/calin/go/src/github.com/libgit2/git2go/status.go:168 +0x11d fp=0xc8204e99e8 sp=0xc8204e9998 ``` --- status.go | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/status.go b/status.go index 3f5a06d..068a474 100644 --- a/status.go +++ b/status.go @@ -126,34 +126,24 @@ type StatusOptions struct { Pathspec []string } -func (opts *StatusOptions) toC() *C.git_status_options { - if opts == nil { - return nil - } - - cpathspec := C.git_strarray{} - if opts.Pathspec != nil { - cpathspec.count = C.size_t(len(opts.Pathspec)) - cpathspec.strings = makeCStringsFromStrings(opts.Pathspec) - defer freeStrarray(&cpathspec) - } - - copts := &C.git_status_options{ - version: C.GIT_STATUS_OPTIONS_VERSION, - show: C.git_status_show_t(opts.Show), - flags: C.uint(opts.Flags), - pathspec: cpathspec, - } - - return copts -} - func (v *Repository) StatusList(opts *StatusOptions) (*StatusList, error) { var ptr *C.git_status_list var copts *C.git_status_options if opts != nil { - copts = opts.toC() + cpathspec := C.git_strarray{} + if opts.Pathspec != nil { + cpathspec.count = C.size_t(len(opts.Pathspec)) + cpathspec.strings = makeCStringsFromStrings(opts.Pathspec) + defer freeStrarray(&cpathspec) + } + + copts = &C.git_status_options{ + version: C.GIT_STATUS_OPTIONS_VERSION, + show: C.git_status_show_t(opts.Show), + flags: C.uint(opts.Flags), + pathspec: cpathspec, + } } else { copts = &C.git_status_options{} ret := C.git_status_init_options(copts, C.GIT_STATUS_OPTIONS_VERSION) -- 2.45.2 From 9397af0854575913f72b10a7ebf474ef023e2e69 Mon Sep 17 00:00:00 2001 From: Jose Alvarez Date: Wed, 16 Sep 2015 16:08:37 -0400 Subject: [PATCH 044/128] Expose baseline field in CheckoutOptions --- checkout.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/checkout.go b/checkout.go index e0c067e..a2e312b 100644 --- a/checkout.go +++ b/checkout.go @@ -44,7 +44,8 @@ type CheckoutOpts struct { 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 - Paths []string + Paths []string + Baseline *Tree } func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOpts { @@ -90,6 +91,10 @@ func populateCheckoutOpts(ptr *C.git_checkout_options, opts *CheckoutOpts) *C.gi ptr.paths.count = C.size_t(len(opts.Paths)) } + if opts.Baseline != nil { + ptr.baseline = opts.Baseline.cast_ptr + } + return ptr } @@ -156,4 +161,4 @@ func (v *Repository) CheckoutTree(tree *Tree, opts *CheckoutOpts) error { } return nil -} \ No newline at end of file +} -- 2.45.2 From d54ea1d6a88406fc29db2596ce71f5c00aa4d205 Mon Sep 17 00:00:00 2001 From: Calin Seciu Date: Mon, 21 Sep 2015 14:50:57 +0300 Subject: [PATCH 045/128] Add stash support --- repository.go | 4 + stash.go | 334 ++++++++++++++++++++++++++++++++++++++++++++++++++ stash_test.go | 198 ++++++++++++++++++++++++++++++ wrapper.c | 8 ++ 4 files changed, 544 insertions(+) create mode 100644 stash.go create mode 100644 stash_test.go diff --git a/repository.go b/repository.go index d8e398b..4bf8531 100644 --- a/repository.go +++ b/repository.go @@ -30,6 +30,9 @@ type Repository struct { // Tags represents the collection of tags and can be used to create, // list and iterate tags in this repository. Tags TagsCollection + // Stashes represents the collection of stashes and can be used to + // save, apply and iterate over stash states in this repository. + Stashes StashCollection } func newRepositoryFromC(ptr *C.git_repository) *Repository { @@ -40,6 +43,7 @@ func newRepositoryFromC(ptr *C.git_repository) *Repository { repo.References.repo = repo repo.Notes.repo = repo repo.Tags.repo = repo + repo.Stashes.repo = repo runtime.SetFinalizer(repo, (*Repository).Free) diff --git a/stash.go b/stash.go new file mode 100644 index 0000000..5142b82 --- /dev/null +++ b/stash.go @@ -0,0 +1,334 @@ +package git + +/* +#include + +extern void _go_git_setup_stash_apply_progress_callbacks(git_stash_apply_options *opts); +extern int _go_git_stash_foreach(git_repository *repo, void *payload); +*/ +import "C" +import ( + "runtime" + "unsafe" +) + +// StashFlag are flags that affect the stash save operation. +type StashFlag int + +const ( + // StashDefault represents no option, default. + StashDefault StashFlag = C.GIT_STASH_DEFAULT + + // StashKeepIndex leaves all changes already added to the + // index intact in the working directory. + StashKeepIndex StashFlag = C.GIT_STASH_KEEP_INDEX + + // StashIncludeUntracked means all untracked files are also + // stashed and then cleaned up from the working directory. + StashIncludeUntracked StashFlag = C.GIT_STASH_INCLUDE_UNTRACKED + + // StashIncludeIgnored means all ignored files are also + // stashed and then cleaned up from the working directory. + StashIncludeIgnored StashFlag = C.GIT_STASH_INCLUDE_IGNORED +) + +// StashCollection represents the possible operations that can be +// performed on the collection of stashes for a repository. +type StashCollection struct { + repo *Repository +} + +// Save saves the local modifications to a new stash. +// +// Stasher is the identity of the person performing the stashing. +// Message is the optional description along with the stashed state. +// Flags control the stashing process and are given as bitwise OR. +func (c *StashCollection) Save( + stasher *Signature, message string, flags StashFlag) (*Oid, error) { + + oid := new(Oid) + + stasherC, err := stasher.toC() + if err != nil { + return nil, err + } + defer C.git_signature_free(stasherC) + + messageC := C.CString(message) + defer C.free(unsafe.Pointer(messageC)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_stash_save( + oid.toC(), c.repo.ptr, + stasherC, messageC, C.uint(flags)) + + if ret < 0 { + return nil, MakeGitError(ret) + } + return oid, nil +} + +// StashApplyFlag are flags that affect the stash apply operation. +type StashApplyFlag int + +const ( + // StashApplyDefault is the default. + StashApplyDefault StashApplyFlag = C.GIT_STASH_APPLY_DEFAULT + + // StashApplyReinstateIndex will try to reinstate not only the + // working tree's changes, but also the index's changes. + StashApplyReinstateIndex StashApplyFlag = C.GIT_STASH_APPLY_REINSTATE_INDEX +) + +// StashApplyProgress are flags describing the progress of the apply operation. +type StashApplyProgress int + +const ( + // StashApplyProgressNone means loading the stashed data from the object store. + StashApplyProgressNone StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_NONE + + // StashApplyProgressLoadingStash means the stored index is being analyzed. + StashApplyProgressLoadingStash StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_LOADING_STASH + + // StashApplyProgressAnalyzeIndex means the stored index is being analyzed. + StashApplyProgressAnalyzeIndex StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX + + // StashApplyProgressAnalyzeModified means the modified files are being analyzed. + StashApplyProgressAnalyzeModified StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED + + // StashApplyProgressAnalyzeUntracked means the untracked and ignored files are being analyzed. + StashApplyProgressAnalyzeUntracked StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED + + // StashApplyProgressCheckoutUntracked means the untracked files are being written to disk. + StashApplyProgressCheckoutUntracked StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED + + // StashApplyProgressCheckoutModified means the modified files are being written to disk. + StashApplyProgressCheckoutModified StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED + + // StashApplyProgressDone means the stash was applied successfully. + StashApplyProgressDone StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_DONE +) + +// StashApplyProgressCallback is the apply operation notification callback. +type StashApplyProgressCallback func(progress StashApplyProgress) error + +type stashApplyProgressData struct { + Callback StashApplyProgressCallback + Error error +} + +//export stashApplyProgressCb +func stashApplyProgressCb(progress C.git_stash_apply_progress_t, handle unsafe.Pointer) int { + payload := pointerHandles.Get(handle) + data, ok := payload.(*stashApplyProgressData) + if !ok { + panic("could not retrieve data for handle") + } + + if data != nil { + err := data.Callback(StashApplyProgress(progress)) + if err != nil { + data.Error = err + return C.GIT_EUSER + } + } + return 0 +} + +// StashApplyOptions represents options to control the apply operation. +type StashApplyOptions struct { + Flags StashApplyFlag + CheckoutOptions CheckoutOpts // options to use when writing files to the working directory + ProgressCallback StashApplyProgressCallback // optional callback to notify the consumer of application progress +} + +// DefaultStashApplyOptions initializes the structure with default values. +func DefaultStashApplyOptions() (StashApplyOptions, error) { + optsC := C.git_stash_apply_options{} + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ecode := C.git_stash_apply_init_options(&optsC, C.GIT_STASH_APPLY_OPTIONS_VERSION) + if ecode < 0 { + return StashApplyOptions{}, MakeGitError(ecode) + } + defer freeStashApplyOptions(&optsC) + + return StashApplyOptions{ + Flags: StashApplyFlag(optsC.flags), + CheckoutOptions: checkoutOptionsFromC(&optsC.checkout_options), + }, nil +} + +func (opts *StashApplyOptions) toC() ( + optsC *C.git_stash_apply_options, progressData *stashApplyProgressData) { + + if opts != nil { + progressData = &stashApplyProgressData{ + Callback: opts.ProgressCallback, + } + + optsC = &C.git_stash_apply_options{ + version: C.GIT_STASH_APPLY_OPTIONS_VERSION, + flags: C.git_stash_apply_flags(opts.Flags), + checkout_options: *opts.CheckoutOptions.toC(), + } + if opts.ProgressCallback != nil { + C._go_git_setup_stash_apply_progress_callbacks(optsC) + optsC.progress_payload = pointerHandles.Track(progressData) + } + } + return +} + +func freeStashApplyOptions(optsC *C.git_stash_apply_options) { + if optsC != nil { + freeCheckoutOpts(&optsC.checkout_options) + if optsC.progress_payload != nil { + pointerHandles.Untrack(optsC.progress_payload) + } + } +} + +// Apply applies a single stashed state from the stash list. +// +// If local changes in the working directory conflict with changes in the +// stash then ErrConflict will be returned. In this case, the index +// will always remain unmodified and all files in the working directory will +// remain unmodified. However, if you are restoring untracked files or +// ignored files and there is a conflict when applying the modified files, +// then those files will remain in the working directory. +// +// If passing the StashApplyReinstateIndex flag and there would be conflicts +// when reinstating the index, the function will return ErrConflict +// and both the working directory and index will be left unmodified. +// +// Note that a minimum checkout strategy of 'CheckoutSafe' is implied. +// +// 'index' is the position within the stash list. 0 points to the most +// recent stashed state. +// +// Returns error code ErrNotFound if there's no stashed state for the given +// index, error code ErrConflict if local changes in the working directory +// conflict with changes in the stash, the user returned error from the +// StashApplyProgressCallback, if any, or other error code. +// +// Error codes can be interogated with IsErrorCode(err, ErrNotFound). +func (c *StashCollection) Apply(index int, opts StashApplyOptions) error { + optsC, progressData := opts.toC() + defer freeStashApplyOptions(optsC) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_stash_apply(c.repo.ptr, C.size_t(index), optsC) + if ret == C.GIT_EUSER { + return progressData.Error + } + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +// StashCallback is called per entry when interating over all +// the stashed states. +// +// 'index' is the position of the current stash in the stash list, +// 'message' is the message used when creating the stash and 'id' +// is the commit id of the stash. +type StashCallback func(index int, message string, id *Oid) error + +type stashCallbackData struct { + Callback StashCallback + Error error +} + +//export stashForeachCb +func stashForeachCb(index C.size_t, message *C.char, id *C.git_oid, handle unsafe.Pointer) int { + payload := pointerHandles.Get(handle) + data, ok := payload.(*stashCallbackData) + if !ok { + panic("could not retrieve data for handle") + } + + err := data.Callback(int(index), C.GoString(message), newOidFromC(id)) + if err != nil { + data.Error = err + return C.GIT_EUSER + } + return 0 +} + +// Foreach loops over all the stashed states and calls the callback +// for each one. +// +// If callback returns an error, this will stop looping. +func (c *StashCollection) Foreach(callback StashCallback) error { + data := stashCallbackData{ + Callback: callback, + } + + handle := pointerHandles.Track(&data) + defer pointerHandles.Untrack(handle) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C._go_git_stash_foreach(c.repo.ptr, handle) + if ret == C.GIT_EUSER { + return data.Error + } + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +// Drop removes a single stashed state from the stash list. +// +// 'index' is the position within the stash list. 0 points +// to the most recent stashed state. +// +// Returns error code ErrNotFound if there's no stashed +// state for the given index. +func (c *StashCollection) Drop(index int) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_stash_drop(c.repo.ptr, C.size_t(index)) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +// Pop applies a single stashed state from the stash list +// and removes it from the list if successful. +// +// 'index' is the position within the stash list. 0 points +// to the most recent stashed state. +// +// 'opts' controls how stashes are applied. +// +// Returns error code ErrNotFound if there's no stashed +// state for the given index. +func (c *StashCollection) Pop(index int, opts StashApplyOptions) error { + optsC, progressData := opts.toC() + defer freeStashApplyOptions(optsC) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_stash_pop(c.repo.ptr, C.size_t(index), optsC) + if ret == C.GIT_EUSER { + return progressData.Error + } + if ret < 0 { + return MakeGitError(ret) + } + return nil +} diff --git a/stash_test.go b/stash_test.go new file mode 100644 index 0000000..180a16b --- /dev/null +++ b/stash_test.go @@ -0,0 +1,198 @@ +package git + +import ( + "fmt" + "io/ioutil" + "os" + "path" + "reflect" + "runtime" + "testing" + "time" +) + +func TestStash(t *testing.T) { + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + prepareStashRepo(t, repo) + + sig := &Signature{ + Name: "Rand Om Hacker", + Email: "random@hacker.com", + When: time.Now(), + } + + stash1, err := repo.Stashes.Save(sig, "First stash", StashDefault) + checkFatal(t, err) + + _, err = repo.LookupCommit(stash1) + checkFatal(t, err) + + b, err := ioutil.ReadFile(pathInRepo(repo, "README")) + checkFatal(t, err) + if string(b) == "Update README goes to stash\n" { + t.Errorf("README still contains the uncommitted changes") + } + + if !fileExistsInRepo(repo, "untracked.txt") { + t.Errorf("untracked.txt doesn't exist in the repo; should be untracked") + } + + // Apply: default + + opts, err := DefaultStashApplyOptions() + checkFatal(t, err) + + err = repo.Stashes.Apply(0, opts) + checkFatal(t, err) + + b, err = ioutil.ReadFile(pathInRepo(repo, "README")) + checkFatal(t, err) + if string(b) != "Update README goes to stash\n" { + t.Errorf("README changes aren't here") + } + + // Apply: no stash for the given index + + err = repo.Stashes.Apply(1, opts) + if !IsErrorCode(err, ErrNotFound) { + t.Errorf("expecting GIT_ENOTFOUND error code %d, got %v", ErrNotFound, err) + } + + // Apply: callback stopped + + opts.ProgressCallback = func(progress StashApplyProgress) error { + if progress == StashApplyProgressCheckoutModified { + return fmt.Errorf("Stop") + } + return nil + } + + err = repo.Stashes.Apply(0, opts) + if err.Error() != "Stop" { + t.Errorf("expecting error 'Stop', got %v", err) + } + + // Create second stash with ignored files + + os.MkdirAll(pathInRepo(repo, "tmp"), os.ModeDir|os.ModePerm) + err = ioutil.WriteFile(pathInRepo(repo, "tmp/ignored.txt"), []byte("Ignore me\n"), 0644) + checkFatal(t, err) + + stash2, err := repo.Stashes.Save(sig, "Second stash", StashIncludeIgnored) + checkFatal(t, err) + + if fileExistsInRepo(repo, "tmp/ignored.txt") { + t.Errorf("tmp/ignored.txt should not exist anymore in the work dir") + } + + // Stash foreach + + expected := []stash{ + {0, "On master: Second stash", stash2.String()}, + {1, "On master: First stash", stash1.String()}, + } + checkStashes(t, repo, expected) + + // Stash pop + + opts, _ = DefaultStashApplyOptions() + err = repo.Stashes.Pop(1, opts) + checkFatal(t, err) + + b, err = ioutil.ReadFile(pathInRepo(repo, "README")) + checkFatal(t, err) + if string(b) != "Update README goes to stash\n" { + t.Errorf("README changes aren't here") + } + + expected = []stash{ + {0, "On master: Second stash", stash2.String()}, + } + checkStashes(t, repo, expected) + + // Stash drop + + err = repo.Stashes.Drop(0) + checkFatal(t, err) + + expected = []stash{} + checkStashes(t, repo, expected) +} + +type stash struct { + index int + msg string + id string +} + +func checkStashes(t *testing.T, repo *Repository, expected []stash) { + var actual []stash + + repo.Stashes.Foreach(func(index int, msg string, id *Oid) error { + stash := stash{index, msg, id.String()} + if len(expected) > len(actual) { + if s := expected[len(actual)]; s.id == "" { + stash.id = "" // don't check id + } + } + actual = append(actual, stash) + return nil + }) + + if len(expected) > 0 && !reflect.DeepEqual(expected, actual) { + // The failure happens at wherever we were called, not here + _, file, line, ok := runtime.Caller(1) + if !ok { + t.Fatalf("Unable to get caller") + } + t.Errorf("%v:%v: expecting %#v\ngot %#v", path.Base(file), line, expected, actual) + } +} + +func prepareStashRepo(t *testing.T, repo *Repository) { + seedTestRepo(t, repo) + + err := ioutil.WriteFile(pathInRepo(repo, ".gitignore"), []byte("tmp\n"), 0644) + checkFatal(t, err) + + sig := &Signature{ + Name: "Rand Om Hacker", + Email: "random@hacker.com", + When: time.Now(), + } + + idx, err := repo.Index() + checkFatal(t, err) + err = idx.AddByPath(".gitignore") + checkFatal(t, err) + treeID, err := idx.WriteTree() + checkFatal(t, err) + err = idx.Write() + checkFatal(t, err) + + currentBranch, err := repo.Head() + checkFatal(t, err) + currentTip, err := repo.LookupCommit(currentBranch.Target()) + checkFatal(t, err) + + message := "Add .gitignore\n" + tree, err := repo.LookupTree(treeID) + checkFatal(t, err) + _, err = repo.CreateCommit("HEAD", sig, sig, message, tree, currentTip) + checkFatal(t, err) + + err = ioutil.WriteFile(pathInRepo(repo, "README"), []byte("Update README goes to stash\n"), 0644) + checkFatal(t, err) + + err = ioutil.WriteFile(pathInRepo(repo, "untracked.txt"), []byte("Hello, World\n"), 0644) + checkFatal(t, err) +} + +func fileExistsInRepo(repo *Repository, name string) bool { + if _, err := os.Stat(pathInRepo(repo, name)); err != nil { + return false + } + return true +} diff --git a/wrapper.c b/wrapper.c index 2b1a180..a01867c 100644 --- a/wrapper.c +++ b/wrapper.c @@ -141,4 +141,12 @@ int _go_git_tag_foreach(git_repository *repo, void *payload) return git_tag_foreach(repo, (git_tag_foreach_cb)&gitTagForeachCb, payload); } +void _go_git_setup_stash_apply_progress_callbacks(git_stash_apply_options *opts) { + opts->progress_cb = (git_stash_apply_progress_cb)stashApplyProgressCb; +} + +int _go_git_stash_foreach(git_repository *repo, void *payload) { + return git_stash_foreach(repo, (git_stash_cb)&stashForeachCb, payload); +} + /* EOF */ -- 2.45.2 From b8283e72771866cae56c9036ace2ff80f125ff50 Mon Sep 17 00:00:00 2001 From: FUJII Ryota Date: Mon, 28 Sep 2015 20:38:04 +0900 Subject: [PATCH 046/128] Add EntryByPath method to Index --- index.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/index.go b/index.go index f4c0c1e..06495b9 100644 --- a/index.go +++ b/index.go @@ -331,6 +331,17 @@ func (v *Index) EntryByIndex(index uint) (*IndexEntry, error) { return newIndexEntryFromC(centry), nil } +func (v *Index) EntryByPath(path string, stage int) (*IndexEntry, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + centry := C.git_index_get_bypath(v.ptr, C.CString(path), C.int(stage)) + if centry == nil { + return nil, MakeGitError(C.GIT_ENOTFOUND) + } + return newIndexEntryFromC(centry), nil +} + func (v *Index) HasConflicts() bool { return C.git_index_has_conflicts(v.ptr) != 0 } -- 2.45.2 From b1d97c1ebd3c66e7311c11c02575d66f41bab953 Mon Sep 17 00:00:00 2001 From: jbranchaud Date: Tue, 6 Oct 2015 15:12:49 -0500 Subject: [PATCH 047/128] Fix typo in README: manaager -> manager --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a5e6100..315032f 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Go bindings for [libgit2](http://libgit2.github.com/). The `master` branch follo Installing ---------- -This project wraps the functionality provided by libgit2. If you're using a stable version, install it to your system via your system's package manaager and then install git2go as usual. +This project wraps the functionality provided by libgit2. If you're using a stable version, install it to your system via your system's package manager and then install git2go as usual. Otherwise (`next` which tracks an unstable version), we need to build libgit2 as well. In order to build it, you need `cmake`, `pkg-config` and a C compiler. You will also need the development packages for OpenSSL and LibSSH2 installed if you want libgit2 to support HTTPS and SSH respectively. -- 2.45.2 From 80cf533fe4e48ddfab3015d9570f2833951c1dea Mon Sep 17 00:00:00 2001 From: David Pierce Date: Sat, 26 Sep 2015 15:37:48 -0700 Subject: [PATCH 048/128] Config#LookupString uses git_buf to load value --- config.go | 8 ++++--- config_test.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 config_test.go diff --git a/config.go b/config.go index 9d25e35..c4c4028 100644 --- a/config.go +++ b/config.go @@ -115,18 +115,20 @@ func (c *Config) LookupInt64(name string) (int64, error) { } func (c *Config) LookupString(name string) (string, error) { - var ptr *C.char cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) + valBuf := C.git_buf{} + runtime.LockOSThread() defer runtime.UnlockOSThread() - if ret := C.git_config_get_string(&ptr, c.ptr, cname); ret < 0 { + if ret := C.git_config_get_string_buf(&valBuf, c.ptr, cname); ret < 0 { return "", MakeGitError(ret) } + defer C.git_buf_free(&valBuf) - return C.GoString(ptr), nil + return C.GoString(valBuf.ptr), nil } func (c *Config) LookupBool(name string) (bool, error) { diff --git a/config_test.go b/config_test.go new file mode 100644 index 0000000..e4a2c1f --- /dev/null +++ b/config_test.go @@ -0,0 +1,58 @@ +package git + +import ( + "os" + "testing" +) + +func setupConfig() (*Config, error) { + var ( + c *Config + err error + p string + ) + + p, err = ConfigFindGlobal() + if err != nil { + return nil, err + } + + c, err = OpenOndisk(nil, p) + if err != nil { + return nil, err + } + + c.SetString("foo.bar", "baz") + + return c, err +} + +func cleanupConfig() { + os.Remove(tempConfig) +} + +func TestConfigLookupString(t *testing.T) { + var ( + err error + val string + c *Config + ) + + c, err = setupConfig() + defer cleanupConfig() + if err != nil { + t.Errorf("Setup error: '%v'. Expected none\n", err) + t.FailNow() + } + defer c.Free() + + val, err = c.LookupString("foo.bar") + if err != nil { + t.Errorf("Got error: '%v', expected none\n", err) + t.FailNow() + } + + if val != "baz" { + t.Errorf("Got '%s', expected 'bar'\n", val) + } +} -- 2.45.2 From 81e0b16d9fbbfa916b34d0fa38967a14f8796f49 Mon Sep 17 00:00:00 2001 From: David Pierce Date: Sat, 26 Sep 2015 16:11:49 -0700 Subject: [PATCH 049/128] Tests config lookup methods --- config_test.go | 73 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/config_test.go b/config_test.go index e4a2c1f..8c2decc 100644 --- a/config_test.go +++ b/config_test.go @@ -5,24 +5,23 @@ import ( "testing" ) +var tempConfig = "./temp.gitconfig" + func setupConfig() (*Config, error) { var ( c *Config err error - p string ) - p, err = ConfigFindGlobal() - if err != nil { - return nil, err - } - - c, err = OpenOndisk(nil, p) + c, err = OpenOndisk(nil, tempConfig) if err != nil { return nil, err } c.SetString("foo.bar", "baz") + c.SetBool("foo.bool", true) + c.SetInt32("foo.int32", 32) + c.SetInt64("foo.int64", 64) return c, err } @@ -31,28 +30,66 @@ func cleanupConfig() { os.Remove(tempConfig) } -func TestConfigLookupString(t *testing.T) { +type TestRunner func(*Config, *testing.T) + +var tests = []TestRunner{ + // LookupString + func(c *Config, t *testing.T) { + val, err := c.LookupString("foo.bar") + if err != nil { + t.Errorf("Got LookupString error: '%v', expected none\n", err) + } + if val != "baz" { + t.Errorf("Got '%s' from LookupString, expected 'bar'\n", val) + } + }, + // LookupBool + func(c *Config, t *testing.T) { + val, err := c.LookupBool("foo.bool") + if err != nil { + t.Errorf("Got LookupBool error: '%v', expected none\n", err) + } + if !val { + t.Errorf("Got %b from LookupBool, expected 'false'\n", val) + } + }, + // LookupInt32 + func(c *Config, t *testing.T) { + val, err := c.LookupInt32("foo.int32") + if err != nil { + t.Errorf("Got LookupInt32 error: '%v', expected none\n", err) + } + if val != 32 { + t.Errorf("Got %v, expected 32\n", val) + } + }, + // LookupInt64 + func(c *Config, t *testing.T) { + val, err := c.LookupInt64("foo.int64") + if err != nil { + t.Errorf("Got LookupInt64 error: '%v', expected none\n", err) + } + if val != 64 { + t.Errorf("Got %v, expected 64\n", val) + } + }, +} + +func TestConfigLookups(t *testing.T) { var ( err error - val string c *Config ) c, err = setupConfig() defer cleanupConfig() + if err != nil { t.Errorf("Setup error: '%v'. Expected none\n", err) - t.FailNow() } defer c.Free() - val, err = c.LookupString("foo.bar") - if err != nil { - t.Errorf("Got error: '%v', expected none\n", err) - t.FailNow() - } - - if val != "baz" { - t.Errorf("Got '%s', expected 'bar'\n", val) + for _, test := range tests { + test(c, t) } } -- 2.45.2 From 22495763b73d1560c5c8b8182ee8b723adee1bcd Mon Sep 17 00:00:00 2001 From: Jose Alvarez Date: Tue, 13 Oct 2015 11:31:00 -0400 Subject: [PATCH 050/128] Expose AddGitIgnoreRules and ClearGitIgnoreRules funcs --- repository.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/repository.go b/repository.go index 62fde6d..12638e1 100644 --- a/repository.go +++ b/repository.go @@ -433,3 +433,24 @@ func (r *Repository) StateCleanup() error { } return nil } +func (r *Repository) AddGitIgnoreRules(rules string) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + crules := C.CString(rules) + defer C.free(unsafe.Pointer(crules)) + if ret := C.git_ignore_add_rule(r.ptr, crules); ret < 0 { + return MakeGitError(ret) + } + return nil +} + +func (r *Repository) ClearGitIgnoreRules() error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if ret := C.git_ignore_clear_internal_rules(r.ptr); ret < 0 { + return MakeGitError(ret) + } + return nil +} -- 2.45.2 From 56cc9e1b0eafb1275be42ca60cfaf78297df8d60 Mon Sep 17 00:00:00 2001 From: FUJII Ryota Date: Fri, 9 Oct 2015 16:28:08 +0900 Subject: [PATCH 051/128] Add support for ignore --- ignore.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 ignore.go diff --git a/ignore.go b/ignore.go new file mode 100644 index 0000000..6b12348 --- /dev/null +++ b/ignore.go @@ -0,0 +1,51 @@ +package git + +/* +#include +*/ +import "C" +import ( + "runtime" + "unsafe" +) + +func (v *Repository) AddIgnoreRule(rules string) error { + crules := C.CString(rules) + defer C.free(unsafe.Pointer(crules)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_ignore_add_rule(v.ptr, crules) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +func (v *Repository) ClearInternalIgnoreRules() error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_ignore_clear_internal_rules(v.ptr) + if ret < 0 { + return MakeGitError(ret) + } + return nil +} + +func (v *Repository) IsPathIgnored(path string) (bool, error) { + var ignored C.int + + cpath := C.CString(path) + defer C.free(unsafe.Pointer(cpath)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_ignore_path_is_ignored(&ignored, v.ptr, cpath) + if ret < 0 { + return false, MakeGitError(ret) + } + return ignored == 1, nil +} -- 2.45.2 From 367cd8eb9b84538933774befa76c099298c32c81 Mon Sep 17 00:00:00 2001 From: Calin Seciu Date: Mon, 26 Oct 2015 16:20:18 +0200 Subject: [PATCH 052/128] Update libgit2 to 821131f The API changes are: - `*Remote.Connect` ```go // from: func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks) error // to: func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks, headers []string) error ``` - `*Remote.ConnectFetch` - `headers` was added as above - `*Remote.ConnectPush` - `headers` was added as above --- config.go | 21 +++++++++++++++++++++ git.go | 22 ++++++++++++++++++++-- index.go | 18 ++++++++++++++++++ merge.go | 7 +++++++ remote.go | 40 ++++++++++++++++++++++++++++++++++------ remote_test.go | 8 ++++---- vendor/libgit2 | 2 +- 7 files changed, 105 insertions(+), 13 deletions(-) diff --git a/config.go b/config.go index 9d25e35..73365c8 100644 --- a/config.go +++ b/config.go @@ -12,6 +12,9 @@ import ( type ConfigLevel int const ( + // System-wide on Windows, for compatibility with portable git + ConfigLevelProgramdata ConfigLevel = C.GIT_CONFIG_LEVEL_PROGRAMDATA + // System-wide configuration file; /etc/gitconfig on Linux systems ConfigLevelSystem ConfigLevel = C.GIT_CONFIG_LEVEL_SYSTEM @@ -410,3 +413,21 @@ func ConfigFindXDG() (string, error) { return C.GoString(buf.ptr), nil } + +// ConfigFindProgramdata locate the path to the configuration file in ProgramData. +// +// Look for the file in %PROGRAMDATA%\Git\config used by portable git. +func ConfigFindProgramdata() (string, error) { + var buf C.git_buf + defer C.git_buf_free(&buf) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_config_find_programdata(&buf) + if ret < 0 { + return "", MakeGitError(ret) + } + + return C.GoString(buf.ptr), nil +} diff --git a/git.go b/git.go index 34d58e6..2b2a909 100644 --- a/git.go +++ b/git.go @@ -52,6 +52,7 @@ const ( // No error ErrOk ErrorCode = C.GIT_OK + // Generic error ErrGeneric ErrorCode = C.GIT_ERROR // Requested object could not be found @@ -62,10 +63,12 @@ const ( ErrAmbigious ErrorCode = C.GIT_EAMBIGUOUS // Output buffer too short to hold data ErrBuffs ErrorCode = C.GIT_EBUFS + // GIT_EUSER is a special error that is never generated by libgit2 // code. You can return it from a callback (e.g to stop an iteration) // to know that it was generated by the callback and not by libgit2. ErrUser ErrorCode = C.GIT_EUSER + // Operation not allowed on bare repository ErrBareRepo ErrorCode = C.GIT_EBAREREPO // HEAD refers to branch with no commits @@ -82,12 +85,27 @@ const ( ErrLocked ErrorCode = C.GIT_ELOCKED // Reference value does not match expected ErrModified ErrorCode = C.GIT_EMODIFIED + // Authentication failed + ErrAuth ErrorCode = C.GIT_EAUTH + // Server certificate is invalid + ErrCertificate ErrorCode = C.GIT_ECERTIFICATE + // Patch/merge has already been applied + ErrApplied ErrorCode = C.GIT_EAPPLIED + // The requested peel operation is not possible + ErrPeel ErrorCode = C.GIT_EPEEL + // Unexpected EOF + ErrEOF ErrorCode = C.GIT_EEOF + // Uncommitted changes in index prevented operation + ErrUncommitted ErrorCode = C.GIT_EUNCOMMITTED + // The operation is not valid for a directory + ErrDirectory ErrorCode = C.GIT_EDIRECTORY + // A merge conflict exists and cannot continue + ErrMergeConflict ErrorCode = C.GIT_EMERGECONFLICT + // Internal only ErrPassthrough ErrorCode = C.GIT_PASSTHROUGH // Signals end of iteration with iterator ErrIterOver ErrorCode = C.GIT_ITEROVER - // Authentication failed - ErrAuth ErrorCode = C.GIT_EAUTH ) var ( diff --git a/index.go b/index.go index 06495b9..1875e32 100644 --- a/index.go +++ b/index.go @@ -26,6 +26,24 @@ const ( IndexAddCheckPathspec IndexAddOpts = C.GIT_INDEX_ADD_CHECK_PATHSPEC ) +type IndexStageOpts int + +const ( + // IndexStageAny matches any index stage. + // + // Some index APIs take a stage to match; pass this value to match + // any entry matching the path regardless of stage. + IndexStageAny IndexStageOpts = C.GIT_INDEX_STAGE_ANY + // IndexStageNormal is a normal staged file in the index. + IndexStageNormal IndexStageOpts = C.GIT_INDEX_STAGE_NORMAL + // IndexStageAncestor is the ancestor side of a conflict. + IndexStageAncestor IndexStageOpts = C.GIT_INDEX_STAGE_ANCESTOR + // IndexStageOurs is the "ours" side of a conflict. + IndexStageOurs IndexStageOpts = C.GIT_INDEX_STAGE_OURS + // IndexStageTheirs is the "theirs" side of a conflict. + IndexStageTheirs IndexStageOpts = C.GIT_INDEX_STAGE_THEIRS +) + type Index struct { ptr *C.git_index } diff --git a/merge.go b/merge.go index a52e8f8..53d5a72 100644 --- a/merge.go +++ b/merge.go @@ -81,7 +81,14 @@ func (r *Repository) AnnotatedCommitFromRef(ref *Reference) (*AnnotatedCommit, e type MergeTreeFlag int const ( + // Detect renames that occur between the common ancestor and the "ours" + // side or the common ancestor and the "theirs" side. This will enable + // the ability to merge between a modified and renamed file. MergeTreeFindRenames MergeTreeFlag = C.GIT_MERGE_TREE_FIND_RENAMES + // If a conflict occurs, exit immediately instead of attempting to + // continue resolving conflicts. The merge operation will fail with + // GIT_EMERGECONFLICT and no index will be returned. + MergeTreeFailOnConflict MergeTreeFlag = C.GIT_MERGE_TREE_FAIL_ON_CONFLICT ) type MergeOptions struct { diff --git a/remote.go b/remote.go index b3aba54..a216513 100644 --- a/remote.go +++ b/remote.go @@ -112,6 +112,9 @@ type FetchOptions struct { // // The default is to auto-follow tags. DownloadTags DownloadTags + + // Headers are extra headers for the fetch operation. + Headers []string } type Remote struct { @@ -157,6 +160,9 @@ type PushOptions struct { RemoteCallbacks RemoteCallbacks PbParallelism uint + + // Headers are extra headers for the push operation. + Headers []string } type RemoteHead struct { @@ -594,6 +600,10 @@ func populateFetchOptions(options *C.git_fetch_options, opts *FetchOptions) { options.prune = C.git_fetch_prune_t(opts.Prune) options.update_fetchhead = cbool(opts.UpdateFetchhead) options.download_tags = C.git_remote_autotag_option_t(opts.DownloadTags) + + options.custom_headers = C.git_strarray{} + options.custom_headers.count = C.size_t(len(opts.Headers)) + options.custom_headers.strings = makeCStringsFromStrings(opts.Headers) } func populatePushOptions(options *C.git_push_options, opts *PushOptions) { @@ -604,6 +614,10 @@ func populatePushOptions(options *C.git_push_options, opts *PushOptions) { options.pb_parallelism = C.uint(opts.PbParallelism) + options.custom_headers = C.git_strarray{} + options.custom_headers.count = C.size_t(len(opts.Headers)) + options.custom_headers.strings = makeCStringsFromStrings(opts.Headers) + populateRemoteCallbacks(&options.callbacks, &opts.RemoteCallbacks) } @@ -626,6 +640,7 @@ func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error var coptions C.git_fetch_options populateFetchOptions(&coptions, opts) defer untrackCalbacksPayload(&coptions.callbacks) + defer freeStrarray(&coptions.custom_headers) runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -637,22 +652,34 @@ func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error return nil } -func (o *Remote) ConnectFetch(callbacks *RemoteCallbacks) error { - return o.Connect(ConnectDirectionFetch, callbacks) +func (o *Remote) ConnectFetch(callbacks *RemoteCallbacks, headers []string) error { + return o.Connect(ConnectDirectionFetch, callbacks, headers) } -func (o *Remote) ConnectPush(callbacks *RemoteCallbacks) error { - return o.Connect(ConnectDirectionPush, callbacks) +func (o *Remote) ConnectPush(callbacks *RemoteCallbacks, headers []string) error { + return o.Connect(ConnectDirectionPush, callbacks, headers) } -func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks) error { +// Connect opens a connection to a remote. +// +// The transport is selected based on the URL. The direction argument +// is due to a limitation of the git protocol (over TCP or SSH) which +// starts up a specific binary which can only do the one or the other. +// +// 'headers' are extra HTTP headers to use in this connection. +func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks, headers []string) error { var ccallbacks C.git_remote_callbacks populateRemoteCallbacks(&ccallbacks, callbacks) + cheaders := C.git_strarray{} + cheaders.count = C.size_t(len(headers)) + cheaders.strings = makeCStringsFromStrings(headers) + defer freeStrarray(&cheaders) + runtime.LockOSThread() defer runtime.UnlockOSThread() - if ret := C.git_remote_connect(o.ptr, C.git_direction(direction), &ccallbacks); ret != 0 { + if ret := C.git_remote_connect(o.ptr, C.git_direction(direction), &ccallbacks, &cheaders); ret != 0 { return MakeGitError(ret) } return nil @@ -713,6 +740,7 @@ func (o *Remote) Push(refspecs []string, opts *PushOptions) error { var coptions C.git_push_options populatePushOptions(&coptions, opts) defer untrackCalbacksPayload(&coptions.callbacks) + defer freeStrarray(&coptions.custom_headers) runtime.LockOSThread() defer runtime.UnlockOSThread() diff --git a/remote_test.go b/remote_test.go index dac3dbe..978b803 100644 --- a/remote_test.go +++ b/remote_test.go @@ -58,7 +58,7 @@ func TestRemoteConnect(t *testing.T) { remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) - err = remote.ConnectFetch(nil) + err = remote.ConnectFetch(nil, nil) checkFatal(t, err) } @@ -69,7 +69,7 @@ func TestRemoteLs(t *testing.T) { remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) - err = remote.ConnectFetch(nil) + err = remote.ConnectFetch(nil, nil) checkFatal(t, err) heads, err := remote.Ls() @@ -87,7 +87,7 @@ func TestRemoteLsFiltering(t *testing.T) { remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) - err = remote.ConnectFetch(nil) + err = remote.ConnectFetch(nil, nil) checkFatal(t, err) heads, err := remote.Ls("master") @@ -166,7 +166,7 @@ func TestRemotePrune(t *testing.T) { rr, err := repo.Remotes.Lookup("origin") checkFatal(t, err) - err = rr.ConnectFetch(nil) + err = rr.ConnectFetch(nil, nil) checkFatal(t, err) err = rr.Prune(nil) diff --git a/vendor/libgit2 b/vendor/libgit2 index ed38e26..821131f 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit ed38e26db5435b519d8b796e4b6c2c660fe982b5 +Subproject commit 821131fdaee74526d84aaf1c6ceddc2139c551df -- 2.45.2 From 3b5633de219eec2908723a8e557758cc055d84d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 26 Oct 2015 21:22:22 +0100 Subject: [PATCH 053/128] Mention that MergeAnalysis is a bitmask --- merge.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/merge.go b/merge.go index bab53e0..272bf6a 100644 --- a/merge.go +++ b/merge.go @@ -178,6 +178,9 @@ const ( MergePreferenceFastForwardOnly MergePreference = C.GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY ) +// MergeAnalysis returns the possible actions which could be taken by +// a 'git-merge' command. There may be multiple answers, so the first +// return value is a bitmask of MergeAnalysis values. func (r *Repository) MergeAnalysis(theirHeads []*AnnotatedCommit) (MergeAnalysis, MergePreference, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() -- 2.45.2 From ae107d5f56a0cb2b9703ad3733143e0f6acc01de Mon Sep 17 00:00:00 2001 From: FUJII Ryota Date: Tue, 27 Oct 2015 15:20:50 +0900 Subject: [PATCH 054/128] Fix memory leaks in NewIndex() and OpenIndex() --- index.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.go b/index.go index 0174dc1..1eb5e9d 100644 --- a/index.go +++ b/index.go @@ -97,7 +97,7 @@ func NewIndex() (*Index, error) { return nil, MakeGitError(err) } - return &Index{ptr: ptr}, nil + return newIndexFromC(ptr), nil } // OpenIndex creates a new index at the given path. If the file does @@ -115,7 +115,7 @@ func OpenIndex(path string) (*Index, error) { return nil, MakeGitError(err) } - return &Index{ptr: ptr}, nil + return newIndexFromC(ptr), nil } // Path returns the index' path on disk or an empty string if it -- 2.45.2 From f18ea412dc8d3a43930bb7f9143dcc2e01d60158 Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Mon, 2 Nov 2015 15:47:59 -0500 Subject: [PATCH 055/128] config_test: properly detect failed config writes This patch fixes the setup stage of the config tests to notice when the writes fail (eg $PWD is a read-only filesystem) and to correctly skip the entire test function as a result. --- config_test.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/config_test.go b/config_test.go index 8c2decc..fea8d8a 100644 --- a/config_test.go +++ b/config_test.go @@ -18,10 +18,22 @@ func setupConfig() (*Config, error) { return nil, err } - c.SetString("foo.bar", "baz") - c.SetBool("foo.bool", true) - c.SetInt32("foo.int32", 32) - c.SetInt64("foo.int64", 64) + err = c.SetString("foo.bar", "baz") + if err != nil { + return nil, err + } + err = c.SetBool("foo.bool", true) + if err != nil { + return nil, err + } + err = c.SetInt32("foo.int32", 32) + if err != nil { + return nil, err + } + err = c.SetInt64("foo.int64", 64) + if err != nil { + return nil, err + } return c, err } @@ -86,6 +98,7 @@ func TestConfigLookups(t *testing.T) { if err != nil { t.Errorf("Setup error: '%v'. Expected none\n", err) + return } defer c.Free() -- 2.45.2 From 714cd56c715d22e9759413783c9c4be0018193e0 Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Mon, 2 Nov 2015 15:51:03 -0500 Subject: [PATCH 056/128] odb: remove debug fmt.Printlns These appear to be left over debug statements, and they also look like they were intended to be fmt.Printf calls anyway. --- odb.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/odb.go b/odb.go index be0870e..b15851f 100644 --- a/odb.go +++ b/odb.go @@ -11,7 +11,6 @@ import ( "reflect" "runtime" "unsafe" - "fmt" ) type Odb struct { @@ -107,9 +106,7 @@ func odbForEachCb(id *C.git_oid, handle unsafe.Pointer) int { } err := data.callback(newOidFromC(id)) - fmt.Println("err %v", err) if err != nil { - fmt.Println("returning EUSER") data.err = err return C.GIT_EUSER } @@ -130,7 +127,6 @@ func (v *Odb) ForEach(callback OdbForEachCallback) error { defer pointerHandles.Untrack(handle) ret := C._go_git_odb_foreach(v.ptr, handle) - fmt.Println("ret %v", ret); if ret == C.GIT_EUSER { return data.err } else if ret < 0 { -- 2.45.2 From 92d736d12c5263826662ad26a152b7f027aa1efa Mon Sep 17 00:00:00 2001 From: Jose Alvarez Date: Thu, 12 Nov 2015 21:15:24 -0500 Subject: [PATCH 057/128] Fix Fetch/Push memory allocation problems The Fetch/Push operations didn't allocate the git_*_options structure and this causes a memory problem in the libgit2 code. Following the example of Clone operation, the Fetch/Push functions allocates the options structure before calling the C. --- remote.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/remote.go b/remote.go index b2fb96f..330f202 100644 --- a/remote.go +++ b/remote.go @@ -623,14 +623,16 @@ func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error crefspecs.strings = makeCStringsFromStrings(refspecs) defer freeStrarray(&crefspecs) - var coptions C.git_fetch_options - populateFetchOptions(&coptions, opts); + coptions := (*C.git_fetch_options)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_fetch_options{})))) + defer C.free(unsafe.Pointer(coptions)) + + populateFetchOptions(coptions, opts) defer untrackCalbacksPayload(&coptions.callbacks) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_fetch(o.ptr, &crefspecs, &coptions, cmsg) + ret := C.git_remote_fetch(o.ptr, &crefspecs, coptions, cmsg) if ret < 0 { return MakeGitError(ret) } @@ -710,14 +712,16 @@ func (o *Remote) Push(refspecs []string, opts *PushOptions) error { crefspecs.strings = makeCStringsFromStrings(refspecs) defer freeStrarray(&crefspecs) - var coptions C.git_push_options - populatePushOptions(&coptions, opts) + coptions := (*C.git_push_options)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_push_options{})))) + defer C.free(unsafe.Pointer(coptions)) + + populatePushOptions(coptions, opts) defer untrackCalbacksPayload(&coptions.callbacks) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_remote_push(o.ptr, &crefspecs, &coptions) + ret := C.git_remote_push(o.ptr, &crefspecs, coptions) if ret < 0 { return MakeGitError(ret) } -- 2.45.2 From 1cdf1d70a2c08b1b87611be11cb448075ea45f2b Mon Sep 17 00:00:00 2001 From: FUJII Ryota Date: Wed, 16 Dec 2015 16:37:50 +0900 Subject: [PATCH 058/128] Fix a memory leak in Index.EntryByPath() --- index.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/index.go b/index.go index 1875e32..8417b65 100644 --- a/index.go +++ b/index.go @@ -350,10 +350,13 @@ func (v *Index) EntryByIndex(index uint) (*IndexEntry, error) { } func (v *Index) EntryByPath(path string, stage int) (*IndexEntry, error) { + cpath := C.CString(path) + defer C.free(unsafe.Pointer(cpath)) + runtime.LockOSThread() defer runtime.UnlockOSThread() - centry := C.git_index_get_bypath(v.ptr, C.CString(path), C.int(stage)) + centry := C.git_index_get_bypath(v.ptr, cpath, C.int(stage)) if centry == nil { return nil, MakeGitError(C.GIT_ENOTFOUND) } -- 2.45.2 From 20ab28bfeacbfd0c0b1a83e471de8de5610f9de8 Mon Sep 17 00:00:00 2001 From: FUJII Ryota Date: Mon, 21 Dec 2015 18:19:03 +0900 Subject: [PATCH 059/128] Add Index.Find() and Index.FindPrefix() --- index.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/index.go b/index.go index 8417b65..16e63a1 100644 --- a/index.go +++ b/index.go @@ -363,6 +363,36 @@ func (v *Index) EntryByPath(path string, stage int) (*IndexEntry, error) { return newIndexEntryFromC(centry), nil } +func (v *Index) Find(path string) (uint, error) { + cpath := C.CString(path) + defer C.free(unsafe.Pointer(cpath)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + var pos C.size_t + ret := C.git_index_find(&pos, v.ptr, cpath) + if ret < 0 { + return uint(0), MakeGitError(ret) + } + return uint(pos), nil +} + +func (v *Index) FindPrefix(prefix string) (uint, error) { + cprefix := C.CString(prefix) + defer C.free(unsafe.Pointer(cprefix)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + var pos C.size_t + ret := C.git_index_find_prefix(&pos, v.ptr, cprefix) + if ret < 0 { + return uint(0), MakeGitError(ret) + } + return uint(pos), nil +} + func (v *Index) HasConflicts() bool { return C.git_index_has_conflicts(v.ptr) != 0 } -- 2.45.2 From 51d3ead30d58d523cef5aaea1666c8e28a94dc42 Mon Sep 17 00:00:00 2001 From: Michael Gehring Date: Mon, 4 Jan 2016 15:02:21 +0100 Subject: [PATCH 060/128] Don't drop CreateBlobFromChunks hintPath argument --- blob.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blob.go b/blob.go index b1fc78a..382bb9e 100644 --- a/blob.go +++ b/blob.go @@ -84,7 +84,7 @@ func (repo *Repository) CreateBlobFromChunks(hintPath string, callback BlobChunk var chintPath *C.char = nil if len(hintPath) > 0 { - C.CString(hintPath) + chintPath = C.CString(hintPath) defer C.free(unsafe.Pointer(chintPath)) } oid := C.git_oid{} -- 2.45.2 From 42b11d403d91e25754fe60c63c26371fbb7a89a9 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 7 Jan 2016 18:37:46 -0800 Subject: [PATCH 061/128] handles, merge, odb: changes for Go 1.6 pointer passing rules See http://tip.golang.org/cmd/cgo/#hdr-Passing_pointers . --- handles.go | 6 +++--- merge.go | 34 +++++++++++++++++++--------------- odb.go | 5 +++-- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/handles.go b/handles.go index a062231..a855717 100644 --- a/handles.go +++ b/handles.go @@ -52,12 +52,12 @@ func (v *HandleList) Track(pointer interface{}) unsafe.Pointer { v.Unlock() - return unsafe.Pointer(&slot) + return unsafe.Pointer(uintptr(slot)) } // Untrack stops tracking the pointer given by the handle func (v *HandleList) Untrack(handle unsafe.Pointer) { - slot := *(*int)(handle) + slot := int(uintptr(handle)) v.Lock() @@ -69,7 +69,7 @@ func (v *HandleList) Untrack(handle unsafe.Pointer) { // Get retrieves the pointer from the given handle func (v *HandleList) Get(handle unsafe.Pointer) interface{} { - slot := *(*int)(handle) + slot := int(uintptr(handle)) v.RLock() diff --git a/merge.go b/merge.go index 272bf6a..535a5e7 100644 --- a/merge.go +++ b/merge.go @@ -85,8 +85,8 @@ const ( ) type MergeOptions struct { - Version uint - TreeFlags MergeTreeFlag + Version uint + TreeFlags MergeTreeFlag RenameThreshold uint TargetLimit uint @@ -98,7 +98,7 @@ type MergeOptions struct { func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions { return MergeOptions{ Version: uint(opts.version), - TreeFlags: MergeTreeFlag(opts.tree_flags), + TreeFlags: MergeTreeFlag(opts.tree_flags), RenameThreshold: uint(opts.rename_threshold), TargetLimit: uint(opts.target_limit), FileFavor: MergeFileFavor(opts.file_favor), @@ -262,10 +262,10 @@ func (r *Repository) MergeBases(one, two *Oid) ([]*Oid, error) { } oids := make([]*Oid, coids.count) - hdr := reflect.SliceHeader { + hdr := reflect.SliceHeader{ Data: uintptr(unsafe.Pointer(coids.ids)), - Len: int(coids.count), - Cap: int(coids.count), + Len: int(coids.count), + Cap: int(coids.count), } goSlice := *(*[]C.git_oid)(unsafe.Pointer(&hdr)) @@ -321,17 +321,21 @@ type MergeFileInput struct { } // populate a C struct with merge file input, make sure to use freeMergeFileInput to clean up allocs -func populateCMergeFileInput(c *C.git_merge_file_input, input MergeFileInput) { +func populateCMergeFileInput(c *C.git_merge_file_input, input MergeFileInput) *C.char { c.path = C.CString(input.Path) + var toFree *C.char if input.Contents != nil { - c.ptr = (*C.char)(unsafe.Pointer(&input.Contents[0])) + toFree = C.CString(string(input.Contents)) + c.ptr = toFree c.size = C.size_t(len(input.Contents)) } c.mode = C.uint(input.Mode) + return toFree } -func freeCMergeFileInput(c *C.git_merge_file_input) { +func freeCMergeFileInput(c *C.git_merge_file_input, toFree *C.char) { C.free(unsafe.Pointer(c.path)) + C.free(unsafe.Pointer(toFree)) } type MergeFileFlags int @@ -382,12 +386,12 @@ func MergeFile(ancestor MergeFileInput, ours MergeFileInput, theirs MergeFileInp var cours C.git_merge_file_input var ctheirs C.git_merge_file_input - populateCMergeFileInput(&cancestor, ancestor) - defer freeCMergeFileInput(&cancestor) - populateCMergeFileInput(&cours, ours) - defer freeCMergeFileInput(&cours) - populateCMergeFileInput(&ctheirs, theirs) - defer freeCMergeFileInput(&ctheirs) + t := populateCMergeFileInput(&cancestor, ancestor) + defer freeCMergeFileInput(&cancestor, t) + t = populateCMergeFileInput(&cours, ours) + defer freeCMergeFileInput(&cours, t) + t = populateCMergeFileInput(&ctheirs, theirs) + defer freeCMergeFileInput(&ctheirs, t) var copts *C.git_merge_file_options if options != nil { diff --git a/odb.go b/odb.go index b15851f..8e8fba0 100644 --- a/odb.go +++ b/odb.go @@ -61,12 +61,13 @@ func (v *Odb) Exists(oid *Oid) bool { func (v *Odb) Write(data []byte, otype ObjectType) (oid *Oid, err error) { oid = new(Oid) - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + cstr := C.CString(string(data)) + defer C.free(unsafe.Pointer(cstr)) runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_odb_write(oid.toC(), v.ptr, unsafe.Pointer(hdr.Data), C.size_t(hdr.Len), C.git_otype(otype)) + ret := C.git_odb_write(oid.toC(), v.ptr, unsafe.Pointer(cstr), C.size_t(len(data)), C.git_otype(otype)) if ret < 0 { return nil, MakeGitError(ret) -- 2.45.2 From 1bc7cf60bd05958ad619c22373183afffb913a0c Mon Sep 17 00:00:00 2001 From: Michael Gehring Date: Mon, 4 Jan 2016 14:47:02 +0100 Subject: [PATCH 062/128] Add missing RepositoryOpenExtended arguments Fixes #277 --- repository.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/repository.go b/repository.go index d8e398b..398f91a 100644 --- a/repository.go +++ b/repository.go @@ -62,15 +62,29 @@ func OpenRepository(path string) (*Repository, error) { return newRepositoryFromC(ptr), nil } -func OpenRepositoryExtended(path string) (*Repository, error) { +type RepositoryOpenFlag int + +const ( + RepositoryOpenNoSearch RepositoryOpenFlag = C.GIT_REPOSITORY_OPEN_NO_SEARCH + RepositoryOpenCrossFs RepositoryOpenFlag = C.GIT_REPOSITORY_OPEN_CROSS_FS + RepositoryOpenBare RepositoryOpenFlag = C.GIT_REPOSITORY_OPEN_BARE +) + +func OpenRepositoryExtended(path string, flags RepositoryOpenFlag, ceiling string) (*Repository, error) { cpath := C.CString(path) defer C.free(unsafe.Pointer(cpath)) + var cceiling *C.char = nil + if len(ceiling) > 0 { + cceiling = C.CString(ceiling) + defer C.free(unsafe.Pointer(cceiling)) + } + runtime.LockOSThread() defer runtime.UnlockOSThread() var ptr *C.git_repository - ret := C.git_repository_open_ext(&ptr, cpath, 0, nil) + ret := C.git_repository_open_ext(&ptr, cpath, C.uint(flags), cceiling) if ret < 0 { return nil, MakeGitError(ret) } -- 2.45.2 From 773ac24a16da6261e0f31b9354a9c890b1002422 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Tue, 2 Feb 2016 19:02:15 +0100 Subject: [PATCH 063/128] Expose git_odb_read_header as Odb.ReadHeader. This function is much faster for discovering sizes for a given OID. --- odb.go | 15 +++++++++++++++ odb_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/odb.go b/odb.go index b15851f..d881f63 100644 --- a/odb.go +++ b/odb.go @@ -54,6 +54,21 @@ func (v *Odb) AddBackend(backend *OdbBackend, priority int) (err error) { return nil } +func (v *Odb) ReadHeader(oid *Oid) (uint64, ObjectType, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + var sz C.size_t + var cotype C.git_otype + + ret := C.git_odb_read_header(&sz, &cotype, v.ptr, oid.toC()) + if ret < 0 { + return 0, C.GIT_OBJ_BAD, MakeGitError(ret) + } + + return uint64(sz), ObjectType(cotype), nil +} + func (v *Odb) Exists(oid *Oid) bool { ret := C.git_odb_exists(v.ptr, oid.toC()) return ret != 0 diff --git a/odb_test.go b/odb_test.go index 0d765b9..dfd2ad0 100644 --- a/odb_test.go +++ b/odb_test.go @@ -6,6 +6,34 @@ import ( "testing" ) +func TestOdbReadHeader(t *testing.T) { + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + _, _ = seedTestRepo(t, repo) + odb, err := repo.Odb() + if err != nil { + t.Fatalf("Odb: %v", err) + } + data := []byte("hello") + id, err := odb.Write(data, ObjectBlob) + if err != nil { + t.Fatalf("odb.Write: %v", err) + } + + sz, typ, err := odb.ReadHeader(id) + if err != nil { + t.Fatalf("ReadHeader: %v", err) + } + + if sz != uint64(len(data)) { + t.Errorf("ReadHeader got size %d, want %d", sz, len(data)) + } + if typ != ObjectBlob { + t.Errorf("ReadHeader got object type %s", typ) + } +} + func TestOdbStream(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) -- 2.45.2 From b876e836fa76747b34832f42c354934854601f7d Mon Sep 17 00:00:00 2001 From: Orivej Desh Date: Sun, 7 Feb 2016 03:12:35 +0000 Subject: [PATCH 064/128] Add RevWalk.SimplifyFirstParent() --- walk.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/walk.go b/walk.go index c314f60..60e618d 100644 --- a/walk.go +++ b/walk.go @@ -194,6 +194,10 @@ func (v *RevWalk) Iterate(fun RevWalkIterator) (err error) { return nil } +func (v *RevWalk) SimplifyFirstParent() { + C.git_revwalk_simplify_first_parent(v.ptr) +} + func (v *RevWalk) Sorting(sm SortType) { C.git_revwalk_sorting(v.ptr, C.uint(sm)) } -- 2.45.2 From a1f25eafec55509d49dffb4c84f7c5b729e6a85e Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 16 Feb 2016 21:34:43 -0800 Subject: [PATCH 065/128] handles, merge: simplify code, don't copy file contents --- handles.go | 14 +++++------- merge.go | 65 ++++++++++++++++++++++++++---------------------------- wrapper.c | 23 +++++++++++++++++++ 3 files changed, 60 insertions(+), 42 deletions(-) diff --git a/handles.go b/handles.go index a855717..f5d30f0 100644 --- a/handles.go +++ b/handles.go @@ -10,15 +10,14 @@ type HandleList struct { sync.RWMutex // stores the Go pointers handles []interface{} - // Indicates which indices are in use, and keeps a pointer to slot int variable (the handle) - // in the Go world, so that the Go garbage collector does not free it. - set map[int]*int + // Indicates which indices are in use. + set map[int]bool } func NewHandleList() *HandleList { return &HandleList{ handles: make([]interface{}, 5), - set: make(map[int]*int), + set: make(map[int]bool), } } @@ -26,8 +25,7 @@ func NewHandleList() *HandleList { // list. You must only run this function while holding a write lock. func (v *HandleList) findUnusedSlot() int { for i := 1; i < len(v.handles); i++ { - _, isUsed := v.set[i] - if !isUsed { + if !v.set[i] { return i } } @@ -48,7 +46,7 @@ func (v *HandleList) Track(pointer interface{}) unsafe.Pointer { slot := v.findUnusedSlot() v.handles[slot] = pointer - v.set[slot] = &slot // Keep a pointer to slot in Go world, so it's not freed by GC. + v.set[slot] = true v.Unlock() @@ -73,7 +71,7 @@ func (v *HandleList) Get(handle unsafe.Pointer) interface{} { v.RLock() - if _, ok := v.set[slot]; !ok { + if !v.set[slot] { panic(fmt.Sprintf("invalid pointer handle: %p", handle)) } diff --git a/merge.go b/merge.go index 535a5e7..06f5676 100644 --- a/merge.go +++ b/merge.go @@ -6,6 +6,7 @@ package git 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 int _go_git_merge_file(git_merge_file_result*, char*, size_t, char*, unsigned int, char*, size_t, char*, unsigned int, char*, size_t, char*, unsigned int, git_merge_file_options*); */ import "C" @@ -85,8 +86,8 @@ const ( ) type MergeOptions struct { - Version uint - TreeFlags MergeTreeFlag + Version uint + TreeFlags MergeTreeFlag RenameThreshold uint TargetLimit uint @@ -98,7 +99,7 @@ type MergeOptions struct { func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions { return MergeOptions{ Version: uint(opts.version), - TreeFlags: MergeTreeFlag(opts.tree_flags), + TreeFlags: MergeTreeFlag(opts.tree_flags), RenameThreshold: uint(opts.rename_threshold), TargetLimit: uint(opts.target_limit), FileFavor: MergeFileFavor(opts.file_favor), @@ -262,10 +263,10 @@ func (r *Repository) MergeBases(one, two *Oid) ([]*Oid, error) { } oids := make([]*Oid, coids.count) - hdr := reflect.SliceHeader{ + hdr := reflect.SliceHeader { Data: uintptr(unsafe.Pointer(coids.ids)), - Len: int(coids.count), - Cap: int(coids.count), + Len: int(coids.count), + Cap: int(coids.count), } goSlice := *(*[]C.git_oid)(unsafe.Pointer(&hdr)) @@ -320,24 +321,6 @@ type MergeFileInput struct { Contents []byte } -// populate a C struct with merge file input, make sure to use freeMergeFileInput to clean up allocs -func populateCMergeFileInput(c *C.git_merge_file_input, input MergeFileInput) *C.char { - c.path = C.CString(input.Path) - var toFree *C.char - if input.Contents != nil { - toFree = C.CString(string(input.Contents)) - c.ptr = toFree - c.size = C.size_t(len(input.Contents)) - } - c.mode = C.uint(input.Mode) - return toFree -} - -func freeCMergeFileInput(c *C.git_merge_file_input, toFree *C.char) { - C.free(unsafe.Pointer(c.path)) - C.free(unsafe.Pointer(toFree)) -} - type MergeFileFlags int const ( @@ -382,16 +365,26 @@ func freeCMergeFileOptions(c *C.git_merge_file_options) { func MergeFile(ancestor MergeFileInput, ours MergeFileInput, theirs MergeFileInput, options *MergeFileOptions) (*MergeFileResult, error) { - var cancestor C.git_merge_file_input - var cours C.git_merge_file_input - var ctheirs C.git_merge_file_input + ancestorPath := C.CString(ancestor.Path) + defer C.free(unsafe.Pointer(ancestorPath)) + var ancestorContents *byte + if len(ancestor.Contents) > 0 { + ancestorContents = &ancestor.Contents[0] + } - t := populateCMergeFileInput(&cancestor, ancestor) - defer freeCMergeFileInput(&cancestor, t) - t = populateCMergeFileInput(&cours, ours) - defer freeCMergeFileInput(&cours, t) - t = populateCMergeFileInput(&ctheirs, theirs) - defer freeCMergeFileInput(&ctheirs, t) + oursPath := C.CString(ours.Path) + defer C.free(unsafe.Pointer(oursPath)) + var oursContents *byte + if len(ours.Contents) > 0 { + oursContents = &ours.Contents[0] + } + + theirsPath := C.CString(theirs.Path) + defer C.free(unsafe.Pointer(theirsPath)) + var theirsContents *byte + if len(theirs.Contents) > 0 { + theirsContents = &theirs.Contents[0] + } var copts *C.git_merge_file_options if options != nil { @@ -408,7 +401,11 @@ func MergeFile(ancestor MergeFileInput, ours MergeFileInput, theirs MergeFileInp defer runtime.UnlockOSThread() var result C.git_merge_file_result - ecode := C.git_merge_file(&result, &cancestor, &cours, &ctheirs, copts) + ecode := C._go_git_merge_file(&result, + (*C.char)(unsafe.Pointer(ancestorContents)), C.size_t(len(ancestor.Contents)), ancestorPath, C.uint(ancestor.Mode), + (*C.char)(unsafe.Pointer(oursContents)), C.size_t(len(ours.Contents)), oursPath, C.uint(ours.Mode), + (*C.char)(unsafe.Pointer(theirsContents)), C.size_t(len(theirs.Contents)), theirsPath, C.uint(theirs.Mode), + copts) if ecode < 0 { return nil, MakeGitError(ecode) } diff --git a/wrapper.c b/wrapper.c index 2b1a180..a0688c0 100644 --- a/wrapper.c +++ b/wrapper.c @@ -141,4 +141,27 @@ int _go_git_tag_foreach(git_repository *repo, void *payload) return git_tag_foreach(repo, (git_tag_foreach_cb)&gitTagForeachCb, payload); } +int _go_git_merge_file(git_merge_file_result* out, char* ancestorContents, size_t ancestorLen, char* ancestorPath, unsigned int ancestorMode, char* oursContents, size_t oursLen, char* oursPath, unsigned int oursMode, char* theirsContents, size_t theirsLen, char* theirsPath, unsigned int theirsMode, git_merge_file_options* copts) { + git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT; + git_merge_file_input ours = GIT_MERGE_FILE_INPUT_INIT; + git_merge_file_input theirs = GIT_MERGE_FILE_INPUT_INIT; + + ancestor.ptr = ancestorContents; + ancestor.size = ancestorLen; + ancestor.path = ancestorPath; + ancestor.mode = ancestorMode; + + ours.ptr = oursContents; + ours.size = oursLen; + ours.path = oursPath; + ours.mode = oursMode; + + theirs.ptr = theirsContents; + theirs.size = theirsLen; + theirs.path = theirsPath; + theirs.mode = theirsMode; + + return git_merge_file(out, &ancestor, &ours, &theirs, copts); +} + /* EOF */ -- 2.45.2 From ddbf1baab1f6d066f2bda591c1e68340845392c8 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 16 Feb 2016 21:36:57 -0800 Subject: [PATCH 066/128] merge: remove whitespace change --- merge.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/merge.go b/merge.go index 06f5676..756c792 100644 --- a/merge.go +++ b/merge.go @@ -265,8 +265,8 @@ func (r *Repository) MergeBases(one, two *Oid) ([]*Oid, error) { oids := make([]*Oid, coids.count) hdr := reflect.SliceHeader { Data: uintptr(unsafe.Pointer(coids.ids)), - Len: int(coids.count), - Cap: int(coids.count), + Len: int(coids.count), + Cap: int(coids.count), } goSlice := *(*[]C.git_oid)(unsafe.Pointer(&hdr)) -- 2.45.2 From dc8b154f4f1b346fb6b8dee99fcfa6e4ca2d2d24 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 16 Feb 2016 21:40:05 -0800 Subject: [PATCH 067/128] odb: don't copy buffer --- odb.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/odb.go b/odb.go index dfad507..9c6baa3 100644 --- a/odb.go +++ b/odb.go @@ -76,13 +76,15 @@ func (v *Odb) Exists(oid *Oid) bool { func (v *Odb) Write(data []byte, otype ObjectType) (oid *Oid, err error) { oid = new(Oid) - cstr := C.CString(string(data)) - defer C.free(unsafe.Pointer(cstr)) + var cptr unsafe.Pointer + if len(data) > 0 { + cptr = unsafe.Pointer(&data[0]) + } runtime.LockOSThread() defer runtime.UnlockOSThread() - ret := C.git_odb_write(oid.toC(), v.ptr, unsafe.Pointer(cstr), C.size_t(len(data)), C.git_otype(otype)) + ret := C.git_odb_write(oid.toC(), v.ptr, cptr, C.size_t(len(data)), C.git_otype(otype)) if ret < 0 { return nil, MakeGitError(ret) -- 2.45.2 From aa59dccea724221f99ea57a8f803101b786809ef Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Tue, 16 Feb 2016 17:22:43 +0100 Subject: [PATCH 068/128] Upgrade to libgit2 to 0f9d15493d5d8ad4353dd7beed52c9567334f6e5 --- blame.go | 6 +++--- diff.go | 6 +++--- merge.go | 10 +++++----- vendor/libgit2 | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/blame.go b/blame.go index c24c934..b07d6bc 100644 --- a/blame.go +++ b/blame.go @@ -58,8 +58,8 @@ func (v *Repository) BlameFile(path string, opts *BlameOptions) (*Blame, error) 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), + min_line: C.size_t(opts.MinLine), + max_line: C.size_t(opts.MaxLine), } if opts.NewestCommit != nil { copts.newest_commit = *opts.NewestCommit.toC() @@ -100,7 +100,7 @@ func (blame *Blame) HunkByIndex(index int) (BlameHunk, error) { } func (blame *Blame) HunkByLine(lineno int) (BlameHunk, error) { - ptr := C.git_blame_get_hunk_byline(blame.ptr, C.uint32_t(lineno)) + ptr := C.git_blame_get_hunk_byline(blame.ptr, C.size_t(lineno)) if ptr == nil { return BlameHunk{}, ErrInvalid } diff --git a/diff.go b/diff.go index de56374..565fcee 100644 --- a/diff.go +++ b/diff.go @@ -550,7 +550,7 @@ func diffOptionsToC(opts *DiffOptions) (copts *C.git_diff_options, notifyData *d if opts.NotifyCallback != nil { C._go_git_setup_diff_notify_callbacks(copts) - copts.notify_payload = pointerHandles.Track(notifyData) + copts.payload = pointerHandles.Track(notifyData) } } return @@ -562,8 +562,8 @@ func freeDiffOptions(copts *C.git_diff_options) { freeStrarray(&cpathspec) C.free(unsafe.Pointer(copts.old_prefix)) C.free(unsafe.Pointer(copts.new_prefix)) - if copts.notify_payload != nil { - pointerHandles.Untrack(copts.notify_payload) + if copts.payload != nil { + pointerHandles.Untrack(copts.payload) } } } diff --git a/merge.go b/merge.go index 0b0a8f1..9ca50fd 100644 --- a/merge.go +++ b/merge.go @@ -84,11 +84,11 @@ const ( // Detect renames that occur between the common ancestor and the "ours" // side or the common ancestor and the "theirs" side. This will enable // the ability to merge between a modified and renamed file. - MergeTreeFindRenames MergeTreeFlag = C.GIT_MERGE_TREE_FIND_RENAMES + MergeTreeFindRenames MergeTreeFlag = C.GIT_MERGE_FIND_RENAMES // If a conflict occurs, exit immediately instead of attempting to // continue resolving conflicts. The merge operation will fail with // GIT_EMERGECONFLICT and no index will be returned. - MergeTreeFailOnConflict MergeTreeFlag = C.GIT_MERGE_TREE_FAIL_ON_CONFLICT + MergeTreeFailOnConflict MergeTreeFlag = C.GIT_MERGE_FAIL_ON_CONFLICT ) type MergeOptions struct { @@ -105,7 +105,7 @@ type MergeOptions struct { func mergeOptionsFromC(opts *C.git_merge_options) MergeOptions { return MergeOptions{ Version: uint(opts.version), - TreeFlags: MergeTreeFlag(opts.tree_flags), + TreeFlags: MergeTreeFlag(opts.flags), RenameThreshold: uint(opts.rename_threshold), TargetLimit: uint(opts.target_limit), FileFavor: MergeFileFavor(opts.file_favor), @@ -131,7 +131,7 @@ func (mo *MergeOptions) toC() *C.git_merge_options { } return &C.git_merge_options{ version: C.uint(mo.Version), - tree_flags: C.git_merge_tree_flag_t(mo.TreeFlags), + flags: C.git_merge_flag_t(mo.TreeFlags), rename_threshold: C.uint(mo.RenameThreshold), target_limit: C.uint(mo.TargetLimit), file_favor: C.git_merge_file_favor_t(mo.FileFavor), @@ -374,7 +374,7 @@ func populateCMergeFileOptions(c *C.git_merge_file_options, options MergeFileOpt c.our_label = C.CString(options.OurLabel) c.their_label = C.CString(options.TheirLabel) c.favor = C.git_merge_file_favor_t(options.Favor) - c.flags = C.uint(options.Flags) + c.flags = C.git_merge_file_flag_t(options.Flags) } func freeCMergeFileOptions(c *C.git_merge_file_options) { diff --git a/vendor/libgit2 b/vendor/libgit2 index 821131f..0f9d154 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 821131fdaee74526d84aaf1c6ceddc2139c551df +Subproject commit 0f9d15493d5d8ad4353dd7beed52c9567334f6e5 -- 2.45.2 From 090dc7ee3901d08d46b02d42b4f1fe248ecc3e6c Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 27 Jan 2016 18:11:12 +0100 Subject: [PATCH 069/128] Use Filemode in TreeBuilder.Insert, and add test coverage for some TreeBuilder methods. --- tree.go | 2 +- tree_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/tree.go b/tree.go index 8288176..eba9f3d 100644 --- a/tree.go +++ b/tree.go @@ -149,7 +149,7 @@ func (v *TreeBuilder) Free() { C.git_treebuilder_free(v.ptr) } -func (v *TreeBuilder) Insert(filename string, id *Oid, filemode int) error { +func (v *TreeBuilder) Insert(filename string, id *Oid, filemode Filemode) error { cfilename := C.CString(filename) defer C.free(unsafe.Pointer(cfilename)) diff --git a/tree_test.go b/tree_test.go index 4c6a4ed..fae395a 100644 --- a/tree_test.go +++ b/tree_test.go @@ -20,3 +20,44 @@ func TestTreeEntryById(t *testing.T) { t.Fatalf("entry id %v was not found", id) } } + +func TestTreeBuilderInsert(t *testing.T) { + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + subTree, err := repo.TreeBuilder() + if err != nil { + t.Fatalf("TreeBuilder: %v", err) + } + defer subTree.Free() + + odb, err := repo.Odb() + if err != nil { + t.Fatalf("repo.Odb: %v", err) + } + blobId, err := odb.Write([]byte("hello"), ObjectBlob) + if err != nil { + t.Fatalf("odb.Write: %v", err) + } + if err = subTree.Insert("subfile", blobId, FilemodeBlobExecutable); err != nil { + t.Fatalf("TreeBuilder.Insert: %v", err) + } + treeID, err := subTree.Write() + if err != nil { + t.Fatalf("TreeBuilder.Write: %v", err) + } + + tree, err := repo.LookupTree(treeID) + if err != nil { + t.Fatalf("LookupTree: %v", err) + } + + entry, err := tree.EntryByPath("subfile") + if err != nil { + t.Fatalf("tree.EntryByPath(%q): %v", "subfile", err) + } + + if !entry.Id.Equal(blobId) { + t.Fatalf("got oid %v, want %v", entry.Id, blobId) + } +} -- 2.45.2 From f1240e6565dfb70cfd22f4025c1453ca92ce0743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 18 Feb 2016 17:07:33 +0100 Subject: [PATCH 070/128] handles: use real pointers to keep track of handles With the change to 1.6 rules, we couldn't use the Go pointers, so we went with casting the list indices into pointers. The runtime does not like this, however. It will sometimes detect that we have a pointer with a very small value and consider it an invalid pointer, bringing down the application with it. Work around that by asking libc for the smallest amount of memory it'll give us so we have an actual allocated pointer to use. We then use this pointer value as the key in our map to find the Go object we're tracking. --- handles.go | 56 +++++++++++++++--------------------------------------- 1 file changed, 15 insertions(+), 41 deletions(-) diff --git a/handles.go b/handles.go index f5d30f0..d27d3c3 100644 --- a/handles.go +++ b/handles.go @@ -1,5 +1,9 @@ package git +/* +#include +*/ +import "C" import ( "fmt" "sync" @@ -9,75 +13,45 @@ import ( type HandleList struct { sync.RWMutex // stores the Go pointers - handles []interface{} - // Indicates which indices are in use. - set map[int]bool + handles map[unsafe.Pointer]interface{} } func NewHandleList() *HandleList { return &HandleList{ - handles: make([]interface{}, 5), - set: make(map[int]bool), + handles: make(map[unsafe.Pointer]interface{}), } } -// findUnusedSlot finds the smallest-index empty space in our -// list. You must only run this function while holding a write lock. -func (v *HandleList) findUnusedSlot() int { - for i := 1; i < len(v.handles); i++ { - if !v.set[i] { - return i - } - } - - // reaching here means we've run out of entries so append and - // return the new index, which is equal to the old length. - slot := len(v.handles) - v.handles = append(v.handles, nil) - - return slot -} - // Track adds the given pointer to the list of pointers to track and // returns a pointer value which can be passed to C as an opaque // pointer. func (v *HandleList) Track(pointer interface{}) unsafe.Pointer { + handle := C.malloc(1) + v.Lock() - - slot := v.findUnusedSlot() - v.handles[slot] = pointer - v.set[slot] = true - + v.handles[handle] = pointer v.Unlock() - return unsafe.Pointer(uintptr(slot)) + return handle } // Untrack stops tracking the pointer given by the handle func (v *HandleList) Untrack(handle unsafe.Pointer) { - slot := int(uintptr(handle)) - v.Lock() - - v.handles[slot] = nil - delete(v.set, slot) - + delete(v.handles, handle) + C.free(handle) v.Unlock() } // Get retrieves the pointer from the given handle func (v *HandleList) Get(handle unsafe.Pointer) interface{} { - slot := int(uintptr(handle)) - v.RLock() + defer v.RUnlock() - if !v.set[slot] { + ptr, ok := v.handles[handle] + if !ok { panic(fmt.Sprintf("invalid pointer handle: %p", handle)) } - ptr := v.handles[slot] - - v.RUnlock() - return ptr } -- 2.45.2 From 251d89e1d41037185df0ea89e9aab208efc40d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 19 Feb 2016 13:59:50 +0100 Subject: [PATCH 071/128] Update vendored libgit2 --- vendor/libgit2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/libgit2 b/vendor/libgit2 index 0f9d154..f596946 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 0f9d15493d5d8ad4353dd7beed52c9567334f6e5 +Subproject commit f596946f09f3c1e51239a24ff41e27f2c1ffa2b7 -- 2.45.2 From 5191254a66ac049611fb16e4bb5f273aeb2ac322 Mon Sep 17 00:00:00 2001 From: Calin Seciu Date: Sat, 20 Feb 2016 14:33:47 +0200 Subject: [PATCH 072/128] Fix problems based on PR comments https://github.com/libgit2/git2go/pull/257#discussion_r53432957 https://github.com/libgit2/git2go/pull/257#discussion_r53443418 --- stash.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/stash.go b/stash.go index 5142b82..7a4fc93 100644 --- a/stash.go +++ b/stash.go @@ -155,8 +155,6 @@ func DefaultStashApplyOptions() (StashApplyOptions, error) { if ecode < 0 { return StashApplyOptions{}, MakeGitError(ecode) } - defer freeStashApplyOptions(&optsC) - return StashApplyOptions{ Flags: StashApplyFlag(optsC.flags), CheckoutOptions: checkoutOptionsFromC(&optsC.checkout_options), @@ -172,10 +170,10 @@ func (opts *StashApplyOptions) toC() ( } optsC = &C.git_stash_apply_options{ - version: C.GIT_STASH_APPLY_OPTIONS_VERSION, - flags: C.git_stash_apply_flags(opts.Flags), - checkout_options: *opts.CheckoutOptions.toC(), + version: C.GIT_STASH_APPLY_OPTIONS_VERSION, + flags: C.git_stash_apply_flags(opts.Flags), } + populateCheckoutOpts(&optsC.checkout_options, &opts.CheckoutOptions) if opts.ProgressCallback != nil { C._go_git_setup_stash_apply_progress_callbacks(optsC) optsC.progress_payload = pointerHandles.Track(progressData) -- 2.45.2 From dc4409793db0205ce7c0783a10363d7d67aee674 Mon Sep 17 00:00:00 2001 From: Calin Seciu Date: Sat, 20 Feb 2016 14:34:29 +0200 Subject: [PATCH 073/128] Remove Untrack() from free() function https://github.com/libgit2/git2go/pull/257#discussion_r53443211 --- stash.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/stash.go b/stash.go index 7a4fc93..8d8f984 100644 --- a/stash.go +++ b/stash.go @@ -182,12 +182,16 @@ func (opts *StashApplyOptions) toC() ( return } +// should be called after every call to toC() as deferred. +func untrackStashApplyOptionsCallback(optsC *C.git_stash_apply_options) { + if optsC != nil && optsC.progress_payload != nil { + pointerHandles.Untrack(optsC.progress_payload) + } +} + func freeStashApplyOptions(optsC *C.git_stash_apply_options) { if optsC != nil { freeCheckoutOpts(&optsC.checkout_options) - if optsC.progress_payload != nil { - pointerHandles.Untrack(optsC.progress_payload) - } } } @@ -217,6 +221,7 @@ func freeStashApplyOptions(optsC *C.git_stash_apply_options) { // Error codes can be interogated with IsErrorCode(err, ErrNotFound). func (c *StashCollection) Apply(index int, opts StashApplyOptions) error { optsC, progressData := opts.toC() + defer untrackStashApplyOptionsCallback(optsC) defer freeStashApplyOptions(optsC) runtime.LockOSThread() @@ -316,6 +321,7 @@ func (c *StashCollection) Drop(index int) error { // state for the given index. func (c *StashCollection) Pop(index int, opts StashApplyOptions) error { optsC, progressData := opts.toC() + defer untrackStashApplyOptionsCallback(optsC) defer freeStashApplyOptions(optsC) runtime.LockOSThread() -- 2.45.2 From 71ff6ab0d5afb13c555da799ee69b055acd3954c Mon Sep 17 00:00:00 2001 From: Calin Seciu Date: Sat, 20 Feb 2016 14:58:48 +0200 Subject: [PATCH 074/128] Fix error after updating to latest changes --- stash.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stash.go b/stash.go index 8d8f984..809732e 100644 --- a/stash.go +++ b/stash.go @@ -62,7 +62,7 @@ func (c *StashCollection) Save( ret := C.git_stash_save( oid.toC(), c.repo.ptr, - stasherC, messageC, C.uint(flags)) + stasherC, messageC, C.uint32_t(flags)) if ret < 0 { return nil, MakeGitError(ret) -- 2.45.2 From e095c85fd06b5820d2f0098ff3e66b266553dbd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 7 Mar 2016 11:16:07 +0100 Subject: [PATCH 075/128] Update to 785d8c --- vendor/libgit2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/libgit2 b/vendor/libgit2 index f596946..785d8c4 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit f596946f09f3c1e51239a24ff41e27f2c1ffa2b7 +Subproject commit 785d8c48ea8725691da3c50e7dae8751523d4c30 -- 2.45.2 From d3bd8903f8310baf5395d63aec1e3d517578156d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 7 Mar 2016 11:33:44 +0100 Subject: [PATCH 076/128] Update libgit2 version to install on Travis to 24 --- script/install-libgit2.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/script/install-libgit2.sh b/script/install-libgit2.sh index 9bf6b37..f2468c8 100755 --- a/script/install-libgit2.sh +++ b/script/install-libgit2.sh @@ -13,9 +13,10 @@ if [ "x$TRAVIS_BRANCH" = "xnext" ]; then fi cd "${HOME}" -wget -O libgit2-0.23.1.tar.gz https://github.com/libgit2/libgit2/archive/v0.23.1.tar.gz -tar -xzvf libgit2-0.23.1.tar.gz -cd libgit2-0.23.1 && mkdir build && cd build +LG2VER="0.24.0" +wget -O libgit2-${LG2VER}.tar.gz https://github.com/libgit2/libgit2/archive/v${LG2VER}.tar.gz +tar -xzvf libgit2-${LG2VER}.tar.gz +cd libgit2-${LG2VER} && mkdir build && cd build cmake -DTHREADSAFE=ON -DBUILD_CLAR=OFF -DCMAKE_BUILD_TYPE="RelWithDebInfo" .. && make && sudo make install sudo ldconfig cd "${TRAVIS_BUILD_DIR}" -- 2.45.2 From 975228d55c7c4875374bf79e022afb8fa725d660 Mon Sep 17 00:00:00 2001 From: Hiroshi Ioka Date: Sun, 6 Mar 2016 12:18:32 +0900 Subject: [PATCH 077/128] add DiffTreeToIndex This is equivalent to `git diff --cached Date: Tue, 29 Mar 2016 20:37:22 +0200 Subject: [PATCH 078/128] Add "Conflicted" git.Delta and git.Status. --- diff.go | 1 + status.go | 1 + 2 files changed, 2 insertions(+) diff --git a/diff.go b/diff.go index 565fcee..38505d2 100644 --- a/diff.go +++ b/diff.go @@ -34,6 +34,7 @@ const ( DeltaIgnored Delta = C.GIT_DELTA_IGNORED DeltaUntracked Delta = C.GIT_DELTA_UNTRACKED DeltaTypeChange Delta = C.GIT_DELTA_TYPECHANGE + DeltaConflicted Delta = C.GIT_DELTA_CONFLICTED ) type DiffLineType int diff --git a/status.go b/status.go index 068a474..e68e6e9 100644 --- a/status.go +++ b/status.go @@ -25,6 +25,7 @@ const ( StatusWtTypeChange Status = C.GIT_STATUS_WT_TYPECHANGE StatusWtRenamed Status = C.GIT_STATUS_WT_RENAMED StatusIgnored Status = C.GIT_STATUS_IGNORED + StatusConflicted Status = C.GIT_STATUS_CONFLICTED ) type StatusEntry struct { -- 2.45.2 From 094bb3767b9168b81e5977ee0358c389e91f3920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20R=C3=B8dtang?= Date: Tue, 29 Mar 2016 20:41:46 +0200 Subject: [PATCH 079/128] Add unimplemented diff delta/flag values. --- diff.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/diff.go b/diff.go index 38505d2..5b96fac 100644 --- a/diff.go +++ b/diff.go @@ -20,6 +20,7 @@ const ( DiffFlagBinary DiffFlag = C.GIT_DIFF_FLAG_BINARY DiffFlagNotBinary DiffFlag = C.GIT_DIFF_FLAG_NOT_BINARY DiffFlagValidOid DiffFlag = C.GIT_DIFF_FLAG_VALID_ID + DiffFlagExists DiffFlag = C.GIT_DIFF_FLAG_EXISTS ) type Delta int @@ -34,6 +35,7 @@ const ( DeltaIgnored Delta = C.GIT_DELTA_IGNORED DeltaUntracked Delta = C.GIT_DELTA_UNTRACKED DeltaTypeChange Delta = C.GIT_DELTA_TYPECHANGE + DeltaUnreadable Delta = C.GIT_DELTA_UNREADABLE DeltaConflicted Delta = C.GIT_DELTA_CONFLICTED ) @@ -373,6 +375,7 @@ const ( DiffIgnoreFilemode DiffOptionsFlag = C.GIT_DIFF_IGNORE_FILEMODE DiffIgnoreSubmodules DiffOptionsFlag = C.GIT_DIFF_IGNORE_SUBMODULES DiffIgnoreCase DiffOptionsFlag = C.GIT_DIFF_IGNORE_CASE + DiffIncludeCaseChange DiffOptionsFlag = C.GIT_DIFF_INCLUDE_CASECHANGE DiffDisablePathspecMatch DiffOptionsFlag = C.GIT_DIFF_DISABLE_PATHSPEC_MATCH DiffSkipBinaryCheck DiffOptionsFlag = C.GIT_DIFF_SKIP_BINARY_CHECK -- 2.45.2 From b99dbb1361938e74312ec8a432730a8bb6ee801f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20R=C3=B8dtang?= Date: Tue, 29 Mar 2016 20:42:19 +0200 Subject: [PATCH 080/128] Add Repository.IsHeadUnborn --- repository.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/repository.go b/repository.go index 2e6b81d..caf7bbe 100644 --- a/repository.go +++ b/repository.go @@ -268,6 +268,17 @@ func (v *Repository) IsHeadDetached() (bool, error) { return ret != 0, nil } +func (v *Repository) IsHeadUnborn() (bool, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_repository_head_unborn(v.ptr) + if ret < 0 { + return false, MakeGitError(ret) + } + return ret != 0, nil +} + func (v *Repository) Walk() (*RevWalk, error) { var walkPtr *C.git_revwalk -- 2.45.2 From 726331dfde5cddabb8ded9967b581efcea77b765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20R=C3=B8dtang?= Date: Tue, 29 Mar 2016 20:42:30 +0200 Subject: [PATCH 081/128] Add Repository.IsEmpty --- repository.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/repository.go b/repository.go index caf7bbe..6ed6066 100644 --- a/repository.go +++ b/repository.go @@ -279,6 +279,18 @@ func (v *Repository) IsHeadUnborn() (bool, error) { return ret != 0, nil } +func (v *Repository) IsEmpty() (bool, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_repository_is_empty(v.ptr) + if ret < 0 { + return false, MakeGitError(ret) + } + + return ret != 0, nil +} + func (v *Repository) Walk() (*RevWalk, error) { var walkPtr *C.git_revwalk -- 2.45.2 From 2be7d7987b40fad0ee064e2feb5a132fd9ed3c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20R=C3=B8dtang?= Date: Tue, 29 Mar 2016 20:42:41 +0200 Subject: [PATCH 082/128] Add Repository.IsShallow --- repository.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/repository.go b/repository.go index 6ed6066..f0a2c74 100644 --- a/repository.go +++ b/repository.go @@ -291,6 +291,17 @@ func (v *Repository) IsEmpty() (bool, error) { return ret != 0, nil } +func (v *Repository) IsShallow() (bool, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_repository_is_shallow(v.ptr) + if ret < 0 { + return false, MakeGitError(ret) + } + return ret != 0, nil +} + func (v *Repository) Walk() (*RevWalk, error) { var walkPtr *C.git_revwalk -- 2.45.2 From 9163ca7d5072114975bed794226dd90d37d856b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Thu, 21 Apr 2016 16:34:51 +0200 Subject: [PATCH 083/128] Update to 1dc4491 --- blob.go | 76 +++++++++++++++++++++++++++++++++++++++----------- remote.go | 55 ++++++++++++++++++++++++++++++++---- remote_test.go | 8 +++--- vendor/libgit2 | 2 +- wrapper.c | 28 ++++++++++--------- 5 files changed, 128 insertions(+), 41 deletions(-) diff --git a/blob.go b/blob.go index 1a86e60..2daebe9 100644 --- a/blob.go +++ b/blob.go @@ -4,15 +4,13 @@ package git #include #include -extern int _go_git_blob_create_fromchunks(git_oid *id, - git_repository *repo, - const char *hintpath, - void *payload); - +int _go_git_writestream_write(git_writestream *stream, const char *buffer, size_t len); +void _go_git_writestream_free(git_writestream *stream); */ import "C" import ( "io" + "reflect" "runtime" "unsafe" ) @@ -78,27 +76,71 @@ func blobChunkCb(buffer *C.char, maxLen C.size_t, handle unsafe.Pointer) int { return len(goBuf) } -func (repo *Repository) CreateBlobFromChunks(hintPath string, callback BlobChunkCallback) (*Oid, error) { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - +func (repo *Repository) CreateFromStream(hintPath string) (*BlobWriteStream, error) { var chintPath *C.char = nil + var stream *C.git_writestream + if len(hintPath) > 0 { chintPath = C.CString(hintPath) defer C.free(unsafe.Pointer(chintPath)) } - oid := C.git_oid{} - payload := &BlobCallbackData{Callback: callback} - handle := pointerHandles.Track(payload) - defer pointerHandles.Untrack(handle) + runtime.LockOSThread() + defer runtime.UnlockOSThread() - ecode := C._go_git_blob_create_fromchunks(&oid, repo.ptr, chintPath, handle) - if payload.Error != nil { - return nil, payload.Error - } + ecode := C.git_blob_create_fromstream(&stream, repo.ptr, chintPath) if ecode < 0 { return nil, MakeGitError(ecode) } + + return newBlobWriteStreamFromC(stream), nil +} + +type BlobWriteStream struct { + ptr *C.git_writestream +} + +func newBlobWriteStreamFromC(ptr *C.git_writestream) *BlobWriteStream { + stream := &BlobWriteStream{ + ptr: ptr, + } + + runtime.SetFinalizer(stream, (*BlobWriteStream).Free) + return stream +} + +// Implement io.Writer +func (stream *BlobWriteStream) Write(p []byte) (int, error) { + header := (*reflect.SliceHeader)(unsafe.Pointer(&p)) + ptr := (*C.char)(unsafe.Pointer(header.Data)) + size := C.size_t(header.Len) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ecode := C._go_git_writestream_write(stream.ptr, ptr, size) + if ecode < 0 { + return 0, MakeGitError(ecode) + } + + return len(p), nil +} + +func (stream *BlobWriteStream) Free() { + runtime.SetFinalizer(stream, nil) + C._go_git_writestream_free(stream.ptr) +} + +func (stream *BlobWriteStream) Commit() (*Oid, error) { + oid := C.git_oid{} + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ecode := C.git_blob_create_fromstream_commit(&oid, stream.ptr) + if ecode < 0 { + return nil, MakeGitError(ecode) + } + return newOidFromC(&oid), nil } diff --git a/remote.go b/remote.go index 8a57280..bacdfa3 100644 --- a/remote.go +++ b/remote.go @@ -117,6 +117,30 @@ type FetchOptions struct { Headers []string } +type ProxyType uint + +const ( + // Do not attempt to connect through a proxy + // + // If built against lbicurl, it itself may attempt to connect + // to a proxy if the environment variables specify it. + ProxyTypeNone ProxyType = C.GIT_PROXY_NONE + + // Try to auto-detect the proxy from the git configuration. + ProxyTypeAuto ProxyType = C.GIT_PROXY_AUTO + + // Connect via the URL given in the options + ProxyTypeSpecified ProxyType = C.GIT_PROXY_SPECIFIED +) + +type ProxyOptions struct { + // The type of proxy to use (or none) + Type ProxyType + + // The proxy's URL + Url string +} + type Remote struct { ptr *C.git_remote callbacks RemoteCallbacks @@ -320,6 +344,20 @@ func pushUpdateReferenceCallback(refname, status *C.char, data unsafe.Pointer) i return int(callbacks.PushUpdateReferenceCallback(C.GoString(refname), C.GoString(status))) } +func populateProxyOptions(ptr *C.git_proxy_options, opts *ProxyOptions) { + C.git_proxy_init_options(ptr, C.GIT_PROXY_OPTIONS_VERSION) + if opts == nil { + return + } + + ptr._type = C.git_proxy_t(opts.Type) + ptr.url = C.CString(opts.Url) +} + +func freeProxyOptions(ptr *C.git_proxy_options) { + C.free(unsafe.Pointer(ptr.url)) +} + func RemoteIsValidName(name string) bool { cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) @@ -654,12 +692,12 @@ func (o *Remote) Fetch(refspecs []string, opts *FetchOptions, msg string) error return nil } -func (o *Remote) ConnectFetch(callbacks *RemoteCallbacks, headers []string) error { - return o.Connect(ConnectDirectionFetch, callbacks, headers) +func (o *Remote) ConnectFetch(callbacks *RemoteCallbacks, proxyOpts *ProxyOptions, headers []string) error { + return o.Connect(ConnectDirectionFetch, callbacks, proxyOpts, headers) } -func (o *Remote) ConnectPush(callbacks *RemoteCallbacks, headers []string) error { - return o.Connect(ConnectDirectionPush, callbacks, headers) +func (o *Remote) ConnectPush(callbacks *RemoteCallbacks, proxyOpts *ProxyOptions, headers []string) error { + return o.Connect(ConnectDirectionPush, callbacks, proxyOpts, headers) } // Connect opens a connection to a remote. @@ -669,19 +707,24 @@ func (o *Remote) ConnectPush(callbacks *RemoteCallbacks, headers []string) error // starts up a specific binary which can only do the one or the other. // // 'headers' are extra HTTP headers to use in this connection. -func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks, headers []string) error { +func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks, proxyOpts *ProxyOptions, headers []string) error { var ccallbacks C.git_remote_callbacks populateRemoteCallbacks(&ccallbacks, callbacks) + var cproxy C.git_proxy_options + populateProxyOptions(&cproxy, proxyOpts) + defer freeProxyOptions(&cproxy) + cheaders := C.git_strarray{} cheaders.count = C.size_t(len(headers)) cheaders.strings = makeCStringsFromStrings(headers) defer freeStrarray(&cheaders) + runtime.LockOSThread() defer runtime.UnlockOSThread() - if ret := C.git_remote_connect(o.ptr, C.git_direction(direction), &ccallbacks, &cheaders); ret != 0 { + if ret := C.git_remote_connect(o.ptr, C.git_direction(direction), &ccallbacks, &cproxy, &cheaders); ret != 0 { return MakeGitError(ret) } return nil diff --git a/remote_test.go b/remote_test.go index 978b803..539b4b1 100644 --- a/remote_test.go +++ b/remote_test.go @@ -58,7 +58,7 @@ func TestRemoteConnect(t *testing.T) { remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) - err = remote.ConnectFetch(nil, nil) + err = remote.ConnectFetch(nil, nil, nil) checkFatal(t, err) } @@ -69,7 +69,7 @@ func TestRemoteLs(t *testing.T) { remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) - err = remote.ConnectFetch(nil, nil) + err = remote.ConnectFetch(nil, nil, nil) checkFatal(t, err) heads, err := remote.Ls() @@ -87,7 +87,7 @@ func TestRemoteLsFiltering(t *testing.T) { remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository") checkFatal(t, err) - err = remote.ConnectFetch(nil, nil) + err = remote.ConnectFetch(nil, nil, nil) checkFatal(t, err) heads, err := remote.Ls("master") @@ -166,7 +166,7 @@ func TestRemotePrune(t *testing.T) { rr, err := repo.Remotes.Lookup("origin") checkFatal(t, err) - err = rr.ConnectFetch(nil, nil) + err = rr.ConnectFetch(nil, nil, nil) checkFatal(t, err) err = rr.Prune(nil) diff --git a/vendor/libgit2 b/vendor/libgit2 index 785d8c4..1dc4491 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 785d8c48ea8725691da3c50e7dae8751523d4c30 +Subproject commit 1dc449105b329ea4f8ea9982bc2da869d231c04a diff --git a/wrapper.c b/wrapper.c index e39665a..7407611 100644 --- a/wrapper.c +++ b/wrapper.c @@ -108,19 +108,6 @@ void _go_git_setup_callbacks(git_remote_callbacks *callbacks) { callbacks->push_update_reference = (push_update_reference_cb) pushUpdateReferenceCallback; } -int _go_blob_chunk_cb(char *buffer, size_t maxLen, void *payload) -{ - return blobChunkCb(buffer, maxLen, payload); -} - -int _go_git_blob_create_fromchunks(git_oid *id, - git_repository *repo, - const char *hintpath, - void *payload) -{ - return git_blob_create_fromchunks(id, repo, hintpath, _go_blob_chunk_cb, payload); -} - int _go_git_index_add_all(git_index *index, const git_strarray *pathspec, unsigned int flags, void *callback) { git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb) &indexMatchedPathCallback : NULL; return git_index_add_all(index, pathspec, flags, cb, callback); @@ -172,4 +159,19 @@ int _go_git_stash_foreach(git_repository *repo, void *payload) { return git_stash_foreach(repo, (git_stash_cb)&stashForeachCb, payload); } +int _go_git_writestream_write(git_writestream *stream, const char *buffer, size_t len) +{ + return stream->write(stream, buffer, len); +} + +int _go_git_writestream_close(git_writestream *stream) +{ + return stream->close(stream); +} + +void _go_git_writestream_free(git_writestream *stream) +{ + stream->free(stream); +} + /* EOF */ -- 2.45.2 From 8eaae73f85dd3df78df80d2dac066eb0866444ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 27 Apr 2016 14:53:21 +0200 Subject: [PATCH 084/128] Error out if we detect an incompatible libgit2 version The master version supports only v0.24 so let's enforce that via the compiler. --- git.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/git.go b/git.go index 45b01d5..ed891e6 100644 --- a/git.go +++ b/git.go @@ -4,6 +4,11 @@ package git #include #include #cgo pkg-config: libgit2 + +#if LIBGIT2_VER_MAJOR != 0 || LIBGIT2_VER_MINOR != 24 +# error "Invalid libgit2 version; this git2go supports libgit2 v0.24" +#endif + */ import "C" import ( -- 2.45.2 From 278dc9ac4fe40b37c68ebc3013467255ab1f832f Mon Sep 17 00:00:00 2001 From: Mirko Nosenzo Date: Sun, 29 May 2016 13:33:53 +0200 Subject: [PATCH 085/128] Checkout Callbacks - Added CheckoutNotifyType mapping git_checkout_notify_t - Added CheckoutOpts.NotifyFlags of type CheckoutNotifyType - Added CheckoutNotifyCallback mapping git_checkout_notify_cb - Added CheckoutProgressCallback mapping git_checkout_progress_cb --- checkout.go | 81 +++++++++++++++++++++++++++++++++++++++++++++++------ wrapper.c | 6 ++++ 2 files changed, 79 insertions(+), 8 deletions(-) diff --git a/checkout.go b/checkout.go index a2e312b..5a61b54 100644 --- a/checkout.go +++ b/checkout.go @@ -2,6 +2,8 @@ package git /* #include + +extern void _go_git_populate_checkout_cb(git_checkout_options *opts); */ import "C" import ( @@ -10,9 +12,18 @@ import ( "unsafe" ) +type CheckoutNotifyType uint type CheckoutStrategy uint const ( + CheckoutNotifyNone CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_NONE + CheckoutNotifyConflict CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_CONFLICT + CheckoutNotifyDirty CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_DIRTY + CheckoutNotifyUpdated CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_UPDATED + CheckoutNotifyUntracked CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_UNTRACKED + CheckoutNotifyIgnored CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_IGNORED + CheckoutNotifyAll CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_ALL + CheckoutNone CheckoutStrategy = C.GIT_CHECKOUT_NONE // Dry run, no actual updates CheckoutSafe CheckoutStrategy = C.GIT_CHECKOUT_SAFE // Allow safe updates that cannot overwrite uncommitted data CheckoutForce CheckoutStrategy = C.GIT_CHECKOUT_FORCE // Allow all updates to force working directory to look like index @@ -37,15 +48,21 @@ const ( CheckoutUpdateSubmodulesIfChanged CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED // Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) ) +type CheckoutNotifyCallback func(why CheckoutNotifyType, path string, baseline, target, workdir DiffFile) ErrorCode +type CheckoutProgressCallback func(path string, completed, total uint) ErrorCode + 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 - Paths []string - Baseline *Tree + 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 + NotifyFlags CheckoutNotifyType // Default will be none + NotifyCallback CheckoutNotifyCallback + ProgressCallback CheckoutProgressCallback + TargetDirectory string // Alternative checkout path to workdir + Paths []string + Baseline *Tree } func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOpts { @@ -55,6 +72,9 @@ func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOpts { opts.DirMode = os.FileMode(c.dir_mode) opts.FileMode = os.FileMode(c.file_mode) opts.FileOpenFlags = int(c.file_open_flags) + opts.NotifyFlags = CheckoutNotifyType(c.notify_flags) + opts.NotifyCallback = pointerHandles.Get(c.notify_payload).(CheckoutOpts).NotifyCallback + opts.ProgressCallback = pointerHandles.Get(c.progress_payload).(CheckoutOpts).ProgressCallback if c.target_directory != nil { opts.TargetDirectory = C.GoString(c.target_directory) } @@ -70,6 +90,35 @@ func (opts *CheckoutOpts) toC() *C.git_checkout_options { return &c } +//export checkoutNotifyCallback +func checkoutNotifyCallback(why C.git_checkout_notify_t, cpath *C.char, cbaseline, ctarget, cworkdir, data unsafe.Pointer) int { + path := C.GoString(cpath) + var baseline, target, workdir DiffFile + if cbaseline != nil { + baseline = diffFileFromC((*C.git_diff_file)(cbaseline)) + } + if ctarget != nil { + target = diffFileFromC((*C.git_diff_file)(ctarget)) + } + if cworkdir != nil { + workdir = diffFileFromC((*C.git_diff_file)(cworkdir)) + } + opts := pointerHandles.Get(data).(CheckoutOpts) + if opts.NotifyCallback == nil { + return 0 + } + return int(opts.NotifyCallback(CheckoutNotifyType(why), path, baseline, target, workdir)) +} + +//export checkoutProgressCallback +func checkoutProgressCallback(path *C.char, completed_steps, total_steps C.size_t, data unsafe.Pointer) int { + opts := pointerHandles.Get(data).(CheckoutOpts) + if opts.ProgressCallback == nil { + return 0 + } + return int(opts.ProgressCallback(C.GoString(path), uint(completed_steps), uint(total_steps))) +} + // Convert the CheckoutOpts struct to the corresponding // C-struct. Returns a pointer to ptr, or nil if opts is nil, in order // to help with what to pass. @@ -83,6 +132,16 @@ 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()) + ptr.notify_flags = C.uint(opts.NotifyFlags) + if opts.NotifyCallback != nil || opts.ProgressCallback != nil { + C._go_git_populate_checkout_cb(ptr) + } + if opts.NotifyCallback != nil { + ptr.notify_payload = pointerHandles.Track(*opts) + } + if opts.ProgressCallback != nil { + ptr.progress_payload = pointerHandles.Track(*opts) + } if opts.TargetDirectory != "" { ptr.target_directory = C.CString(opts.TargetDirectory) } @@ -106,6 +165,12 @@ func freeCheckoutOpts(ptr *C.git_checkout_options) { if ptr.paths.count > 0 { freeStrarray(&ptr.paths) } + if ptr.notify_payload != nil { + pointerHandles.Untrack(ptr.notify_payload) + } + if ptr.progress_payload != nil { + pointerHandles.Untrack(ptr.progress_payload) + } } // Updates files in the index and the working tree to match the content of diff --git a/wrapper.c b/wrapper.c index a0688c0..f4b4ecc 100644 --- a/wrapper.c +++ b/wrapper.c @@ -10,6 +10,12 @@ void _go_git_populate_remote_cb(git_clone_options *opts) opts->remote_cb = (git_remote_create_cb)remoteCreateCallback; } +void _go_git_populate_checkout_cb(git_checkout_options *opts) +{ + opts->notify_cb = (git_checkout_notify_cb)checkoutNotifyCallback; + opts->progress_cb = (git_checkout_progress_cb)checkoutProgressCallback; +} + int _go_git_visit_submodule(git_repository *repo, void *fct) { return git_submodule_foreach(repo, (gogit_submodule_cbk)&SubmoduleVisitor, fct); -- 2.45.2 From 8919236801226cf64fc42ccdaf420e00d590317a Mon Sep 17 00:00:00 2001 From: Mirko Nosenzo Date: Sun, 29 May 2016 14:49:00 +0200 Subject: [PATCH 086/128] ResetDefaultToCommit maps git_reset_default Added support for default reset behavior --- reset.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/reset.go b/reset.go index 9da7625..031f5bd 100644 --- a/reset.go +++ b/reset.go @@ -24,3 +24,19 @@ func (r *Repository) ResetToCommit(commit *Commit, resetType ResetType, opts *Ch } return nil } + +func (r *Repository) ResetDefaultToCommit(commit *Commit, pathspecs []string) error { + cpathspecs := C.git_strarray{} + cpathspecs.count = C.size_t(len(pathspecs)) + cpathspecs.strings = makeCStringsFromStrings(pathspecs) + defer freeStrarray(&cpathspecs) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ret := C.git_reset_default(r.ptr, commit.ptr, &cpathspecs) + + if ret < 0 { + return MakeGitError(ret) + } + return nil +} -- 2.45.2 From 8b855ce7650de8aabb9d23c4cbfef9467c4d4f77 Mon Sep 17 00:00:00 2001 From: Mirko Nosenzo Date: Sun, 29 May 2016 14:46:55 +0200 Subject: [PATCH 087/128] Tag Remove Added support for removal of a Tag --- repository.go | 2 +- tag.go | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/repository.go b/repository.go index 77e9f9c..efc506e 100644 --- a/repository.go +++ b/repository.go @@ -28,7 +28,7 @@ type Repository struct { // read, write and delete notes from this repository. Notes NoteCollection // Tags represents the collection of tags and can be used to create, - // list and iterate tags in this repository. + // list, iterate and remove tags in this repository. Tags TagsCollection } diff --git a/tag.go b/tag.go index 8957430..81d7258 100644 --- a/tag.go +++ b/tag.go @@ -83,6 +83,21 @@ func (c *TagsCollection) Create( return oid, nil } +func (c *TagsCollection) Remove(name string) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + ret := C.git_tag_delete(c.repo.ptr, cname) + if ret < 0 { + return MakeGitError(ret) + } + + return nil +} + // CreateLightweight creates a new lightweight tag pointing to a commit // and returns the id of the target object. // -- 2.45.2 From 393098522c3f01b1cd765581c4d4e6d43f3a02d0 Mon Sep 17 00:00:00 2001 From: Mirko Nosenzo Date: Sun, 29 May 2016 14:44:29 +0200 Subject: [PATCH 088/128] Remote Refinements - Fixed credentialsCallback return value for missing callback - Added Remote Rename - Added Remote Disconnect --- remote.go | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/remote.go b/remote.go index 8a57280..d063c17 100644 --- a/remote.go +++ b/remote.go @@ -215,7 +215,7 @@ func completionCallback(completion_type C.git_remote_completion_type, data unsaf func credentialsCallback(_cred **C.git_cred, _url *C.char, _username_from_url *C.char, allowed_types uint, data unsafe.Pointer) int { callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks) if callbacks.CredentialsCallback == nil { - return 0 + return C.GIT_PASSTHROUGH } url := C.GoString(_url) username_from_url := C.GoString(_username_from_url) @@ -454,6 +454,25 @@ func (o *Remote) PushUrl() string { return C.GoString(C.git_remote_pushurl(o.ptr)) } +func (c *RemoteCollection) Rename(remote, newname string) error { + cproblems := C.git_strarray{} + + cnewname := C.CString(newname) + defer C.free(unsafe.Pointer(cnewname)) + cremote := C.CString(remote) + defer C.free(unsafe.Pointer(cremote)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_remote_rename(&cproblems, c.repo.ptr, cremote, cnewname) + if ret < 0 { + return MakeGitError(ret) + } + C.git_strarray_free(&cproblems) + return nil +} + func (c *RemoteCollection) SetUrl(remote, url string) error { curl := C.CString(url) defer C.free(unsafe.Pointer(curl)) @@ -687,6 +706,13 @@ func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks, return nil } +func (o *Remote) Disconnect() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + C.git_remote_disconnect(o.ptr) +} + func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) { var refs **C.git_remote_head -- 2.45.2 From 298f2e2111905388e3d10cd0db0221a22dc7a62f Mon Sep 17 00:00:00 2001 From: Mirko Nosenzo Date: Sun, 29 May 2016 13:13:58 +0200 Subject: [PATCH 089/128] BranchAll maps GIT_BRANCH_ALL Added support to All Branch Iteration and Lookup --- branch.go | 1 + 1 file changed, 1 insertion(+) diff --git a/branch.go b/branch.go index df72dba..a869054 100644 --- a/branch.go +++ b/branch.go @@ -13,6 +13,7 @@ import ( type BranchType uint const ( + BranchAll BranchType = C.GIT_BRANCH_ALL BranchLocal BranchType = C.GIT_BRANCH_LOCAL BranchRemote BranchType = C.GIT_BRANCH_REMOTE ) -- 2.45.2 From 82f86f2f13213cfadfafc07a033c269095386dbc Mon Sep 17 00:00:00 2001 From: Mirko Nosenzo Date: Sun, 29 May 2016 14:37:37 +0200 Subject: [PATCH 090/128] StatusConflicted maps GIT_STATUS_CONFLICTED Added support for file in conflicted status --- status.go | 1 + 1 file changed, 1 insertion(+) diff --git a/status.go b/status.go index 068a474..e68e6e9 100644 --- a/status.go +++ b/status.go @@ -25,6 +25,7 @@ const ( StatusWtTypeChange Status = C.GIT_STATUS_WT_TYPECHANGE StatusWtRenamed Status = C.GIT_STATUS_WT_RENAMED StatusIgnored Status = C.GIT_STATUS_IGNORED + StatusConflicted Status = C.GIT_STATUS_CONFLICTED ) type StatusEntry struct { -- 2.45.2 From db6f44c71ce60777f5ad6c839f626370abf58799 Mon Sep 17 00:00:00 2001 From: Mirko Nosenzo Date: Sun, 29 May 2016 15:14:16 +0200 Subject: [PATCH 091/128] Checkout Callbacks Payload Check Checking payloads before using them --- checkout.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/checkout.go b/checkout.go index 5a61b54..ba411ff 100644 --- a/checkout.go +++ b/checkout.go @@ -73,8 +73,12 @@ func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOpts { opts.FileMode = os.FileMode(c.file_mode) opts.FileOpenFlags = int(c.file_open_flags) opts.NotifyFlags = CheckoutNotifyType(c.notify_flags) - opts.NotifyCallback = pointerHandles.Get(c.notify_payload).(CheckoutOpts).NotifyCallback - opts.ProgressCallback = pointerHandles.Get(c.progress_payload).(CheckoutOpts).ProgressCallback + if c.notify_payload != nil { + opts.NotifyCallback = pointerHandles.Get(c.notify_payload).(CheckoutOpts).NotifyCallback + } + if c.progress_payload != nil { + opts.ProgressCallback = pointerHandles.Get(c.progress_payload).(CheckoutOpts).ProgressCallback + } if c.target_directory != nil { opts.TargetDirectory = C.GoString(c.target_directory) } -- 2.45.2 From 18802c24e363d7f4982af86a3e9be437cb8585b2 Mon Sep 17 00:00:00 2001 From: Mirko Nosenzo Date: Sun, 29 May 2016 15:27:48 +0200 Subject: [PATCH 092/128] Checkout Callbacks Payload Check in Callback Functions Checking payloads in callback functions --- checkout.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/checkout.go b/checkout.go index ba411ff..7377d51 100644 --- a/checkout.go +++ b/checkout.go @@ -96,6 +96,9 @@ func (opts *CheckoutOpts) toC() *C.git_checkout_options { //export checkoutNotifyCallback func checkoutNotifyCallback(why C.git_checkout_notify_t, cpath *C.char, cbaseline, ctarget, cworkdir, data unsafe.Pointer) int { + if data == nil { + return 0 + } path := C.GoString(cpath) var baseline, target, workdir DiffFile if cbaseline != nil { @@ -116,6 +119,9 @@ func checkoutNotifyCallback(why C.git_checkout_notify_t, cpath *C.char, cbaselin //export checkoutProgressCallback func checkoutProgressCallback(path *C.char, completed_steps, total_steps C.size_t, data unsafe.Pointer) int { + if data == nil { + return 0 + } opts := pointerHandles.Get(data).(CheckoutOpts) if opts.ProgressCallback == nil { return 0 -- 2.45.2 From 981538924c7607980dd8328564cad6c0aedd6154 Mon Sep 17 00:00:00 2001 From: Travis Lane Date: Thu, 7 Apr 2016 21:51:00 -0700 Subject: [PATCH 093/128] diff: Add DiffStats String This implements git_diff_stats_to_buf which provides the output for git diff --stats. --- diff.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/diff.go b/diff.go index 9a7ac78..8ae0512 100644 --- a/diff.go +++ b/diff.go @@ -217,6 +217,33 @@ func (stats *DiffStats) FilesChanged() int { return int(C.git_diff_stats_files_changed(stats.ptr)) } +type DiffStatsFormat int + +const ( + DiffStatsNone DiffStatsFormat = C.GIT_DIFF_STATS_NONE + DiffStatsFull DiffStatsFormat = C.GIT_DIFF_STATS_FULL + DiffStatsShort DiffStatsFormat = C.GIT_DIFF_STATS_SHORT + DiffStatsNumber DiffStatsFormat = C.GIT_DIFF_STATS_NUMBER + DiffStatsIncludeSummary DiffStatsFormat = C.GIT_DIFF_STATS_INCLUDE_SUMMARY +) + +func (stats *DiffStats) String(format DiffStatsFormat, + width uint) (string, error) { + buf := C.git_buf{} + defer C.git_buf_free(&buf) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_diff_stats_to_buf(&buf, + stats.ptr, C.git_diff_stats_format_t(format), C.size_t(width)) + if ret < 0 { + return "", MakeGitError(ret) + } + + return C.GoString(buf.ptr), nil +} + func (diff *Diff) Stats() (*DiffStats, error) { stats := new(DiffStats) -- 2.45.2 From a2f93e91d253d9ac29e666fd7d3d9508a2b23134 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Wed, 22 Jun 2016 14:47:53 -0700 Subject: [PATCH 094/128] Add NewCredSshKeyFromMemory to the credentials helpers. Allowing to use public and private keys from memory without reading them from disk and without using an ssh agent. Signed-off-by: David Calavera --- credentials.go | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/credentials.go b/credentials.go index bb0ec41..4e42b6e 100644 --- a/credentials.go +++ b/credentials.go @@ -44,13 +44,15 @@ func NewCredUserpassPlaintext(username string, password string) (int, Cred) { return int(ret), cred } -func NewCredSshKey(username string, publickey string, privatekey string, passphrase string) (int, Cred) { +// NewCredSshKey creates new ssh credentials reading the public and private keys +// from the file system. +func NewCredSshKey(username string, publicKeyPath string, privateKeyPath string, passphrase string) (int, Cred) { cred := Cred{} cusername := C.CString(username) defer C.free(unsafe.Pointer(cusername)) - cpublickey := C.CString(publickey) + cpublickey := C.CString(publicKeyPath) defer C.free(unsafe.Pointer(cpublickey)) - cprivatekey := C.CString(privatekey) + cprivatekey := C.CString(privateKeyPath) defer C.free(unsafe.Pointer(cprivatekey)) cpassphrase := C.CString(passphrase) defer C.free(unsafe.Pointer(cpassphrase)) @@ -58,6 +60,22 @@ func NewCredSshKey(username string, publickey string, privatekey string, passphr return int(ret), cred } +// NewCredSshKeyFromMemory creates new ssh credentials using the publicKey and privateKey +// arguments as the values for the public and private keys. +func NewCredSshKeyFromMemory(username string, publicKey string, privateKey string, passphrase string) (int, Cred) { + cred := Cred{} + cusername := C.CString(username) + defer C.free(unsafe.Pointer(cusername)) + cpublickey := C.CString(publicKey) + defer C.free(unsafe.Pointer(cpublickey)) + cprivatekey := C.CString(privateKey) + defer C.free(unsafe.Pointer(cprivatekey)) + cpassphrase := C.CString(passphrase) + defer C.free(unsafe.Pointer(cpassphrase)) + ret := C.git_cred_ssh_key_memory_new(&cred.ptr, cusername, cpublickey, cprivatekey, cpassphrase) + return int(ret), cred +} + func NewCredSshKeyFromAgent(username string) (int, Cred) { cred := Cred{} cusername := C.CString(username) -- 2.45.2 From 6fa4c8ea15887c9a8fc96e9d052112a4ef5170cd Mon Sep 17 00:00:00 2001 From: Mirko Nosenzo Date: Sat, 25 Jun 2016 11:30:35 +0200 Subject: [PATCH 095/128] Checkout Payload Pointer Fix - Tracking pointer once - Tracking pointer and not struct --- checkout.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/checkout.go b/checkout.go index 7377d51..a511306 100644 --- a/checkout.go +++ b/checkout.go @@ -74,10 +74,10 @@ func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOpts { opts.FileOpenFlags = int(c.file_open_flags) opts.NotifyFlags = CheckoutNotifyType(c.notify_flags) if c.notify_payload != nil { - opts.NotifyCallback = pointerHandles.Get(c.notify_payload).(CheckoutOpts).NotifyCallback + opts.NotifyCallback = pointerHandles.Get(c.notify_payload).(*CheckoutOpts).NotifyCallback } if c.progress_payload != nil { - opts.ProgressCallback = pointerHandles.Get(c.progress_payload).(CheckoutOpts).ProgressCallback + opts.ProgressCallback = pointerHandles.Get(c.progress_payload).(*CheckoutOpts).ProgressCallback } if c.target_directory != nil { opts.TargetDirectory = C.GoString(c.target_directory) @@ -110,7 +110,7 @@ func checkoutNotifyCallback(why C.git_checkout_notify_t, cpath *C.char, cbaselin if cworkdir != nil { workdir = diffFileFromC((*C.git_diff_file)(cworkdir)) } - opts := pointerHandles.Get(data).(CheckoutOpts) + opts := pointerHandles.Get(data).(*CheckoutOpts) if opts.NotifyCallback == nil { return 0 } @@ -122,7 +122,7 @@ func checkoutProgressCallback(path *C.char, completed_steps, total_steps C.size_ if data == nil { return 0 } - opts := pointerHandles.Get(data).(CheckoutOpts) + opts := pointerHandles.Get(data).(*CheckoutOpts) if opts.ProgressCallback == nil { return 0 } @@ -146,11 +146,12 @@ func populateCheckoutOpts(ptr *C.git_checkout_options, opts *CheckoutOpts) *C.gi if opts.NotifyCallback != nil || opts.ProgressCallback != nil { C._go_git_populate_checkout_cb(ptr) } + payload := pointerHandles.Track(opts) if opts.NotifyCallback != nil { - ptr.notify_payload = pointerHandles.Track(*opts) + ptr.notify_payload = payload } if opts.ProgressCallback != nil { - ptr.progress_payload = pointerHandles.Track(*opts) + ptr.progress_payload = payload } if opts.TargetDirectory != "" { ptr.target_directory = C.CString(opts.TargetDirectory) @@ -178,9 +179,6 @@ func freeCheckoutOpts(ptr *C.git_checkout_options) { if ptr.notify_payload != nil { pointerHandles.Untrack(ptr.notify_payload) } - if ptr.progress_payload != nil { - pointerHandles.Untrack(ptr.progress_payload) - } } // Updates files in the index and the working tree to match the content of -- 2.45.2 From c6b94a160eef7546fa61e9e06f50f272d68bab8c Mon Sep 17 00:00:00 2001 From: Mirko Nosenzo Date: Thu, 7 Jul 2016 11:59:37 +0200 Subject: [PATCH 096/128] Returning Problems on Remote Rename Problems can be returned as string array on RemoteCollection Rename --- remote.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/remote.go b/remote.go index d063c17..cfe6e86 100644 --- a/remote.go +++ b/remote.go @@ -454,9 +454,9 @@ func (o *Remote) PushUrl() string { return C.GoString(C.git_remote_pushurl(o.ptr)) } -func (c *RemoteCollection) Rename(remote, newname string) error { +func (c *RemoteCollection) Rename(remote, newname string) ([]string, error) { cproblems := C.git_strarray{} - + defer freeStrarray(&cproblems) cnewname := C.CString(newname) defer C.free(unsafe.Pointer(cnewname)) cremote := C.CString(remote) @@ -467,10 +467,10 @@ func (c *RemoteCollection) Rename(remote, newname string) error { ret := C.git_remote_rename(&cproblems, c.repo.ptr, cremote, cnewname) if ret < 0 { - return MakeGitError(ret) + problems := makeStringsFromCStrings(cproblems.strings, int(cproblems.count)) + return problems, MakeGitError(ret) } - C.git_strarray_free(&cproblems) - return nil + return []string{}, nil } func (c *RemoteCollection) SetUrl(remote, url string) error { -- 2.45.2 From cf7553e72cea560d3398b7f0a5e50f812e91443a Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Tue, 19 Jul 2016 22:48:52 +0300 Subject: [PATCH 097/128] odb: Expose git_odb_object_type() as OdbObject.Type() It might be needed when one is writing `git cat-file --batch` equivalent which has output format SP SP LF LF --- odb.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/odb.go b/odb.go index 9c6baa3..28bfad6 100644 --- a/odb.go +++ b/odb.go @@ -226,6 +226,10 @@ func (object *OdbObject) Len() (len uint64) { return uint64(C.git_odb_object_size(object.ptr)) } +func (object *OdbObject) Type() ObjectType { + return ObjectType(C.git_odb_object_type(object.ptr)) +} + func (object *OdbObject) Data() (data []byte) { var c_blob unsafe.Pointer = C.git_odb_object_data(object.ptr) var blob []byte -- 2.45.2 From aa7dfab1cf7534f7a3b99c2688c874b0af09d9f4 Mon Sep 17 00:00:00 2001 From: Mirko Nosenzo Date: Sun, 24 Jul 2016 14:20:12 +0200 Subject: [PATCH 098/128] Clone Fix freeCheckoutOpts called twice --- clone.go | 1 - 1 file changed, 1 deletion(-) diff --git a/clone.go b/clone.go index e80d14d..1ff5124 100644 --- a/clone.go +++ b/clone.go @@ -41,7 +41,6 @@ func Clone(url string, path string, options *CloneOptions) (*Repository, error) var ptr *C.git_repository ret := C.git_clone(&ptr, curl, cpath, copts) - freeCheckoutOpts(&copts.checkout_opts) if ret < 0 { return nil, MakeGitError(ret) -- 2.45.2 From 380684bb107cd23d9baae15be330954e04fa088b Mon Sep 17 00:00:00 2001 From: Alan Johnson Date: Thu, 4 Aug 2016 09:43:44 -0400 Subject: [PATCH 099/128] Fixing issue with error conversion. --- walk.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/walk.go b/walk.go index 60e618d..a64934a 100644 --- a/walk.go +++ b/walk.go @@ -173,7 +173,7 @@ func (v *RevWalk) Iterate(fun RevWalkIterator) (err error) { return nil } if err != nil { - if err.(GitError).Code == ErrIterOver { + if err.(*GitError).Code == ErrIterOver { err = nil } -- 2.45.2 From 37d3c2d9ad4c4e970cac02faec8ad184412c34e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 5 Aug 2016 15:15:22 +0200 Subject: [PATCH 100/128] Update libgit2 to 73dab769 This version reloads the index on checkout, which showed we were not persisting the updated index to disk and thus would have conflicts on checkout. --- git_test.go | 4 ++++ vendor/libgit2 | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/git_test.go b/git_test.go index 58caf71..6a58844 100644 --- a/git_test.go +++ b/git_test.go @@ -58,6 +58,8 @@ func seedTestRepo(t *testing.T, repo *Repository) (*Oid, *Oid) { checkFatal(t, err) err = idx.AddByPath("README") checkFatal(t, err) + err = idx.Write() + checkFatal(t, err) treeId, err := idx.WriteTree() checkFatal(t, err) @@ -91,6 +93,8 @@ func updateReadme(t *testing.T, repo *Repository, content string) (*Oid, *Oid) { checkFatal(t, err) err = idx.AddByPath("README") checkFatal(t, err) + err = idx.Write() + checkFatal(t, err) treeId, err := idx.WriteTree() checkFatal(t, err) diff --git a/vendor/libgit2 b/vendor/libgit2 index 1dc4491..73dab76 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 1dc449105b329ea4f8ea9982bc2da869d231c04a +Subproject commit 73dab7692e780e1df96093a54854795428eb66b4 -- 2.45.2 From e55c00eca7e70e2d02860cda3cdc9169a88ece36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 27 Aug 2016 19:21:05 +0200 Subject: [PATCH 101/128] Run the tests in parallel This saves about 1s, or 1/3 of the test runtime. The linking is still much slower, but this we can control. --- blame_test.go | 1 + blob_test.go | 1 + branch_test.go | 2 ++ cherrypick_test.go | 1 + clone_test.go | 2 ++ config_test.go | 1 + describe_test.go | 1 + diff_test.go | 4 +++- git_test.go | 2 ++ index_test.go | 7 +++++++ merge_test.go | 5 +++++ note_test.go | 4 ++++ object_test.go | 3 +++ odb_test.go | 5 ++++- patch_test.go | 1 + push_test.go | 1 + reference_test.go | 6 ++++++ remote_test.go | 7 +++++++ reset_test.go | 1 + revparse_test.go | 3 +++ status_test.go | 2 ++ submodule_test.go | 1 + tag_test.go | 5 +++++ tree_test.go | 2 ++ 24 files changed, 66 insertions(+), 2 deletions(-) diff --git a/blame_test.go b/blame_test.go index a2a4d38..ec96af7 100644 --- a/blame_test.go +++ b/blame_test.go @@ -6,6 +6,7 @@ import ( ) func TestBlame(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/blob_test.go b/blob_test.go index 2b5ec4f..719d185 100644 --- a/blob_test.go +++ b/blob_test.go @@ -5,6 +5,7 @@ import ( ) func TestCreateBlobFromBuffer(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/branch_test.go b/branch_test.go index a0834a8..01a2e28 100644 --- a/branch_test.go +++ b/branch_test.go @@ -5,6 +5,7 @@ import ( ) func TestBranchIterator(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -27,6 +28,7 @@ func TestBranchIterator(t *testing.T) { } func TestBranchIteratorEach(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/cherrypick_test.go b/cherrypick_test.go index a3246bd..bfa5ca8 100644 --- a/cherrypick_test.go +++ b/cherrypick_test.go @@ -33,6 +33,7 @@ func readReadme(t *testing.T, repo *Repository) string { } func TestCherrypick(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/clone_test.go b/clone_test.go index a6bbf94..24c3a09 100644 --- a/clone_test.go +++ b/clone_test.go @@ -10,6 +10,7 @@ const ( ) func TestClone(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -35,6 +36,7 @@ func TestClone(t *testing.T) { } func TestCloneWithCallback(t *testing.T) { + t.Parallel() testPayload := 0 repo := createTestRepo(t) diff --git a/config_test.go b/config_test.go index fea8d8a..f31e73e 100644 --- a/config_test.go +++ b/config_test.go @@ -88,6 +88,7 @@ var tests = []TestRunner{ } func TestConfigLookups(t *testing.T) { + t.Parallel() var ( err error c *Config diff --git a/describe_test.go b/describe_test.go index 25af107..f0a45f4 100644 --- a/describe_test.go +++ b/describe_test.go @@ -8,6 +8,7 @@ import ( ) func TestDescribeCommit(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/diff_test.go b/diff_test.go index 850ed8e..6fbad51 100644 --- a/diff_test.go +++ b/diff_test.go @@ -7,6 +7,7 @@ import ( ) func TestFindSimilar(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -61,7 +62,7 @@ func TestFindSimilar(t *testing.T) { } func TestDiffTreeToTree(t *testing.T) { - + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -189,6 +190,7 @@ func createTestTrees(t *testing.T, repo *Repository) (originalTree *Tree, newTre } func TestDiffBlobs(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/git_test.go b/git_test.go index 58caf71..bdb6837 100644 --- a/git_test.go +++ b/git_test.go @@ -109,6 +109,7 @@ func updateReadme(t *testing.T, repo *Repository, content string) (*Oid, *Oid) { } func TestOidZero(t *testing.T) { + t.Parallel() var zeroId Oid if !zeroId.IsZero() { @@ -117,6 +118,7 @@ func TestOidZero(t *testing.T) { } func TestEmptyOid(t *testing.T) { + t.Parallel() _, err := NewOid("") if err == nil || !IsErrorCode(err, ErrGeneric) { t.Fatal("Should have returned invalid error") diff --git a/index_test.go b/index_test.go index 5f6b375..600b8b1 100644 --- a/index_test.go +++ b/index_test.go @@ -8,6 +8,7 @@ import ( ) func TestCreateRepoAndStage(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -24,6 +25,7 @@ func TestCreateRepoAndStage(t *testing.T) { } func TestIndexReadTree(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -53,6 +55,7 @@ func TestIndexReadTree(t *testing.T) { } func TestIndexWriteTreeTo(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -72,6 +75,7 @@ func TestIndexWriteTreeTo(t *testing.T) { } func TestIndexAddAndWriteTreeTo(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -106,6 +110,7 @@ func TestIndexAddAndWriteTreeTo(t *testing.T) { } func TestIndexAddAllNoCallback(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -127,6 +132,7 @@ func TestIndexAddAllNoCallback(t *testing.T) { } func TestIndexAddAllCallback(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -155,6 +161,7 @@ func TestIndexAddAllCallback(t *testing.T) { } func TestIndexOpen(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/merge_test.go b/merge_test.go index 8059727..f2c84bc 100644 --- a/merge_test.go +++ b/merge_test.go @@ -6,6 +6,7 @@ import ( ) func TestMergeWithSelf(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -24,6 +25,7 @@ func TestMergeWithSelf(t *testing.T) { } func TestMergeAnalysisWithSelf(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -46,6 +48,7 @@ func TestMergeAnalysisWithSelf(t *testing.T) { } func TestMergeSameFile(t *testing.T) { + t.Parallel() file := MergeFileInput{ Path: "test", Mode: 33188, @@ -68,6 +71,7 @@ func TestMergeSameFile(t *testing.T) { } func TestMergeTreesWithoutAncestor(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -125,6 +129,7 @@ func appendCommit(t *testing.T, repo *Repository) (*Oid, *Oid) { } func TestMergeBase(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/note_test.go b/note_test.go index 27e04be..9f64eb8 100644 --- a/note_test.go +++ b/note_test.go @@ -8,6 +8,7 @@ import ( ) func TestCreateNote(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -27,6 +28,7 @@ func TestCreateNote(t *testing.T) { } func TestNoteIterator(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -63,6 +65,7 @@ func TestNoteIterator(t *testing.T) { } func TestRemoveNote(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -86,6 +89,7 @@ func TestRemoveNote(t *testing.T) { } func TestDefaultNoteRef(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/object_test.go b/object_test.go index 2ae2a6a..cb262de 100644 --- a/object_test.go +++ b/object_test.go @@ -5,6 +5,7 @@ import ( ) func TestObjectPoymorphism(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -88,6 +89,7 @@ func checkOwner(t *testing.T, repo *Repository, obj Object) { } func TestObjectOwner(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -104,6 +106,7 @@ func TestObjectOwner(t *testing.T) { } func TestObjectPeel(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/odb_test.go b/odb_test.go index dfd2ad0..3d22fc9 100644 --- a/odb_test.go +++ b/odb_test.go @@ -7,6 +7,7 @@ import ( ) func TestOdbReadHeader(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -35,6 +36,7 @@ func TestOdbReadHeader(t *testing.T) { } func TestOdbStream(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -64,7 +66,7 @@ func TestOdbStream(t *testing.T) { } func TestOdbHash(t *testing.T) { - + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -92,6 +94,7 @@ Initial commit.` } func TestOdbForeach(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/patch_test.go b/patch_test.go index 2d52fb4..291c705 100644 --- a/patch_test.go +++ b/patch_test.go @@ -6,6 +6,7 @@ import ( ) func TestPatch(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/push_test.go b/push_test.go index 8f6e806..f372882 100644 --- a/push_test.go +++ b/push_test.go @@ -5,6 +5,7 @@ import ( ) func TestRemotePush(t *testing.T) { + t.Parallel() repo := createBareTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/reference_test.go b/reference_test.go index 761daf8..b6721e1 100644 --- a/reference_test.go +++ b/reference_test.go @@ -8,6 +8,7 @@ import ( ) func TestRefModification(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -53,6 +54,7 @@ func TestRefModification(t *testing.T) { } func TestReferenceIterator(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -131,6 +133,7 @@ func TestReferenceIterator(t *testing.T) { } func TestReferenceOwner(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -150,6 +153,7 @@ func TestReferenceOwner(t *testing.T) { } func TestUtil(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -177,6 +181,7 @@ func TestUtil(t *testing.T) { } func TestIsNote(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -210,6 +215,7 @@ func TestIsNote(t *testing.T) { } func TestReferenceIsValidName(t *testing.T) { + t.Parallel() if !ReferenceIsValidName("HEAD") { t.Errorf("HEAD should be a valid reference name") } diff --git a/remote_test.go b/remote_test.go index 978b803..3d00640 100644 --- a/remote_test.go +++ b/remote_test.go @@ -6,6 +6,7 @@ import ( ) func TestListRemotes(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -33,6 +34,7 @@ func assertHostname(cert *Certificate, valid bool, hostname string, t *testing.T } func TestCertificateCheck(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -52,6 +54,7 @@ func TestCertificateCheck(t *testing.T) { } func TestRemoteConnect(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -63,6 +66,7 @@ func TestRemoteConnect(t *testing.T) { } func TestRemoteLs(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -81,6 +85,7 @@ func TestRemoteLs(t *testing.T) { } func TestRemoteLsFiltering(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -107,6 +112,7 @@ func TestRemoteLsFiltering(t *testing.T) { } func TestRemotePruneRefs(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -129,6 +135,7 @@ func TestRemotePruneRefs(t *testing.T) { } func TestRemotePrune(t *testing.T) { + t.Parallel() remoteRepo := createTestRepo(t) defer cleanupTestRepo(t, remoteRepo) diff --git a/reset_test.go b/reset_test.go index ec578bd..45777e4 100644 --- a/reset_test.go +++ b/reset_test.go @@ -6,6 +6,7 @@ import ( ) func TestResetToCommit(t *testing.T) { + t.Parallel() repo := createTestRepo(t) seedTestRepo(t, repo) // create commit to reset to diff --git a/revparse_test.go b/revparse_test.go index 75e9ffd..2835434 100644 --- a/revparse_test.go +++ b/revparse_test.go @@ -5,6 +5,7 @@ import ( ) func TestRevparse(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -17,6 +18,7 @@ func TestRevparse(t *testing.T) { } func TestRevparseSingle(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -29,6 +31,7 @@ func TestRevparseSingle(t *testing.T) { } func TestRevparseExt(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/status_test.go b/status_test.go index 5b97b00..17ed94f 100644 --- a/status_test.go +++ b/status_test.go @@ -7,6 +7,7 @@ import ( ) func TestStatusFile(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -27,6 +28,7 @@ func TestStatusFile(t *testing.T) { } func TestStatusList(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/submodule_test.go b/submodule_test.go index 43c890a..fa2e98c 100644 --- a/submodule_test.go +++ b/submodule_test.go @@ -5,6 +5,7 @@ import ( ) func TestSubmoduleForeach(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/tag_test.go b/tag_test.go index 2fdfe00..3404923 100644 --- a/tag_test.go +++ b/tag_test.go @@ -7,6 +7,7 @@ import ( ) func TestCreateTag(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -26,6 +27,7 @@ func TestCreateTag(t *testing.T) { } func TestCreateTagLightweight(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -50,6 +52,7 @@ func TestCreateTagLightweight(t *testing.T) { } func TestListTags(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -79,6 +82,7 @@ func TestListTags(t *testing.T) { } func TestListTagsWithMatch(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -116,6 +120,7 @@ func TestListTagsWithMatch(t *testing.T) { } func TestTagForeach(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) diff --git a/tree_test.go b/tree_test.go index fae395a..f5b6822 100644 --- a/tree_test.go +++ b/tree_test.go @@ -3,6 +3,7 @@ package git import "testing" func TestTreeEntryById(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -22,6 +23,7 @@ func TestTreeEntryById(t *testing.T) { } func TestTreeBuilderInsert(t *testing.T) { + t.Parallel() repo := createTestRepo(t) defer cleanupTestRepo(t, repo) -- 2.45.2 From 3c1ba8c40e4d654bfca8b535c861a63c41b16f27 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring Date: Sat, 27 Aug 2016 20:44:46 +0200 Subject: [PATCH 102/128] Add test for slice-to-slice and GCo pointer detection --- .travis.yml | 1 + blob_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/.travis.yml b/.travis.yml index f796389..016cf2d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ go: - 1.3 - 1.4 - 1.5 + - 1.6 - tip matrix: diff --git a/blob_test.go b/blob_test.go index 2b5ec4f..652c50c 100644 --- a/blob_test.go +++ b/blob_test.go @@ -1,9 +1,21 @@ package git import ( + "bytes" "testing" ) +type bufWrapper struct { + buf [64]byte + pointer []byte +} + +func doublePointerBytes() []byte { + o := &bufWrapper{} + o.pointer = o.buf[0:10] + return o.pointer[0:1] +} + func TestCreateBlobFromBuffer(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) @@ -14,4 +26,18 @@ func TestCreateBlobFromBuffer(t *testing.T) { if id.String() != "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391" { t.Fatal("Empty buffer did not deliver empty blob id") } + + for _, data := range []([]byte){[]byte("hello there"), doublePointerBytes()} { + expected := make([]byte, len(data)) + copy(expected, data) + id, err = repo.CreateBlobFromBuffer(data) + checkFatal(t, err) + + blob, err := repo.LookupBlob(id) + checkFatal(t, err) + if !bytes.Equal(blob.Contents(), expected) { + t.Fatal("Loaded bytes don't match original bytes:", + blob.Contents(), "!=", expected) + } + } } -- 2.45.2 From b5d213c2c1229ea5de524ee24a9d9635a9cf303f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 27 Aug 2016 20:47:41 +0200 Subject: [PATCH 103/128] Remove unecessary copy --- blob_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/blob_test.go b/blob_test.go index 652c50c..6b7cd49 100644 --- a/blob_test.go +++ b/blob_test.go @@ -28,16 +28,14 @@ func TestCreateBlobFromBuffer(t *testing.T) { } for _, data := range []([]byte){[]byte("hello there"), doublePointerBytes()} { - expected := make([]byte, len(data)) - copy(expected, data) id, err = repo.CreateBlobFromBuffer(data) checkFatal(t, err) blob, err := repo.LookupBlob(id) checkFatal(t, err) - if !bytes.Equal(blob.Contents(), expected) { + if !bytes.Equal(blob.Contents(), data) { t.Fatal("Loaded bytes don't match original bytes:", - blob.Contents(), "!=", expected) + blob.Contents(), "!=", data) } } } -- 2.45.2 From b41e4c4ac7c7ec4d45ec5d8903077bd01264549f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 27 Aug 2016 20:51:13 +0200 Subject: [PATCH 104/128] Work around Go 1.6's CGo pointer check It depends heavily on the expression at the call site an whether it can figure out whether we're using a slice or not, so provid an incantation that does this. --- blob.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/blob.go b/blob.go index 1a86e60..8b3e94f 100644 --- a/blob.go +++ b/blob.go @@ -37,15 +37,24 @@ func (repo *Repository) CreateBlobFromBuffer(data []byte) (*Oid, error) { defer runtime.UnlockOSThread() var id C.git_oid - var ptr unsafe.Pointer + var size C.size_t + // Go 1.6 added some increased checking of passing pointer to + // C, but its check depends on its expectations of waht we + // pass to the C function, so unless we take the address of + // its contents at the call site itself, it can fail when + // 'data' is a slice of a slice. + // + // When we're given an empty slice, create a dummy one where 0 + // isn't out of bounds. if len(data) > 0 { - ptr = unsafe.Pointer(&data[0]) + size = C.size_t(len(data)) } else { - ptr = unsafe.Pointer(nil) + data = []byte{0} + size = C.size_t(0) } - ecode := C.git_blob_create_frombuffer(&id, repo.ptr, ptr, C.size_t(len(data))) + ecode := C.git_blob_create_frombuffer(&id, repo.ptr, unsafe.Pointer(&data[0]), size) if ecode < 0 { return nil, MakeGitError(ecode) } -- 2.45.2 From 5c678353faa4f180ee4ad8a5e58ca71e093cf757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 27 Aug 2016 20:52:07 +0200 Subject: [PATCH 105/128] Add Go 1.7 to the build list --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 016cf2d..79ad168 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ go: - 1.4 - 1.5 - 1.6 + - 1.7 - tip matrix: -- 2.45.2 From 92fa6357aee0c477e7090f6baf6251e969541123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 27 Aug 2016 21:11:22 +0200 Subject: [PATCH 106/128] Bring back the Makefile from 'next' --- Makefile | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 9c42283..3040857 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,11 @@ default: test -test: - go run script/check-MakeGitError-thread-lock.go - go test ./... +build-libgit2: + ./script/build-libgit2-static.sh -install: - go install ./... +test: build-libgit2 + go run script/check-MakeGitError-thread-lock.go + ./script/with-static.sh go test ./... + +install: build-libgit2 + ./script/with-static.sh go install ./... -- 2.45.2 From 8eb8fa3725d8d857c58fcb0b7d64a85d8b7bebdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 27 Aug 2016 21:35:26 +0200 Subject: [PATCH 107/128] Also remove a pkg-config directive that snuck in --- git.go | 1 - 1 file changed, 1 deletion(-) diff --git a/git.go b/git.go index ed891e6..a3c40e3 100644 --- a/git.go +++ b/git.go @@ -3,7 +3,6 @@ package git /* #include #include -#cgo pkg-config: libgit2 #if LIBGIT2_VER_MAJOR != 0 || LIBGIT2_VER_MINOR != 24 # error "Invalid libgit2 version; this git2go supports libgit2 v0.24" -- 2.45.2 From 6ffad323ba4f723479d04182224297d90c0f9084 Mon Sep 17 00:00:00 2001 From: Mirko Nosenzo Date: Sun, 28 Aug 2016 11:21:10 +0200 Subject: [PATCH 108/128] Removed Useless Argument Check --- checkout.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/checkout.go b/checkout.go index a511306..f5822c9 100644 --- a/checkout.go +++ b/checkout.go @@ -119,9 +119,6 @@ func checkoutNotifyCallback(why C.git_checkout_notify_t, cpath *C.char, cbaselin //export checkoutProgressCallback func checkoutProgressCallback(path *C.char, completed_steps, total_steps C.size_t, data unsafe.Pointer) int { - if data == nil { - return 0 - } opts := pointerHandles.Get(data).(*CheckoutOpts) if opts.ProgressCallback == nil { return 0 -- 2.45.2 From 208cdaef7687dcb05294833c84f50031a45aa5a1 Mon Sep 17 00:00:00 2001 From: Alan Johnson Date: Mon, 29 Aug 2016 09:10:56 -0400 Subject: [PATCH 109/128] Removes redundant iteration over check. --- walk.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/walk.go b/walk.go index a64934a..ab1de61 100644 --- a/walk.go +++ b/walk.go @@ -173,10 +173,6 @@ func (v *RevWalk) Iterate(fun RevWalkIterator) (err error) { return nil } if err != nil { - if err.(*GitError).Code == ErrIterOver { - err = nil - } - return err } -- 2.45.2 From 74bc3c6242f8696553aa2529767219ede1ff47ad Mon Sep 17 00:00:00 2001 From: Michael Daffin Date: Mon, 5 Sep 2016 15:56:09 +0100 Subject: [PATCH 110/128] Add check for ErrIterOver in BranchIterator.ForEach The BranchIterator.ForEach currently returns the ErrIterOver error if no error had occured during the iteration. This leads to a rather unhelpful blank error message with the error code -31 when iterating over the branches. This commit adds a check for ErrIterOver at the end of the ForEach method so that the client code only has to worry about checking for nil as apose to checking for the ErrIterOver error. --- branch.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/branch.go b/branch.go index a869054..d381c23 100644 --- a/branch.go +++ b/branch.go @@ -73,6 +73,10 @@ func (i *BranchIterator) ForEach(f BranchIteratorFunc) error { } } + if err != nil && IsErrorCode(err, ErrIterOver) { + return nil + } + return err } -- 2.45.2 From d2b8c99ba7169a312f317e1222af4a7b561e377b Mon Sep 17 00:00:00 2001 From: Karsten Dambekalns Date: Fri, 9 Sep 2016 15:27:07 +0200 Subject: [PATCH 111/128] Add method to fetch raw commit message The existing `Commit.Message()` returns the trimmed commit message. In some cases it is important to retrieve the exact commit message, even if it contains surrounding newlines. This adds a new `Commit.RawMessage()` to be able to do that. --- commit.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/commit.go b/commit.go index 6830da3..07b7c37 100644 --- a/commit.go +++ b/commit.go @@ -22,6 +22,10 @@ func (c Commit) Message() string { return C.GoString(C.git_commit_message(c.cast_ptr)) } +func (c Commit) RawMessage() string { + return C.GoString(C.git_commit_message_raw(c.cast_ptr)) +} + func (c Commit) Summary() string { return C.GoString(C.git_commit_summary(c.cast_ptr)) } -- 2.45.2 From a16e24a99e0138e5ee897f80a53a97a2bd4b9c49 Mon Sep 17 00:00:00 2001 From: Geordie Henderson Date: Fri, 16 Sep 2016 21:49:54 -0700 Subject: [PATCH 112/128] Write the index before writing the index tree in seedTestRepo test helper func --- git_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/git_test.go b/git_test.go index bdb6837..3385a72 100644 --- a/git_test.go +++ b/git_test.go @@ -58,6 +58,8 @@ func seedTestRepo(t *testing.T, repo *Repository) (*Oid, *Oid) { checkFatal(t, err) err = idx.AddByPath("README") checkFatal(t, err) + err = idx.Write() + checkFatal(t, err) treeId, err := idx.WriteTree() checkFatal(t, err) -- 2.45.2 From b829eb1edb54ffde40b13d5131e81fdb51faf0f9 Mon Sep 17 00:00:00 2001 From: Mark Karpeles Date: Fri, 30 Sep 2016 23:00:20 +0900 Subject: [PATCH 113/128] odb & refdb: make New*BackendFromC take unsafe.Pointer as argument allowing argument to be set from different package --- odb.go | 4 ++-- refdb.go | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/odb.go b/odb.go index 9c6baa3..9ba2ea2 100644 --- a/odb.go +++ b/odb.go @@ -36,8 +36,8 @@ func NewOdb() (odb *Odb, err error) { return odb, nil } -func NewOdbBackendFromC(ptr *C.git_odb_backend) (backend *OdbBackend) { - backend = &OdbBackend{ptr} +func NewOdbBackendFromC(ptr unsafe.Pointer) (backend *OdbBackend) { + backend = &OdbBackend{(*C.git_odb_backend)(ptr)} return backend } diff --git a/refdb.go b/refdb.go index 0d1e241..4e3ebf9 100644 --- a/refdb.go +++ b/refdb.go @@ -9,6 +9,7 @@ extern void _go_git_refdb_backend_free(git_refdb_backend *backend); import "C" import ( "runtime" + "unsafe" ) type Refdb struct { @@ -34,8 +35,8 @@ func (v *Repository) NewRefdb() (refdb *Refdb, err error) { return refdb, nil } -func NewRefdbBackendFromC(ptr *C.git_refdb_backend) (backend *RefdbBackend) { - backend = &RefdbBackend{ptr} +func NewRefdbBackendFromC(ptr unsafe.Pointer) (backend *RefdbBackend) { + backend = &RefdbBackend{(*C.git_refdb_backend)(ptr)} return backend } -- 2.45.2 From 717a47f7547811622fb1c08c28262a8c63af96fe Mon Sep 17 00:00:00 2001 From: Mirko Nosenzo Date: Sat, 8 Oct 2016 11:15:47 +0200 Subject: [PATCH 114/128] Remote Rename Fix Problem string array is returned if no error is occurred --- remote.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/remote.go b/remote.go index cfe6e86..c537932 100644 --- a/remote.go +++ b/remote.go @@ -467,10 +467,11 @@ func (c *RemoteCollection) Rename(remote, newname string) ([]string, error) { ret := C.git_remote_rename(&cproblems, c.repo.ptr, cremote, cnewname) if ret < 0 { - problems := makeStringsFromCStrings(cproblems.strings, int(cproblems.count)) - return problems, MakeGitError(ret) + return []string{}, MakeGitError(ret) } - return []string{}, nil + + problems := makeStringsFromCStrings(cproblems.strings, int(cproblems.count)) + return problems, nil } func (c *RemoteCollection) SetUrl(remote, url string) error { -- 2.45.2 From c18c8693feb805d9ffbd1d22444683ce5ac96477 Mon Sep 17 00:00:00 2001 From: ezwiebel Date: Thu, 20 Oct 2016 15:49:24 +1100 Subject: [PATCH 115/128] Implement git_index_remove_directory in index wrapper --- index.go | 16 ++++++++++++++++ index_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/index.go b/index.go index ae94864..2afdcdf 100644 --- a/index.go +++ b/index.go @@ -278,6 +278,22 @@ func (v *Index) RemoveByPath(path string) error { return nil } +// RemoveDirectory removes all entries from the index under a given directory. +func (v *Index) RemoveDirectory(dir string, stage int) error { + cstr := C.CString(dir) + defer C.free(unsafe.Pointer(cstr)) + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + ret := C.git_index_remove_directory(v.ptr, cstr, C.int(stage)) + if ret < 0 { + return MakeGitError(ret) + } + + return nil +} + func (v *Index) WriteTreeTo(repo *Repository) (*Oid, error) { oid := new(Oid) diff --git a/index_test.go b/index_test.go index 5f6b375..3a32168 100644 --- a/index_test.go +++ b/index_test.go @@ -105,6 +105,46 @@ func TestIndexAddAndWriteTreeTo(t *testing.T) { } } +func TestIndexRemoveDirectory(t *testing.T) { + repo := createTestRepo(t) + defer cleanupTestRepo(t, repo) + + odb, err := repo.Odb() + checkFatal(t, err) + + blobID, err := odb.Write([]byte("fou\n"), ObjectBlob) + checkFatal(t, err) + + idx, err := NewIndex() + checkFatal(t, err) + + entryCount := idx.EntryCount() + if entryCount != 0 { + t.Fatal("Index should count 0 entry") + } + + entry := IndexEntry{ + Path: "path/to/LISEZ_MOI", + Id: blobID, + Mode: FilemodeBlob, + } + + err = idx.Add(&entry) + checkFatal(t, err) + + entryCount = idx.EntryCount() + if entryCount != 1 { + t.Fatal("Index should count 1 entry") + } + + err = idx.RemoveDirectory("path", 0) + + entryCount = idx.EntryCount() + if entryCount != 0 { + t.Fatal("Index should count 0 entry") + } +} + func TestIndexAddAllNoCallback(t *testing.T) { repo := createTestRepo(t) defer cleanupTestRepo(t, repo) -- 2.45.2 From e8062bcaddc2533f28b9af6323f507a96bc1b092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 31 Oct 2016 19:31:43 +0100 Subject: [PATCH 116/128] Update to libgit2 a051ee3 --- vendor/libgit2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/libgit2 b/vendor/libgit2 index 73dab76..a051ee3 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 73dab7692e780e1df96093a54854795428eb66b4 +Subproject commit a051ee31f2a3790410c2b482611f691135d2eae0 -- 2.45.2 From adb1770ff3f9d7b242ba8b8eac31186db92a46d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 31 Oct 2016 21:09:24 +0100 Subject: [PATCH 117/128] Add Features() to retrieve the compile-time features of libgit2 --- features.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 features.go diff --git a/features.go b/features.go new file mode 100644 index 0000000..f6474a0 --- /dev/null +++ b/features.go @@ -0,0 +1,30 @@ +package git + +/* +#include +*/ +import "C" + +type Feature int + +const ( + // libgit2 was built with threading support + FeatureThreads Feature = C.GIT_FEATURE_THREADS + + // libgit2 was built with HTTPS support built-in + FeatureHttps Feature = C.GIT_FEATURE_HTTPS + + // libgit2 was build with SSH support built-in + FeatureSsh Feature = C.GIT_FEATURE_SSH + + // libgit2 was built with nanosecond support for files + FeatureNSec Feature = C.GIT_FEATURE_NSEC +) + +// Features returns a bit-flag of Feature values indicating which features the +// loaded libgit2 library has. +func Features() Feature { + features := C.git_libgit2_features() + + return Feature(features) +} -- 2.45.2 From a37f7f30ff94e32b20866cf2fa28496c60826278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 31 Oct 2016 21:12:03 +0100 Subject: [PATCH 118/128] Panic if libgit2 is not thread-aware Go calling C is inherently multi-threaded. If libgit2 cannot handle threading, then we're going to crash at some random point. Crash right at the start so we know what's happening. --- git.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/git.go b/git.go index a3c40e3..53b1a12 100644 --- a/git.go +++ b/git.go @@ -124,6 +124,15 @@ func init() { C.git_libgit2_init() + // Due to the multithreaded nature of Go and its interaction with + // calling C functions, we cannot work with a library that was not built + // with multi-threading support. The most likely outcome is a segfault + // or panic at an incomprehensible time, so let's make it easy by + // panicking right here. + if Features()&FeatureThreads == 0 { + panic("libgit2 was not built with threading support") + } + // 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 -- 2.45.2 From 28bc42ce822a6f5397bfa520c8c0cf8e56ee73fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 31 Oct 2016 21:34:00 +0100 Subject: [PATCH 119/128] travis: only build master and next The other branches will get built as part of a PR. --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 79ad168..8a81f16 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,3 +17,8 @@ go: matrix: allow_failures: - go: tip + +branches: + only: + - master + - next -- 2.45.2 From 9c5fb973fcc1acd7cd55e120fefef101be46e60a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 19 Dec 2015 01:30:28 +0100 Subject: [PATCH 120/128] Get rid of the with-static.sh script CGO can perform variable substitution in the directives, so we don't need to use a script to set up the variables; we can let the go tool do it for us. --- Makefile | 4 ++-- README.md | 11 +++++------ git.go | 4 ++++ script/with-static.sh | 12 ------------ 4 files changed, 11 insertions(+), 20 deletions(-) delete mode 100755 script/with-static.sh diff --git a/Makefile b/Makefile index 3040857..39fc558 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ build-libgit2: test: build-libgit2 go run script/check-MakeGitError-thread-lock.go - ./script/with-static.sh go test ./... + go test ./... install: build-libgit2 - ./script/with-static.sh go install ./... + go install ./... diff --git a/README.md b/README.md index 315032f..bd918d6 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ to use a version of git2go which will work against libgit2 v0.22 and dynamically import "github.com/libgit2/git2go" -to use the version which works against the latest release. +to use the 'master' branch, which works against the latest release of libgit2, whichever that one is at the time. ### From `next` @@ -44,15 +44,14 @@ libgit2 uses OpenSSL and LibSSH2 for performing encrypted network connections. F Running the tests ----------------- -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 +For the stable version, `go test` will work as usual. For the `next` branch, similarly to installing, running the tests requires building a local libgit2 library, so the Makefile provides a wrapper that makes sure it's built make test -Alternatively, if you want to pass arguments to `go test`, you can use the script that sets it all up +Alternatively, you can build the library manually first and then run the tests - ./script/with-static.sh go test -v - -which will run the specified arguments with the correct environment variables. + ./script/build-libgit2-static.sh + go test -v License ------- diff --git a/git.go b/git.go index 53b1a12..77394cf 100644 --- a/git.go +++ b/git.go @@ -1,6 +1,10 @@ package git /* +#cgo CFLAGS: -I${SRCDIR}/vendor/libgit2/include +#cgo LDFLAGS: -L${SRCDIR}/vendor/libgit2/build/ -lgit2 +#cgo windows LDFLAGS: -lwinhttp +#cgo !windows pkg-config: --static ${SRCDIR}/vendor/libgit2/build/libgit2.pc #include #include diff --git a/script/with-static.sh b/script/with-static.sh deleted file mode 100755 index 3f60e31..0000000 --- a/script/with-static.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -set -ex - -export BUILD="$PWD/vendor/libgit2/build" -export PCFILE="$BUILD/libgit2.pc" - -FLAGS=$(pkg-config --static --libs $PCFILE) || exit 1 -export CGO_LDFLAGS="$BUILD/libgit2.a -L$BUILD ${FLAGS}" -export CGO_CFLAGS="-I$PWD/vendor/libgit2/include" - -$@ -- 2.45.2 From 7f426f2435087a35155b4e8b859fb52f09f79a72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 27 Aug 2016 21:58:05 +0200 Subject: [PATCH 121/128] Only test against 1.5 and up Go 1.5 is the first one which supports the variable replacement we're using for the flags. Any older versions aren't supported by the Go team now that 1.7 is out, so you should be on one of these versions. --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 79ad168..fb68ca9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,6 @@ sudo: required install: ./script/install-libgit2.sh go: - - 1.1 - - 1.2 - - 1.3 - - 1.4 - 1.5 - 1.6 - 1.7 -- 2.45.2 From a41e2b66449f05093de71f8debc71092f0a8117e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 19 Nov 2016 15:09:41 +0100 Subject: [PATCH 122/128] Update to 0.25-rc1 --- git.go | 4 ++-- vendor/libgit2 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/git.go b/git.go index 77394cf..cb973ee 100644 --- a/git.go +++ b/git.go @@ -8,8 +8,8 @@ package git #include #include -#if LIBGIT2_VER_MAJOR != 0 || LIBGIT2_VER_MINOR != 24 -# error "Invalid libgit2 version; this git2go supports libgit2 v0.24" +#if LIBGIT2_VER_MAJOR != 0 || LIBGIT2_VER_MINOR != 25 +# error "Invalid libgit2 version; this git2go supports libgit2 v0.25" #endif */ diff --git a/vendor/libgit2 b/vendor/libgit2 index a051ee3..ae5838f 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit a051ee31f2a3790410c2b482611f691135d2eae0 +Subproject commit ae5838f118a4819e608990a815bf8fc482be5772 -- 2.45.2 From b8a9efd21f5ea094976ff2c53c4aec1cf6014876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 23 Dec 2016 19:14:05 +0000 Subject: [PATCH 123/128] Build /v\d+/ branches --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 8a81f16..26524e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,4 +21,5 @@ matrix: branches: only: - master + - /v\d+/ - next -- 2.45.2 From 5d0a4c752a74258a5f42e40fccd2908ac4e336b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 9 Jan 2017 22:13:18 +0000 Subject: [PATCH 124/128] Bump vendored libgit2 to ee89941fa --- vendor/libgit2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/libgit2 b/vendor/libgit2 index ae5838f..ee89941 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit ae5838f118a4819e608990a815bf8fc482be5772 +Subproject commit ee89941fa2b861332defb5e3a8ce12c23c496ed7 -- 2.45.2 From 9f4e0a46b6f4cefd652f1e20c068e5545d7cfc9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 20 Jan 2017 13:13:49 +0000 Subject: [PATCH 125/128] Update libgit2 to df4dfaad --- describe_test.go | 6 ++++-- vendor/libgit2 | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/describe_test.go b/describe_test.go index f0a45f4..3181f23 100644 --- a/describe_test.go +++ b/describe_test.go @@ -93,14 +93,16 @@ func TestDescribeCommit(t *testing.T) { func checkDescribeNoRefsFound(t *testing.T, err error) { // The failure happens at wherever we were called, not here _, file, line, ok := runtime.Caller(1) + expectedString := "no reference found, cannot describe anything" if !ok { t.Fatalf("Unable to get caller") } - if err == nil || !strings.Contains(err.Error(), "No reference found, cannot describe anything") { + if err == nil || !strings.Contains(err.Error(), expectedString) { t.Fatalf( - "%s:%v: was expecting error 'No reference found, cannot describe anything', got %v", + "%s:%v: was expecting error %v, got %v", path.Base(file), line, + expectedString, err, ) } diff --git a/vendor/libgit2 b/vendor/libgit2 index ee89941..df4dfaa 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit ee89941fa2b861332defb5e3a8ce12c23c496ed7 +Subproject commit df4dfaadcf709646ebab2e57e3589952cf1ac809 -- 2.45.2 From b020c1140a2cb8be18141498a6cab5440409dc24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 20 Jan 2017 13:48:39 +0000 Subject: [PATCH 126/128] Update the description of the branches in README. --- README.md | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index bd918d6..4ecfa34 100644 --- a/README.md +++ b/README.md @@ -2,44 +2,47 @@ 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) +Go bindings for [libgit2](http://libgit2.github.com/). -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. +### Which branch to use + +The numbered branches work against the version of libgit2 as specified by their number. You can import them in your project via gopkg.in, e.g. if you have libgit2 v0.25 installed you'd import with + + import "gopkg.in/libgit2/git2go.v25" + +which will ensure there are no sudden changes to the API. + +The `master` branch follows the tip of libgit2 itself (with some lag) and as such has no guarantees on its own API nor does it have expectations the stability of libgit2's. Thus this only supports statically linking against libgit2. Installing ---------- -This project wraps the functionality provided by libgit2. If you're using a stable version, install it to your system via your system's package manager and then install git2go as usual. +This project wraps the functionality provided by libgit2. It thus needs it in order to perform the work. -Otherwise (`next` which tracks an unstable version), we need to build libgit2 as well. In order to build it, you need `cmake`, `pkg-config` and a C compiler. You will also need the development packages for OpenSSL and LibSSH2 installed if you want libgit2 to support HTTPS and SSH respectively. +This project wraps the functionality provided by libgit2. If you're using a versioned branch, install it to your system via your system's package manager and then install git2go. -### Stable version -git2go has `master` which tracks the latest release of libgit2, and versioned branches which indicate which version of libgit2 they work against. Install the development package 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 +### Versioned branch, dynamic linking - import "gopkg.in/libgit2/git2go.v22" +When linking dynamically against a released version of libgit2, install it via your system's package manager. CGo will take care of finding its pkg-config file and set up the linking. Import via gopkg.in, e.g. to work against libgit2 v0.25 -to use a version of git2go which will work against libgit2 v0.22 and dynamically link to the library. You can use + import "gopkg.in/libgit2/git2go.v25" - import "github.com/libgit2/git2go" +### Master branch, or static linking -to use the 'master' branch, which works against the latest release of libgit2, whichever that one is at the time. - -### From `next` - -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. +If using `master` or building a branch statically, we need to build libgit2 first. In order to build it, you need `cmake`, `pkg-config` and a C compiler. You will also need the development packages for OpenSSL (outside of Windows or macOS) and LibSSH2 installed if you want libgit2 to support HTTPS and SSH respectively. Note that even if libgit2 is included in the resulting binary, its dependencies will not be. Run `go get -d github.com/libgit2/git2go` to download the code and go to your `$GOPATH/src/github.com/libgit2/git2go` directory. 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 make install -will compile libgit2. Run `go install` so that it's statically linked to the git2go package. +will compile libgit2, link it into git2go and install it. Parallelism 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. +libgit2 may use 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 ----------------- -- 2.45.2 From f503d918aa62cc995aa8a500106b1d0edcc82bd1 Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Fri, 24 Feb 2017 22:55:14 +0100 Subject: [PATCH 127/128] Fix memleaks in Tree.EntryBy(Name/Path/Index), fixes #313 --- tree.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tree.go b/tree.go index aad2c8d..a4b350b 100644 --- a/tree.go +++ b/tree.go @@ -51,6 +51,7 @@ func (t Tree) EntryByName(filename string) *TreeEntry { if entry == nil { return nil } + defer C.git_tree_entry_free(entry) return newTreeEntry(entry) } @@ -69,6 +70,7 @@ func (t Tree) EntryByPath(path string) (*TreeEntry, error) { if ret < 0 { return nil, MakeGitError(ret) } + defer C.git_tree_entry_free(entry) return newTreeEntry(entry), nil } @@ -78,6 +80,7 @@ func (t Tree) EntryByIndex(index uint64) *TreeEntry { if entry == nil { return nil } + defer C.git_tree_entry_free(entry) return newTreeEntry(entry) } -- 2.45.2 From ee2a0238687f72c4319cb599cbcdbee064ad3508 Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Sat, 25 Feb 2017 02:13:57 +0100 Subject: [PATCH 128/128] Remove unnecessary C.git_tree_entry_free calls ... MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As per the docs, these entries belong to the tree and don’t need to be freed by the caller: * https://libgit2.github.com/libgit2/#HEAD/group/tree/git_tree_entry_bynam e --- tree.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/tree.go b/tree.go index a4b350b..cdb5581 100644 --- a/tree.go +++ b/tree.go @@ -51,7 +51,6 @@ func (t Tree) EntryByName(filename string) *TreeEntry { if entry == nil { return nil } - defer C.git_tree_entry_free(entry) return newTreeEntry(entry) } @@ -80,7 +79,6 @@ func (t Tree) EntryByIndex(index uint64) *TreeEntry { if entry == nil { return nil } - defer C.git_tree_entry_free(entry) return newTreeEntry(entry) } -- 2.45.2