Repository: libgit2/git2go (go)

Issue:
Support git_remote_create_with_opts
Hello, first of all, thanks for such a great project!

It would be nice to support  https://libgit2.org/libgit2/#HEAD/group/remote/git_remote_create_with_opts , which is introduced in v0.28.0 . Thank you!




Pull request description:
Support git_remote_create_with_opts
Closes #645 

Patch:
diff --git a/remote.go b/remote.go
index 1887f79..08d8414 100644
--- a/remote.go
+++ b/remote.go
@@ -18,6 +18,23 @@ import (
 	"unsafe"
 )
 
+// RemoteCreateOptionsFlag is Remote creation options flags
+type RemoteCreateOptionsFlag uint
+
+const (
+	// Ignore the repository apply.insteadOf configuration
+	RemoteCreateSkipInsteadof RemoteCreateOptionsFlag = C.GIT_REMOTE_CREATE_SKIP_INSTEADOF
+	// Don't build a fetchspec from the name if none is set
+	RemoteCreateSkipDefaultFetchspec RemoteCreateOptionsFlag = C.GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC
+)
+
+// RemoteCreateOptions contains options for creating a remote
+type RemoteCreateOptions struct {
+	Name       string
+	FetchSpec  string
+	Flags      RemoteCreateOptionsFlag
+}
+
 type TransferProgress struct {
 	TotalObjects    uint
 	IndexedObjects  uint
@@ -539,6 +556,28 @@ func (c *RemoteCollection) Create(name string, url string) (*Remote, error) {
 	return remote, nil
 }
 
+//CreateWithOptions Creates a repository object with extended options.
+func (c *RemoteCollection) CreateWithOptions(url string, option *RemoteCreateOptions) (*Remote, error) {
+	remote := &Remote{repo: c.repo}
+
+	curl := C.CString(url)
+	defer C.free(unsafe.Pointer(curl))
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	copts := populateRemoteCreateOptions(&C.git_remote_create_options{}, option, c.repo)
+	defer freeRemoteCreateOptions(copts)
+	ret := C.git_remote_create_with_opts(&remote.ptr, curl, copts)
+	runtime.KeepAlive(c.repo)
+	if ret < 0 {
+		return nil, MakeGitError(ret)
+	}
+
+	runtime.SetFinalizer(remote, (*Remote).Free)
+	return remote, nil
+}
+
 func (c *RemoteCollection) Delete(name string) error {
 	cname := C.CString(name)
 	defer C.free(unsafe.Pointer(cname))
@@ -1027,3 +1066,45 @@ func (o *Remote) Prune(callbacks *RemoteCallbacks) error {
 	}
 	return nil
 }
+
+// DefaultApplyOptions returns default options for remote create
+func DefaultRemoteCreateOptions() (*RemoteCreateOptions, error) {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	opts := C.git_remote_create_options{}
+	ecode := C.git_remote_create_options_init(&opts, C.GIT_REMOTE_CREATE_OPTIONS_VERSION)
+	if ecode < 0 {
+		return nil, MakeGitError(ecode)
+	}
+
+	return &RemoteCreateOptions{
+		Flags: RemoteCreateOptionsFlag(opts.flags),
+	}, nil
+}
+
+func populateRemoteCreateOptions(copts *C.git_remote_create_options, opts *RemoteCreateOptions, repo *Repository) *C.git_remote_create_options {
+	C.git_remote_create_options_init(copts, C.GIT_REMOTE_CREATE_OPTIONS_VERSION)
+	if opts == nil {
+		return nil
+	}
+
+	var cRepository *C.git_repository
+	if repo != nil {
+		cRepository = repo.ptr
+	}
+	copts.repository = cRepository
+	copts.name = C.CString(opts.Name)
+	copts.fetchspec = C.CString(opts.FetchSpec)
+	copts.flags = C.uint(opts.Flags)
+
+	return copts
+}
+
+func freeRemoteCreateOptions(ptr *C.git_remote_create_options) {
+	if ptr == nil {
+		return
+	}
+	C.free(unsafe.Pointer(ptr.name))
+	C.free(unsafe.Pointer(ptr.fetchspec))
+}
diff --git a/repository.go b/repository.go
index d2b9961..bba5700 100644
--- a/repository.go
+++ b/repository.go
@@ -688,3 +688,39 @@ func (r *Repository) ClearGitIgnoreRules() error {
 	}
 	return nil
 }
+
+// Message retrieves git's prepared message.
+// Operations such as git revert/cherry-pick/merge with the -n option stop just
+// short of creating a commit with the changes and save their prepared message
+// in .git/MERGE_MSG so the next git-commit execution can present it to the
+// user for them to amend if they wish.
+//
+// Use this function to get the contents of this file. Don't forget to remove
+// the file after you create the commit.
+func (r *Repository) Message() (string, error) {
+	buf := C.git_buf{}
+	defer C.git_buf_dispose(&buf)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	cErr := C.git_repository_message(&buf, r.ptr)
+	runtime.KeepAlive(r)
+	if cErr < 0 {
+		return "", MakeGitError(cErr)
+	}
+	return C.GoString(buf.ptr), nil
+}
+
+// RemoveMessage removes git's prepared message.
+func (r *Repository) RemoveMessage() error {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	cErr := C.git_repository_message_remove(r.ptr)
+	runtime.KeepAlive(r)
+	if cErr < 0 {
+		return MakeGitError(cErr)
+	}
+	return  nil
+}


Test patch:
diff --git a/merge_test.go b/merge_test.go
index d49d07c..72b1a26 100644
--- a/merge_test.go
+++ b/merge_test.go
@@ -43,6 +43,14 @@ func TestMergeWithSelf(t *testing.T) {
 	mergeHeads[0] = mergeHead
 	err = repo.Merge(mergeHeads, nil, nil)
 	checkFatal(t, err)
+
+	mergeMessage, err := repo.Message()
+	checkFatal(t, err)
+
+	expectedMessage := "Merge branch 'master'\n"
+	if mergeMessage != expectedMessage {
+		t.Errorf("merge Message = %v, want %v", mergeMessage, expectedMessage)
+	}
 }
 
 func TestMergeAnalysisWithSelf(t *testing.T) {
diff --git a/remote_test.go b/remote_test.go
index 16ac06b..22fd292 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -80,6 +80,28 @@ func TestRemoteConnect(t *testing.T) {
 	checkFatal(t, err)
 }
 
+func TestRemoteConnectOption(t *testing.T) {
+	t.Parallel()
+	repo := createTestRepo(t)
+	defer cleanupTestRepo(t, repo)
+
+	config, err := repo.Config()
+	checkFatal(t, err)
+	err = config.SetString("url.git@github.com:.insteadof", "https://github.com/")
+	checkFatal(t, err)
+
+	option, err := DefaultRemoteCreateOptions()
+	checkFatal(t, err)
+	option.Name = "origin"
+	option.Flags = RemoteCreateSkipInsteadof
+
+	remote, err := repo.Remotes.CreateWithOptions("https://github.com/libgit2/TestGitRepository", option)
+	checkFatal(t, err)
+
+	err = remote.ConnectFetch(nil, nil, nil)
+	checkFatal(t, err)
+}
+
 func TestRemoteLs(t *testing.T) {
 	t.Parallel()
 	repo := createTestRepo(t)