summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml11
-rw-r--r--branch.go36
-rw-r--r--checkout.go14
-rw-r--r--cherrypick_test.go2
-rw-r--r--clone.go18
-rw-r--r--clone_test.go16
-rw-r--r--describe.go222
-rw-r--r--describe_test.go106
-rw-r--r--git.go4
-rw-r--r--index.go42
-rw-r--r--merge.go10
-rw-r--r--merge_test.go6
-rw-r--r--note.go121
-rw-r--r--note_test.go14
-rw-r--r--object.go26
-rw-r--r--object_test.go60
-rw-r--r--odb.go8
-rw-r--r--odb_test.go2
-rw-r--r--push_test.go8
-rw-r--r--reference.go178
-rw-r--r--reference_test.go73
-rw-r--r--remote.go284
-rw-r--r--remote_test.go78
-rw-r--r--repository.go371
-rw-r--r--reset.go26
-rw-r--r--reset_test.go45
-rw-r--r--revparse_test.go2
-rwxr-xr-xscript/build-libgit2-static.sh19
-rw-r--r--script/check-MakeGitError-thread-lock.go2
-rwxr-xr-xscript/install-libgit2.sh21
-rwxr-xr-xscript/with-static.sh12
-rw-r--r--submodule.go120
-rw-r--r--submodule_test.go4
-rw-r--r--tag.go167
-rw-r--r--tag_test.go157
-rw-r--r--tree.go18
-rw-r--r--tree_test.go22
m---------vendor/libgit20
-rw-r--r--wrapper.c9
39 files changed, 1590 insertions, 744 deletions
diff --git a/.travis.yml b/.travis.yml
index f8b7e93..209d89f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,13 +1,8 @@
language: go
-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}"
+sudo: required
+
+install: ./script/install-libgit2.sh
go:
- 1.1
diff --git a/branch.go b/branch.go
index 8cf73b6..df72dba 100644
--- a/branch.go
+++ b/branch.go
@@ -90,31 +90,17 @@ 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) {
var ptr *C.git_reference
cBranchName := C.CString(branchName)
defer C.free(unsafe.Pointer(cBranchName))
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(&ptr, repo.ptr, cBranchName, target.cast_ptr, cForce, cSignature, cmsg)
+ ret := C.git_branch_create(&ptr, repo.ptr, cBranchName, target.cast_ptr, cForce)
if ret < 0 {
return nil, MakeGitError(ret)
}
@@ -132,30 +118,16 @@ 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)
defer C.free(unsafe.Pointer(cNewBranchName))
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 9874d2b..e0c067e 100644
--- a/checkout.go
+++ b/checkout.go
@@ -15,18 +15,24 @@ 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 09bc524..a3246bd 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/clone.go b/clone.go
index 1265d7f..b8579b5 100644
--- a/clone.go
+++ b/clone.go
@@ -11,19 +11,17 @@ import (
"unsafe"
)
-type RemoteCreateCallback func(repo Repository, name, url string) (*Remote, ErrorCode)
+type RemoteCreateCallback func(repo *Repository, name, url string) (*Remote, ErrorCode)
type CloneOptions struct {
*CheckoutOpts
- *RemoteCallbacks
+ *FetchOptions
Bare bool
CheckoutBranch string
RemoteCreateCallback RemoteCreateCallback
}
func Clone(url string, path string, options *CloneOptions) (*Repository, error) {
- repo := new(Repository)
-
curl := C.CString(url)
defer C.free(unsafe.Pointer(curl))
@@ -40,21 +38,23 @@ 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)
if ret < 0 {
return nil, MakeGitError(ret)
}
- runtime.SetFinalizer(repo, (*Repository).Free)
- return repo, nil
+ return newRepositoryFromC(ptr), 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)}
+ repo := newRepositoryFromC((*C.git_repository)(crepo))
if opts, ok := pointerHandles.Get(payload).(CloneOptions); ok {
remote, err := opts.RemoteCreateCallback(repo, name, url)
@@ -83,7 +83,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/clone_test.go b/clone_test.go
index 86fced8..a6bbf94 100644
--- a/clone_test.go
+++ b/clone_test.go
@@ -18,10 +18,20 @@ 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")
+ }
}
func TestCloneWithCallback(t *testing.T) {
@@ -37,10 +47,10 @@ func TestCloneWithCallback(t *testing.T) {
opts := CloneOptions{
Bare: true,
- RemoteCreateCallback: func(r Repository, name, url string) (*Remote, ErrorCode) {
+ RemoteCreateCallback: func(r *Repository, name, url string) (*Remote, ErrorCode) {
testPayload += 1
- remote, err := r.CreateRemote(REMOTENAME, url)
+ remote, err := r.Remotes.Create(REMOTENAME, url)
if err != nil {
return nil, ErrGeneric
}
@@ -58,7 +68,7 @@ func TestCloneWithCallback(t *testing.T) {
t.Fatal("Payload's value has not been changed")
}
- remote, err := repo2.LookupRemote(REMOTENAME)
+ remote, err := repo2.Remotes.Lookup(REMOTENAME)
if err != nil || remote == nil {
t.Fatal("Remote was not created properly")
}
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 <git2.h>
+*/
+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 <mark> (-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,
+ )
+ }
+}
diff --git a/git.go b/git.go
index a44459f..7c7f99c 100644
--- a/git.go
+++ b/git.go
@@ -77,8 +77,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/merge.go b/merge.go
index 183305c..bab53e0 100644
--- a/merge.go
+++ b/merge.go
@@ -85,8 +85,8 @@ const (
)
type MergeOptions struct {
- Version uint
- Flags 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),
- Flags: MergeTreeFlag(opts.flags),
+ TreeFlags: MergeTreeFlag(opts.tree_flags),
RenameThreshold: uint(opts.rename_threshold),
TargetLimit: uint(opts.target_limit),
FileFavor: MergeFileFavor(opts.file_favor),
@@ -124,7 +124,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),
@@ -364,7 +364,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/merge_test.go b/merge_test.go
index c09deed..ad01319 100644
--- a/merge_test.go
+++ b/merge_test.go
@@ -11,7 +11,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)
@@ -29,7 +29,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)
@@ -109,7 +109,7 @@ func appendCommit(t *testing.T, repo *Repository) (*Oid, *Oid) {
tree, err := repo.LookupTree(treeId)
checkFatal(t, err)
- ref, err := repo.LookupReference("HEAD")
+ ref, err := repo.References.Lookup("HEAD")
checkFatal(t, err)
parent, err := ref.Peel(ObjectCommit)
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(&note.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/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.
+}
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/push_test.go b/push_test.go
index e36e407..8f6e806 100644
--- a/push_test.go
+++ b/push_test.go
@@ -11,17 +11,17 @@ 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)
- 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")
+ _, 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 ac3580c..140082f 100644
--- a/reference.go
+++ b/reference.go
@@ -21,13 +21,137 @@ 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)
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 +160,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.git_signature_free(csig)
-
var cmsg *C.char
if msg == "" {
cmsg = nil
@@ -50,7 +168,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 +176,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.git_signature_free(csig)
-
var cmsg *C.char
if msg == "" {
cmsg = nil
@@ -78,7 +190,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 +212,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.git_signature_free(csig)
-
var cmsg *C.char
if msg == "" {
cmsg = nil
@@ -122,7 +228,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)
@@ -209,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)
@@ -319,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 5720a66..761daf8 100644
--- a/reference_test.go
+++ b/reference_test.go
@@ -13,21 +13,14 @@ 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: "[email protected]",
- When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc),
- }
- _, err = repo.CreateReference("refs/tags/tree", treeId, true, sig, "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)
@@ -51,9 +44,9 @@ 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")
+ tag, err = repo.References.Lookup("refs/tags/renamed")
checkFatal(t, err)
checkRefType(t, ref, ReferenceOid)
@@ -84,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, sig, "headOne")
+ _, err = repo.References.Create("refs/heads/one", commitId, true, "headOne")
checkFatal(t, err)
- _, err = repo.CreateReference("refs/heads/two", commitId, true, sig, "headTwo")
+ _, err = repo.References.Create("refs/heads/two", commitId, true, "headTwo")
checkFatal(t, err)
- _, err = repo.CreateReference("refs/heads/three", commitId, true, sig, "headThree")
+ _, err = repo.References.Create("refs/heads/three", commitId, true, "headThree")
checkFatal(t, err)
iter, err := repo.NewReferenceIterator()
@@ -143,7 +136,7 @@ func TestReferenceOwner(t *testing.T) {
commitId, _ := seedTestRepo(t, repo)
- ref, err := repo.CreateReference("refs/heads/foo", commitId, true, nil, "")
+ ref, err := repo.References.Create("refs/heads/foo", commitId, true, "")
checkFatal(t, err)
owner := ref.Owner()
@@ -162,10 +155,10 @@ func TestUtil(t *testing.T) {
commitId, _ := seedTestRepo(t, repo)
- ref, err := repo.CreateReference("refs/heads/foo", commitId, true, nil, "")
+ 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 {
@@ -176,13 +169,55 @@ 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")
}
}
+func TestIsNote(t *testing.T) {
+ repo := createTestRepo(t)
+ defer cleanupTestRepo(t, repo)
+
+ commitID, _ := seedTestRepo(t, repo)
+
+ sig := &Signature{
+ Name: "Rand Om Hacker",
+ Email: "[email protected]",
+ 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 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 {
diff --git a/remote.go b/remote.go
index 0635608..b2fb96f 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
+ FetchPruneUnspecified FetchPrune = C.GIT_FETCH_PRUNE_UNSPECIFIED
+ // 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.
+ 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
+
+ // 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
@@ -108,6 +153,9 @@ type HostkeyCertificate struct {
}
type PushOptions struct {
+ // Callbacks to use for this push operation
+ RemoteCallbacks RemoteCallbacks
+
PbParallelism uint
}
@@ -123,6 +171,12 @@ func newRemoteHeadFromC(ptr *C.git_remote_head) RemoteHead {
}
}
+func untrackCalbacksPayload(callbacks *C.git_remote_callbacks) {
+ if callbacks != nil && callbacks.payload != nil {
+ pointerHandles.Untrack(callbacks.payload)
+ }
+}
+
func populateRemoteCallbacks(ptr *C.git_remote_callbacks, callbacks *RemoteCallbacks) {
C.git_remote_init_callbacks(ptr, C.GIT_REMOTE_CALLBACKS_VERSION)
if callbacks == nil {
@@ -160,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)
}
@@ -267,41 +323,22 @@ 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)
-
- callbacks := C.git_remote_get_callbacks(r.ptr)
- if callbacks != nil && callbacks.payload != nil {
- pointerHandles.Untrack(callbacks.payload)
- }
-
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)
}
@@ -311,7 +348,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)
@@ -322,7 +359,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)
}
@@ -330,21 +367,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)
@@ -357,7 +394,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)
}
@@ -365,18 +402,16 @@ func (repo *Repository) CreateRemoteWithFetchspec(name string, url string, fetch
return remote, nil
}
-func (repo *Repository) CreateAnonymousRemote(url, fetch string) (*Remote, error) {
+func (c *RemoteCollection) CreateAnonymous(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, c.repo.ptr, curl)
if ret < 0 {
return nil, MakeGitError(ret)
}
@@ -384,7 +419,7 @@ func (repo *Repository) CreateAnonymousRemote(url, fetch 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)
@@ -393,7 +428,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)
}
@@ -401,22 +436,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)}
-}
-
func (o *Remote) Name() string {
return C.GoString(C.git_remote_name(o.ptr))
}
@@ -429,42 +448,48 @@ func (o *Remote) PushUrl() string {
return C.GoString(C.git_remote_pushurl(o.ptr))
}
-func (o *Remote) SetUrl(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)
+ 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(c.repo.ptr, cremote, curl)
if ret < 0 {
return MakeGitError(ret)
}
return nil
}
-func (o *Remote) SetPushUrl(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)
+ 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(c.repo.ptr, cremote, curl)
if ret < 0 {
return MakeGitError(ret)
}
return nil
}
-func (o *Remote) AddFetch(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)
+ 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(c.repo.ptr, cremote, crefspec)
if ret < 0 {
return MakeGitError(ret)
}
@@ -525,30 +550,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 (c *RemoteCollection) AddPush(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(c.repo.ptr, cremote, crefspec)
if ret < 0 {
return MakeGitError(ret)
}
@@ -570,53 +581,37 @@ 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 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)
}
-func (o *Remote) UpdateFetchHead() bool {
- return C.git_remote_update_fetchhead(o.ptr) > 0
+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; 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.git_signature_free(csig)
- }
-
+// 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 {
var cmsg *C.char = nil
if msg != "" {
cmsg = C.CString(msg)
@@ -628,29 +623,36 @@ func (o *Remote) Fetch(refspecs []string, sig *Signature, msg string) error {
crefspecs.strings = makeCStringsFromStrings(refspecs)
defer freeStrarray(&crefspecs)
+ var coptions C.git_fetch_options
+ populateFetchOptions(&coptions, opts);
+ defer untrackCalbacksPayload(&coptions.callbacks)
+
runtime.LockOSThread()
defer runtime.UnlockOSThread()
- ret := C.git_remote_fetch(o.ptr, &crefspecs, csig, 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
@@ -702,39 +704,20 @@ 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.git_signature_free(csig)
- }
-
- var cmsg *C.char
- if msg == "" {
- cmsg = nil
- } else {
- cmsg = C.CString(msg)
- defer C.free(unsafe.Pointer(cmsg))
- }
-
- var copts C.git_push_options
- C.git_push_init_options(&copts, C.GIT_PUSH_OPTIONS_VERSION)
- if opts != nil {
- copts.pb_parallelism = C.uint(opts.PbParallelism)
- }
-
+func (o *Remote) Push(refspecs []string, opts *PushOptions) error {
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()
- ret := C.git_remote_push(o.ptr, &crefspecs, &copts, csig, cmsg)
+ ret := C.git_remote_push(o.ptr, &crefspecs, &coptions)
if ret < 0 {
return MakeGitError(ret)
}
@@ -745,11 +728,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 25ee13d..73c637f 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -3,35 +3,13 @@ package git
import (
"fmt"
"testing"
- "time"
)
-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)
- _, err := repo.CreateRemote("test", "git://foo/bar")
+ _, err := repo.Remotes.Create("test", "git://foo/bar")
checkFatal(t, err)
@@ -39,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)
@@ -58,18 +36,18 @@ 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)
- 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{}, nil, "")
+ err = remote.Fetch([]string{}, &options, "")
checkFatal(t, err)
}
@@ -77,10 +55,10 @@ 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()
+ err = remote.ConnectFetch(nil)
checkFatal(t, err)
}
@@ -88,10 +66,10 @@ 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()
+ err = remote.ConnectFetch(nil)
checkFatal(t, err)
heads, err := remote.Ls()
@@ -106,10 +84,10 @@ 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()
+ err = remote.ConnectFetch(nil)
checkFatal(t, err)
heads, err := remote.Ls("master")
@@ -139,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() {
@@ -159,13 +137,7 @@ func TestRemotePrune(t *testing.T) {
checkFatal(t, err)
defer commit.Free()
- sig := &Signature{
- Name: "Rand Om Hacker",
- Email: "[email protected]",
- 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)
@@ -176,13 +148,13 @@ 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"}, sig, "")
+ err = remote.Fetch([]string{"test-prune"}, nil, "")
checkFatal(t, err)
- _, err = repo.CreateReference("refs/remotes/origin/test-prune", head, true, sig, "remote reference")
+ _, err = repo.References.Create("refs/remotes/origin/test-prune", head, true, "remote reference")
checkFatal(t, err)
err = remoteRef.Delete()
@@ -191,16 +163,16 @@ 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()
+ 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")
+ _, 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 996e966..62fde6d 100644
--- a/repository.go
+++ b/repository.go
@@ -12,76 +12,99 @@ 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
+ // 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
+ // 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 OpenRepository(path string) (*Repository, error) {
- repo := new(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
+ repo.Tags.repo = repo
+
+ runtime.SetFinalizer(repo, (*Repository).Free)
+ return repo
+}
+
+func OpenRepository(path string) (*Repository, error) {
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)
}
- runtime.SetFinalizer(repo, (*Repository).Free)
- 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)
}
- runtime.SetFinalizer(repo, (*Repository).Free)
- 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)
}
- runtime.SetFinalizer(repo, (*Repository).Free)
- 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)
}
- runtime.SetFinalizer(repo, (*Repository).Free)
- return repo, nil
+ return newRepositoryFromC(ptr), nil
}
func (v *Repository) SetRefdb(refdb *Refdb) {
@@ -176,22 +199,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
@@ -206,49 +213,25 @@ 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.git_signature_free(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.git_signature_free(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)
}
@@ -267,71 +250,6 @@ func (v *Repository) IsHeadDetached() (bool, error) {
return ret != 0, nil
}
-func (v *Repository) CreateReference(name string, id *Oid, force bool, sig *Signature, 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.git_signature_free(csig)
-
- 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), csig, cmsg)
- if ecode < 0 {
- return nil, MakeGitError(ecode)
- }
-
- return newReferenceFromC(ptr, v), nil
-}
-
-func (v *Repository) CreateSymbolicReference(name, target string, force bool, sig *Signature, 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.git_signature_free(csig)
-
- 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), csig, cmsg)
- if ecode < 0 {
- return nil, MakeGitError(ecode)
- }
-
- return newReferenceFromC(ptr, v), nil
-}
-
func (v *Repository) Walk() (*RevWalk, error) {
var walkPtr *C.git_revwalk
@@ -403,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)
@@ -513,169 +401,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,
- 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(&note.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) {
- var ptr *C.char
-
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
-
- if ret := C.git_note_default_ref(&ptr, v.ptr); ret < 0 {
- return "", MakeGitError(ret)
- }
-
- return C.GoString(ptr), nil
-}
-
type RepositoryState int
const (
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 <git2.h>
+*/
+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))
+ }
+}
diff --git a/revparse_test.go b/revparse_test.go
index 2ccdca2..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, nil, "")
+ ref, err := repo.References.Create("refs/heads/master", treeId, true, "")
checkFatal(t, err)
obj, ref, err := repo.RevparseExt("master")
diff --git a/script/build-libgit2-static.sh b/script/build-libgit2-static.sh
new file mode 100755
index 0000000..5723721
--- /dev/null
+++ b/script/build-libgit2-static.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+set -ex
+
+VENDORED_PATH=vendor/libgit2
+
+cd $VENDORED_PATH &&
+mkdir -p install/lib &&
+mkdir -p build &&
+cd build &&
+cmake -DTHREADSAFE=ON \
+ -DBUILD_CLAR=OFF \
+ -DBUILD_SHARED_LIBS=OFF \
+ -DCMAKE_C_FLAGS=-fPIC \
+ -DCMAKE_BUILD_TYPE="RelWithDebInfo" \
+ -DCMAKE_INSTALL_PREFIX=../install \
+ .. &&
+
+cmake --build .
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 (
diff --git a/script/install-libgit2.sh b/script/install-libgit2.sh
new file mode 100755
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}"
diff --git a/script/with-static.sh b/script/with-static.sh
new file mode 100755
index 0000000..3f60e31
--- /dev/null
+++ b/script/with-static.sh
@@ -0,0 +1,12 @@
+#!/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"
+
+$@
diff --git a/submodule.go b/submodule.go
index 3882462..4a32ce4 100644
--- a/submodule.go
+++ b/submodule.go
@@ -14,9 +14,8 @@ import (
// SubmoduleUpdateOptions
type SubmoduleUpdateOptions struct {
*CheckoutOpts
- *RemoteCallbacks
+ *FetchOptions
CloneCheckoutStrategy CheckoutStrategy
- Signature *Signature
}
// Submodule
@@ -27,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
@@ -37,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
@@ -71,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))
@@ -86,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)
}
@@ -107,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)
@@ -132,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)
}
@@ -161,23 +162,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 ?
- return &Repository{repo}
-}
-
func (sub *Submodule) Name() string {
n := C.git_submodule_name(sub.ptr)
return C.GoString(n)
@@ -193,14 +177,16 @@ func (sub *Submodule) Url() string {
return C.GoString(n)
}
-func (sub *Submodule) SetUrl(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)
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(c.repo.ptr, csubmodule, curl)
if ret < 0 {
return MakeGitError(ret)
}
@@ -236,9 +222,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 (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(c.repo.ptr, csubmodule, C.git_submodule_ignore_t(ignore))
+ if ret < 0 {
+ return MakeGitError(ret)
+ }
+
+ return nil
}
func (sub *Submodule) UpdateStrategy() SubmoduleUpdate {
@@ -246,20 +242,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 (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(c.repo.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 (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(sub.ptr, 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))
}
@@ -289,38 +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
-}
-
-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
+ return newRepositoryFromC(ptr), nil
}
func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error {
@@ -349,14 +335,8 @@ 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)
- sig, err := opts.Signature.toC()
- if err != nil {
- return err
- }
- ptr.signature = sig
-
return nil
}
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
})
diff --git a/tag.go b/tag.go
index 89ac8bd..ca85156 100644
--- a/tag.go
+++ b/tag.go
@@ -2,8 +2,14 @@ package git
/*
#include <git2.h>
+
+extern int _go_git_tag_foreach(git_repository *repo, void *payload);
*/
import "C"
+import (
+ "runtime"
+ "unsafe"
+)
// Tag
type Tag struct {
@@ -42,3 +48,164 @@ 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
+}
+
+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.
+//
+// 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/<name>"). 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..2fdfe00 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)
@@ -39,7 +180,21 @@ 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
+}
+
+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: "[email protected]",
+ When: time.Date(2013, 03, 06, 14, 30, 0, 0, loc),
+ }
+
+ tagId, err := repo.Tags.Create(name, commit, sig, message)
checkFatal(t, err)
return tagId
}
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)
+ }
+}
diff --git a/vendor/libgit2 b/vendor/libgit2
new file mode 160000
+Subproject fb84cde81e11947add4ff8bb9b4084f7d76e656
diff --git a/wrapper.c b/wrapper.c
index 3b88f93..2b1a180 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -64,7 +64,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);
}
int _go_git_diff_blobs(git_blob *old, const char *old_path, git_blob *new, const char *new_path, git_diff_options *opts, int eachFile, int eachHunk, int eachLine, void *payload)
@@ -85,7 +85,7 @@ int _go_git_diff_blobs(git_blob *old, const char *old_path, git_blob *new, const
lcb = (git_diff_line_cb)&diffForEachLineCb;
}
- return git_diff_blobs(old, old_path, new, new_path, opts, fcb, hcb, lcb, payload);
+ return git_diff_blobs(old, old_path, new, new_path, opts, fcb, NULL, hcb, lcb, payload);
}
void _go_git_setup_diff_notify_callbacks(git_diff_options *opts) {
@@ -136,4 +136,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 */